fish-lsp
Version:
LSP implementation for fish/fish-shell
227 lines (226 loc) • 8.78 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.containsRange = containsRange;
exports.precedesRange = precedesRange;
exports.canRenamePosition = canRenamePosition;
exports.getRenameSymbolType = getRenameSymbolType;
exports.getRenameLocations = getRenameLocations;
exports.getReferenceLocations = getReferenceLocations;
exports.getRenameFiles = getRenameFiles;
exports.getRenameWorkspaceEdit = getRenameWorkspaceEdit;
exports.findDefinitionSymbols = findDefinitionSymbols;
const document_symbol_1 = require("./document-symbol");
const vscode_languageserver_1 = require("vscode-languageserver");
const tree_sitter_1 = require("./utils/tree-sitter");
const node_types_1 = require("./utils/node-types");
function containsRange(range, otherRange) {
if (otherRange.start.line < range.start.line || otherRange.end.line < range.start.line) {
return false;
}
if (otherRange.start.line > range.end.line || otherRange.end.line > range.end.line) {
return false;
}
if (otherRange.start.line === range.start.line && otherRange.start.character < range.start.character) {
return false;
}
if (otherRange.end.line === range.end.line && otherRange.end.character > range.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 canRenamePosition(analyzer, document, position) {
return !!analyzer.findDocumentSymbol(document, position);
}
function getRenameSymbolType(analyzer, document, position) {
const symbol = analyzer.findDocumentSymbol(document, position);
if (!symbol) {
return 'local';
}
if ((0, document_symbol_1.isGlobalSymbol)(symbol) || (0, document_symbol_1.isUniversalSymbol)(symbol)) {
return 'global';
}
return 'local';
}
function findLocations(uri, nodes, matchName) {
const 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);
};
const matchingNames = nodes.filter(node => node.text === matchName);
const uniqueRanges = [];
matchingNames.forEach(node => {
const range = (0, tree_sitter_1.getRange)(node);
if (uniqueRanges.some(u => equalRanges(u, range))) {
return;
}
uniqueRanges.push(range);
});
return uniqueRanges.map(range => vscode_languageserver_1.Location.create(uri, range));
}
function findLocalLocations(analyzer, document, position) {
const symbol = findDefinitionSymbols(analyzer, document, position).pop();
if (!symbol) {
return [];
}
const nodesToSearch = (0, tree_sitter_1.getChildNodes)(symbol.scope.scopeNode);
return findLocations(document.uri, nodesToSearch, symbol.name);
}
function removeLocalSymbols(matchSymbol, nodes, symbols) {
const name = matchSymbol.name;
const matchingSymbols = (0, document_symbol_1.filterLocalSymbols)(symbols.filter(symbol => symbol.name === name)).map(symbol => symbol.scope.scopeNode);
const matchingNodes = nodes.filter(node => node.text === name);
if (matchingSymbols.length === 0 || matchSymbol.kind === vscode_languageserver_1.SymbolKind.Function) {
return matchingNodes;
}
return matchingNodes.filter((node) => {
if (matchingSymbols.some(scopeNode => containsRange((0, tree_sitter_1.getRange)(scopeNode), (0, tree_sitter_1.getRange)(node)))) {
return false;
}
return true;
});
}
function findGlobalLocations(analyzer, document, position) {
const locations = [];
const symbol = analyzer.findDocumentSymbol(document, position);
if (!symbol) {
return [];
}
const uris = analyzer.cache.uris();
for (const uri of uris) {
const doc = analyzer.getDocument(uri);
if (!doc.isAutoloaded()) {
continue;
}
const rootNode = analyzer.getRootNode(doc);
const toSearchNodes = removeLocalSymbols(symbol, (0, tree_sitter_1.getChildNodes)(rootNode), analyzer.cache.getFlatDocumentSymbols(uri));
const newLocations = findLocations(uri, toSearchNodes, symbol.name);
locations.push(...newLocations);
}
return locations;
}
function getRenameLocations(analyzer, document, position) {
if (!canRenamePosition(analyzer, document, position)) {
return [];
}
const renameScope = getRenameSymbolType(analyzer, document, position);
switch (renameScope) {
case 'local':
return findLocalLocations(analyzer, document, position);
case 'global':
return findGlobalLocations(analyzer, document, position);
default:
return [];
}
}
function getReferenceLocations(analyzer, document, position) {
const node = analyzer.nodeAtPoint(document.uri, position.line, position.character);
if (!node)
return [];
const symbol = analyzer.getDefinition(document, position);
if (symbol) {
const doc = analyzer.getDocument(symbol.uri);
const { scopeTag } = symbol.scope;
switch (scopeTag) {
case 'global':
case 'universal':
return findGlobalLocations(analyzer, doc, symbol.selectionRange.start);
case 'local':
default:
return findLocalLocations(analyzer, document, symbol.selectionRange.start);
}
}
if ((0, node_types_1.isCommandName)(node)) {
const uris = analyzer.cache.uris();
const locations = [];
for (const uri of uris) {
const doc = analyzer.getDocument(uri);
const rootNode = analyzer.getRootNode(doc);
const nodes = (0, tree_sitter_1.getChildNodes)(rootNode).filter(n => (0, node_types_1.isCommandName)(n));
const newLocations = findLocations(uri, nodes, node.text);
locations.push(...newLocations);
}
return locations;
}
return [];
}
const createRenameFile = (oldUri, newUri) => {
return {
kind: 'rename',
oldUri,
newUri,
};
};
function getRenameFiles(analyzer, document, position, newName) {
const renameFiles = [];
const symbol = analyzer.findDocumentSymbol(document, position);
if (!symbol) {
return null;
}
if (symbol.kind !== vscode_languageserver_1.SymbolKind.Function) {
return null;
}
if ((0, document_symbol_1.symbolIsImmutable)(symbol)) {
return null;
}
if (symbol.scope.scopeTag === 'global') {
analyzer.getExistingAutoloadedFiles(symbol.name).forEach(uri => {
const newUri = uri.replace(symbol.name, newName);
renameFiles.push(createRenameFile(uri, newUri));
});
}
return renameFiles;
}
function getRenameWorkspaceEdit(analyzer, document, position, newName) {
const locations = getRenameLocations(analyzer, document, position);
if (!locations || locations.length === 0) {
return null;
}
const changes = {};
for (const location of locations) {
const uri = location.uri;
const edits = changes[uri] || [];
edits.push(vscode_languageserver_1.TextEdit.replace(location.range, newName));
changes[uri] = edits;
}
const documentChanges = getRenameFiles(analyzer, document, position, newName);
if (documentChanges && documentChanges.length > 0) {
return { changes, documentChanges };
}
return { changes };
}
function findDefinitionSymbols(analyzer, document, position) {
const symbols = [];
const localSymbols = analyzer.getFlatDocumentSymbols(document.uri);
const toFind = analyzer.nodeAtPoint(document.uri, position.line, position.character);
if (!toFind) {
return [];
}
const localSymbol = analyzer.findDocumentSymbol(document, position);
if (localSymbol) {
symbols.push(localSymbol);
}
else {
const toAdd = localSymbols.filter((s) => {
const variableBefore = s.kind === vscode_languageserver_1.SymbolKind.Variable ? precedesRange(s.selectionRange, (0, tree_sitter_1.getRange)(toFind)) : true;
return (s.name === toFind.text
&& containsRange((0, tree_sitter_1.getRange)(s.scope.scopeNode), (0, tree_sitter_1.getRange)(toFind))
&& variableBefore);
});
symbols.push(...toAdd);
}
if (!symbols.length) {
symbols.push(...analyzer.globalSymbols.find(toFind.text));
}
return symbols;
}