GeezerVison is showing github.com — repl: make autocomplete case-insensitive · nodejs/node@66d956c
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/repl.js +11-5Lines changed: 11 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -94,6 +94,7 @@ const { | 94 | 94 | StringPrototypeStartsWith, | 95 | 95 | StringPrototypeTrim, | 96 | 96 | StringPrototypeTrimLeft, | | 97 | + StringPrototypeToLocaleLowerCase, | 97 | 98 | Symbol, | 98 | 99 | SyntaxError, | 99 | 100 | SyntaxErrorPrototype, | @@ -1301,8 +1302,8 @@ function complete(line, callback) { | 1301 | 1302 | // Ignore right whitespace. It could change the outcome. | 1302 | 1303 | line = StringPrototypeTrimLeft(line); | 1303 | 1304 |
| 1304 | | - // REPL commands (e.g. ".break"). | 1305 | 1305 | let filter = ''; | | 1306 | + // REPL commands (e.g. ".break"). | 1306 | 1307 | if (RegExpPrototypeTest(/^\s*\.(\w*)$/, line)) { | 1307 | 1308 | ArrayPrototypePush(completionGroups, ObjectKeys(this.commands)); | 1308 | 1309 | completeOn = StringPrototypeMatch(line, /^\s*\.(\w*)$/)[1]; | @@ -1545,11 +1546,16 @@ function complete(line, callback) { | 1545 | 1546 | // Filter, sort (within each group), uniq and merge the completion groups. | 1546 | 1547 | if (completionGroups.length && filter) { | 1547 | 1548 | const newCompletionGroups = []; | | 1549 | + const lowerCaseFilter = StringPrototypeToLocaleLowerCase(filter); | 1548 | 1550 | 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 | + }); | 1553 | 1559 | if (filteredGroup.length) { | 1554 | 1560 | ArrayPrototypePush(newCompletionGroups, filteredGroup); | 1555 | 1561 | } | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl-history-navigation.js +74-51Lines changed: 74 additions & 51 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -74,6 +74,7 @@ const tests = [ | 74 | 74 | env: { NODE_REPL_HISTORY: defaultHistoryPath }, | 75 | 75 | test: [ 'let ab = 45', ENTER, | 76 | 76 | '555 + 909', ENTER, | | 77 | + 'let autocompleteMe = 123', ENTER, | 77 | 78 | '{key : {key2 :[] }}', ENTER, | 78 | 79 | 'Array(100).fill(1).map((e, i) => i ** i)', LEFT, LEFT, DELETE, | 79 | 80 | '2', ENTER], | @@ -82,7 +83,7 @@ const tests = [ | 82 | 83 | }, | 83 | 84 | { | 84 | 85 | 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], | 86 | 87 | expected: [prompt, | 87 | 88 | `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, | 88 | 89 | prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + | @@ -92,13 +93,15 @@ const tests = [ | 92 | 93 | ' 2025, 2116, 2209,...', | 93 | 94 | `${prompt}{key : {key2 :[] }}`, | 94 | 95 | prev && '\n// { key: { key2: [] } }', | | 96 | + `${prompt}let autocompleteMe = 123`, | 95 | 97 | `${prompt}555 + 909`, | 96 | 98 | prev && '\n// 1464', | 97 | 99 | `${prompt}let ab = 45`, | 98 | 100 | prompt, | 99 | 101 | `${prompt}let ab = 45`, | 100 | 102 | `${prompt}555 + 909`, | 101 | 103 | prev && '\n// 1464', | | 104 | + `${prompt}let autocompleteMe = 123`, | 102 | 105 | `${prompt}{key : {key2 :[] }}`, | 103 | 106 | prev && '\n// { key: { key2: [] } }', | 104 | 107 | `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, | @@ -128,7 +131,7 @@ const tests = [ | 128 | 131 | preview: false, | 129 | 132 | showEscapeCodes: true, | 130 | 133 | test: [ | 131 | | - '55', UP, UP, UP, UP, UP, UP, ENTER, | | 134 | + '55', UP, UP, UP, UP, UP, UP, UP, ENTER, | 132 | 135 | ], | 133 | 136 | expected: [ | 134 | 137 | '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', | @@ -185,10 +188,10 @@ const tests = [ | 185 | 188 | ENTER, | 186 | 189 | 'veryLongName'.repeat(30), | 187 | 190 | ENTER, | 188 | | - `${'\x1B[90m \x1B[39m'.repeat(235)} fun`, | | 191 | + `${'\x1B[90m \x1B[39m'.repeat(229)} aut`, | 189 | 192 | ESCAPE, | 190 | 193 | ENTER, | 191 | | - `${' '.repeat(236)} fun`, | | 194 | + `${' '.repeat(230)} aut`, | 192 | 195 | ESCAPE, | 193 | 196 | ENTER, | 194 | 197 | ], | @@ -236,19 +239,20 @@ const tests = [ | 236 | 239 | prompt, '\x1B[3G', | 237 | 240 | // 1. UP | 238 | 241 | // 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 | 241 | 244 | '\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', | 245 | 249 | '\x1B[0K', | 246 | 250 | // 2. UP | 247 | 251 | '\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', | 252 | 256 | // Preview cleanup | 253 | 257 | '\x1B[0K', | 254 | 258 | // 3. UP | @@ -326,8 +330,8 @@ const tests = [ | 326 | 330 | skip: !process.features.inspector, | 327 | 331 | checkTotal: true, | 328 | 332 | test: [ | 329 | | - 'fu', | 330 | | - 'n', | | 333 | + 'au', | | 334 | + 't', | 331 | 335 | RIGHT, | 332 | 336 | BACKSPACE, | 333 | 337 | LEFT, | @@ -353,74 +357,93 @@ const tests = [ | 353 | 357 | // K = Erase in line; 0 = right; 1 = left; 2 = total | 354 | 358 | expected: [ | 355 | 359 | // 0. | 356 | | - // 'f' | 357 | | - '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'f', | | 360 | + // 'a' | | 361 | + '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'a', | 358 | 362 | // '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 | 361 | 367 | '\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', | 363 | 371 | // 1. Right. Cleanup | 364 | 372 | '\x1B[0K', | 365 | | - 'ction', | | 373 | + 'ocompleteMe', | | 374 | + '\n// 123', '\x1B[17G', | | 375 | + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', | 366 | 376 | // 2. Backspace. Refresh | 367 | | - '\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G', | | 377 | + '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G', | 368 | 378 | // 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', | 370 | 382 | // 3. Left. Cleanup | 371 | 383 | '\x1B[0K', | 372 | | - '\x1B[1D', '\x1B[10G', ' // n', '\x1B[9G', | | 384 | + '\x1B[1D', '\x1B[16G', ' // e', '\x1B[15G', | 373 | 385 | // 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', | 376 | 388 | // 5. 'A' - Cleanup | 377 | | - '\x1B[10G', '\x1B[0K', '\x1B[8G', | | 389 | + '\x1B[16G', '\x1B[0K', '\x1B[14G', | 378 | 390 | // Refresh | 379 | | - '\x1B[1G', '\x1B[0J', `${prompt}functAio`, '\x1B[9G', | | 391 | + '\x1B[1G', '\x1B[0J', `${prompt}autocompletAeM`, '\x1B[15G', | 380 | 392 | // 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', | 384 | 397 | // 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', | 387 | 402 | // 8. Backspace. Refresh | 388 | | - '\x1B[1G', '\x1B[0J', `${prompt}functio`, '\x1B[10G', | | 403 | + '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G', | 389 | 404 | // 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', | 391 | 408 | // 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', | 393 | 410 | // 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', | 395 | 414 | // 11. ESCAPE | 396 | 415 | '\x1B[0K', | 397 | 416 | // 12. ENTER | 398 | 417 | '\r\n', | 399 | | - 'Uncaught ReferenceError: functio is not defined\n', | | 418 | + 'Uncaught ReferenceError: autocompleteM is not defined\n', | 400 | 419 | '\x1B[1G', '\x1B[0J', | 401 | 420 | // 13. UP | 402 | 421 | 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', | 406 | 426 | // 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', | 409 | 429 | // 15. ENTER | 410 | | - '\x1B[0K', '\x1B[9G', '\x1B[1C', | | 430 | + '\x1B[0K', '\x1B[15G', '\x1B[1C', | 411 | 431 | '\r\n', | 412 | | - 'Uncaught ReferenceError: functio is not defined\n', | | 432 | + 'Uncaught ReferenceError: autocompleteM is not defined\n', | 413 | 433 | '\x1B[1G', '\x1B[0J', | 414 | | - '> ', '\x1B[3G', | | 434 | + prompt, '\x1B[3G', | 415 | 435 | // 16. UP | 416 | 436 | '\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', | 420 | 442 | // 17. ENTER | 421 | | - 'n', '\r\n', | | 443 | + 'e', '\r\n', | | 444 | + '123\n', | 422 | 445 | '\x1B[1G', '\x1B[0J', | 423 | | - '... ', '\x1B[5G', | | 446 | + prompt, '\x1B[3G', | 424 | 447 | '\r\n', | 425 | 448 | ], | 426 | 449 | clean: true | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl-reverse-search.js +1-3Lines changed: 1 addition & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -212,9 +212,7 @@ const tests = [ | 212 | 212 | expected: [ | 213 | 213 | '\x1B[1G', '\x1B[0J', | 214 | 214 | 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', | 218 | 216 | '\x1B[3G', '\x1B[0J', | 219 | 217 | '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G', | 220 | 218 | '\x1B[3G', '\x1B[0J', | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-repl-tab-complete.js +52-5Lines changed: 52 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -205,6 +205,38 @@ testMe.complete('str.len', common.mustCall(function(error, data) { | 205 | 205 |
| 206 | 206 | putIn.run(['.clear']); | 207 | 207 |
| | 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 | + | 208 | 240 | // Tab completion should not break on spaces | 209 | 241 | const spaceTimeout = setTimeout(function() { | 210 | 242 | throw new Error('timeout'); | @@ -588,12 +620,27 @@ const testNonGlobal = repl.start({ | 588 | 620 | useGlobal: false | 589 | 621 | }); | 590 | 622 |
| 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 | +]; | 593 | 643 |
| 594 | | -if (common.hasIntl) { | 595 | | - builtins[0].push('Intl'); | 596 | | -} | 597 | 644 | testNonGlobal.complete('I', common.mustCall((error, data) => { | 598 | 645 | assert.deepStrictEqual(data, builtins); | 599 | 646 | })); | |
|