UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

234 lines (233 loc) 8.4 kB
"use strict"; 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, }), ]; }