fish-lsp
Version:
LSP implementation for fish/fish-shell
150 lines (149 loc) • 8.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FunctionEventOptions = exports.FunctionOptions = void 0;
exports.findFunctionDefinitionChildren = findFunctionDefinitionChildren;
exports.processArgvDefinition = processArgvDefinition;
exports.isFunctionDefinitionName = isFunctionDefinitionName;
exports.isFunctionVariableDefinitionName = isFunctionVariableDefinitionName;
exports.findFunctionOptionNamedArguments = findFunctionOptionNamedArguments;
exports.processFunctionDefinition = processFunctionDefinition;
const options_1 = require("./options");
const symbol_1 = require("./symbol");
const node_types_1 = require("../utils/node-types");
const snippets_1 = require("../utils/snippets");
const definition_scope_1 = require("../utils/definition-scope");
const translation_1 = require("../utils/translation");
const tree_sitter_1 = require("../utils/tree-sitter");
const markdown_builder_1 = require("../utils/markdown-builder");
const symbol_kinds_1 = require("./symbol-kinds");
exports.FunctionOptions = [
options_1.Option.create('-a', '--argument-names').withMultipleValues(),
options_1.Option.create('-d', '--description').withValue(),
options_1.Option.create('-w', '--wraps').withValue(),
options_1.Option.create('-e', '--on-event').withValue(),
options_1.Option.create('-v', '--on-variable').withValue(),
options_1.Option.create('-j', '--on-job-exit').withValue(),
options_1.Option.create('-p', '--on-process-exit').withValue(),
options_1.Option.create('-s', '--on-signal').withValue(),
options_1.Option.create('-S', '--no-scope-shadowing'),
options_1.Option.create('-V', '--inherit-variable').withValue(),
];
exports.FunctionEventOptions = [
options_1.Option.create('-e', '--on-event').withValue(),
options_1.Option.create('-v', '--on-variable').withValue(),
options_1.Option.create('-j', '--on-job-exit').withValue(),
options_1.Option.create('-p', '--on-process-exit').withValue(),
options_1.Option.create('-s', '--on-signal').withValue(),
];
function isFunctionDefinition(node) {
return node.type === 'function_definition';
}
function findFunctionDefinitionChildren(node) {
return node.childrenForFieldName('option').filter(n => !(0, node_types_1.isEscapeSequence)(n) && !(0, node_types_1.isNewline)(n));
}
function processArgvDefinition(document, node) {
if (!document.isAutoloaded() && node.type === 'program') {
return [
symbol_1.FishSymbol.fromObject({
name: 'argv',
node: node,
focusedNode: node.firstChild,
fishKind: symbol_kinds_1.FishSymbolKindMap.variable,
document,
uri: document.uri,
detail: snippets_1.PrebuiltDocumentationMap.getByName('argv').pop()?.description || 'the list of arguments passed to the function',
scope: definition_scope_1.DefinitionScope.create(node, 'local'),
selectionRange: {
start: { line: 0, character: 0 },
end: { line: 0, character: 0 },
},
range: (0, tree_sitter_1.getRange)(node),
children: [],
}),
];
}
return [];
}
function isFunctionDefinitionName(node) {
if (!node.parent || !isFunctionDefinition(node.parent))
return false;
return !!node.parent.firstNamedChild && node.parent.firstNamedChild.equals(node);
}
function isFunctionVariableDefinitionName(node) {
if (!node.parent || !isFunctionDefinition(node.parent))
return false;
const { variableNodes } = findFunctionOptionNamedArguments(node.parent);
const definitionNode = variableNodes.find(n => n.equals(node));
return !!definitionNode && definitionNode.equals(node);
}
function findFunctionOptionNamedArguments(node) {
const variableNodes = [];
const eventNodes = [];
const focused = node.childrenForFieldName('option').filter(n => !(0, node_types_1.isEscapeSequence)(n) && !(0, node_types_1.isNewline)(n));
const flagsSet = (0, options_1.findOptionsSet)(focused, exports.FunctionOptions);
for (const flag of flagsSet) {
const { option, value: focused } = flag;
switch (true) {
case option.isOption('-a', '--argument-names'):
case option.isOption('-V', '--inherit-variable'):
variableNodes.push(focused);
break;
case option.isOption('-e', '--on-event'):
eventNodes.push(focused);
break;
default:
break;
}
}
return {
variableNodes,
eventNodes,
flagsSet,
};
}
function processFunctionDefinition(document, node, children = []) {
if (!isFunctionDefinition(node))
return [];
const autoloadScope = (0, translation_1.isAutoloadedUriLoadsFunctionName)(document);
const focusedNode = node.firstNamedChild;
const isGlobal = autoloadScope(focusedNode) ? 'global' : 'local';
if (!focusedNode)
return [];
const functionSymbol = symbol_1.FishSymbol.create(focusedNode.text, node, focusedNode, symbol_kinds_1.FishSymbolKindMap.function, document, document.uri, node.text, definition_scope_1.DefinitionScope.create(node.parent, isGlobal));
const focused = node.childrenForFieldName('option').filter(n => !(0, node_types_1.isEscapeSequence)(n) && !(0, node_types_1.isNewline)(n));
functionSymbol.addChildren(symbol_1.FishSymbol.create('argv', node, node.firstNamedChild, symbol_kinds_1.FishSymbolKindMap.function_variable, document, document.uri, snippets_1.PrebuiltDocumentationMap.getByName('argv').pop()?.description || 'the list of arguments passed to the function', definition_scope_1.DefinitionScope.create(node, 'local')));
if (!focused)
return [functionSymbol];
const { flagsSet } = findFunctionOptionNamedArguments(node);
for (const flag of flagsSet) {
const { option, value: focused } = flag;
switch (true) {
case option.isOption('-a', '--argument-names'):
case option.isOption('-V', '--inherit-variable'):
functionSymbol.addChildren(symbol_1.FishSymbol.create(focused.text, node, focused, symbol_kinds_1.FishSymbolKindMap.function_variable, document, document.uri, focused.text, definition_scope_1.DefinitionScope.create(node, 'local')));
break;
case option.isOption('-e', '--on-event'):
functionSymbol.addChildren(symbol_1.FishSymbol.create(focused.text, node, focused, symbol_kinds_1.FishSymbolKindMap.function_event, document, document.uri, [
`${markdown_builder_1.md.boldItalic('Generic Event:')} ${markdown_builder_1.md.inlineCode(focused.text)}`,
`${markdown_builder_1.md.boldItalic('Event Handler:')} ${markdown_builder_1.md.inlineCode(focusedNode.text)}`,
markdown_builder_1.md.separator(),
markdown_builder_1.md.codeBlock('fish', [
`### function definition: '${focusedNode.text}'`,
focusedNode?.parent?.text.toString(),
'',
'### Use the builtin `emit`, to fire this event:',
`emit ${focused.text.toString()}`,
`emit ${focused.text.toString()} with arguments # Specifies \`$argv\` to the event handler`,
].join('\n')),
markdown_builder_1.md.separator(),
markdown_builder_1.md.boldItalic('SEE ALSO:'),
' • Emit Events: https://fishshell.com/docs/current/cmds/emit.html',
' • Emit Handling: https://fishshell.com/docs/current/language.html#event',
].join(markdown_builder_1.md.newline()), definition_scope_1.DefinitionScope.create(node, 'global')));
break;
default:
break;
}
}
return [functionSymbol.addChildren(...children)];
}