fish-lsp
Version:
LSP implementation for fish/fish-shell
256 lines (255 loc) • 11.7 kB
JavaScript
;
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;
};
}