UNPKG

appcenter-cli

Version:

Command line tool for Visual Studio App Center

172 lines (171 loc) 6.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setupAutoCompleteForShell = exports.executeAutoComplete = void 0; const Path = require("path"); const Fs = require("fs"); const misc_1 = require("../misc"); const omelette = require("omelette"); const appName = misc_1.scriptName; function getAutoCompleteObject() { return omelette(appName); } function executeAutoComplete() { const autoCompleteObject = getAutoCompleteObject(); autoCompleteObject.on("complete", function (fragment, data) { const line = data.line; const reply = data.reply; const argsLine = line.substring(appName.length); const args = argsLine.match(/\S+/g) || []; const lineEndsWithWhitespaceChar = /\s{1}/.test(last(line)); const autocompleteTree = JSON.parse(Fs.readFileSync(Path.join(__dirname, "..", "..", "autocomplete-tree.json"), "utf8")); const expandedAutoCompleteTree = getAutoCompleteTreeWithExpandedHelp(autocompleteTree); const getReply = getReplyHandler(lineEndsWithWhitespaceChar); reply(getReply(args, expandedAutoCompleteTree)); }); autoCompleteObject.init(); } exports.executeAutoComplete = executeAutoComplete; function setupAutoCompleteForShell(path, shell) { const autoCompleteObject = getAutoCompleteObject(); let initFile = path; if (shell) { autoCompleteObject.shell = shell; } else { autoCompleteObject.shell = autoCompleteObject.getActiveShell(); } if (!initFile) { initFile = autoCompleteObject.getDefaultShellInitFile(); } let initFileContent; try { initFileContent = Fs.readFileSync(initFile, { encoding: "utf-8" }); } catch (exception) { throw `Can't read init file (${initFile}): ${exception}`; } try { // For bash we need to enable bash_completion before appcenter cli completion if (autoCompleteObject.shell === "bash" && initFileContent.indexOf("begin bash_completion configuration") === -1) { const sources = `[ -f /usr/local/etc/bash_completion ] && . /usr/local/etc/bash_completion [ -f /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion [ -f /etc/bash_completion ] && . /etc/bash_completion`; const template = ` # begin bash_completion configuration for ${appName} completion ${sources} # end bash_completion configuration for ${appName} completion `; Fs.appendFileSync(initFile, template); } if (initFileContent.indexOf(`begin ${appName} completion`) === -1) { autoCompleteObject.setupShellInitFile(initFile); } } catch (exception) { throw `Can't setup autocomplete. Please make sure that init file (${initFile}) exist and you have write permissions: ${exception}`; } } exports.setupAutoCompleteForShell = setupAutoCompleteForShell; function getReplyHandler(lineEndsWithWhitespaceChar) { return function getReply(args, autocompleteTree) { const currentArg = head(args); const commandsAndCategories = Object.keys(autocompleteTree); if (currentArg === undefined) { // no more args - show all of the items at the current level return commandsAndCategories; } else { // check what arg points to const entity = autocompleteTree[currentArg]; if (entity) { // arg points to an existing command or category const restOfArgs = tail(args); if (restOfArgs.length || lineEndsWithWhitespaceChar) { if (entity instanceof Array) { // it is command const getCommandReply = getCommandReplyHandler(lineEndsWithWhitespaceChar); return getCommandReply(restOfArgs, entity); } else { // it is category return getReply(restOfArgs, entity); } } else { // if last arg has no trailing whitespace, it should be added return [currentArg]; } } else { // arg points to nothing specific - return commands and categories which start with arg return commandsAndCategories.filter((commandOrCategory) => commandOrCategory.startsWith(currentArg)); } } }; } function getCommandReplyHandler(lineEndsWithWhitespaceChar) { return function getCommandReply(args, optionsNames) { const currentArg = head(args); if (currentArg === undefined) { // no more args, returning remaining optionsNames return optionsNames.map((option) => option.long || option.short); } else { const restOfArgs = tail(args); if (restOfArgs.length || lineEndsWithWhitespaceChar) { const filteredOptions = optionsNames.filter((option) => option.long !== currentArg && option.short !== currentArg); return getCommandReply(restOfArgs, filteredOptions); } else { const candidates = []; for (const option of optionsNames) { if (option.long && option.long.startsWith(currentArg)) { candidates.push(option.long); } else if (option.short && option.short.startsWith(currentArg)) { candidates.push(option.short); } } return candidates; } } }; } function getAutoCompleteTreeWithExpandedHelp(originalTree) { // "help" command prefixes command path to show help for it const helpTree = cloneDeep(originalTree, (entry) => (entry instanceof Array ? cloneDeep(originalTree["help"]) : undefined)); delete helpTree["help"]; const expandedTree = cloneDeep(originalTree); expandedTree["help"] = helpTree; return expandedTree; } // utility functions (to avoid loading lodash for performance reasons) function last(line) { return line.substr(-1, 1); } function head(array) { return array[0]; } function tail(array) { return array.slice(1); } function cloneDeep(...args) { const item = args[0]; const handler = args[1]; const handlerResult = handler && handler(item); if (handlerResult !== undefined) { return handlerResult; } if (item instanceof Array) { return item.map((subItem) => cloneDeep(subItem, handler)); } if (item instanceof Object) { const cloneObject = {}; const keys = Object.keys(item); for (const key of keys) { cloneObject[key] = cloneDeep(item[key], handler); } return cloneObject; } return item; }