UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

212 lines (211 loc) 8.09 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VariableDefinitionFlag = exports.DefinitionScope = void 0; exports.getVariableScope = getVariableScope; exports.getScope = getScope; exports.expandEntireVariableLine = expandEntireVariableLine; exports.setQuery = setQuery; const NodeTypes = __importStar(require("./node-types")); const translation_1 = require("./translation"); const tree_sitter_1 = require("./tree-sitter"); var DefinitionScope; (function (DefinitionScope) { function create(scopeNode, scopeTag) { return { scopeNode, scopeTag, containsPosition: (position) => (0, tree_sitter_1.isPositionWithinRange)(position, (0, tree_sitter_1.getRange)(scopeNode)), }; } DefinitionScope.create = create; })(DefinitionScope || (exports.DefinitionScope = DefinitionScope = {})); class VariableDefinitionFlag { short; long; constructor(short, long) { this.short = short; this.long = long; } isMatch(node) { if (!NodeTypes.isOption(node)) { return false; } if (NodeTypes.isShortOption(node)) { return node.text.slice(1).split('').includes(this.short); } if (NodeTypes.isLongOption(node)) { return node.text.slice(2) === this.long; } return false; } get kind() { return this.long; } } exports.VariableDefinitionFlag = VariableDefinitionFlag; const variableDefinitionFlags = [ new VariableDefinitionFlag('g', 'global'), new VariableDefinitionFlag('l', 'local'), new VariableDefinitionFlag('', 'inherit'), //new VariableDefinitionFlag('x', 'export'), new VariableDefinitionFlag('f', 'function'), new VariableDefinitionFlag('U', 'universal'), ]; const hasParentFunction = (node) => { return !!(0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isFunctionDefinition); }; function getMatchingFlags(focusedNode, nodes) { for (const node of nodes) { const match = variableDefinitionFlags.find(flag => flag.isMatch(node)); if (match) { return match; } } return hasParentFunction(focusedNode) ? new VariableDefinitionFlag('f', 'function') : new VariableDefinitionFlag('', 'inherit'); } function findScopeFromFlag(node, flag) { let scopeNode = node.parent; let scopeFlag = flag.kind; switch (flag.kind) { case 'global': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isProgram); scopeFlag = 'global'; break; case 'universal': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isProgram); scopeFlag = 'universal'; break; case 'local': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isScope); //scopeFlag = 'local' break; case 'function': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isFunctionDefinition); scopeFlag = 'function'; break; case 'for_scope': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isFunctionDefinition); scopeFlag = 'function'; if (!scopeNode) { scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isProgram); scopeFlag = 'global'; } break; // case 'for_scope': // scopeNode = firstAncestorMatch(node, NodeTypes.isFunctionDefinition); // scopeFlag = 'function'; // if (!scopeNode) { // scopeNode = firstAncestorMatch(node, NodeTypes.isProgram); // scopeFlag = 'global'; // } // break; case 'inherit': scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isScope); scopeFlag = 'inherit'; break; default: scopeNode = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isScope); //scopeFlag = 'local' break; } const finalScopeNode = scopeNode || node.parent; return DefinitionScope.create(finalScopeNode, scopeFlag); } function getVariableScope(node) { const definitionNodes = expandEntireVariableLine(node); const keywordNode = definitionNodes[0]; let matchingFlag = null; switch (keywordNode.text) { case 'for': matchingFlag = new VariableDefinitionFlag('', 'for_scope'); break; case 'set': case 'read': case 'function': default: matchingFlag = getMatchingFlags(node, definitionNodes); break; } const scope = findScopeFromFlag(node, matchingFlag); return scope; } function getScope(document, node) { if (NodeTypes.isFunctionDefinitionName(node)) { const isAutoloadedName = (0, translation_1.isAutoloadedUriLoadsFunctionName)(document); // gets <HERE> from ~/.config/fish/functions/<HERE>.fish // const loadedName = pathToRelativeFunctionName(uri); // we know node.parent must exist because a isFunctionDefinitionName() must have // a isFunctionDefinition() parent node. We know there must be atleast one parent // because isProgram() is a valid parent node. const parents = (0, tree_sitter_1.getParentNodes)(node.parent.parent) || (0, tree_sitter_1.getParentNodes)(node.parent); const firstParent = parents .filter(n => NodeTypes.isProgram(n) || NodeTypes.isFunctionDefinition(n)) .at(0); // if the function name is autoloaded or in config.fish if (isAutoloadedName(node)) { const program = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isProgram); return DefinitionScope.create(program, 'global'); } return DefinitionScope.create(firstParent, 'local'); } else if (NodeTypes.isVariableDefinitionName(node)) { return getVariableScope(node); } // should not ever happen with current LSP implementation const scope = (0, tree_sitter_1.firstAncestorMatch)(node, NodeTypes.isScope); return DefinitionScope.create(scope, 'local'); } function expandEntireVariableLine(node) { const results = [node]; let current = node.previousSibling; while (current !== null) { if (!current || NodeTypes.isNewline(current)) { break; } results.unshift(current); current = current.previousSibling; } current = node.nextSibling; while (current !== null) { if (!current || NodeTypes.isNewline(current)) { break; } results.push(current); current = current.nextSibling; } return results; } function setQuery(searchNodes) { const queryFlag = new VariableDefinitionFlag('q', 'query'); for (const flag of searchNodes) { if (queryFlag.isMatch(flag)) { return true; } } return false; }