Skip to content

Commit b3164ae

Browse files
diosneycjihrig
authored andcommitted
repl: add support for custom completions
Allow user code to override the default `complete()` function from `readline.Interface`. See: https://nodejs.org/api/readline.html#readline_use_of_the_completer_function Ref: nodejs/node-v0.x-archive#8484 PR-URL: #7527 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Lance Ball <[email protected]>
1 parent c967af8 commit b3164ae

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

‎doc/api/repl.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ added: v0.1.91
382382
`undefined`. Defaults to `false`.
383383
*`writer`{Function} The function to invoke to format the output of each
384384
command before writing to `output`. Defaults to [`util.inspect()`][].
385+
*`completer`{Function} An optional function used for custom Tab auto
386+
completion. See [`readline.InterfaceCompleter`][] for an example.
385387
*`replMode` - A flag that specifies whether the default evaluator executes
386388
all JavaScript commands in strict mode, default mode, or a hybrid mode
387389
("magic" mode.) Acceptable values are:
@@ -526,3 +528,4 @@ see: https://gist.github.com/2053342
526528
[`util.inspect()`]: util.html#util_util_inspect_object_options
527529
[here]: util.html#util_custom_inspect_function_on_objects
528530
[`readline.Interface`]: readline.html#readline_class_interface
531+
[`readline.InterfaceCompleter`]: readline.html#readline_use_of_the_completer_function

‎lib/repl.js‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -386,14 +386,15 @@ function REPLServer(prompt,
386386
self.bufferedCommand='';
387387
self.lines.level=[];
388388

389-
functioncomplete(text,callback){
390-
self.complete(text,callback);
391-
}
389+
// Figure out which "complete" function to use.
390+
self.completer=(typeofoptions.completer==='function')
391+
? options.completer
392+
: complete;
392393

393394
Interface.call(this,{
394395
input: self.inputStream,
395396
output: self.outputStream,
396-
completer: complete,
397+
completer: self.completer,
397398
terminal: options.terminal,
398399
historySize: options.historySize,
399400
prompt
@@ -706,6 +707,10 @@ function filteredOwnPropertyNames(obj){
706707
returnObject.getOwnPropertyNames(obj).filter(intFilter);
707708
}
708709

710+
REPLServer.prototype.complete=function(){
711+
this.completer.apply(this,arguments);
712+
};
713+
709714
// Provide a list of completions for the given leading text. This is
710715
// given to the readline interface for handling tab completion.
711716
//
@@ -716,7 +721,7 @@ function filteredOwnPropertyNames(obj){
716721
//
717722
// Warning: This eval's code like "foo.bar.baz", so it will run property
718723
// getter code.
719-
REPLServer.prototype.complete=function(line,callback){
724+
functioncomplete(line,callback){
720725
// There may be local variables to evaluate, try a nested REPL
721726
if(this.bufferedCommand!==undefined&&this.bufferedCommand.length){
722727
// Get a new array of inputed lines
@@ -975,7 +980,7 @@ REPLServer.prototype.complete = function(line, callback){
975980

976981
callback(null,[completions||[],completeOn]);
977982
}
978-
};
983+
}
979984

980985

981986
/**

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

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ testMe.complete('console.lo', common.mustCall(function(error, data){
3232
assert.deepStrictEqual(data,[['console.log'],'console.lo']);
3333
}));
3434

35-
// Tab Complete will return globaly scoped variables
35+
// Tab Complete will return globally scoped variables
3636
putIn.run(['};']);
3737
testMe.complete('inner.o',common.mustCall(function(error,data){
3838
assert.deepStrictEqual(data,works);
@@ -283,3 +283,68 @@ if (typeof Intl === 'object'){
283283
testNonGlobal.complete('I',common.mustCall((error,data)=>{
284284
assert.deepStrictEqual(data,builtins);
285285
}));
286+
287+
// To test custom completer function.
288+
// Sync mode.
289+
constcustomCompletions='aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' ');
290+
consttestCustomCompleterSyncMode=repl.start({
291+
prompt: '',
292+
input: putIn,
293+
output: putIn,
294+
completer: functioncompleterSyncMode(line){
295+
consthits=customCompletions.filter((c)=>{
296+
returnc.indexOf(line)===0;
297+
});
298+
// Show all completions if none found.
299+
return[hits.length ? hits : customCompletions,line];
300+
}
301+
});
302+
303+
// On empty line should output all the custom completions
304+
// without complete anything.
305+
testCustomCompleterSyncMode.complete('',common.mustCall((error,data)=>{
306+
assert.deepStrictEqual(data,[
307+
customCompletions,
308+
''
309+
]);
310+
}));
311+
312+
// On `a` should output `aaa aa1 aa2` and complete until `aa`.
313+
testCustomCompleterSyncMode.complete('a',common.mustCall((error,data)=>{
314+
assert.deepStrictEqual(data,[
315+
'aaa aa1 aa2'.split(' '),
316+
'a'
317+
]);
318+
}));
319+
320+
// To test custom completer function.
321+
// Async mode.
322+
consttestCustomCompleterAsyncMode=repl.start({
323+
prompt: '',
324+
input: putIn,
325+
output: putIn,
326+
completer: functioncompleterAsyncMode(line,callback){
327+
consthits=customCompletions.filter((c)=>{
328+
returnc.indexOf(line)===0;
329+
});
330+
// Show all completions if none found.
331+
callback(null,[hits.length ? hits : customCompletions,line]);
332+
}
333+
});
334+
335+
// On empty line should output all the custom completions
336+
// without complete anything.
337+
testCustomCompleterAsyncMode.complete('',common.mustCall((error,data)=>{
338+
assert.deepStrictEqual(data,[
339+
customCompletions,
340+
''
341+
]);
342+
}));
343+
344+
// On `a` should output `aaa aa1 aa2` and complete until `aa`.
345+
testCustomCompleterAsyncMode.complete('a',common.mustCall((error,data)=>{
346+
assert.deepStrictEqual(data,[
347+
'aaa aa1 aa2'.split(' '),
348+
'a'
349+
]);
350+
}));

0 commit comments

Comments
(0)