fish-lsp
Version:
LSP implementation for fish/fish-shell
234 lines (233 loc) • 8.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FishAlias = void 0;
exports.isAliasDefinitionName = isAliasDefinitionName;
exports.isAliasDefinitionValue = isAliasDefinitionValue;
exports.processAliasCommand = processAliasCommand;
const symbol_1 = require("./symbol");
const definition_scope_1 = require("../utils/definition-scope");
const tree_sitter_1 = require("../utils/tree-sitter");
const node_types_1 = require("../utils/node-types");
const builtins_1 = require("../utils/builtins");
const markdown_builder_1 = require("../utils/markdown-builder");
const flatten_1 = require("../utils/flatten");
var FishAlias;
(function (FishAlias) {
function isAlias(node) {
return (0, node_types_1.isCommandWithName)(node, 'alias');
}
FishAlias.isAlias = isAlias;
function getInfo(node) {
if (!(0, node_types_1.isCommandWithName)(node, 'alias'))
return null;
const firstArg = node.firstNamedChild?.nextNamedSibling;
if (!firstArg)
return null;
let name;
let value;
let hasEquals;
if (firstArg.text.includes('=')) {
const [nameStr, ...valueParts] = firstArg.text.split('=');
if (!nameStr || valueParts.length === 0)
return null;
name = nameStr;
value = valueParts.join('=').replace(/^['"]|['"]$/g, '');
hasEquals = true;
}
else {
const valueNode = firstArg.nextNamedSibling;
if (!valueNode)
return null;
name = firstArg.text;
value = valueNode.text.replace(/^['"]|['"]$/g, '');
hasEquals = false;
}
const words = value.split(/\s+/);
const firstWord = words.at(0);
const lastWord = words.at(-1);
let prefix = '';
if (firstWord === name) {
prefix = (0, builtins_1.isBuiltin)(name) ? 'builtin' : 'command';
}
const shouldWrap = firstWord !== name && lastWord !== name;
const wraps = shouldWrap ? value : null;
return {
name,
value,
prefix,
wraps,
hasEquals,
};
}
FishAlias.getInfo = getInfo;
function toFunction(node) {
const aliasInfo = getInfo(node);
if (!aliasInfo)
return null;
const { name, value, prefix, wraps, hasEquals } = aliasInfo;
const escapedValue = value.replace(/'/g, "\\'");
const description = hasEquals ?
`alias ${name}=${escapedValue}` :
`alias ${name} ${escapedValue}`;
const functionParts = [
`function ${name}`,
wraps ? `--wraps='${escapedValue}'` : '',
`--description '${description}'`,
].filter(Boolean).join(' ');
const functionBody = prefix ?
` ${prefix} ${value} $argv` :
` ${value} $argv`;
return [
functionParts,
functionBody,
'end',
].join('\n');
}
FishAlias.toFunction = toFunction;
function getNameRange(node) {
const aliasInfo = getInfo(node);
if (!aliasInfo)
return null;
const nameNode = node.firstNamedChild?.nextNamedSibling;
if (!nameNode)
return null;
if (!aliasInfo.hasEquals) {
return (0, tree_sitter_1.getRange)(nameNode);
}
const nameLength = aliasInfo.name.length;
return {
start: {
line: nameNode.startPosition.row,
character: nameNode.startPosition.column,
},
end: {
line: nameNode.endPosition.row,
character: nameNode.startPosition.column + nameLength,
},
};
}
FishAlias.getNameRange = getNameRange;
function buildDetail(node) {
const aliasInfo = getInfo(node);
if (!aliasInfo)
return null;
const { name } = aliasInfo;
const detail = toFunction(node);
if (!detail)
return null;
return [
`(${markdown_builder_1.md.italic('alias')}) ${name}`,
markdown_builder_1.md.separator(),
markdown_builder_1.md.codeBlock('fish', node.text),
markdown_builder_1.md.separator(),
markdown_builder_1.md.codeBlock('fish', detail),
].join('\n');
}
FishAlias.buildDetail = buildDetail;
function toFishDocumentSymbol(child, parent, document, children = []) {
const aliasInfo = getInfo(parent);
if (!aliasInfo)
return null;
const { name } = aliasInfo;
const detail = toFunction(parent);
if (!detail)
return null;
const selectionRange = getNameRange(parent);
if (!selectionRange)
return null;
const detailText = buildDetail(parent);
if (!detailText)
return null;
return symbol_1.FishSymbol.fromObject({
name,
document,
uri: document.uri,
node: parent,
focusedNode: child,
detail: detailText,
fishKind: 'ALIAS',
range: (0, tree_sitter_1.getRange)(parent),
selectionRange,
scope: (0, definition_scope_1.getScope)(document, child),
children,
});
}
FishAlias.toFishDocumentSymbol = toFishDocumentSymbol;
})(FishAlias || (exports.FishAlias = FishAlias = {}));
function getAliasScopeModifier(document, node) {
const autoloadType = document.getAutoloadType();
switch (autoloadType) {
case 'conf.d':
case 'config':
return (0, node_types_1.isTopLevelDefinition)(node) ? 'global' : 'local';
case 'functions':
return 'local';
default:
return 'local';
}
}
function isAliasDefinitionName(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, 'alias'))
return false;
const firstChild = isConcatenated
? parentNode.firstNamedChild
: parentNode.firstChild;
if (firstChild && firstChild.equals(node))
return false;
const args = parentNode.childrenForFieldName('argument');
const aliasName = isConcatenated
? args.at(0)?.firstChild
: args.at(0);
return !!aliasName && aliasName.equals(node);
}
function isAliasDefinitionValue(node) {
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, 'alias'))
return false;
const firstChild = isConcatenated
? parentNode.firstNamedChild?.nextNamedSibling
: parentNode.firstChild;
if (firstChild && firstChild.equals(node))
return false;
const args = (0, flatten_1.flattenNested)(...parentNode.childrenForFieldName('argument'))
.filter(a => a.isNamed);
const aliasValue = args.at(-1);
return !!aliasValue && aliasValue.equals(node);
}
function processAliasCommand(document, node, children = []) {
const modifier = getAliasScopeModifier(document, node);
const definitionNode = node.firstNamedChild;
const info = FishAlias.getInfo(node);
const detail = FishAlias.buildDetail(node);
const nameRange = FishAlias.getNameRange(node);
if (!info || !detail)
return [];
return [
symbol_1.FishSymbol.fromObject({
name: info.name,
node,
focusedNode: definitionNode,
range: (0, tree_sitter_1.getRange)(node),
selectionRange: nameRange || (0, tree_sitter_1.getRange)(definitionNode),
fishKind: 'ALIAS',
document,
uri: document.uri,
detail,
scope: definition_scope_1.DefinitionScope.create(node.parent, modifier),
children,
}),
];
}