Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Petushkov <sergey.petushkov@protonmail.com>2022-06-12 14:25:22 +0300
committerGitHub <noreply@github.com>2022-06-12 14:25:22 +0300
commit156365ebfc4493592f223be208f28a9e7f3ef851 (patch)
treefe546aa68ef9af6dea9cdab4217751b7a51d5edf
parent167ea801e3800e7936d3ca6604d88cabcd4956e3 (diff)
repl: make autocomplete case-insensitive
This changes autocomplete suggestion filter to ignore input case allowing for more autosuggest results shown on the screen Fixes: https://github.com/nodejs/node/issues/41631 PR-URL: https://github.com/nodejs/node/pull/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>
-rw-r--r--lib/repl.js16
-rw-r--r--test/parallel/test-repl-history-navigation.js125
-rw-r--r--test/parallel/test-repl-reverse-search.js4
-rw-r--r--test/parallel/test-repl-tab-complete.js57
4 files changed, 138 insertions, 64 deletions
diff --git a/lib/repl.js b/lib/repl.js
index 7c17272f6e6..eeb8594eff4 100644
--- a/lib/repl.js
+++ b/lib/repl.js
@@ -94,6 +94,7 @@ const {
StringPrototypeStartsWith,
StringPrototypeTrim,
StringPrototypeTrimLeft,
+ StringPrototypeToLocaleLowerCase,
Symbol,
SyntaxError,
SyntaxErrorPrototype,
@@ -1301,8 +1302,8 @@ function complete(line, callback) {
// Ignore right whitespace. It could change the outcome.
line = StringPrototypeTrimLeft(line);
- // REPL commands (e.g. ".break").
let filter = '';
+ // REPL commands (e.g. ".break").
if (RegExpPrototypeTest(/^\s*\.(\w*)$/, line)) {
ArrayPrototypePush(completionGroups, ObjectKeys(this.commands));
completeOn = StringPrototypeMatch(line, /^\s*\.(\w*)$/)[1];
@@ -1545,11 +1546,16 @@ function complete(line, callback) {
// Filter, sort (within each group), uniq and merge the completion groups.
if (completionGroups.length && filter) {
const newCompletionGroups = [];
+ const lowerCaseFilter = StringPrototypeToLocaleLowerCase(filter);
ArrayPrototypeForEach(completionGroups, (group) => {
- const filteredGroup = ArrayPrototypeFilter(
- group,
- (str) => StringPrototypeStartsWith(str, filter)
- );
+ const filteredGroup = ArrayPrototypeFilter(group, (str) => {
+ // Filter is always case-insensitive following chromium autocomplete
+ // behavior.
+ return StringPrototypeStartsWith(
+ StringPrototypeToLocaleLowerCase(str),
+ lowerCaseFilter
+ );
+ });
if (filteredGroup.length) {
ArrayPrototypePush(newCompletionGroups, filteredGroup);
}
diff --git a/test/parallel/test-repl-history-navigation.js b/test/parallel/test-repl-history-navigation.js
index 29cb7816f0f..39ccc3732c3 100644
--- a/test/parallel/test-repl-history-navigation.js
+++ b/test/parallel/test-repl-history-navigation.js
@@ -74,6 +74,7 @@ const tests = [
env: { NODE_REPL_HISTORY: defaultHistoryPath },
test: [ 'let ab = 45', ENTER,
'555 + 909', ENTER,
+ 'let autocompleteMe = 123', ENTER,
'{key : {key2 :[] }}', ENTER,
'Array(100).fill(1).map((e, i) => i ** i)', LEFT, LEFT, DELETE,
'2', ENTER],
@@ -82,7 +83,7 @@ const tests = [
},
{
env: { NODE_REPL_HISTORY: defaultHistoryPath },
- test: [UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN],
+ test: [UP, UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN, DOWN],
expected: [prompt,
`${prompt}Array(100).fill(1).map((e, i) => i ** 2)`,
prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' +
@@ -92,6 +93,7 @@ const tests = [
' 2025, 2116, 2209,...',
`${prompt}{key : {key2 :[] }}`,
prev && '\n// { key: { key2: [] } }',
+ `${prompt}let autocompleteMe = 123`,
`${prompt}555 + 909`,
prev && '\n// 1464',
`${prompt}let ab = 45`,
@@ -99,6 +101,7 @@ const tests = [
`${prompt}let ab = 45`,
`${prompt}555 + 909`,
prev && '\n// 1464',
+ `${prompt}let autocompleteMe = 123`,
`${prompt}{key : {key2 :[] }}`,
prev && '\n// { key: { key2: [] } }',
`${prompt}Array(100).fill(1).map((e, i) => i ** 2)`,
@@ -128,7 +131,7 @@ const tests = [
preview: false,
showEscapeCodes: true,
test: [
- '55', UP, UP, UP, UP, UP, UP, ENTER,
+ '55', UP, UP, UP, UP, UP, UP, UP, ENTER,
],
expected: [
'\x1B[1G', '\x1B[0J', prompt, '\x1B[3G',
@@ -185,10 +188,10 @@ const tests = [
ENTER,
'veryLongName'.repeat(30),
ENTER,
- `${'\x1B[90m \x1B[39m'.repeat(235)} fun`,
+ `${'\x1B[90m \x1B[39m'.repeat(229)} aut`,
ESCAPE,
ENTER,
- `${' '.repeat(236)} fun`,
+ `${' '.repeat(230)} aut`,
ESCAPE,
ENTER,
],
@@ -236,19 +239,20 @@ const tests = [
prompt, '\x1B[3G',
// 1. UP
// This exceeds the maximum columns (250):
- // Whitespace + prompt + ' // '.length + 'function'.length
- // 236 + 2 + 4 + 8
+ // Whitespace + prompt + ' // '.length + 'autocompleteMe'.length
+ // 230 + 2 + 4 + 14
'\x1B[1G', '\x1B[0J',
- `${prompt}${' '.repeat(236)} fun`, '\x1B[243G',
- ' // ction', '\x1B[243G',
- ' // ction', '\x1B[243G',
+ `${prompt}${' '.repeat(230)} aut`, '\x1B[237G',
+ ' // ocompleteMe', '\x1B[237G',
+ '\n// 123', '\x1B[237G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
'\x1B[0K',
// 2. UP
'\x1B[1G', '\x1B[0J',
- `${prompt}${' '.repeat(235)} fun`, '\x1B[242G',
- // TODO(BridgeAR): Investigate why the preview is generated twice.
- ' // ction', '\x1B[242G',
- ' // ction', '\x1B[242G',
+ `${prompt}${' '.repeat(229)} aut`, '\x1B[236G',
+ ' // ocompleteMe', '\x1B[236G',
+ '\n// 123', '\x1B[236G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// Preview cleanup
'\x1B[0K',
// 3. UP
@@ -326,8 +330,8 @@ const tests = [
skip: !process.features.inspector,
checkTotal: true,
test: [
- 'fu',
- 'n',
+ 'au',
+ 't',
RIGHT,
BACKSPACE,
LEFT,
@@ -353,74 +357,93 @@ const tests = [
// K = Erase in line; 0 = right; 1 = left; 2 = total
expected: [
// 0.
- // 'f'
- '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'f',
+ // 'a'
+ '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'a',
// 'u'
- 'u', ' // nction', '\x1B[5G',
- // 'n' - Cleanup
+ 'u', ' // tocompleteMe', '\x1B[5G',
+ '\n// 123', '\x1B[5G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
+ // 't' - Cleanup
'\x1B[0K',
- 'n', ' // ction', '\x1B[6G',
+ 't', ' // ocompleteMe', '\x1B[6G',
+ '\n// 123', '\x1B[6G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 1. Right. Cleanup
'\x1B[0K',
- 'ction',
+ 'ocompleteMe',
+ '\n// 123', '\x1B[17G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 2. Backspace. Refresh
- '\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
+ '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G',
// Autocomplete and refresh?
- ' // n', '\x1B[10G', ' // n', '\x1B[10G',
+ ' // e', '\x1B[16G',
+ '\n// 123', '\x1B[16G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 3. Left. Cleanup
'\x1B[0K',
- '\x1B[1D', '\x1B[10G', ' // n', '\x1B[9G',
+ '\x1B[1D', '\x1B[16G', ' // e', '\x1B[15G',
// 4. Left. Cleanup
- '\x1B[10G', '\x1B[0K', '\x1B[9G',
- '\x1B[1D', '\x1B[10G', ' // n', '\x1B[8G',
+ '\x1B[16G', '\x1B[0K', '\x1B[15G',
+ '\x1B[1D', '\x1B[16G', ' // e', '\x1B[14G',
// 5. 'A' - Cleanup
- '\x1B[10G', '\x1B[0K', '\x1B[8G',
+ '\x1B[16G', '\x1B[0K', '\x1B[14G',
// Refresh
- '\x1B[1G', '\x1B[0J', `${prompt}functAio`, '\x1B[9G',
+ '\x1B[1G', '\x1B[0J', `${prompt}autocompletAeM`, '\x1B[15G',
// 6. Backspace. Refresh
- '\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[8G', '\x1B[10G', ' // n',
- '\x1B[8G', '\x1B[10G', ' // n',
- '\x1B[8G', '\x1B[10G',
+ '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`,
+ '\x1B[14G', '\x1B[16G', ' // e',
+ '\x1B[14G', '\x1B[16G', ' // e',
+ '\x1B[14G', '\x1B[16G',
// 7. Go to end. Cleanup
- '\x1B[0K', '\x1B[8G', '\x1B[2C',
- 'n',
+ '\x1B[0K', '\x1B[14G', '\x1B[2C',
+ 'e',
+ '\n// 123', '\x1B[17G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 8. Backspace. Refresh
- '\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G',
+ '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G',
// Autocomplete
- ' // n', '\x1B[10G', ' // n', '\x1B[10G',
+ ' // e', '\x1B[16G',
+ '\n// 123', '\x1B[16G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 9. Word left. Cleanup
- '\x1B[0K', '\x1B[7D', '\x1B[10G', ' // n', '\x1B[3G', '\x1B[10G',
+ '\x1B[0K', '\x1B[13D', '\x1B[16G', ' // e', '\x1B[3G', '\x1B[16G',
// 10. Word right. Cleanup
- '\x1B[0K', '\x1B[3G', '\x1B[7C', ' // n', '\x1B[10G',
+ '\x1B[0K', '\x1B[3G', '\x1B[13C', ' // e', '\x1B[16G',
+ '\n// 123', '\x1B[16G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 11. ESCAPE
'\x1B[0K',
// 12. ENTER
'\r\n',
- 'Uncaught ReferenceError: functio is not defined\n',
+ 'Uncaught ReferenceError: autocompleteM is not defined\n',
'\x1B[1G', '\x1B[0J',
// 13. UP
prompt, '\x1B[3G', '\x1B[1G', '\x1B[0J',
- `${prompt}functio`, '\x1B[10G',
- ' // n', '\x1B[10G',
- ' // n', '\x1B[10G',
+ `${prompt}autocompleteM`, '\x1B[16G',
+ ' // e', '\x1B[16G',
+ '\n// 123', '\x1B[16G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
// 14. LEFT
- '\x1B[0K', '\x1B[1D',
- '\x1B[10G', ' // n', '\x1B[9G', '\x1B[10G',
+ '\x1B[0K', '\x1B[1D', '\x1B[16G',
+ ' // e', '\x1B[15G', '\x1B[16G',
// 15. ENTER
- '\x1B[0K', '\x1B[9G', '\x1B[1C',
+ '\x1B[0K', '\x1B[15G', '\x1B[1C',
'\r\n',
- 'Uncaught ReferenceError: functio is not defined\n',
+ 'Uncaught ReferenceError: autocompleteM is not defined\n',
'\x1B[1G', '\x1B[0J',
- '> ', '\x1B[3G',
+ prompt, '\x1B[3G',
// 16. UP
'\x1B[1G', '\x1B[0J',
- '> functio', '\x1B[10G',
- ' // n', '\x1B[10G',
- ' // n', '\x1B[10G', '\x1B[0K',
+ `${prompt}autocompleteM`, '\x1B[16G',
+ ' // e', '\x1B[16G',
+ '\n// 123', '\x1B[16G',
+ '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A',
+ '\x1B[0K',
// 17. ENTER
- 'n', '\r\n',
+ 'e', '\r\n',
+ '123\n',
'\x1B[1G', '\x1B[0J',
- '... ', '\x1B[5G',
+ prompt, '\x1B[3G',
'\r\n',
],
clean: true
diff --git a/test/parallel/test-repl-reverse-search.js b/test/parallel/test-repl-reverse-search.js
index 5165dc2820d..2808c953431 100644
--- a/test/parallel/test-repl-reverse-search.js
+++ b/test/parallel/test-repl-reverse-search.js
@@ -212,9 +212,7 @@ const tests = [
expected: [
'\x1B[1G', '\x1B[0J',
prompt, '\x1B[3G',
- 'f', 'u', ' // nction',
- '\x1B[5G', '\x1B[0K',
- '\nbck-i-search: _', '\x1B[1A', '\x1B[5G',
+ 'f', 'u', '\nbck-i-search: _', '\x1B[1A', '\x1B[5G',
'\x1B[3G', '\x1B[0J',
'{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G',
'\x1B[3G', '\x1B[0J',
diff --git a/test/parallel/test-repl-tab-complete.js b/test/parallel/test-repl-tab-complete.js
index 5b60c88dc71..cc211d6da8a 100644
--- a/test/parallel/test-repl-tab-complete.js
+++ b/test/parallel/test-repl-tab-complete.js
@@ -205,6 +205,38 @@ testMe.complete('str.len', common.mustCall(function(error, data) {
putIn.run(['.clear']);
+// Tab completion should be case-insensitive if member part is lower-case
+putIn.run([
+ 'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
+]);
+testMe.complete(
+ 'foo.b',
+ common.mustCall(function(error, data) {
+ assert.deepStrictEqual(data, [
+ ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
+ 'foo.b',
+ ]);
+ })
+);
+
+putIn.run(['.clear']);
+
+// Tab completion should be case-insensitive if member part is upper-case
+putIn.run([
+ 'var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };',
+]);
+testMe.complete(
+ 'foo.B',
+ common.mustCall(function(error, data) {
+ assert.deepStrictEqual(data, [
+ ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'],
+ 'foo.B',
+ ]);
+ })
+);
+
+putIn.run(['.clear']);
+
// Tab completion should not break on spaces
const spaceTimeout = setTimeout(function() {
throw new Error('timeout');
@@ -588,12 +620,27 @@ const testNonGlobal = repl.start({
useGlobal: false
});
-const builtins = [['Infinity', 'Int16Array', 'Int32Array',
- 'Int8Array'], 'I'];
+const builtins = [
+ [
+ 'if',
+ 'import',
+ 'in',
+ 'instanceof',
+ '',
+ 'Infinity',
+ 'Int16Array',
+ 'Int32Array',
+ 'Int8Array',
+ ...(common.hasIntl ? ['Intl'] : []),
+ 'inspector',
+ 'isFinite',
+ 'isNaN',
+ '',
+ 'isPrototypeOf',
+ ],
+ 'I',
+];
-if (common.hasIntl) {
- builtins[0].push('Intl');
-}
testNonGlobal.complete('I', common.mustCall((error, data) => {
assert.deepStrictEqual(data, builtins);
}));