Argument completion for commands in The Console
Argument completion should improve developer’s everyday life. Everywhere. That’s why I decided to have argument completion in The Console. To have the best thing - I want scriptable argument completion with already built-in filesystem paths completion.
Giving a few words about the process between hitting a TAB key and performing the magic.
Completion algorithm
Argument completion analyzes everything before caret position and after first token which is supposed to be a command name.
Let’s take a look at example of path completion:
- given
cd c:/program f[caret_here]z
- it will ignore last character (the letter
z
) andcd
which is a command name, that givesc:/program f
- tokenize selected part by spaces or taking quotes into account (e.g. “this string is a full token” because it’s contained in quotes
"
) - first take last token and pass it to completion algorithms, in this case it will be just f
- the token (
f
) cannot be completed, take one more token (last two tokens) and try auto-complete again, in this case it will takec:/program f
- the last string will be probably completed to
C:\Program Files\
Now, what does it mean to pass an argument to completion algorithms:
- ask custom (scripted) command completion (module or script)
- ask built-in absolute path completion
- all results are gathered and then:
- if there are multiple results then display dynamic list to choose between options
- if there is only one option then apply it, by replacing the part with suggestion
- if there is no completion suggestion then continue with no action
Scripting: custom completion
Now, let’s apply it to modules and scripts in The Console.
Modules
Modules can implement multiple commands. Some of commands could auto-complete in different ways.
In other situation every single command in module could run argument completion the same way, e.g. filesystem
module commands are always about file/folder path completion.
Let’s take a look at this module definition:
var currentDir = 'C:/'
module.exports = {
onload: function() { },
onunload: function() { },
commands: {
cd: function(args) {
/* TODO change directory */
},
ls: function(args) {
/* TODO list current directory contents */
}
}
}
We’ve got two commands here: cd and ls . Both receive path arguments.
module.exports = {
commands: {
cd: function(args) { /* ... */ },
ls: function(args) { /* ... */ }
},
// this argument completer will work for all commands of this module
completeArgument: function(arg) {
// simply use built-in algorithm to complete paths
return Utils.completePath(arg)
}
}
This way, all argument for commands in this module are auto-completed using a single function.
Another way to provide argument completion is to annotate command function with _completeArgument
property. First implement path completer and a simple helper to wrap command functions which will improve readability:
function completePath(arg) {
// simply use built-in algorithm to complete paths
return Utils.completePath(arg)
}
function withCompletion(func) {
func._completeArgument = completePathArgument
return func
}
and apply it to the cd
command:
module.exports = {
commands: {
cd: withCompletion(function(args) { /* ... */ }),
ls: function(args) { /* ... */ }
}
}
Single-file scripts
Single-file script is a code evaluated in JavaScript engine every time when command is invoked. That’s why it’s not easy to predefine whether script provides argument completion or not before evaluation is performed.
For now, a solution is to prepend script file with something like this:
/// provides: argumentCompletion
if (argToComplete) {
return Utils.completePath(argToComplete)
}
Where provides is supposed to have an array of features that script supports.
So, a simple case of pwd command script would be:
/// provides: argumentCompletion
if (argToComplete) {
return Utils.completePath(argToComplete)
}
assert(args.length == 0, "Please, no args.")
console.log(getCurrentPath())
Summary
By all means, argument completion for paths is an easy (well, somehow) job to do. However, sometimes user may want to provide more sophisticated ways of completing argument, e.g. listing possible argument options or fixing argument formats like date, time or other values. The Console gives this feature and even more - it’s even possible to print an error during completion!