fish-lsp
Version:
LSP implementation for fish/fish-shell
451 lines (450 loc) • 14.9 kB
JavaScript
;
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 });
}