Skip to content

Commit 7556e01

Browse files
tyomitchdpgeorge
authored andcommitted
py/repl: Refactor autocomplete, extracting reusable parts.
Originally at adafruit#4548 Signed-off-by: Artyom Skrobov <[email protected]>
1 parent f85ea8d commit 7556e01

File tree

1 file changed

+86
-66
lines changed

1 file changed

+86
-66
lines changed

py/repl.c

Lines changed: 86 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,87 @@ bool mp_repl_continue_with_input(const char *input) {
143143
return false;
144144
}
145145

146+
STATIC bool test_qstr(mp_obj_t obj, qstr name) {
147+
// try object member
148+
mp_obj_t dest[2];
149+
mp_load_method_protected(obj, name, dest, true);
150+
return dest[0] != MP_OBJ_NULL;
151+
}
152+
153+
STATIC const char *find_completions(const char *s_start, size_t s_len,
154+
mp_obj_t obj, size_t *match_len, qstr *q_first, qstr *q_last) {
155+
156+
const char *match_str = NULL;
157+
*match_len = 0;
158+
*q_first = *q_last = 0;
159+
size_t nqstr = QSTR_TOTAL();
160+
for (qstr q = MP_QSTR_ + 1; q < nqstr; ++q) {
161+
size_t d_len;
162+
const char *d_str = (const char *)qstr_data(q, &d_len);
163+
// special case; filter out words that begin with underscore
164+
// unless there's already a partial match
165+
if (s_len == 0 && d_str[0] == '_') {
166+
continue;
167+
}
168+
if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
169+
if (test_qstr(obj, q)) {
170+
if (match_str == NULL) {
171+
match_str = d_str;
172+
*match_len = d_len;
173+
} else {
174+
// search for longest common prefix of match_str and d_str
175+
// (assumes these strings are null-terminated)
176+
for (size_t j = s_len; j <= *match_len && j <= d_len; ++j) {
177+
if (match_str[j] != d_str[j]) {
178+
*match_len = j;
179+
break;
180+
}
181+
}
182+
}
183+
if (*q_first == 0) {
184+
*q_first = q;
185+
}
186+
*q_last = q;
187+
}
188+
}
189+
}
190+
return match_str;
191+
}
192+
193+
STATIC void print_completions(const mp_print_t *print,
194+
const char *s_start, size_t s_len,
195+
mp_obj_t obj, qstr q_first, qstr q_last) {
196+
197+
#define WORD_SLOT_LEN (16)
198+
#define MAX_LINE_LEN (4 * WORD_SLOT_LEN)
199+
200+
int line_len = MAX_LINE_LEN; // force a newline for first word
201+
for (qstr q = q_first; q <= q_last; ++q) {
202+
size_t d_len;
203+
const char *d_str = (const char *)qstr_data(q, &d_len);
204+
if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
205+
if (test_qstr(obj, q)) {
206+
int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len;
207+
if (gap < 2) {
208+
gap += WORD_SLOT_LEN;
209+
}
210+
if (line_len + gap + d_len <= MAX_LINE_LEN) {
211+
// TODO optimise printing of gap?
212+
for (int j = 0; j < gap; ++j) {
213+
mp_print_str(print, " ");
214+
}
215+
mp_print_str(print, d_str);
216+
line_len += gap + d_len;
217+
} else {
218+
mp_printf(print, "\n%s", d_str);
219+
line_len = d_len;
220+
}
221+
}
222+
}
223+
}
224+
mp_print_str(print, "\n");
225+
}
226+
146227
size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print, const char **compl_str) {
147228
// scan backwards to find start of "a.b.c" chain
148229
const char *org_str = str;
@@ -155,8 +236,6 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
155236
}
156237
}
157238

158-
size_t nqstr = QSTR_TOTAL();
159-
160239
// begin search in outer global dict which is accessed from __main__
161240
mp_obj_t obj = MP_OBJ_FROM_PTR(&mp_module___main__);
162241
mp_obj_t dest[2];
@@ -196,40 +275,10 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
196275
}
197276

198277
// look for matches
199-
const char *match_str = NULL;
200-
size_t match_len = 0;
201-
qstr q_first = 0, q_last = 0;
202-
for (qstr q = MP_QSTR_ + 1; q < nqstr; ++q) {
203-
size_t d_len;
204-
const char *d_str = (const char *)qstr_data(q, &d_len);
205-
// special case; filter out words that begin with underscore
206-
// unless there's already a partial match
207-
if (s_len == 0 && d_str[0] == '_') {
208-
continue;
209-
}
210-
if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
211-
mp_load_method_protected(obj, q, dest, true);
212-
if (dest[0] != MP_OBJ_NULL) {
213-
if (match_str == NULL) {
214-
match_str = d_str;
215-
match_len = d_len;
216-
} else {
217-
// search for longest common prefix of match_str and d_str
218-
// (assumes these strings are null-terminated)
219-
for (size_t j = s_len; j <= match_len && j <= d_len; ++j) {
220-
if (match_str[j] != d_str[j]) {
221-
match_len = j;
222-
break;
223-
}
224-
}
225-
}
226-
if (q_first == 0) {
227-
q_first = q;
228-
}
229-
q_last = q;
230-
}
231-
}
232-
}
278+
size_t match_len;
279+
qstr q_first, q_last;
280+
const char *match_str =
281+
find_completions(s_start, s_len, obj, &match_len, &q_first, &q_last);
233282

234283
// nothing found
235284
if (q_first == 0) {
@@ -257,36 +306,7 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
257306
}
258307

259308
// multiple matches found, print them out
260-
261-
#define WORD_SLOT_LEN (16)
262-
#define MAX_LINE_LEN (4 * WORD_SLOT_LEN)
263-
264-
int line_len = MAX_LINE_LEN; // force a newline for first word
265-
for (qstr q = q_first; q <= q_last; ++q) {
266-
size_t d_len;
267-
const char *d_str = (const char *)qstr_data(q, &d_len);
268-
if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) {
269-
mp_load_method_protected(obj, q, dest, true);
270-
if (dest[0] != MP_OBJ_NULL) {
271-
int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len;
272-
if (gap < 2) {
273-
gap += WORD_SLOT_LEN;
274-
}
275-
if (line_len + gap + d_len <= MAX_LINE_LEN) {
276-
// TODO optimise printing of gap?
277-
for (int j = 0; j < gap; ++j) {
278-
mp_print_str(print, " ");
279-
}
280-
mp_print_str(print, d_str);
281-
line_len += gap + d_len;
282-
} else {
283-
mp_printf(print, "\n%s", d_str);
284-
line_len = d_len;
285-
}
286-
}
287-
}
288-
}
289-
mp_print_str(print, "\n");
309+
print_completions(print, s_start, s_len, obj, q_first, q_last);
290310

291311
return (size_t)(-1); // indicate many matches
292312
}

0 commit comments

Comments
 (0)