diff --git a/CliClient/app/autocompletion.js b/CliClient/app/autocompletion.js new file mode 100644 index 000000000..d7b263d71 --- /dev/null +++ b/CliClient/app/autocompletion.js @@ -0,0 +1,105 @@ +var { app } = require('./app.js'); +var { Note } = require('lib/models/note.js'); +var { Folder } = require('lib/models/folder.js'); +var { Tag } = require('lib/models/tag.js'); +var { cliUtils } = require('./cli-utils.js'); +var yargParser = require('yargs-parser'); + +async function handleAutocompletionPromise(line) { + // Auto-complete the command name + const names = await app().commandNames(); + let words = line.split(' '); + //If there is only one word and it is not already a command name then you + //should look for commmands it could be + if (words.length == 1) { + if (names.indexOf(words[0]) === -1) { + let x = names.filter((n) => n.indexOf(words[0]) === 0); + return x.length > 0 ? x.map((a) => a + ' ') : line; + } else { + return line; + } + } + //There is more than one word and it is a command + const metadata = (await app().commandMetadata())[words[0]]; + //If for some reason this command does not have any associated metadata + //just don't autocomplete. However, this should not happen. + if (metadata === undefined) { + return line; + } + //complete an option + let next = words.length > 1 ? words[words.length - 1] : ''; + let l = []; + for (let i = 0; i 1 && options[1].indexOf(next) === 0) { + l.push(line.slice(0, line.lastIndexOf(next)) + options[1] + ' '); + } + } + //Complete an argument + //Determine the number of positional arguments by counting the number of + //words that don't start with a - less one for the command name + const positionalArgs = words.filter((a)=>a.indexOf('-') !== 0 && a !== '').length - 1; + + let cmdUsage = yargParser(metadata.usage)['_']; + cmdUsage.splice(0, 1); + + if (cmdUsage.length <= positionalArgs) return line; + + let argName = cmdUsage[positionalArgs]; + argName = cliUtils.parseCommandArg(argName).name; + + if (argName == 'note' || argName == 'note-pattern' && app().currentFolder()) { + const notes = await Note.previews(app().currentFolder().id, { titlePattern: next + '*' }); + l = l.concat(notes.map((n) => line.slice(0, line.lastIndexOf(next)) + n.title + ' ')); + } + + if (argName == 'notebook') { + const folders = await Folder.search({ titlePattern: next + '*' }); + l = l.concat(folders.map((n) => line.slice(0, line.lastIndexOf(next)) + n.title + ' ')); + } + + if (argName == 'tag') { + let tags = await Tag.search({ titlePattern: next + '*' }); + l = l.concat(tags.map((n) => line.slice(0, line.lastIndexOf(next)) + n.title + ' ')); + } + + if (argName == 'tag-command') { + let c = filterList(['add', 'remove', 'list'], next); + l = l.concat(c.map((n) => line.slice(0, line.lastIndexOf(next)) + n + ' ')) + } + + if (argName == 'todo-command') { + let c = filterList(['toggle', 'clear'], next); + l = l.concat(c.map((n) => line.slice(0, line.lastIndexOf(next)) + n + ' ')) + } + return l.length > 0 ? l : line; + +} +function handleAutocompletion(str, callback) { + handleAutocompletionPromise(str).then(function(res) { + callback(undefined, res); + }); +} + +function filterList(list, next) { + let output = []; + for (let i = 0; i < list.length; i++) { + if (list[i].indexOf(next) !== 0) continue; + output.push(list[i]); + } + return output; +} + +module.exports = { handleAutocompletion }; diff --git a/CliClient/app/gui/StatusBarWidget.js b/CliClient/app/gui/StatusBarWidget.js index 0b21d2a35..584ff7091 100644 --- a/CliClient/app/gui/StatusBarWidget.js +++ b/CliClient/app/gui/StatusBarWidget.js @@ -3,6 +3,7 @@ const chalk = require('chalk'); const termutils = require('tkwidgets/framework/termutils.js'); const stripAnsi = require('strip-ansi'); const { app } = require('../app.js'); +const { handleAutocompletion } = require('../autocompletion.js'); class StatusBarWidget extends BaseWidget { @@ -113,7 +114,7 @@ class StatusBarWidget extends BaseWidget { cancelable: true, history: this.history, default: this.promptState_.initialText, - autoComplete: this.autocomplete_, + autoComplete: handleAutocompletion, autoCompleteMenu: true, autoCompleteHint : true, };