fish-lsp
Version:
LSP implementation for fish/fish-shell
168 lines (167 loc) • 6.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.execEscapedCommand = execEscapedCommand;
exports.execCmd = execCmd;
exports.execAsyncF = execAsyncF;
exports.execAsyncFish = execAsyncFish;
exports.execPrintLsp = execPrintLsp;
exports.execCompletions = execCompletions;
exports.execSubCommandCompletions = execSubCommandCompletions;
exports.execCompleteLine = execCompleteLine;
exports.execCompleteSpace = execCompleteSpace;
exports.execCompleteCmdArgs = execCompleteCmdArgs;
exports.execCommandDocs = execCommandDocs;
exports.execCommandType = execCommandType;
exports.documentCommandDescription = documentCommandDescription;
exports.execFindDependency = execFindDependency;
const child_process_1 = require("child_process");
const path_1 = require("path");
const util_1 = require("util");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
/**
* @async execEscapedComplete() - executes the fish command with
*
* @param {string} cmd - the current command to complete
*
* @returns {Promise<string[]>} - the array of completions, types will need to be added when
* the fish completion command is implemented
*/
async function execEscapedCommand(cmd) {
const escapedCommand = cmd.replace(/(["'$`\\])/g, '\\$1');
const { stdout } = await execFileAsync('fish', ['-P', '--command', escapedCommand]);
if (!stdout)
return [''];
return stdout.trim().split('\n');
}
async function execCmd(cmd) {
const { stdout, stderr } = await execAsync(cmd, { shell: 'fish' });
if (stderr)
return [''];
return stdout
.toString()
.trim()
.split('\n');
}
async function execAsyncF(cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/exec.fish');
const child = await execFileAsync(file, [cmd]);
return child.stdout.toString().trim();
}
/**
* Wrapper for `execAsync()` a.k.a, `promisify(exec)`
* Executes the `cmd` in a fish subprocess
*
* @param cmd - the string to wrap in `fish -c '${cmd}'`
*
* @returns Promise<{stdout, stderr}>
*/
async function execAsyncFish(cmd) {
return await execAsync(`fish -c '${cmd}'`);
}
/**
* Subshell print alias for inlay hints -- too slow for running on large documents
* and/or workspaces
*/
async function execPrintLsp(line) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/printflsp.fish');
const child = await execFileAsync(file, [line]);
if (child.stderr) {
return child.stdout.trim();
}
return child.stdout.trim();
}
//
// Similar to the above execPrintLsp
// export async function execInlayHintType(...cmd: string[]): Promise<string> {
// const child = await execEscapedCommand(`type -t ${cmd.join(' ')} 2>/dev/null`);
// return child.join(' ');
// }
async function execCompletions(...cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/get-completion.fish');
const cmpArgs = ['1', `${cmd.join(' ').trim()}`];
const cmps = await execFileAsync(file, cmpArgs);
return cmps.stdout.trim().split('\n');
}
async function execSubCommandCompletions(...cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/get-completion.fish');
const cmpArgs = ['2', cmd.join(' ')];
const cmps = await execFileAsync(file, cmpArgs);
return cmps.stdout.trim().split('\n');
}
async function execCompleteLine(cmd) {
const escapedCommand = cmd.replace(/(["'$`\\/])/g, '\\$1');
const completeString = `complete --do-complete='${escapedCommand}'`;
const child = await execCmd(completeString);
return child || [];
}
async function execCompleteSpace(cmd) {
const escapedCommand = cmd.replace(/(["'$`\\])/g, '\\$1');
const completeString = `fish -c "complete --do-complete='${escapedCommand} '"`;
const child = await execAsync(completeString);
if (child.stderr) {
return [''];
}
return child.stdout.trim().split('\n');
}
async function execCompleteCmdArgs(cmd) {
const exec = (0, path_1.resolve)(__dirname, '../../fish_files/get-command-options.fish');
const args = (0, child_process_1.execFile)(exec, [cmd]);
const results = args.toString().trim().split('\n');
let i = 0;
const fixedResults = [];
while (i < results.length) {
const line = results[i];
if (cmd === 'test') {
fixedResults.push(line);
}
else if (!line.startsWith('-', 0)) {
//fixedResults.slice(i-1, i).join(' ')
fixedResults.push(fixedResults.pop() + ' ' + line.trim());
}
else {
fixedResults.push(line);
}
i++;
}
return fixedResults;
}
async function execCommandDocs(cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/get-documentation.fish');
const docs = await execFileAsync(file, [cmd]);
const out = docs.stdout;
return out.toString().trim();
}
/**
* runs: ../fish_files/get-type.fish <cmd>
*
* @param {string} cmd - command type from document to resolve
* @returns {Promise<string>}
* 'command' -> cmd has man
* 'file' -> cmd is fish function
* '' -> cmd is neither
*/
async function execCommandType(cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/get-type.fish');
const cmdCheck = cmd.split(' ')[0]?.trim();
const docs = await execFileAsync(file, [cmdCheck]);
if (docs.stderr) {
return '';
}
return docs.stdout.toString().trim();
}
async function documentCommandDescription(cmd) {
const cmdDescription = await execAsync(`fish -c "__fish_describe_command ${cmd}" | head -n1`);
return cmdDescription.stdout.trim() || cmd;
}
async function execFindDependency(cmd) {
const file = (0, path_1.resolve)(__dirname, '../../fish_files/get-dependency.fish');
const docs = (0, child_process_1.execFileSync)(file, [cmd]);
return docs.toString().trim();
}
// open the uri and read the file
// export async function execOpenFile(uri: string): Promise<string> {
// const fileUri = URI.parse(uri).fsPath;
// const file = await promises.readFile(fileUri.toString(), 'utf8');
// return file.toString();
// }