Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 66d956c

Browse filesBrowse files
gribnoysupdanielleadams
authored andcommitted
repl: make autocomplete case-insensitive
This changes autocomplete suggestion filter to ignore input case allowing for more autosuggest results shown on the screen Fixes: #41631 PR-URL: #41632 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 931ecfa commit 66d956c
Copy full SHA for 66d956c

File tree

Expand file treeCollapse file tree

4 files changed

+138
-64
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+138
-64
lines changed
Open diff view settings
Collapse file

‎lib/repl.js‎

Copy file name to clipboardExpand all lines: lib/repl.js
+11-5Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const {
9494
StringPrototypeStartsWith,
9595
StringPrototypeTrim,
9696
StringPrototypeTrimLeft,
97+
StringPrototypeToLocaleLowerCase,
9798
Symbol,
9899
SyntaxError,
99100
SyntaxErrorPrototype,
@@ -1301,8 +1302,8 @@ function complete(line, callback) {
13011302
// Ignore right whitespace. It could change the outcome.
13021303
line = StringPrototypeTrimLeft(line);
13031304

1304-
// REPL commands (e.g. ".break").
13051305
let filter = '';
1306+
// REPL commands (e.g. ".break").
13061307
if (RegExpPrototypeTest(/^\s*\.(\w*)$/, line)) {
13071308
ArrayPrototypePush(completionGroups, ObjectKeys(this.commands));
13081309
completeOn = StringPrototypeMatch(line, /^\s*\.(\w*)$/)[1];
@@ -1545,11 +1546,16 @@ function complete(line, callback) {
15451546
// Filter, sort (within each group), uniq and merge the completion groups.
15461547
if (completionGroups.length && filter) {
15471548
const newCompletionGroups = [];
1549+
const lowerCaseFilter = StringPrototypeToLocaleLowerCase(filter);
15481550
ArrayPrototypeForEach(completionGroups, (group) => {
1549-
const filteredGroup = ArrayPrototypeFilter(
1550-
group,
1551-
(str) => StringPrototypeStartsWith(str, filter)
1552-
);
1551+
const filteredGroup = ArrayPrototypeFilter(group, (str) => {
1552+
// Filter is always case-insensitive following chromium autocomplete
1553+
// behavior.
1554+
return StringPrototypeStartsWith(
1555+
StringPrototypeToLocaleLowerCase(str),
1556+
lowerCaseFilter
1557+
);
1558+
});
15531559
if (filteredGroup.length) {
15541560
ArrayPrototypePush(newCompletionGroups, filteredGroup);
15551561
}
Collapse file

‎test/parallel/test-repl-history-navigation.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-repl-history-navigation.js
+74-51Lines changed: 74 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const tests = [
7474
env: { NODE_REPL_HISTORY: defaultHistoryPath },
7575
test: [ 'let ab = 45', ENTER,
7676
'555 + 909', ENTER,
77+
'let autocompleteMe = 123', ENTER,
7778
'{key : {key2 :[] }}', ENTER,
7879
'Array(100).fill(1).map((e, i) => i ** i)', LEFT, LEFT, DELETE,
7980
'2', ENTER],
@@ -82,7 +83,7 @@ const tests = [
8283
},
8384
{
8485
env: { NODE_REPL_HISTORY: defaultHistoryPath },
85-
test: [UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN],
86+
test: [UP, UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN, DOWN],
8687
expected: [prompt,
8788
`${prompt}Array(100).fill(1).map((e, i) => i ** 2)`,
8889
prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' +
@@ -92,13 +93,15 @@ const tests = [
9293
' 2025, 2116, 2209,...',
9394
`${prompt}{key : {key2 :[] }}`,
9495
prev && '\n// { key: { key2: [] } }',
96+
`${prompt}let autocompleteMe = 123`,
9597
`${prompt}555 + 909`,
9698
prev && '\n// 1464',
9799
`${prompt}let ab = 45`,
98100
prompt,
99101
`${prompt}let ab = 45`,
100102
`${prompt}555 + 909`,
101103
prev && '\n// 1464',
104+
`${prompt}let autocompleteMe = 123`,
102105
`${prompt}{key : {key2 :[] }}`,
103106
prev && '\n// { key: { key2: [] } }',
104107
`${prompt}Array(100).fill(1).map((e, i) => i ** 2)`,
@@ -128,7 +131,7 @@ const tests = [
128131
preview: false,
129132
showEscapeCodes: true,
130133
test: [
131-
'55', UP, UP, UP, UP, UP, UP, ENTER,
134+
'55', UP, UP, UP, UP, UP, UP, UP, ENTER,
132135
],
133136
expected: [
134137
'\x1B[1G', '\x1B[0J', prompt, '\x1B[3G',
@@ -185,10 +188,10 @@ const tests = [
185188
ENTER,
186189
'veryLongName'.repeat(30),
187190
ENTER,
188-
`${'\x1B[90m \x1B[39m'.repeat(235)} fun`,
191+
`${'\x1B[90m \x1B[39m'.repeat(229)} aut`,
189192
ESCAPE,
190193
ENTER,
191-
`${' '.repeat(236)} fun`,
194+
`${' '.repeat(230)} aut`,
192195
ESCAPE,
193196
ENTER,
194197
],
@@ -236,19 +239,20 @@ const tests = [
236239
prompt, '\x1B[3G',
237240
// 1. UP
238241
// This exceeds the maximum columns (250):
239-
// Whitespace + prompt + ' // '.length + 'function'.length
240-
// 236 + 2 + 4 + 8
242+
// Whitespace + prompt + ' // '.length + 'autocompleteMe'.length
243+
// 230 + 2 + 4 + 14
241244
'\x1B[1G', '\x1B[0J',
242-
`${prompt}${' '.repeat(236)} fun`, '\x1B[243G',
243-
' // ction', '\x1B[243G',
244-
' // ction', '\x1B[243G',
245+
`${prompt}${' '.repeat(230)} aut`, '\x1B[237G',
246+
' // ocompleteMe', '\x1B[237G',
247+
'\n// 123', '\x1B[237G',
248+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
245249
'\x1B[0K',
246250
// 2. UP
247251
'\x1B[1G', '\x1B[0J',
248-
`${prompt}${' '.repeat(235)} fun`, '\x1B[242G',
249-
// TODO(BridgeAR): Investigate why the preview is generated twice.
250-
' // ction', '\x1B[242G',
251-
' // ction', '\x1B[242G',
252+
`${prompt}${' '.repeat(229)} aut`, '\x1B[236G',
253+
' // ocompleteMe', '\x1B[236G',
254+
'\n// 123', '\x1B[236G',
255+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
252256
// Preview cleanup
253257
'\x1B[0K',
254258
// 3. UP
@@ -326,8 +330,8 @@ const tests = [
326330
skip: !process.features.inspector,
327331
checkTotal: true,
328332
test: [
329-
'fu',
330-
'n',
333+
'au',
334+
't',
331335
RIGHT,
332336
BACKSPACE,
333337
LEFT,
@@ -353,74 +357,93 @@ const tests = [
353357
// K = Erase in line; 0 = right; 1 = left; 2 = total
354358
expected: [
355359
// 0.
356-
// 'f'
357-
'\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'f',
360+
// 'a'
361+
'\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'a',
358362
// 'u'
359-
'u', ' // nction', '\x1B[5G',
360-
// 'n' - Cleanup
363+
'u', ' // tocompleteMe', '\x1B[5G',
364+
'\n// 123', '\x1B[5G',
365+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
366+
// 't' - Cleanup
361367
'\x1B[0K',
362-
'n', ' // ction', '\x1B[6G',
368+
't', ' // ocompleteMe', '\x1B[6G',
369+
'\n// 123', '\x1B[6G',
370+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
363371
// 1. Right. Cleanup
364372
'\x1B[0K',
365-
'ction',
373+
'ocompleteMe',
374+
'\n// 123', '\x1B[17G',
375+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
366376
// 2. Backspace. Refresh
367-
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
377+
'\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G',
368378
// Autocomplete and refresh?
369-
' // n', '\x1B[10G', ' // n', '\x1B[10G',
379+
' // e', '\x1B[16G',
380+
'\n// 123', '\x1B[16G',
381+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
370382
// 3. Left. Cleanup
371383
'\x1B[0K',
372-
'\x1B[1D', '\x1B[10G', ' // n', '\x1B[9G',
384+
'\x1B[1D', '\x1B[16G', ' // e', '\x1B[15G',
373385
// 4. Left. Cleanup
374-
'\x1B[10G', '\x1B[0K', '\x1B[9G',
375-
'\x1B[1D', '\x1B[10G', ' // n', '\x1B[8G',
386+
'\x1B[16G', '\x1B[0K', '\x1B[15G',
387+
'\x1B[1D', '\x1B[16G', ' // e', '\x1B[14G',
376388
// 5. 'A' - Cleanup
377-
'\x1B[10G', '\x1B[0K', '\x1B[8G',
389+
'\x1B[16G', '\x1B[0K', '\x1B[14G',
378390
// Refresh
379-
'\x1B[1G', '\x1B[0J', `${prompt}functAio`, '\x1B[9G',
391+
'\x1B[1G', '\x1B[0J', `${prompt}autocompletAeM`, '\x1B[15G',
380392
// 6. Backspace. Refresh
381-
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[8G', '\x1B[10G', ' // n',
382-
'\x1B[8G', '\x1B[10G', ' // n',
383-
'\x1B[8G', '\x1B[10G',
393+
'\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`,
394+
'\x1B[14G', '\x1B[16G', ' // e',
395+
'\x1B[14G', '\x1B[16G', ' // e',
396+
'\x1B[14G', '\x1B[16G',
384397
// 7. Go to end. Cleanup
385-
'\x1B[0K', '\x1B[8G', '\x1B[2C',
386-
'n',
398+
'\x1B[0K', '\x1B[14G', '\x1B[2C',
399+
'e',
400+
'\n// 123', '\x1B[17G',
401+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
387402
// 8. Backspace. Refresh
388-
'\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
403+
'\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G',
389404
// Autocomplete
390-
' // n', '\x1B[10G', ' // n', '\x1B[10G',
405+
' // e', '\x1B[16G',
406+
'\n// 123', '\x1B[16G',
407+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
391408
// 9. Word left. Cleanup
392-
'\x1B[0K', '\x1B[7D', '\x1B[10G', ' // n', '\x1B[3G', '\x1B[10G',
409+
'\x1B[0K', '\x1B[13D', '\x1B[16G', ' // e', '\x1B[3G', '\x1B[16G',
393410
// 10. Word right. Cleanup
394-
'\x1B[0K', '\x1B[3G', '\x1B[7C', ' // n', '\x1B[10G',
411+
'\x1B[0K', '\x1B[3G', '\x1B[13C', ' // e', '\x1B[16G',
412+
'\n// 123', '\x1B[16G',
413+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
395414
// 11. ESCAPE
396415
'\x1B[0K',
397416
// 12. ENTER
398417
'\r\n',
399-
'Uncaught ReferenceError: functio is not defined\n',
418+
'Uncaught ReferenceError: autocompleteM is not defined\n',
400419
'\x1B[1G', '\x1B[0J',
401420
// 13. UP
402421
prompt, '\x1B[3G', '\x1B[1G', '\x1B[0J',
403-
`${prompt}functio`, '\x1B[10G',
404-
' // n', '\x1B[10G',
405-
' // n', '\x1B[10G',
422+
`${prompt}autocompleteM`, '\x1B[16G',
423+
' // e', '\x1B[16G',
424+
'\n// 123', '\x1B[16G',
425+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
406426
// 14. LEFT
407-
'\x1B[0K', '\x1B[1D',
408-
'\x1B[10G', ' // n', '\x1B[9G', '\x1B[10G',
427+
'\x1B[0K', '\x1B[1D', '\x1B[16G',
428+
' // e', '\x1B[15G', '\x1B[16G',
409429
// 15. ENTER
410-
'\x1B[0K', '\x1B[9G', '\x1B[1C',
430+
'\x1B[0K', '\x1B[15G', '\x1B[1C',
411431
'\r\n',
412-
'Uncaught ReferenceError: functio is not defined\n',
432+
'Uncaught ReferenceError: autocompleteM is not defined\n',
413433
'\x1B[1G', '\x1B[0J',
414-
'> ', '\x1B[3G',
434+
prompt, '\x1B[3G',
415435
// 16. UP
416436
'\x1B[1G', '\x1B[0J',
417-
'> functio', '\x1B[10G',
418-
' // n', '\x1B[10G',
419-
' // n', '\x1B[10G', '\x1B[0K',
437+
`${prompt}autocompleteM`, '\x1B[16G',
438+
' // e', '\x1B[16G',
439+
'\n// 123', '\x1B[16G',
440+
'\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
441+
'\x1B[0K',
420442
// 17. ENTER
421-
'n', '\r\n',
443+
'e', '\r\n',
444+
'123\n',
422445
'\x1B[1G', '\x1B[0J',
423-
'... ', '\x1B[5G',
446+
prompt, '\x1B[3G',
424447
'\r\n',
425448
],
426449
clean: true
Collapse file

‎test/parallel/test-repl-reverse-search.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-repl-reverse-search.js
+1-3Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,7 @@ const tests = [
212212
expected: [
213213
'\x1B[1G', '\x1B[0J',
214214
prompt, '\x1B[3G',
215-
'f', 'u', ' // nction',
216-
'\x1B[5G', '\x1B[0K',
217-
'\nbck-i-search: _', '\x1B[1A', '\x1B[5G',
215+
'f', 'u', '\nbck-i-search: _', '\x1B[1A', '\x1B[5G',
218216
'\x1B[3G', '\x1B[0J',
219217
'{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G',
220218
'\x1B[3G', '\x1B[0J',
Collapse file

‎test/parallel/test-repl-tab-complete.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-repl-tab-complete.js
+52-5Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,38 @@ testMe.complete('str.len', common.mustCall(function(error, data) {
205205

206206
putIn.run(['.clear']);
207207

208+
// Tab completion should be case-insensitive if member part is lower-case
209+
putIn.run([
210+
'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
211+
]);
212+
testMe.complete(
213+
'foo.b',
214+
common.mustCall(function(error, data) {
215+
assert.deepStrictEqual(data, [
216+
['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
217+
'foo.b',
218+
]);
219+
})
220+
);
221+
222+
putIn.run(['.clear']);
223+
224+
// Tab completion should be case-insensitive if member part is upper-case
225+
putIn.run([
226+
'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
227+
]);
228+
testMe.complete(
229+
'foo.B',
230+
common.mustCall(function(error, data) {
231+
assert.deepStrictEqual(data, [
232+
['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
233+
'foo.B',
234+
]);
235+
})
236+
);
237+
238+
putIn.run(['.clear']);
239+
208240
// Tab completion should not break on spaces
209241
const spaceTimeout = setTimeout(function() {
210242
throw new Error('timeout');
@@ -588,12 +620,27 @@ const testNonGlobal = repl.start({
588620
useGlobal: false
589621
});
590622

591-
const builtins = [['Infinity', 'Int16Array', 'Int32Array',
592-
'Int8Array'], 'I'];
623+
const builtins = [
624+
[
625+
'if',
626+
'import',
627+
'in',
628+
'instanceof',
629+
'',
630+
'Infinity',
631+
'Int16Array',
632+
'Int32Array',
633+
'Int8Array',
634+
...(common.hasIntl ? ['Intl'] : []),
635+
'inspector',
636+
'isFinite',
637+
'isNaN',
638+
'',
639+
'isPrototypeOf',
640+
],
641+
'I',
642+
];
593643

594-
if (common.hasIntl) {
595-
builtins[0].push('Intl');
596-
}
597644
testNonGlobal.complete('I', common.mustCall((error, data) => {
598645
assert.deepStrictEqual(data, builtins);
599646
}));

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.