fish-lsp
Version:
LSP implementation for fish/fish-shell
152 lines (151 loc) • 5.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isExportDefinition = isExportDefinition;
exports.isExportVariableDefinitionName = isExportVariableDefinitionName;
exports.findVariableDefinitionNameNode = findVariableDefinitionNameNode;
exports.extractExportVariable = extractExportVariable;
exports.buildExportDetail = buildExportDetail;
exports.processExportCommand = processExportCommand;
const vscode_languageserver_1 = require("vscode-languageserver");
const node_types_1 = require("../utils/node-types");
const symbol_1 = require("./symbol");
const definition_scope_1 = require("../utils/definition-scope");
const tree_sitter_1 = require("../utils/tree-sitter");
const markdown_builder_1 = require("../utils/markdown-builder");
const translation_1 = require("../utils/translation");
function isExportDefinition(node) {
return (0, node_types_1.isCommandWithName)(node, 'export') && node.children.length >= 2;
}
function isExportVariableDefinitionName(node) {
if ((0, node_types_1.isString)(node) || (0, node_types_1.isConcatenation)(node))
return false;
if (!node.parent)
return false;
const isConcatenated = (0, node_types_1.isConcatenation)(node.parent);
let parentNode = node.parent;
if (isConcatenated)
parentNode = parentNode.parent;
if (!parentNode || !(0, node_types_1.isCommandWithName)(parentNode, 'export'))
return false;
const firstChild = isConcatenated
? parentNode.firstNamedChild
: parentNode.firstChild;
if (firstChild && firstChild.equals(node))
return false;
const args = parentNode.childrenForFieldName('argument');
const exportName = isConcatenated
? args.at(0)?.firstChild
: args.at(0);
return !!exportName && exportName.equals(node);
}
function findVariableDefinitionNameNode(node) {
function getName(node) {
let current = node;
while (current && current.type === 'concatenation') {
current = current.firstChild;
}
if (!current)
return undefined;
return current;
}
function getValue(node) {
let current = node;
while (current && current.type === 'concatenation') {
current = current.lastChild;
}
if (!current)
return undefined;
return current;
}
let isConcatenation = false;
const nameNode = getName(node);
const valueNode = getValue(node);
const isValueString = !!valueNode && (0, node_types_1.isString)(valueNode);
const isNonEscaped = !!valueNode && !!nameNode && nameNode.equals(valueNode);
if (!nameNode || !valueNode) {
return {
nameNode,
valueNode,
isConcatenation: false,
isValueString,
isNonEscaped,
};
}
if (nameNode?.equals(valueNode)) {
return {
nameNode,
valueNode,
isConcatenation,
isValueString,
isNonEscaped,
};
}
isConcatenation = true;
return {
nameNode,
valueNode,
isConcatenation,
isValueString,
isNonEscaped,
};
}
function extractExportVariable(node) {
const argument = node.firstChild?.nextNamedSibling;
if (!argument) {
return null;
}
const [name, ...valueParts] = argument.text.split('=');
const value = valueParts.join('=');
const nameStart = {
line: argument.startPosition.row,
character: argument.startPosition.column,
};
const nameEnd = {
line: nameStart.line,
character: nameStart.character + name.length,
};
return { name, value, nameRange: vscode_languageserver_1.Range.create(nameStart, nameEnd) };
}
function buildExportDetail(doc, commandNode, variableDefinitionNode) {
const commandText = commandNode.text;
const extracted = extractExportVariable(variableDefinitionNode);
if (!extracted)
return '';
const { name, value } = extracted;
const detail = [
`${markdown_builder_1.md.bold('(variable)')} ${markdown_builder_1.md.inlineCode(name)}`,
`${markdown_builder_1.md.italic('globally')} scoped, ${markdown_builder_1.md.italic('exported')}`,
`located in file: ${markdown_builder_1.md.inlineCode((0, translation_1.uriToReadablePath)(doc.uri))}`,
markdown_builder_1.md.separator(),
markdown_builder_1.md.codeBlock('fish', commandText),
markdown_builder_1.md.separator(),
markdown_builder_1.md.codeBlock('fish', `set -gx ${name} ${value}`),
].join(markdown_builder_1.md.newline());
return detail;
}
function processExportCommand(document, node, children = []) {
if (!isExportDefinition(node))
return [];
const found = findVariableDefinitionNameNode(node);
const varDefNode = found?.nameNode;
if (!found || !varDefNode)
return [];
const { name, nameRange, } = extractExportVariable(varDefNode);
const scope = definition_scope_1.DefinitionScope.create(node.parent || node, 'global');
const detail = buildExportDetail(document, node, found.nameNode);
return [
symbol_1.FishSymbol.fromObject({
name,
node,
focusedNode: varDefNode,
range: (0, tree_sitter_1.getRange)(node),
selectionRange: nameRange,
fishKind: 'EXPORT',
document,
uri: document.uri,
detail,
scope,
children,
}),
];
}