kjelsrud.dev/node_modules/@astrojs/language-server/dist/plugins/PluginHost.js
2023-07-19 21:31:30 +02:00

206 lines
10 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginHost = void 0;
const vscode_languageserver_1 = require("vscode-languageserver");
const documents_1 = require("../core/documents");
const utils_1 = require("../utils");
var ExecuteMode;
(function (ExecuteMode) {
ExecuteMode[ExecuteMode["None"] = 0] = "None";
ExecuteMode[ExecuteMode["FirstNonNull"] = 1] = "FirstNonNull";
ExecuteMode[ExecuteMode["Collect"] = 2] = "Collect";
})(ExecuteMode || (ExecuteMode = {}));
class PluginHost {
constructor(docManager) {
this.docManager = docManager;
this.plugins = [];
this.pluginHostConfig = {
filterIncompleteCompletions: true,
definitionLinkSupport: false,
};
}
initialize(pluginHostConfig) {
this.pluginHostConfig = pluginHostConfig;
}
registerPlugin(plugin) {
this.plugins.push(plugin);
}
async getCompletions(textDocument, position, completionContext, cancellationToken) {
const document = this.getDocument(textDocument.uri);
const completions = await Promise.all(this.plugins.map(async (plugin) => {
const result = await this.tryExecutePlugin(plugin, 'getCompletions', [document, position, completionContext, cancellationToken], null);
if (result) {
return { result: result, plugin: plugin.__name };
}
})).then((fullCompletions) => fullCompletions.filter(utils_1.isNotNullOrUndefined));
const html = completions.find((completion) => completion.plugin === 'html');
const ts = completions.find((completion) => completion.plugin === 'typescript');
const astro = completions.find((completion) => completion.plugin === 'astro');
if (html && ts) {
const inComponentStartTag = (0, documents_1.isInComponentStartTag)(document.html, document.offsetAt(position));
// If the HTML plugin returned completions, it's highly likely that TS ones are duplicate
if (html.result.items.length > 0) {
ts.result.items = [];
}
// Inside components, if the Astro plugin has completions we don't want the TS ones are they're duplicates
if (astro && astro.result.items.length > 0 && inComponentStartTag) {
ts.result.items = [];
}
}
let flattenedCompletions = completions.flatMap((completion) => completion.result.items);
const isIncomplete = completions.reduce((incomplete, completion) => incomplete || completion.result.isIncomplete, false);
// If the result is incomplete, we need to filter the results ourselves
// to throw out non-matching results. VSCode does filter client-side,
// but other IDEs might not.
if (isIncomplete && this.pluginHostConfig.filterIncompleteCompletions) {
const offset = document.offsetAt(position);
// Assumption for performance reasons:
// Noone types import names longer than 20 characters and still expects perfect autocompletion.
const text = document.getText().substring(Math.max(0, offset - 20), offset);
const start = (0, utils_1.regexLastIndexOf)(text, /[\W\s]/g) + 1;
const filterValue = text.substring(start).toLowerCase();
flattenedCompletions = flattenedCompletions.filter((comp) => comp.label.toLowerCase().includes(filterValue));
}
return vscode_languageserver_1.CompletionList.create(flattenedCompletions, isIncomplete);
}
async resolveCompletion(textDocument, completionItem) {
const document = this.getDocument(textDocument.uri);
const result = await this.execute('resolveCompletion', [document, completionItem], ExecuteMode.FirstNonNull);
return result ?? completionItem;
}
async getDiagnostics(textDocument) {
const document = this.getDocument(textDocument.uri);
const diagnostics = await this.execute('getDiagnostics', [document], ExecuteMode.Collect);
return diagnostics;
}
async doHover(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return this.execute('doHover', [document, position], ExecuteMode.FirstNonNull);
}
async formatDocument(textDocument, options) {
const document = this.getDocument(textDocument.uri);
return await this.execute('formatDocument', [document, options], ExecuteMode.Collect);
}
async getCodeActions(textDocument, range, context, cancellationToken) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getCodeActions', [document, range, context, cancellationToken], ExecuteMode.Collect);
}
async doTagComplete(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return this.execute('doTagComplete', [document, position], ExecuteMode.FirstNonNull);
}
async getFoldingRanges(textDocument) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getFoldingRanges', [document], ExecuteMode.Collect);
}
async getDocumentSymbols(textDocument, cancellationToken) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getDocumentSymbols', [document, cancellationToken], ExecuteMode.Collect);
}
async getSemanticTokens(textDocument, range, cancellationToken) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getSemanticTokens', [document, range, cancellationToken], ExecuteMode.FirstNonNull);
}
async getLinkedEditingRanges(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getLinkedEditingRanges', [document, position], ExecuteMode.FirstNonNull);
}
async fileReferences(textDocument) {
const document = this.getDocument(textDocument.uri);
return await this.execute('fileReferences', [document], ExecuteMode.FirstNonNull);
}
async getDefinitions(textDocument, position) {
const document = this.getDocument(textDocument.uri);
const definitions = await this.execute('getDefinitions', [document, position], ExecuteMode.Collect);
if (this.pluginHostConfig.definitionLinkSupport) {
return definitions;
}
else {
return definitions.map((def) => ({ range: def.targetSelectionRange, uri: def.targetUri }));
}
}
getTypeDefinitions(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return this.execute('getTypeDefinitions', [document, position], ExecuteMode.FirstNonNull);
}
getImplementations(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return this.execute('getImplementation', [document, position], ExecuteMode.FirstNonNull);
}
getReferences(textdocument, position, context) {
const document = this.getDocument(textdocument.uri);
return this.execute('findReferences', [document, position, context], ExecuteMode.FirstNonNull);
}
async prepareRename(textDocument, position) {
const document = this.getDocument(textDocument.uri);
return await this.execute('prepareRename', [document, position], ExecuteMode.FirstNonNull);
}
async rename(textDocument, position, newName) {
const document = this.getDocument(textDocument.uri);
return this.execute('rename', [document, position, newName], ExecuteMode.FirstNonNull);
}
async getDocumentColors(textDocument) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getDocumentColors', [document], ExecuteMode.Collect);
}
async getColorPresentations(textDocument, range, color) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getColorPresentations', [document, range, color], ExecuteMode.Collect);
}
async getInlayHints(textDocument, range, cancellationToken) {
const document = this.getDocument(textDocument.uri);
return (await this.execute('getInlayHints', [document, range], ExecuteMode.FirstNonNull)) ?? [];
}
async getSignatureHelp(textDocument, position, context, cancellationToken) {
const document = this.getDocument(textDocument.uri);
return await this.execute('getSignatureHelp', [document, position, context, cancellationToken], ExecuteMode.FirstNonNull);
}
async onWatchFileChanges(onWatchFileChangesParams) {
for (const support of this.plugins) {
await support.onWatchFileChanges?.(onWatchFileChangesParams);
}
}
updateNonAstroFile(fileName, changes, text) {
for (const support of this.plugins) {
support.updateNonAstroFile?.(fileName, changes, text);
}
}
getDocument(uri) {
const document = this.docManager.get(uri);
if (!document) {
throw new Error('Cannot call methods on an unopened document');
}
return document;
}
async execute(name, args, mode) {
const plugins = this.plugins.filter((plugin) => typeof plugin[name] === 'function');
switch (mode) {
case ExecuteMode.FirstNonNull:
for (const plugin of plugins) {
const res = await this.tryExecutePlugin(plugin, name, args, null);
if (res != null) {
return res;
}
}
return null;
case ExecuteMode.Collect:
return (await Promise.all(plugins.map((plugin) => {
let ret = this.tryExecutePlugin(plugin, name, args, []);
return ret;
}))).flat();
case ExecuteMode.None:
await Promise.all(plugins.map((plugin) => this.tryExecutePlugin(plugin, name, args, null)));
return;
}
}
async tryExecutePlugin(plugin, fnName, args, failValue) {
try {
return await plugin[fnName](...args);
}
catch (e) {
console.error(e);
return failValue;
}
}
}
exports.PluginHost = PluginHost;