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