UNPKG

fish-lsp

Version:

LSP implementation for fish/fish-shell

451 lines (450 loc) 14.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isSyntaxNode = isSyntaxNode; exports.getChildNodes = getChildNodes; exports.getNamedChildNodes = getNamedChildNodes; exports.findChildNodes = findChildNodes; exports.getParentNodes = getParentNodes; exports.getParentNodesGen = getParentNodesGen; exports.nodesGen = nodesGen; exports.namedNodesGen = namedNodesGen; exports.findFirstParent = findFirstParent; exports.getSiblingNodes = getSiblingNodes; exports.findFirstNamedSibling = findFirstNamedSibling; exports.findFirstSibling = findFirstSibling; exports.findEnclosingScope = findEnclosingScope; exports.getNodeText = getNodeText; exports.getNodesTextAsSingleLine = getNodesTextAsSingleLine; exports.firstAncestorMatch = firstAncestorMatch; exports.ancestorMatch = ancestorMatch; exports.descendantMatch = descendantMatch; exports.hasNode = hasNode; exports.getNamedNeighbors = getNamedNeighbors; exports.getRange = getRange; exports.findNodeAt = findNodeAt; exports.equalRanges = equalRanges; exports.containsRange = containsRange; exports.precedesRange = precedesRange; exports.getNodeAt = getNodeAt; exports.containsNode = containsNode; exports.getNodeAtRange = getNodeAtRange; exports.positionToPoint = positionToPoint; exports.pointToPosition = pointToPosition; exports.rangeToPoint = rangeToPoint; exports.getRangeWithPrecedingComments = getRangeWithPrecedingComments; exports.getPrecedingComments = getPrecedingComments; exports.isFishExtension = isFishExtension; exports.isPositionWithinRange = isPositionWithinRange; exports.isPositionAfter = isPositionAfter; exports.isNodeWithinRange = isNodeWithinRange; exports.isNodeWithinOtherNode = isNodeWithinOtherNode; exports.getLeafNodes = getLeafNodes; exports.getLastLeafNode = getLastLeafNode; exports.getNodeAtPosition = getNodeAtPosition; const path_1 = require("path"); const vscode_languageserver_1 = require("vscode-languageserver"); const node_types_1 = require("./node-types"); function isSyntaxNode(obj) { return typeof obj === 'object' && obj !== null && 'id' in obj && 'type' in obj && 'text' in obj && 'tree' in obj && 'startPosition' in obj && 'endPosition' in obj && 'children' in obj && 'equals' in obj && 'isNamed' in obj && 'isMissing' in obj && 'isError' in obj && 'isExtra' in obj && typeof obj.id === 'number' && typeof obj.isNamed === 'boolean' && typeof obj.isMissing === 'boolean' && typeof obj.isError === 'boolean' && typeof obj.isExtra === 'boolean' && typeof obj.type === 'string' && typeof obj.text === 'string' && typeof obj.equals === 'function' && Array.isArray(obj.children); } function getChildNodes(root) { const queue = [root]; const result = []; while (queue.length) { const current = queue.shift(); if (current) { result.push(current); } if (current && current.children) { queue.unshift(...current.children); } } return result; } function getNamedChildNodes(root) { const queue = [root]; const result = []; while (queue.length) { const current = queue.shift(); if (current && current.isNamed) { result.push(current); } if (current && current.children) { queue.unshift(...current.children); } } return result; } function findChildNodes(root, predicate) { const queue = [root]; const result = []; while (queue.length) { const current = queue.shift(); if (current && predicate(current)) { result.push(current); } if (current && current.children) { queue.unshift(...current.children); } } return result; } function getParentNodes(child) { const result = []; let current = child; while (current !== null) { if (current) { result.push(current); } current = current?.parent || null; } return result; } function* getParentNodesGen(child, includeSelf = false) { let current = includeSelf ? child : child.parent; while (current !== null) { yield current; current = current.parent; } } function* nodesGen(node) { const queue = [node]; while (queue.length) { const n = queue.shift(); if (!n) { return; } if (n.children.length) { queue.unshift(...n.children); } yield n; } } function* namedNodesGen(node) { const queue = [node]; while (queue.length) { const n = queue.shift(); if (!n?.isNamed) { return; } if (n.children.length) { queue.unshift(...n.children); } yield n; } } function findFirstParent(node, predicate) { let current = node.parent; while (current !== null) { if (predicate(current)) { return current; } current = current.parent; } return null; } function getSiblingNodes(node, predicate, direction = 'before') { const siblingFunc = (n) => direction === 'before' ? n.previousNamedSibling : n.nextNamedSibling; let current = node; const result = []; while (current) { current = siblingFunc(current); if (current && predicate(current)) { result.push(current); } } return result; } function findFirstNamedSibling(node, predicate, direction = 'before') { const siblingFunc = (n) => direction === 'before' ? n.previousNamedSibling : n.nextNamedSibling; let current = node; while (current) { current = siblingFunc(current); if (current && predicate(current)) { return current; } } return null; } function findFirstSibling(node, predicate, direction = 'before') { const siblingFunc = (n) => direction === 'before' ? n.previousSibling : n.nextSibling; let current = node; while (current) { current = siblingFunc(current); if (current && predicate(current)) { return current; } } return null; } const findFirstParentFunctionOrProgram = (parent) => { const result = findFirstParent(parent, n => (0, node_types_1.isFunctionDefinition)(n) || (0, node_types_1.isProgram)(n)); if (result) { return result; } return parent; }; function findEnclosingScope(node) { let parent = node.parent || node; if ((0, node_types_1.isFunctionDefinitionName)(node)) { return findFirstParentFunctionOrProgram(parent); } else if (node.text === 'argv') { parent = findFirstParentFunctionOrProgram(parent); return (0, node_types_1.isFunctionDefinition)(parent) ? parent.firstNamedChild || parent : parent; } else if ((0, node_types_1.isVariable)(node)) { parent = findFirstParent(node, n => (0, node_types_1.isScope)(n)) || parent; return (0, node_types_1.isForLoop)(parent) && (0, node_types_1.findForLoopVariable)(parent)?.text === node.text ? parent : findFirstParent(node, n => (0, node_types_1.isProgram)(n) || (0, node_types_1.isFunctionDefinitionName)(n)) || parent; } else if ((0, node_types_1.isCommandName)(node)) { return findFirstParent(node, n => (0, node_types_1.isProgram)(n)) || parent; } else { return findFirstParent(node, n => (0, node_types_1.isScope)(n)) || parent; } } function getNodeText(node) { if (!node) { return ''; } if ((0, node_types_1.isFunctionDefinition)(node)) { return node.child(1)?.text || ''; } if ((0, node_types_1.isVariableDefinition)(node)) { const defVar = (0, node_types_1.findSetDefinedVariable)(node); return defVar.text || ''; } return node.text !== null ? node.text.trim() : ''; } function getNodesTextAsSingleLine(nodes) { let text = ''; for (const node of nodes) { text += ' ' + node.text.split('\n').map(n => n.split(' ').map(n => n.trim()).join(' ')).map(n => n.trim()).join(';'); if (!text.endsWith(';')) { text += ';'; } } return text.replaceAll(/;+/g, ';').trim(); } function firstAncestorMatch(start, predicate) { const ancestors = getParentNodes(start) || []; const root = ancestors[ancestors.length - 1]; for (const p of ancestors) { if (!predicate(p)) { continue; } return p; } return !!root && predicate(root) ? root : null; } function ancestorMatch(start, predicate, inclusive = true) { const ancestors = getParentNodes(start) || []; const searchNodes = []; for (const p of ancestors) { searchNodes.push(...getChildNodes(p)); } const results = searchNodes.filter(neighbor => predicate(neighbor)); return inclusive ? results : results.filter(ancestor => ancestor !== start); } function descendantMatch(start, predicate, inclusive = true) { const descendants = []; descendants.push(...getChildNodes(start)); const results = descendants.filter(descendant => predicate(descendant)); return inclusive ? results : results.filter(r => r !== start); } function hasNode(allNodes, matchNode) { for (const node of allNodes) { if (node.equals(matchNode)) { return true; } } return false; } function getNamedNeighbors(node) { return node.parent?.namedChildren || []; } function getRange(node) { return vscode_languageserver_1.Range.create(node.startPosition.row, node.startPosition.column, node.endPosition.row, node.endPosition.column); } function findNodeAt(tree, line, column) { if (!tree.rootNode) { return null; } let currentCol = column; const currentLine = line; while (currentLine > 0) { const currentNode = tree.rootNode.descendantForPosition({ row: currentLine, column: currentCol }); if (currentNode) { return currentNode; } currentCol--; } return tree.rootNode.descendantForPosition({ row: line, column }); } function equalRanges(a, b) { return (a.start.line === b.start.line && a.start.character === b.start.character && a.end.line === b.end.line && a.end.character === b.end.character); } function containsRange(outer, inner) { if (inner.start.line < outer.start.line || inner.end.line < outer.start.line) { return false; } if (inner.start.line > outer.end.line || inner.end.line > outer.end.line) { return false; } if (inner.start.line === outer.start.line && inner.start.character < outer.start.character) { return false; } if (inner.end.line === outer.end.line && inner.end.character > outer.end.character) { return false; } return true; } function precedesRange(before, after) { if (before.start.line < after.start.line) { return true; } if (before.start.line === after.start.line && before.start.character < after.start.character) { return true; } return false; } function getNodeAt(tree, line, column) { if (!tree.rootNode) { return null; } return tree.rootNode.descendantForPosition({ row: line, column }); } function containsNode(outer, inner) { return containsRange(getRange(outer), getRange(inner)); } function getNodeAtRange(root, range) { return root.descendantForPosition(positionToPoint(range.start), positionToPoint(range.end)); } function positionToPoint(pos) { return { row: pos.line, column: pos.character, }; } function pointToPosition(point) { return { line: point.row, character: point.column, }; } function rangeToPoint(range) { return { row: range.start.line, column: range.start.character, }; } function getRangeWithPrecedingComments(node) { let currentNode = node.previousNamedSibling; let previousNode = node; while (currentNode?.type === 'comment') { previousNode = currentNode; currentNode = currentNode.previousNamedSibling; } return vscode_languageserver_1.Range.create(pointToPosition(previousNode.startPosition), pointToPosition(node.endPosition)); } function getPrecedingComments(node) { if (!node) { return ''; } const comments = commentsHelper(node); if (!comments) { return node.text; } return [ commentsHelper(node), node.text, ].join('\n'); } function commentsHelper(node) { if (!node) { return ''; } const comment = []; let currentNode = node.previousNamedSibling; while (currentNode?.type === 'comment') { comment.unshift(currentNode.text); currentNode = currentNode.previousNamedSibling; } return comment.join('\n'); } function isFishExtension(path) { const ext = (0, path_1.extname)(path).toLowerCase(); return ext === '.fish'; } function isPositionWithinRange(position, range) { const doesStartInside = position.line > range.start.line || position.line === range.start.line && position.character >= range.start.character; const doesEndInside = position.line < range.end.line || position.line === range.end.line && position.character <= range.end.character; return doesStartInside && doesEndInside; } function isPositionAfter(first, second) { return (first.line < second.line || first.line === second.line && first.character < second.character); } function isNodeWithinRange(node, range) { const doesStartInside = node.startPosition.row > range.start.line || node.startPosition.row === range.start.line && node.startPosition.column >= range.start.character; const doesEndInside = node.endPosition.row < range.end.line || node.endPosition.row === range.end.line && node.endPosition.column <= range.end.character; return doesStartInside && doesEndInside; } function isNodeWithinOtherNode(node, otherNode) { return isNodeWithinRange(node, getRange(otherNode)); } function getLeafNodes(node) { function gatherLeafNodes(node, leafNodes = []) { if (node.childCount === 0 && node.text !== '') { leafNodes.push(node); return leafNodes; } for (const child of node.children) { leafNodes = gatherLeafNodes(child, leafNodes); } return leafNodes; } return gatherLeafNodes(node); } function getLastLeafNode(node, maxIndex = Infinity) { const allLeafNodes = getLeafNodes(node).filter(leaf => leaf.startPosition.column < maxIndex); return allLeafNodes[allLeafNodes.length - 1]; } function getNodeAtPosition(tree, position) { return tree.rootNode.descendantForPosition({ row: position.line, column: position.character }); }