UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

280 lines (279 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ErrorNodeTypes = void 0; exports.findErrorCause = findErrorCause; exports.isExtraEnd = isExtraEnd; exports.isZeroIndex = isZeroIndex; exports.isSingleQuoteVariableExpansion = isSingleQuoteVariableExpansion; exports.isAlias = isAlias; exports.isUniversalDefinition = isUniversalDefinition; exports.isSourceFilename = isSourceFilename; exports.isDotSourceCommand = isDotSourceCommand; exports.isTestCommandVariableExpansionWithoutString = isTestCommandVariableExpansionWithoutString; exports.isConditionalStatement = isConditionalStatement; exports.isFirstNodeInConditionalExecution = isFirstNodeInConditionalExecution; exports.isConditionalWithoutQuietCommand = isConditionalWithoutQuietCommand; exports.isVariableDefinitionWithExpansionCharacter = isVariableDefinitionWithExpansionCharacter; exports.isMatchingCompleteOptionIsCommand = isMatchingCompleteOptionIsCommand; exports.isMatchingAbbrFunction = isMatchingAbbrFunction; exports.isAbbrDefinitionName = isAbbrDefinitionName; exports.isArgparseWithoutEndStdin = isArgparseWithoutEndStdin; exports.isFunctionWithEventHookCallback = isFunctionWithEventHookCallback; exports.isFishLspDeprecatedVariableName = isFishLspDeprecatedVariableName; exports.getDeprecatedFishLspMessage = getDeprecatedFishLspMessage; const node_types_1 = require("../utils/node-types"); const tree_sitter_1 = require("../utils/tree-sitter"); const options_1 = require("../parsing/options"); const source_1 = require("../parsing/source"); const error_codes_1 = require("./error-codes"); const references_1 = require("../references"); exports.ErrorNodeTypes = { ['function']: 'end', ['while']: 'end', ['begin']: 'end', ['for']: 'end', ['if']: 'end', ['"']: '"', ["'"]: "'", ['{']: '}', ['[']: ']', ['(']: ')', }; function isStartTokenType(str) { return ['function', 'while', 'if', 'for', 'begin', '[', '{', '(', "'", '"'].includes(str); } function findErrorCause(children) { const stack = []; for (const node of children) { if (isStartTokenType(node.type)) { const expectedEndToken = exports.ErrorNodeTypes[node.type]; const matchIndex = stack.findIndex(item => item.type === expectedEndToken); if (matchIndex !== -1) { stack.splice(matchIndex, 1); } else { stack.push({ node, type: expectedEndToken }); } } else if (Object.values(exports.ErrorNodeTypes).includes(node.type)) { stack.push({ node, type: node.type }); } } return stack.length > 0 ? stack[0]?.node || null : null; } function isExtraEnd(node) { return node.type === 'command' && node.text === 'end'; } function isZeroIndex(node) { return node.type === 'index' && node.text === '0'; } function isSingleQuoteVariableExpansion(node) { if (node.type !== 'single_quote_string') { return false; } if (node.parent && (0, node_types_1.isCommandWithName)(node.parent, 'string')) { return false; } const variableRegex = /(?<!\\)\$\w+/; return variableRegex.test(node.text); } function isAlias(node) { return (0, node_types_1.isCommandWithName)(node, 'alias'); } function isUniversalDefinition(node) { if (!(0, node_types_1.isOption)(node)) return false; const parent = (0, node_types_1.findParentCommand)(node); if (!parent) return false; if ((0, node_types_1.isCommandWithName)(parent, 'read', 'set')) { const definitionName = parent .childrenForFieldName('argument') .find(c => !(0, node_types_1.isOption)(c) && (0, node_types_1.isVariableDefinitionName)(c)); if (!definitionName || !(0, tree_sitter_1.precedesRange)((0, tree_sitter_1.getRange)(node), (0, tree_sitter_1.getRange)(definitionName))) { return false; } return (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-U', '--universal')); } return false; } function isSourceFilename(node) { if ((0, source_1.isSourceCommandArgumentName)(node)) { const isExisting = (0, source_1.isExistingSourceFilenameNode)(node); if (!isExisting) { if (node.type === 'variable_expansion') { return false; } if ((0, node_types_1.isString)(node)) { return false; } return true; } return !isExisting; } return false; } function isDotSourceCommand(node) { if (node.parent && (0, node_types_1.isCommandWithName)(node.parent, '.')) { return node.parent.firstNamedChild?.equals(node) || false; } return false; } function isTestCommandVariableExpansionWithoutString(node) { const parent = node.parent; const previousSibling = node.previousSibling; if (!parent || !previousSibling) return false; if (!(0, node_types_1.isCommandWithName)(parent, 'test', '[')) return false; if ((0, node_types_1.isMatchingOption)(previousSibling, options_1.Option.short('-n'), options_1.Option.short('-z'))) { return !(0, node_types_1.isString)(node) && !!parent.child(2) && parent.child(2).equals(node); } return false; } function isInsideStatementCondition(statement, node) { const conditionNode = statement.childForFieldName('condition'); if (!conditionNode) return false; return (0, tree_sitter_1.isNodeWithinOtherNode)(node, conditionNode); } function isConditionalStatement(node) { if (!node.isNamed) return false; if (['\n', ';'].includes(node?.previousSibling?.type || '')) return false; let curr = node.parent; while (curr) { if (curr.type === 'conditional_execution') { curr = curr?.parent; } else if ((0, node_types_1.isIfOrElseIfConditional)(curr)) { return isInsideStatementCondition(curr, node); } else { break; } } return false; } function checkConditionalStartsWith(node) { if (node.type === 'conditional_execution') { return node.text.startsWith('&&') || node.text.startsWith('||') || node.text.startsWith('and') || node.text.startsWith('or'); } return false; } function isFirstNodeInConditionalExecution(node) { if (!node.isNamed) return false; if (['\n', ';'].includes(node?.type || '')) return false; if (isConditionalStatement(node)) return false; if (node.parent && node.parent.type === 'conditional_execution' && !checkConditionalStartsWith(node.parent)) { return node.parent.firstNamedChild?.equals(node) || false; } const next = node.nextNamedSibling; if (!next) return false; return next.type === 'conditional_execution' && checkConditionalStartsWith(next); } function hasCommandSubstitution(node) { return node.childrenForFieldName('argument').filter(c => c.type === 'command_substitution').length > 0; } function isConditionalWithoutQuietCommand(node) { if (!(0, node_types_1.isCommandWithName)(node, 'command', 'type', 'set', 'string', 'abbr', 'builtin', 'functions', 'jobs')) return false; if (!isConditionalStatement(node) && !isFirstNodeInConditionalExecution(node)) return false; if ((0, node_types_1.isCommandWithName)(node, 'set') && hasCommandSubstitution(node)) { return false; } const flags = node?.childrenForFieldName('argument') .filter(n => (0, node_types_1.isMatchingOption)(n, options_1.Option.create('-q', '--quiet')) || (0, node_types_1.isMatchingOption)(n, options_1.Option.create('-q', '--query'))) || []; return flags.length === 0; } function isVariableDefinitionWithExpansionCharacter(node, definedVariableExpansions = {}) { if (!(0, node_types_1.isVariableDefinitionName)(node)) return false; const parent = (0, node_types_1.findParentCommand)(node); if (parent && (0, node_types_1.isCommandWithName)(parent, 'set', 'read')) { if (!(0, node_types_1.isVariableDefinitionName)(node)) return false; const name = node.text.startsWith('$') ? node.text.slice(1) : node.text; if (!name || name.length === 0) return false; if (definedVariableExpansions[name] && definedVariableExpansions[name]?.some(scope => (0, tree_sitter_1.isNodeWithinOtherNode)(node, scope))) { return false; } return node.type === 'variable_expansion' || node.text.startsWith('$'); } return false; } function isMatchingCompleteOptionIsCommand(node) { return (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-n', '--condition').withValue()) || (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-a', '--arguments').withValue()) || (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-c', '--command').withValue()); } function isMatchingAbbrFunction(node) { return (0, node_types_1.isMatchingOption)(node, options_1.Option.create('-f', '--function').withValue()); } function isAbbrDefinitionName(node) { const parent = (0, node_types_1.findParentCommand)(node); if (!parent) return false; if (!(0, node_types_1.isCommandWithName)(parent, 'abbr')) return false; const child = parent.childrenForFieldName('argument') .filter(n => !(0, node_types_1.isOption)(n)) .find(n => n.type === 'word' && n.text !== '--' && !(0, node_types_1.isString)(n)); return child ? child.equals(node) : false; } function isArgparseWithoutEndStdin(node) { if (!(0, node_types_1.isCommandWithName)(node, 'argparse')) return false; const endStdin = (0, tree_sitter_1.getChildNodes)(node).find(n => (0, node_types_1.isEndStdinCharacter)(n)); if (!endStdin) return true; return false; } function isFunctionWithEventHookCallback(doc, handler, allFunctions) { const docType = doc.getAutoloadType(); return (node) => { if (docType !== 'functions') return false; if (!(0, node_types_1.isFunctionDefinitionName)(node)) return false; if (docType === 'functions' && handler.isCodeEnabledAtNode(error_codes_1.ErrorCodes.autoloadedFunctionWithEventHookUnused, node)) { const funcSymbol = allFunctions.find(symbol => symbol.name === node.text); if (funcSymbol && funcSymbol.hasEventHook()) { const refs = (0, references_1.getReferences)(doc, funcSymbol.toPosition()).filter(ref => !funcSymbol.equalsLocation(ref) && !ref.uri.includes('completions/') && ref.uri !== doc.uri); if (refs.length === 0) return true; } } return false; }; } function isFishLspDeprecatedVariableName(node) { if ((0, node_types_1.isVariableDefinitionName)(node)) { return node.text === 'fish_lsp_logfile'; } if (node.type === 'variable_name') { return node.text === 'fish_lsp_logfile'; } return node.text === 'fish_lsp_logfile'; } function getDeprecatedFishLspMessage(node) { switch (node.text) { case 'fish_lsp_logfile': return `REPLACE \`${node.text}\` with \`fish_lsp_log_file\``; default: return ''; } }