UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

628 lines (627 loc) 21.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findMatchingOptions = exports.isMatchingOption = exports.isDefinitionName = exports.isEmittedEventDefinitionName = exports.isArgparseVariableDefinitionName = exports.isExportVariableDefinitionName = exports.isAliasDefinitionName = exports.isFunctionDefinitionName = exports.isVariableDefinitionName = void 0; exports.isVariableDefinition = isVariableDefinition; exports.isComment = isComment; exports.isShebang = isShebang; exports.isFunctionDefinition = isFunctionDefinition; exports.isCommand = isCommand; exports.isTopLevelFunctionDefinition = isTopLevelFunctionDefinition; exports.isTopLevelDefinition = isTopLevelDefinition; exports.isDefinition = isDefinition; exports.isCommandName = isCommandName; exports.isProgram = isProgram; exports.isError = isError; exports.isForLoop = isForLoop; exports.isIfStatement = isIfStatement; exports.isElseStatement = isElseStatement; exports.isConditional = isConditional; exports.isIfOrElseIfConditional = isIfOrElseIfConditional; exports.isPossibleUnreachableStatement = isPossibleUnreachableStatement; exports.isClause = isClause; exports.isStatement = isStatement; exports.isBlock = isBlock; exports.isEnd = isEnd; exports.isScope = isScope; exports.isSemicolon = isSemicolon; exports.isNewline = isNewline; exports.isBlockBreak = isBlockBreak; exports.isString = isString; exports.isStringCharacter = isStringCharacter; exports.isEmptyString = isEmptyString; exports.isEndStdinCharacter = isEndStdinCharacter; exports.isEscapeSequence = isEscapeSequence; exports.isLongOption = isLongOption; exports.isShortOption = isShortOption; exports.isOption = isOption; exports.isOptionValue = isOptionValue; exports.isJoinedShortOption = isJoinedShortOption; exports.hasShortOptionCharacter = hasShortOptionCharacter; exports.isPipe = isPipe; exports.gatherSiblingsTillEol = gatherSiblingsTillEol; exports.isBeforeCommand = isBeforeCommand; exports.isVariableExpansion = isVariableExpansion; exports.isVariableExpansionWithName = isVariableExpansionWithName; exports.isVariable = isVariable; exports.isCompleteFlagCommandName = isCompleteFlagCommandName; exports.findPreviousSibling = findPreviousSibling; exports.findParentCommand = findParentCommand; exports.isConcatenation = isConcatenation; exports.isAliasWithName = isAliasWithName; exports.findParentFunction = findParentFunction; exports.findParentVariableDefinitionKeyword = findParentVariableDefinitionKeyword; exports.findForLoopVariable = findForLoopVariable; exports.findSetDefinedVariable = findSetDefinedVariable; exports.hasParent = hasParent; exports.findParent = findParent; exports.hasParentFunction = hasParentFunction; exports.findFunctionScope = findFunctionScope; exports.scopeCheck = scopeCheck; exports.wordNodeIsCommand = wordNodeIsCommand; exports.isSwitchStatement = isSwitchStatement; exports.isCaseClause = isCaseClause; exports.isReturn = isReturn; exports.isConditionalCommand = isConditionalCommand; exports.isCommandFlag = isCommandFlag; exports.isRegexArgument = isRegexArgument; exports.isUnmatchedStringCharacter = isUnmatchedStringCharacter; exports.isPartialForLoop = isPartialForLoop; exports.isInlineComment = isInlineComment; exports.isCommandWithName = isCommandWithName; exports.isArgumentThatCanContainCommandCalls = isArgumentThatCanContainCommandCalls; exports.isStringWithCommandCall = isStringWithCommandCall; exports.isReturnStatusNumber = isReturnStatusNumber; exports.isCompleteCommandName = isCompleteCommandName; const tree_sitter_1 = require("./tree-sitter"); const barrel_1 = require("../parsing/barrel"); Object.defineProperty(exports, "isDefinitionName", { enumerable: true, get: function () { return barrel_1.isDefinitionName; } }); Object.defineProperty(exports, "isEmittedEventDefinitionName", { enumerable: true, get: function () { return barrel_1.isEmittedEventDefinitionName; } }); const options_1 = require("../parsing/options"); const barrel_2 = require("../parsing/barrel"); Object.defineProperty(exports, "isVariableDefinitionName", { enumerable: true, get: function () { return barrel_2.isVariableDefinitionName; } }); Object.defineProperty(exports, "isFunctionDefinitionName", { enumerable: true, get: function () { return barrel_2.isFunctionDefinitionName; } }); Object.defineProperty(exports, "isAliasDefinitionName", { enumerable: true, get: function () { return barrel_2.isAliasDefinitionName; } }); Object.defineProperty(exports, "isExportVariableDefinitionName", { enumerable: true, get: function () { return barrel_2.isExportVariableDefinitionName; } }); Object.defineProperty(exports, "isArgparseVariableDefinitionName", { enumerable: true, get: function () { return barrel_2.isArgparseVariableDefinitionName; } }); function isVariableDefinition(node) { return (0, barrel_2.isVariableDefinitionName)(node); } function isComment(node) { return node.type === 'comment' && !isShebang(node); } function isShebang(node) { const parent = node.parent; if (!parent || !isProgram(parent)) { return false; } const firstLine = parent.firstChild; if (!firstLine) { return false; } if (!node.equals(firstLine)) { return false; } return (firstLine.type === 'comment' && firstLine.text.startsWith('#!') && firstLine.text.includes('fish')); } function isFunctionDefinition(node) { return node.type === 'function_definition'; } function isCommand(node) { return [ 'command', 'test_command', 'command_substitution', ].includes(node.type); } function isTopLevelFunctionDefinition(node) { if (isFunctionDefinition(node)) { return !!(node.parent && isTopLevelDefinition(node.parent)); } if ((0, barrel_2.isFunctionDefinitionName)(node)) { return !!(node.parent && node.parent.parent && isTopLevelDefinition(node.parent.parent)); } return false; } function isTopLevelDefinition(node) { let currentNode = node; while (currentNode) { if (!currentNode) break; if (isProgram(currentNode)) { return true; } if (isFunctionDefinition(currentNode)) { return false; } currentNode = currentNode.parent; } return true; } function isDefinition(node) { return (0, barrel_2.isFunctionDefinitionName)(node) || (0, barrel_2.isVariableDefinitionName)(node); } function isCommandName(node) { const parent = node.parent || node; const cmdName = parent?.firstNamedChild || node?.firstNamedChild; if (!parent || !cmdName) { return false; } if (!isCommand(parent)) { return false; } return node.type === 'word' && node.equals(cmdName); } function isProgram(node) { return node.type === 'program' || node.parent === null; } function isError(node = null) { if (node) { return node.type === 'ERROR'; } return false; } function isForLoop(node) { return node.type === 'for_statement'; } function isIfStatement(node) { return node.type === 'if_statement'; } function isElseStatement(node) { return node.type === 'else_clause'; } function isConditional(node) { return ['if_statement', 'else_if_clause', 'else_clause'].includes(node.type); } function isIfOrElseIfConditional(node) { return ['if_statement', 'else_if_clause'].includes(node.type); } function isPossibleUnreachableStatement(node) { if (isIfStatement(node)) { return node.lastNamedChild?.type === 'else_clause'; } else if (node.type === 'for_statement') { return true; } else if (node.type === 'switch_statement') { return false; } return false; } function isClause(node) { return [ 'case_clause', 'else_clause', 'else_if_clause', ].includes(node.type); } function isStatement(node) { return [ 'for_statement', 'switch_statement', 'while_statement', 'if_statement', 'begin_statement', ].includes(node.type); } function isBlock(node) { return isClause(node) || isStatement(node); } function isEnd(node) { return node.type === 'end'; } function isScope(node) { return isProgram(node) || isFunctionDefinition(node) || isStatement(node); } function isSemicolon(node) { return node.type === ';' && node.text === ';'; } function isNewline(node) { return node.type === '\n'; } function isBlockBreak(node) { return isEnd(node) || isSemicolon(node) || isNewline(node); } function isString(node) { return [ 'double_quote_string', 'single_quote_string', ].includes(node.type); } function isStringCharacter(node) { return [ "'", '"', ].includes(node.type); } function isEmptyString(node) { return isString(node) && node.text.length === 2; } function isEndStdinCharacter(node) { return '--' === node.text && node.type === 'word'; } function isEscapeSequence(node) { return node.type === 'escape_sequence'; } function isLongOption(node) { return node.text.startsWith('--') && !isEndStdinCharacter(node); } function isShortOption(node) { return node.text.startsWith('-') && !isLongOption(node) && node.text !== '-'; } function isOption(node) { if (isEndStdinCharacter(node)) return false; return isShortOption(node) || isLongOption(node); } function isOptionValue(node) { if (isEndStdinCharacter(node)) return false; if ((0, barrel_1.isDefinitionName)(node)) return false; if (!node.parent) return false; if (isOption(node) && node.text.includes('=') && node.type === 'word') { return true; } if (isString(node) && node.previousNamedSibling && isOption(node.previousNamedSibling)) { return true; } if (node.type === 'word' && node.previousSibling && isOption(node.previousSibling)) { return true; } return false; } function isJoinedShortOption(node) { if (isLongOption(node)) return false; return isShortOption(node) && node.text.slice(1).length > 1; } function hasShortOptionCharacter(node, findChar) { if (isLongOption(node)) return false; return isShortOption(node) && node.text.slice(1).includes(findChar); } var options_2 = require("../parsing/options"); Object.defineProperty(exports, "isMatchingOption", { enumerable: true, get: function () { return options_2.isMatchingOption; } }); Object.defineProperty(exports, "findMatchingOptions", { enumerable: true, get: function () { return options_2.findMatchingOptions; } }); function isPipe(node) { return node.type === 'pipe'; } function gatherSiblingsTillEol(node) { const siblings = []; let next = node.nextSibling; while (next && !isNewline(next)) { siblings.push(next); next = next.nextSibling; } return siblings; } function isBeforeCommand(node) { return [ 'file_redirect', 'redirect', 'redirected_statement', 'conditional_execution', 'stream_redirect', 'pipe', ].includes(node.type) || isFunctionDefinition(node) || isStatement(node) || isSemicolon(node) || isNewline(node) || isEnd(node); } function isVariableExpansion(node) { return node.type === 'variable_expansion'; } function isVariableExpansionWithName(node, variableName) { return node.type === 'variable_expansion' && node.text === `$${variableName}`; } function isVariable(node) { if (isVariableDefinition(node)) { return true; } else { return ['variable_expansion', 'variable_name'].includes(node.type); } } function isCompleteFlagCommandName(node) { if (node.parent && isCommandWithName(node, 'set')) { const children = node.parent.childrenForFieldName('arguments').filter(n => !isOption(n)); if (children && children.at(0)?.equals(node)) { return node.text.startsWith('_flag_'); } } return false; } function findPreviousSibling(node) { let currentNode = node; if (!currentNode) { return null; } while (currentNode !== null) { if (isCommand(currentNode)) { return currentNode; } currentNode = currentNode.parent; } return null; } function findParentCommand(node) { let currentNode = node; if (!currentNode) { return null; } while (currentNode !== null) { if (isCommand(currentNode)) { return currentNode; } currentNode = currentNode.parent; } return null; } function isConcatenation(node) { return node.type === 'concatenation'; } function isAliasWithName(node, aliasName) { if ((0, barrel_2.isAliasDefinitionName)(node)) { return node.text.split('=').at(0) === aliasName; } return false; } function findParentFunction(node) { let currentNode = node; if (!currentNode) { return null; } while (currentNode !== null) { if (isFunctionDefinition(currentNode)) { return currentNode; } currentNode = currentNode.parent; } return null; } function findParentVariableDefinitionKeyword(node) { if (!node || !(0, barrel_2.isVariableDefinitionName)(node)) return null; const currentNode = node; const parent = currentNode?.parent; if (!currentNode || !parent) { return null; } const varKeyword = parent.firstChild?.text.trim() || ''; if (!varKeyword) { return null; } if (barrel_1.VariableDefinitionKeywords.includes(varKeyword)) { return parent; } return null; } function findForLoopVariable(node) { for (let i = 0; i < node.children.length; i++) { const child = node.children[i]; if (child?.type === 'variable_name') { return child; } } return null; } function findSetDefinedVariable(node) { const parent = findParentCommand(node); if (!parent) { return null; } const children = parent.children; let i = 1; let child = children[i]; while (child !== undefined) { if (!child.text.startsWith('-')) { return child; } if (i === children.length - 1) { return null; } child = children[i++]; } return child; } function hasParent(node, callbackfn) { let currentNode = node; while (currentNode !== null) { if (callbackfn(currentNode)) { return true; } currentNode = currentNode.parent; } return false; } function findParent(node, callbackfn) { let currentNode = node; while (currentNode !== null) { if (callbackfn(currentNode)) { return currentNode; } currentNode = currentNode.parent; } return null; } function hasParentFunction(node) { let currentNode = node; while (currentNode !== null) { if (isFunctionDefinition(currentNode) || currentNode.type === 'function') { return true; } if (currentNode.parent === null) { return false; } currentNode = currentNode?.parent; } return false; } function findFunctionScope(node) { while (node.parent !== null) { if (isFunctionDefinition(node)) { return node; } node = node.parent; } return node; } function scopeCheck(node1, node2) { const scope1 = findFunctionScope(node1); const scope2 = findFunctionScope(node2); if (isProgram(scope1)) { return true; } return scope1 === scope2; } function wordNodeIsCommand(node) { if (node.type !== 'word') { return false; } return node.parent ? isCommand(node.parent) && node.parent.firstChild?.text === node.text : false; } function isSwitchStatement(node) { return node.type === 'switch_statement'; } function isCaseClause(node) { return node.type === 'case_clause'; } function isReturn(node) { return node.type === 'return' && node.firstChild?.text === 'return'; } function isConditionalCommand(node) { return node.type === 'conditional_execution'; } function isCommandFlag(node) { return [ 'test_option', 'word', 'escape_sequence', ].includes(node.type) || node.text.startsWith('-') || findParentCommand(node) !== null; } function isRegexArgument(n) { return n.text === '--regex' || n.text === '-r'; } function isUnmatchedStringCharacter(node) { if (!isStringCharacter(node)) { return false; } if (node.parent && isString(node.parent)) { return false; } return true; } function isPartialForLoop(node) { const semiCompleteForLoop = ['for', 'i', 'in', '_']; const errorNode = node.parent; if (node.text === 'for' && node.type === 'for') { if (!errorNode) { return true; } if ((0, tree_sitter_1.getLeafNodes)(errorNode).length < semiCompleteForLoop.length) { return true; } return false; } if (!errorNode) { return false; } return (errorNode.hasError && errorNode.text.startsWith('for') && !errorNode.text.includes(' in ')); } function isInlineComment(node) { if (!isComment(node)) return false; const previousSibling = node.previousNamedSibling; if (!previousSibling) return false; return previousSibling?.startPosition.row === node.startPosition.row && previousSibling?.type !== 'comment'; } function isCommandWithName(node, ...commandNames) { if (node.type !== 'command') return false; return !!node.firstChild && commandNames.includes(node.firstChild.text); } function isArgumentThatCanContainCommandCalls(node) { if ((0, barrel_1.isDefinitionName)(node) || isCommand(node) || isCommandName(node) || !node.isNamed) return false; const parent = findParent(node, (n) => isCommand(n) || isFunctionDefinition(n)); if (!parent) return false; if (isFunctionDefinition(parent)) { return (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-w', '--wraps').withValue()); } const commandName = parent.firstNamedChild?.text; if (!commandName) return false; switch (commandName) { case 'complete': return (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-w', '--wraps').withValue()) || (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-c', '--command').withValue()) || (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-a', '--arguments').withValue()) || (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-n', '--condition').withValue()); case 'alias': case 'bind': return true; case 'abbr': return (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-f', '--function').withValue()) || (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-c', '--command').withValue()); case 'argparse': return (0, options_1.isMatchingOptionValue)(node, options_1.Option.create('-n', '--name').withValue()); default: return false; } } function isStringWithCommandCall(node) { if (!isString(node)) return false; const parent = findParent(node, (n) => isFunctionDefinition(n) || isCommand(n)); if (!parent) return false; if (isFunctionDefinition(parent)) { return (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-w', '--wraps').withValue()); } if (isCommand(parent)) { const parentCommandName = parent.firstChild?.text; if (!parentCommandName) return false; switch (parentCommandName) { case 'complete': return (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-w', '--wraps').withValue()) || (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-c', '--command').withValue()) || (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-a', '--arguments').withValue()) || (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-n', '--condition').withValue()); case 'alias': case 'bind': return true; case 'abbr': return (0, options_1.isMatchingOptionOrOptionValue)(node, options_1.Option.create('-f', '--function').withValue()); } } return false; } function isReturnStatusNumber(node) { if (node.type !== 'integer') return false; const parent = node.parent; if (!parent) return false; return parent.type === 'return'; } function isCompleteCommandName(node) { if (!node.parent || !isCommand(node.parent)) return false; if (!isCommandWithName(node.parent, 'complete')) return false; const previousSibling = node.previousNamedSibling; if (!previousSibling) return false; if ((0, options_1.isMatchingOption)(previousSibling, options_1.Option.create('-c', '--command').withValue())) { return !isOption(node); } return false; }