UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

150 lines (149 loc) 6.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findFlagsToComplete = findFlagsToComplete; exports.buildCompleteString = buildCompleteString; exports.createArgparseCompletionsCodeAction = createArgparseCompletionsCodeAction; const node_types_1 = require("../utils/node-types"); const tree_sitter_1 = require("../utils/tree-sitter"); const vscode_languageserver_1 = require("vscode-languageserver"); const refactors_1 = require("./refactors"); const translation_1 = require("../utils/translation"); const logger_1 = require("../logger"); const options_1 = require("../parsing/options"); function parseArgparseFlag(text) { const beforeEquals = text.split('=')[0]; if (beforeEquals.includes('/')) { const [short, long] = beforeEquals.split('/'); return { shortOption: short, longOption: long === '' ? '' : long, }; } return { longOption: beforeEquals, }; } function isSkipablePreviousOption(node) { if (node.text.includes('=')) return false; return (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-N', '--min-args')) || (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-n', '--name')) || (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-x', '--exclusive')) || (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-X', '--max-args')); } function findFlagsToComplete(node) { if (!(0, node_types_1.isCommandWithName)(node, 'argparse')) return []; const flags = []; for (const child of (0, tree_sitter_1.getChildNodes)(node)) { if ((0, node_types_1.isEndStdinCharacter)(child)) break; if ((0, node_types_1.isCommandName)(child)) continue; if ((0, node_types_1.isOption)(child)) continue; const prev = child.previousSibling; if (prev && (0, node_types_1.isOption)(prev) && isSkipablePreviousOption(prev)) continue; if ((0, node_types_1.isString)(child)) { const text = child.text.slice(1, -1); flags.push(parseArgparseFlag(text)); continue; } if (child.type === 'word' && !child.text.startsWith('-')) { flags.push(parseArgparseFlag(child.text)); } } return flags; } function buildCompleteString(commandName, flags) { return flags.map(flag => { let text = `complete -c ${commandName}`; if (flag.shortOption) { text += ` -s ${flag.shortOption}`; } if (flag.longOption) { text += ` -l ${flag.longOption}`; } return text; }).join('\n'); } function buildConfdCompletions(argparseNode, functionNode, functionNameNode, doc) { logger_1.logger.log(buildConfdCompletions.name, 'params', { argparseNode: argparseNode.text, functionNode: functionNode.text, functionNameNode: functionNameNode.text, doc: doc.uri, }); const completionPath = doc.getRelativeFilenameToWorkspace(); const flags = findFlagsToComplete(argparseNode); if (!(0, node_types_1.isFunctionDefinition)(functionNode)) { return undefined; } const functionName = functionNode.firstNamedChild.text; const completionText = buildCompleteString(functionName, flags); const selectedText = `\n# auto generated by fish-lsp\n${completionText}\n`; const shortPath = (0, translation_1.uriToReadablePath)(completionPath); const changeAnnotation = { label: `Create completions for '${functionName}' in file: ${shortPath}`, description: `Create completions for '${functionName}' to file: ${shortPath}`, }; const workspaceEdit = { documentChanges: [ vscode_languageserver_1.TextDocumentEdit.create(vscode_languageserver_1.VersionedTextDocumentIdentifier.create(doc.uri, 0), [vscode_languageserver_1.TextEdit.insert((0, tree_sitter_1.getRange)(functionNode).end, selectedText)]), ], changeAnnotations: { [changeAnnotation.label]: changeAnnotation }, }; logger_1.logger.log(buildConfdCompletions.name, 'return', { textEdits: workspaceEdit.documentChanges, }); return { title: `Create completions for: ${functionName}`, kind: vscode_languageserver_1.CodeActionKind.QuickFix, edit: workspaceEdit, }; } function getNodesForArgparse(selectedNode) { const node = selectedNode; if ((0, node_types_1.isCommandWithName)(node, 'argparse')) { const functionNode = (0, node_types_1.findParentFunction)(node); return { argparseNode: node, functionNode: functionNode, functionNameNode: functionNode?.firstNamedChild, }; } if (node.type === 'word' && node.parent && (0, node_types_1.isCommandWithName)(node.parent, 'argparse')) { const functionNode = (0, node_types_1.findParentFunction)(node.parent); return { argparseNode: node.parent, functionNode: functionNode, functionNameNode: functionNode?.firstNamedChild, }; } if (node.type === 'function_definition') { return { argparseNode: (0, tree_sitter_1.getChildNodes)(node).find(n => (0, node_types_1.isCommandWithName)(n, 'argparse')), functionNode: node, functionNameNode: node.firstNamedChild, }; } return { argparseNode: undefined, functionNode: undefined, functionNameNode: undefined, }; } function createArgparseCompletionsCodeAction(node, doc) { const autoloadType = doc.getAutoloadType(); const { argparseNode, functionNode, functionNameNode } = getNodesForArgparse(node); if (!argparseNode || !functionNode || !functionNameNode) return undefined; if (autoloadType === 'functions') { return (0, refactors_1.extractFunctionWithArgparseToCompletionsFile)(doc, (0, tree_sitter_1.getRange)(functionNode), functionNode); } if (autoloadType === 'conf.d') { return buildConfdCompletions(node, functionNode, functionNameNode, doc); } return undefined; }