UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

256 lines (255 loc) 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.variablesWithoutLocalDocumentation = void 0; exports.handleHover = handleHover; exports.getHoverForFlag = getHoverForFlag; exports.collectCommandString = collectCommandString; exports.isPrebuiltVariableExpansion = isPrebuiltVariableExpansion; exports.getPrebuiltVariableExpansionDocs = getPrebuiltVariableExpansionDocs; exports.getVariableExpansionDocs = getVariableExpansionDocs; const node_1 = require("vscode-languageserver-protocol/node"); const documentation_1 = require("./documentation"); const exec_1 = require("./utils/exec"); const node_types_1 = require("./utils/node-types"); const tree_sitter_1 = require("./utils/tree-sitter"); const translation_1 = require("./utils/translation"); const logger_1 = require("./logger"); const snippets_1 = require("./utils/snippets"); const markdown_builder_1 = require("./utils/markdown-builder"); const process_env_1 = require("./utils/process-env"); async function handleHover(analyzer, document, position, current, cache) { if ((0, node_types_1.isOption)(current)) { return await getHoverForFlag(current); } const local = analyzer.getDefinition(document, position); logger_1.logger.log({ handleHover: handleHover.name, symbol: local?.name, position, current }); if (local) { return { contents: local.toMarkupContent(), range: local.selectionRange, }; } const { kindType, kindString } = (0, translation_1.symbolKindsFromNode)(current); const symbolType = ['function', 'class', 'variable'].includes(kindString) ? kindType : undefined; if (cache.find(current.text) !== undefined) { await cache.resolve(current.text, document.uri, symbolType); const item = symbolType ? cache.find(current.text, symbolType) : cache.getItem(current.text); if (item && item?.docs) { return { contents: { kind: node_1.MarkupKind.Markdown, value: item.docs.toString(), }, }; } } const commandString = await collectCommandString(current); const result = await (0, documentation_1.documentationHoverProvider)(commandString); logger_1.logger.log({ commandString, result }); return result; } async function getHoverForFlag(current) { const commandNode = (0, tree_sitter_1.findFirstParent)(current, n => (0, node_types_1.isCommand)(n) || (0, node_types_1.isFunctionDefinition)(n)); if (!commandNode) { return null; } let commandStr = [commandNode.child(0)?.text || '']; const flags = []; let hasFlags = false; for (const child of commandNode?.children || []) { if (!hasFlags && !child.text.startsWith('-')) { commandStr = await appendToCommand(commandStr, child.text); } else if (child.text.startsWith('-')) { flags.push(child.text); hasFlags = true; } } const flagCompletions = await (0, exec_1.execCompletions)(...commandStr, '-'); const shouldSplitShortFlags = hasOldUnixStyleFlags(flagCompletions); const fixedFlags = spiltShortFlags(flags, !shouldSplitShortFlags); const found = flagCompletions .map(line => line.split('\t')) .filter(line => fixedFlags.includes(line[0])) .map(line => line.join('\t')); const prebuiltDocs = snippets_1.PrebuiltDocumentationMap.findMatchingNames(commandStr.join('-'), 'command').find(doc => doc.name === commandStr.join('-')); const description = !prebuiltDocs ? '' : prebuiltDocs?.description || ''; return { contents: (0, documentation_1.enrichCommandWithFlags)(commandStr.join('-'), description, found), }; } function hasOldUnixStyleFlags(allFlags) { for (const line of allFlags.map(line => line.split('\t'))) { const flag = line[0]; if (flag.startsWith('-') && !flag.startsWith('--')) { if (flag.length > 2) { return true; } } } return false; } function spiltShortFlags(flags, shouldSplit) { const newFlags = []; for (let flag of flags) { flag = flag.split('=')[0]; if (flag.startsWith('-') && !flag.startsWith('--')) { if (flag.length > 2 && shouldSplit) { newFlags.push(...flag.split('').map(f => '-' + f)); continue; } } newFlags.push(flag); } return newFlags; } async function appendToCommand(commands, subCommand) { const completions = await (0, exec_1.execSubCommandCompletions)(...commands, ' '); if (completions.includes(subCommand)) { commands.push(subCommand); return commands; } else { return commands; } } async function collectCommandString(current) { const commandNode = (0, tree_sitter_1.findFirstParent)(current, n => (0, node_types_1.isCommand)(n)); if (!commandNode) { return ''; } const commandNodeText = commandNode.child(0)?.text; const subCommandName = commandNode.child(1)?.text; if (subCommandName?.startsWith('-')) { return commandNodeText || ''; } const commandText = [commandNodeText, subCommandName].join('-'); const docs = await (0, exec_1.execCommandDocs)(commandText); if (docs) { return commandText; } return commandNodeText || ''; } const allVariables = snippets_1.PrebuiltDocumentationMap.getByType('variable'); function isPrebuiltVariableExpansion(node) { if ((0, node_types_1.isVariableExpansion)(node)) { const variableName = node.text.slice(1); return allVariables.some(variable => variable.name === variableName); } return false; } function getPrebuiltVariableExpansionDocs(node) { if ((0, node_types_1.isVariableExpansion)(node)) { const variableName = node.text.slice(1); const variable = allVariables.find(variable => variable.name === variableName); if (variable) { return (0, documentation_1.enrichToMarkdown)([ `(${markdown_builder_1.md.italic('variable')}) - ${markdown_builder_1.md.inlineCode('$' + variableName)}`, markdown_builder_1.md.separator(), variable.description, ].join('\n')); } } return null; } exports.variablesWithoutLocalDocumentation = [ '$status', '$pipestatus', ]; function getVariableExpansionDocs(analyzer, doc, position) { function isVariablesWithoutLocalDocumentation(current) { return exports.variablesWithoutLocalDocumentation.includes('$' + current.text); } function getPrebuiltVariableHoverContent(current) { const docObject = allVariables.find(variable => variable.name === current.text); if (!docObject) return null; return [ `(${markdown_builder_1.md.italic('variable')}) ${markdown_builder_1.md.bold(current.text)}`, markdown_builder_1.md.separator(), docObject.description, ].join('\n'); } return function isPrebuiltExpansionDocsForVariable(current) { if ((0, node_types_1.isVariableDefinitionName)(current)) { const variableName = current.text; const parent = (0, node_types_1.findParentCommand)(current); if (process_env_1.AutoloadedPathVariables.has(variableName)) { return { contents: (0, documentation_1.enrichToMarkdown)([ process_env_1.AutoloadedPathVariables.getHoverDocumentation(variableName), markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', parent?.text || ''), ].join('\n')), }; } if (isVariablesWithoutLocalDocumentation(current)) { return { contents: (0, documentation_1.enrichToMarkdown)([ getPrebuiltVariableHoverContent(current), markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', parent?.text || ''), ].join('\n')), }; } if (allVariables.find(variable => variable.name === current.text)) { return { contents: (0, documentation_1.enrichToMarkdown)([ getPrebuiltVariableHoverContent(current), markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', parent?.text || ''), ].join('\n')), }; } return null; } if (current.type === 'variable_name' && current.parent && (0, node_types_1.isVariableExpansion)(current.parent)) { const variableName = current.text; if (process_env_1.AutoloadedPathVariables.has(variableName)) { return { contents: (0, documentation_1.enrichToMarkdown)(process_env_1.AutoloadedPathVariables.getHoverDocumentation(variableName)), }; } const node = current.parent; if ((0, node_types_1.isVariableExpansionWithName)(node, 'argv')) { const parentNode = (0, node_types_1.findParent)(node, (n) => (0, node_types_1.isProgram)(n) || (0, node_types_1.isFunctionDefinition)(n)); const variableName = node.text.slice(1); const variableDocObj = allVariables.find(variable => variable.name === variableName); if ((0, node_types_1.isFunctionDefinition)(parentNode)) { const functionName = parentNode.firstNamedChild; return { contents: (0, documentation_1.enrichToMarkdown)([ `(${markdown_builder_1.md.italic('variable')}) ${markdown_builder_1.md.bold('$argv')}`, `argument of function ${markdown_builder_1.md.bold(functionName.text)}`, markdown_builder_1.md.separator(), variableDocObj?.description, markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', parentNode.text), ].join('\n')), }; } else if ((0, node_types_1.isProgram)(parentNode)) { return { contents: (0, documentation_1.enrichToMarkdown)([ `(${markdown_builder_1.md.italic('variable')}) ${markdown_builder_1.md.bold('$argv')}`, `arguments of script ${markdown_builder_1.md.bold((0, translation_1.uriToPath)(doc.uri))}`, markdown_builder_1.md.separator(), variableDocObj?.description, markdown_builder_1.md.separator(), markdown_builder_1.md.codeBlock('fish', parentNode.text), ].join('\n')), }; } } else if (exports.variablesWithoutLocalDocumentation.includes(node.text)) { return { contents: getPrebuiltVariableExpansionDocs(node) }; } else if (!analyzer.getDefinition(doc, position) && isPrebuiltVariableExpansion(node)) { const contents = getPrebuiltVariableExpansionDocs(node); if (contents) return { contents }; } } return null; }; }