import { containsAllStrings, longestCommonStart } from "../utils/StringHelperFunctions"; /** * Implements tab completion for the Terminal * * @param command {string} Terminal command, excluding the last incomplete argument * @param arg {string} Last argument that is being completed * @param allPossibilities {string[]} All values that `arg` can complete to */ export function tabCompletion( command: string, arg: string, allPossibilities: string[], oldValue: string, ): string[] | string | undefined { if (!(allPossibilities.constructor === Array)) { return; } if (!containsAllStrings(allPossibilities)) { return; } // Remove all options in allPossibilities that do not match the current string // that we are attempting to autocomplete if (arg === "") { for (let i = allPossibilities.length - 1; i >= 0; --i) { if (!allPossibilities[i].toLowerCase().startsWith(command.toLowerCase())) { allPossibilities.splice(i, 1); } } } else { for (let i = allPossibilities.length - 1; i >= 0; --i) { if (!allPossibilities[i].toLowerCase().startsWith(arg.toLowerCase())) { allPossibilities.splice(i, 1); } } } const semiColonIndex = oldValue.lastIndexOf(";"); let val = ""; if (allPossibilities.length === 0) { return; } else if (allPossibilities.length === 1) { if (arg === "") { //Autocomplete command val = allPossibilities[0]; } else { val = command + " " + allPossibilities[0]; } if (semiColonIndex === -1) { // No semicolon, so replace the whole command return val; } else { // Replace only after the last semicolon return oldValue.slice(0, semiColonIndex + 1) + " " + val; } } else { const longestStartSubstr = longestCommonStart(allPossibilities); /** * If the longest common starting substring of remaining possibilities is the same * as whatevers already in terminal, just list all possible options. Otherwise, * change the input in the terminal to the longest common starting substr */ if (arg === "") { if (longestStartSubstr === command) { return allPossibilities; } else { if (semiColonIndex === -1) { // No semicolon, so replace the whole command return longestStartSubstr; } else { // Replace only after the last semicolon return `${oldValue.slice(0, semiColonIndex + 1)} ${longestStartSubstr}`; } } } else { if (longestStartSubstr === arg) { // List all possible options return allPossibilities; } else { if (semiColonIndex == -1) { // No semicolon, so replace the whole command return `${command} ${longestStartSubstr}`; } else { // Replace only after the last semicolon return `${oldValue.slice(0, semiColonIndex + 1)} ${command} ${longestStartSubstr}`; } } } } }