26
26
27
27
#include <string.h>
28
28
#include "py/obj.h"
29
+ #include "py/objmodule.h"
29
30
#include "py/runtime.h"
30
31
#include "py/builtin.h"
31
32
#include "py/repl.h"
@@ -143,6 +144,93 @@ bool mp_repl_continue_with_input(const char *input) {
143
144
return false;
144
145
}
145
146
147
+ STATIC bool test_qstr (mp_obj_t obj , qstr name ) {
148
+ if (obj ) {
149
+ // try object member
150
+ mp_obj_t dest [2 ];
151
+ mp_load_method_protected (obj , name , dest , true);
152
+ return dest [0 ] != MP_OBJ_NULL ;
153
+ } else {
154
+ // try builtin module
155
+ return mp_map_lookup ((mp_map_t * )& mp_builtin_module_map ,
156
+ MP_OBJ_NEW_QSTR (name ), MP_MAP_LOOKUP );
157
+ }
158
+ }
159
+
160
+ STATIC const char * find_completions (const char * s_start , size_t s_len ,
161
+ mp_obj_t obj , size_t * match_len , qstr * q_first , qstr * q_last ) {
162
+
163
+ const char * match_str = NULL ;
164
+ * match_len = 0 ;
165
+ * q_first = * q_last = 0 ;
166
+ size_t nqstr = QSTR_TOTAL ();
167
+ for (qstr q = MP_QSTR_ + 1 ; q < nqstr ; ++ q ) {
168
+ size_t d_len ;
169
+ const char * d_str = (const char * )qstr_data (q , & d_len );
170
+ if (s_len <= d_len && strncmp (s_start , d_str , s_len ) == 0 ) {
171
+ if (test_qstr (obj , q )) {
172
+ // special case; filter out words that begin with underscore
173
+ // unless there's already a partial match
174
+ if (s_len == 0 && d_str [0 ] == '_' ) {
175
+ continue ;
176
+ }
177
+ if (match_str == NULL ) {
178
+ match_str = d_str ;
179
+ * match_len = d_len ;
180
+ } else {
181
+ // search for longest common prefix of match_str and d_str
182
+ // (assumes these strings are null-terminated)
183
+ for (size_t j = s_len ; j <= * match_len && j <= d_len ; ++ j ) {
184
+ if (match_str [j ] != d_str [j ]) {
185
+ * match_len = j ;
186
+ break ;
187
+ }
188
+ }
189
+ }
190
+ if (* q_first == 0 ) {
191
+ * q_first = q ;
192
+ }
193
+ * q_last = q ;
194
+ }
195
+ }
196
+ }
197
+ return match_str ;
198
+ }
199
+
200
+ STATIC void print_completions (const mp_print_t * print ,
201
+ const char * s_start , size_t s_len ,
202
+ mp_obj_t obj , qstr q_first , qstr q_last ) {
203
+
204
+ #define WORD_SLOT_LEN (16)
205
+ #define MAX_LINE_LEN (4 * WORD_SLOT_LEN)
206
+
207
+ int line_len = MAX_LINE_LEN ; // force a newline for first word
208
+ for (qstr q = q_first ; q <= q_last ; ++ q ) {
209
+ size_t d_len ;
210
+ const char * d_str = (const char * )qstr_data (q , & d_len );
211
+ if (s_len <= d_len && strncmp (s_start , d_str , s_len ) == 0 ) {
212
+ if (test_qstr (obj , q )) {
213
+ int gap = (line_len + WORD_SLOT_LEN - 1 ) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len ;
214
+ if (gap < 2 ) {
215
+ gap += WORD_SLOT_LEN ;
216
+ }
217
+ if (line_len + gap + d_len <= MAX_LINE_LEN ) {
218
+ // TODO optimise printing of gap?
219
+ for (int j = 0 ; j < gap ; ++ j ) {
220
+ mp_print_str (print , " " );
221
+ }
222
+ mp_print_str (print , d_str );
223
+ line_len += gap + d_len ;
224
+ } else {
225
+ mp_printf (print , "\n%s" , d_str );
226
+ line_len = d_len ;
227
+ }
228
+ }
229
+ }
230
+ }
231
+ mp_print_str (print , "\n" );
232
+ }
233
+
146
234
size_t mp_repl_autocomplete (const char * str , size_t len , const mp_print_t * print , const char * * compl_str ) {
147
235
// scan backwards to find start of "a.b.c" chain
148
236
const char * org_str = str ;
@@ -155,137 +243,83 @@ size_t mp_repl_autocomplete(const char *str, size_t len, const mp_print_t *print
155
243
}
156
244
}
157
245
158
- size_t nqstr = QSTR_TOTAL ();
159
-
160
246
// begin search in outer global dict which is accessed from __main__
161
247
mp_obj_t obj = MP_OBJ_FROM_PTR (& mp_module___main__ );
162
248
mp_obj_t dest [2 ];
163
249
250
+ const char * s_start ;
251
+ size_t s_len ;
252
+
164
253
for (;;) {
165
254
// get next word in string to complete
166
- const char * s_start = str ;
255
+ s_start = str ;
167
256
while (str < top && * str != '.' ) {
168
257
++ str ;
169
258
}
170
- size_t s_len = str - s_start ;
171
-
172
- if (str < top ) {
173
- // a complete word, lookup in current object
174
- qstr q = qstr_find_strn (s_start , s_len );
175
- if (q == MP_QSTR_NULL ) {
176
- // lookup will fail
177
- return 0 ;
178
- }
179
- mp_load_method_protected (obj , q , dest , true);
180
- obj = dest [0 ]; // attribute, method, or MP_OBJ_NULL if nothing found
181
-
182
- if (obj == MP_OBJ_NULL ) {
183
- // lookup failed
184
- return 0 ;
185
- }
259
+ s_len = str - s_start ;
186
260
187
- // skip '.' to move to next word
188
- ++ str ;
189
-
190
- } else {
261
+ if (str == top ) {
191
262
// end of string, do completion on this partial name
263
+ break ;
264
+ }
192
265
193
- // look for matches
194
- const char * match_str = NULL ;
195
- size_t match_len = 0 ;
196
- qstr q_first = 0 , q_last = 0 ;
197
- for (qstr q = MP_QSTR_ + 1 ; q < nqstr ; ++ q ) {
198
- size_t d_len ;
199
- const char * d_str = (const char * )qstr_data (q , & d_len );
200
- if (s_len <= d_len && strncmp (s_start , d_str , s_len ) == 0 ) {
201
- mp_load_method_protected (obj , q , dest , true);
202
- if (dest [0 ] != MP_OBJ_NULL ) {
203
- // special case; filter out words that begin with underscore
204
- // unless there's already a partial match
205
- if (s_len == 0 && d_str [0 ] == '_' ) {
206
- continue ;
207
- }
208
- if (match_str == NULL ) {
209
- match_str = d_str ;
210
- match_len = d_len ;
211
- } else {
212
- // search for longest common prefix of match_str and d_str
213
- // (assumes these strings are null-terminated)
214
- for (size_t j = s_len ; j <= match_len && j <= d_len ; ++ j ) {
215
- if (match_str [j ] != d_str [j ]) {
216
- match_len = j ;
217
- break ;
218
- }
219
- }
220
- }
221
- if (q_first == 0 ) {
222
- q_first = q ;
223
- }
224
- q_last = q ;
225
- }
226
- }
227
- }
228
-
229
- // nothing found
230
- if (q_first == 0 ) {
231
- if (s_len == 0 ) {
232
- * compl_str = " " ;
233
- return 4 ;
234
- }
235
- // If there're no better alternatives, and if it's first word
236
- // in the line, try to complete "import".
237
- if (s_start == org_str ) {
238
- static const char import_str [] = "import " ;
239
- if (memcmp (s_start , import_str , s_len ) == 0 ) {
240
- * compl_str = import_str + s_len ;
241
- return sizeof (import_str ) - 1 - s_len ;
242
- }
243
- }
244
-
245
- return 0 ;
246
- }
266
+ // a complete word, lookup in current object
267
+ qstr q = qstr_find_strn (s_start , s_len );
268
+ if (q == MP_QSTR_NULL ) {
269
+ // lookup will fail
270
+ return 0 ;
271
+ }
272
+ mp_load_method_protected (obj , q , dest , true);
273
+ obj = dest [0 ]; // attribute, method, or MP_OBJ_NULL if nothing found
247
274
248
- // 1 match found, or multiple matches with a common prefix
249
- if (q_first == q_last || match_len > s_len ) {
250
- * compl_str = match_str + s_len ;
251
- return match_len - s_len ;
252
- }
275
+ if (obj == MP_OBJ_NULL ) {
276
+ // lookup failed
277
+ return 0 ;
278
+ }
253
279
254
- // multiple matches found, print them out
280
+ // skip '.' to move to next word
281
+ ++ str ;
282
+ }
255
283
256
- #define WORD_SLOT_LEN (16)
257
- #define MAX_LINE_LEN (4 * WORD_SLOT_LEN)
284
+ // look for matches
285
+ size_t match_len ;
286
+ qstr q_first , q_last ;
287
+ const char * match_str =
288
+ find_completions (s_start , s_len , obj , & match_len , & q_first , & q_last );
258
289
259
- int line_len = MAX_LINE_LEN ; // force a newline for first word
260
- for (qstr q = q_first ; q <= q_last ; ++ q ) {
261
- size_t d_len ;
262
- const char * d_str = (const char * )qstr_data (q , & d_len );
263
- if (s_len <= d_len && strncmp (s_start , d_str , s_len ) == 0 ) {
264
- mp_load_method_protected (obj , q , dest , true);
265
- if (dest [0 ] != MP_OBJ_NULL ) {
266
- int gap = (line_len + WORD_SLOT_LEN - 1 ) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len ;
267
- if (gap < 2 ) {
268
- gap += WORD_SLOT_LEN ;
269
- }
270
- if (line_len + gap + d_len <= MAX_LINE_LEN ) {
271
- // TODO optimise printing of gap?
272
- for (int j = 0 ; j < gap ; ++ j ) {
273
- mp_print_str (print , " " );
274
- }
275
- mp_print_str (print , d_str );
276
- line_len += gap + d_len ;
277
- } else {
278
- mp_printf (print , "\n%s" , d_str );
279
- line_len = d_len ;
280
- }
281
- }
282
- }
290
+ // nothing found
291
+ if (q_first == 0 ) {
292
+ // If there're no better alternatives, and if it's first word
293
+ // in the line, try to complete "import".
294
+ static const char import_str [] = "import " ;
295
+ if (s_start == org_str && s_len > 0 ) {
296
+ if (memcmp (s_start , import_str , s_len ) == 0 ) {
297
+ * compl_str = import_str + s_len ;
298
+ return sizeof (import_str ) - 1 - s_len ;
283
299
}
284
- mp_print_str (print , "\n" );
285
-
286
- return (size_t )(-1 ); // indicate many matches
300
+ }
301
+ // after "import", suggest built-in modules
302
+ if (len >= 7 && !memcmp (org_str , import_str , 7 )) {
303
+ obj = NULL ;
304
+ match_str = find_completions (
305
+ s_start , s_len , obj , & match_len , & q_first , & q_last );
306
+ }
307
+ if (q_first == 0 ) {
308
+ * compl_str = " " ;
309
+ return s_len ? 0 : 4 ;
287
310
}
288
311
}
312
+
313
+ // 1 match found, or multiple matches with a common prefix
314
+ if (q_first == q_last || match_len > s_len ) {
315
+ * compl_str = match_str + s_len ;
316
+ return match_len - s_len ;
317
+ }
318
+
319
+ // multiple matches found, print them out
320
+ print_completions (print , s_start , s_len , obj , q_first , q_last );
321
+
322
+ return (size_t )(-1 ); // indicate many matches
289
323
}
290
324
291
325
#endif // MICROPY_HELPER_REPL
0 commit comments