fish-lsp
Version:
LSP implementation for fish/fish-shell
297 lines (296 loc) • 12.8 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.ArparseOptions = void 0;
exports.findArgparseOptions = findArgparseOptions;
exports.findArgparseDefinitionNames = findArgparseDefinitionNames;
exports.isArgparseVariableDefinitionName = isArgparseVariableDefinitionName;
exports.convertNodeRangeWithPrecedingFlag = convertNodeRangeWithPrecedingFlag;
exports.isGlobalArgparseDefinition = isGlobalArgparseDefinition;
exports.getGlobalArgparseLocations = getGlobalArgparseLocations;
exports.getArgparseDefinitionName = getArgparseDefinitionName;
exports.isCompletionArgparseFlagWithCommandName = isCompletionArgparseFlagWithCommandName;
exports.processArgparseCommand = processArgparseCommand;
const node_types_1 = require("../utils/node-types");
const options_1 = require("./options");
const symbol_1 = require("./symbol");
const definition_scope_1 = require("../utils/definition-scope");
const tree_sitter_1 = require("../utils/tree-sitter");
const analyze_1 = require("../analyze");
const path_1 = __importStar(require("path"));
const file_operations_1 = require("../utils/file-operations");
const translation_1 = require("../utils/translation");
const workspace_manager_1 = require("../utils/workspace-manager");
const logger_1 = require("../logger");
exports.ArparseOptions = [
options_1.Option.create('-n', '--name').withValue(),
options_1.Option.create('-x', '--exclusive').withValue(),
options_1.Option.create('-N', '--min-args').withValue(),
options_1.Option.create('-X', '--max-args').withValue(),
options_1.Option.create('-i', '--ignore-unknown'),
options_1.Option.create('-s', '--stop-nonopt'),
options_1.Option.create('-h', '--help'),
];
const isBefore = (a, b) => a.startIndex < b.startIndex;
function findArgparseOptions(node) {
if ((0, node_types_1.isCommandWithName)(node, 'argparse'))
return undefined;
const endChar = node.children.find(node => (0, node_types_1.isEndStdinCharacter)(node));
if (!endChar)
return undefined;
const nodes = node.childrenForFieldName('argument')
.filter(n => !(0, node_types_1.isEscapeSequence)(n) && isBefore(n, endChar))
.filter(n => !(0, node_types_1.isVariableExpansion)(n) || n.type !== 'variable_name');
return (0, options_1.findOptions)(nodes, exports.ArparseOptions);
}
function findArgparseDefinitionNames(node) {
if (!node || !(0, node_types_1.isCommandWithName)(node, 'argparse'))
return [];
const endChar = node.children.find(node => (0, node_types_1.isEndStdinCharacter)(node));
if (!endChar)
return [];
const nodes = node.childrenForFieldName('argument')
.filter(n => !(0, node_types_1.isEscapeSequence)(n) && isBefore(n, endChar))
.filter(n => !(0, node_types_1.isVariableExpansion)(n) || n.type !== 'variable_name');
const { remaining } = (0, options_1.findOptions)(nodes, exports.ArparseOptions);
return remaining;
}
function isArgparseVariableDefinitionName(node) {
if (!node.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'argparse'))
return false;
const children = findArgparseDefinitionNames(node.parent);
return !!children.some(n => n.equals(node));
}
function convertNodeRangeWithPrecedingFlag(node) {
const range = (0, tree_sitter_1.getRange)(node);
if (node.text.startsWith('_flag_')) {
range.start = {
line: range.start.line,
character: range.start.character + 6,
};
}
return range;
}
function isGlobalArgparseDefinition(document, symbol) {
if (!symbol.isArgparse() || !symbol.isFunction())
return false;
let parent = symbol.parent;
if (symbol.isFunction() && symbol.isGlobal()) {
parent = symbol;
}
if (parent && parent?.isFunction()) {
const functionName = parent.name;
if (document.getAutoLoadName() !== functionName) {
return false;
}
const filepath = document.getFilePath();
const workspaceDirectory = workspace_manager_1.workspaceManager.findContainingWorkspace(document.uri)?.path || (0, path_1.dirname)((0, path_1.dirname)(filepath));
const completionFile = document.getAutoloadType() === 'conf.d' || document.getAutoloadType() === 'config'
? document.getFilePath()
: path_1.default.join(workspaceDirectory, 'completions', document.getFilename());
if (process.env.NODE_ENV !== 'test' && !file_operations_1.SyncFileHelper.isFile(completionFile)) {
return false;
}
return analyze_1.analyzer.getFlatCompletionSymbols((0, translation_1.pathToUri)(completionFile)).length > 0;
}
return false;
}
function getGlobalArgparseLocations(document, symbol) {
if (isGlobalArgparseDefinition(document, symbol)) {
const filepath = (0, translation_1.uriToPath)(document.uri);
const workspaceDirectory = workspace_manager_1.workspaceManager.findContainingWorkspace(document.uri)?.path || (0, path_1.dirname)((0, path_1.dirname)(filepath));
logger_1.logger.log(`Getting global argparse locations for symbol: ${symbol.name} in file: ${filepath}`, {
filepath,
workspaceDirectory,
});
const completionFile = document.getAutoloadType() === 'conf.d' || document.getAutoloadType() === 'config'
? document.getFilePath()
: path_1.default.join(workspaceDirectory, 'completions', document.getFilename());
if (process.env.NODE_ENV !== 'test' && !file_operations_1.SyncFileHelper.isFile(completionFile)) {
logger_1.logger.debug({
env: 'test',
message: `Completion file does not exist: ${completionFile}`,
});
return [];
}
logger_1.logger.debug({
message: `Getting global argparse locations for symbol: ${symbol.name} in file: ${completionFile}`,
});
const completionLocations = analyze_1.analyzer
.getFlatCompletionSymbols((0, translation_1.pathToUri)(completionFile))
.filter(s => s.isNonEmpty())
.filter(s => s.equalsArgparse(symbol) || s.equalsCommand(symbol))
.map(s => s.toLocation());
logger_1.logger.log(`Found ${completionLocations.length} global argparse locations for symbol: ${symbol.name}`, 'HERE');
return completionLocations;
}
logger_1.logger.warning(`no global argparse locations found for symbol: ${symbol.name}`, 'HERE');
return [];
}
function getArgparseScopeModifier(document, _node) {
const autoloadType = document.getAutoloadType();
switch (autoloadType) {
case 'conf.d':
case 'config':
case 'functions':
return 'local';
default:
return 'local';
}
}
function getArgparseDefinitionName(node) {
if (!node.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'complete'))
return '';
if (node.text) {
const text = `_flag_${node.text}`;
return text.replace(/-/, '_');
}
return '';
}
function isCompletionArgparseFlagWithCommandName(node, commandName, flagName, opts) {
if (!node?.parent || !(0, node_types_1.isCommandWithName)(node.parent, 'complete'))
return false;
const parent = node.parent;
if (opts?.discardIfContainsOptions) {
for (const option of opts.discardIfContainsOptions) {
if (parent.children.some(c => option.matches(c))) {
return false;
}
}
}
let completeCmdName = !!parent.children.find(c => c.previousSibling &&
(0, options_1.isMatchingOption)(c.previousSibling, options_1.Option.create('-c', '--command')) &&
c.text === commandName);
if (opts?.noCommandNameAllowed && !completeCmdName) {
completeCmdName = !parent.children.some(c => c.previousSibling &&
(0, options_1.isMatchingOption)(c.previousSibling, options_1.Option.create('-c', '--command')));
}
const option = flagName.length === 1
? options_1.Option.create('-s', '--short')
: options_1.Option.create('-l', '--long');
const completeFlagName = !!(node.previousSibling &&
option.equals(node.previousSibling) &&
node.text === flagName);
return completeCmdName && completeFlagName;
}
function createSelectionRange(node, flags, flag, idx) {
const range = (0, tree_sitter_1.getRange)(node);
const text = node.text;
const shortenedFlag = flag.replace(/^_flag_/, '');
if (flags.length === 2 && idx === 0) {
if ((0, node_types_1.isString)(node)) {
range.start = {
line: range.start.line,
character: range.start.character + 1,
};
range.end = {
line: range.start.line,
character: range.start.character - 1,
};
}
return {
start: range.start,
end: {
line: range.start.line,
character: range.start.character + shortenedFlag.length,
},
};
}
else if (flags.length === 2 && idx === 1) {
return {
start: {
line: range.start.line,
character: range.start.character + text.indexOf('/') + 1,
},
end: {
line: range.end.line,
character: range.start.character + text.indexOf('/') + 1 + shortenedFlag.length,
},
};
}
else if (flags.length === 1) {
if ((0, node_types_1.isString)(node)) {
return {
start: {
line: range.start.line,
character: range.start.character + 1,
},
end: {
line: range.start.line,
character: range.start.character + 1 + shortenedFlag.length,
},
};
}
else {
return (0, tree_sitter_1.getRange)(node);
}
}
return range;
}
function splitSlash(str) {
const results = str.split('/')
.map(s => s.trim().replace(/-/g, '_'));
const maxResults = results.length < 2 ? results.length : 2;
return results.slice(0, maxResults);
}
function getNames(flags) {
return flags.map(flag => {
return `_flag_${flag}`;
});
}
function processArgparseCommand(document, node, children = []) {
const result = [];
const modifier = getArgparseScopeModifier(document, node);
const focuesedNodes = findArgparseDefinitionNames(node);
for (const n of focuesedNodes) {
let flagNames = n.text;
if (!flagNames)
continue;
if ((0, node_types_1.isString)(n))
flagNames = flagNames.slice(1, -1);
if (flagNames.includes('='))
flagNames = flagNames.slice(0, flagNames.indexOf('='));
const seenFlags = splitSlash(flagNames);
const names = getNames(seenFlags);
const flags = names.map((flagName, idx) => {
const selectedRange = createSelectionRange(n, seenFlags, flagName, idx);
return symbol_1.FishSymbol.fromObject({
name: flagName,
node: node,
focusedNode: n,
fishKind: 'ARGPARSE',
document: document,
uri: document.uri,
detail: n.text,
range: (0, tree_sitter_1.getRange)(n),
selectionRange: selectedRange,
scope: definition_scope_1.DefinitionScope.create(node.parent, modifier),
children,
}).addAliasedNames(...names);
});
result.push(...flags);
}
return result;
}