svelte-language-server
Version:
A language server for Svelte
205 lines • 10.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeLensProviderImpl = void 0;
const typescript_1 = __importDefault(require("typescript"));
const vscode_languageserver_1 = require("vscode-languageserver");
const documents_1 = require("../../../lib/documents");
const utils_1 = require("../../../utils");
const utils_2 = require("../utils");
const utils_3 = require("./utils");
const svelte2tsx_1 = require("svelte2tsx");
class CodeLensProviderImpl {
constructor(lsAndTsDocResolver, referenceProvider, implementationProvider, configManager) {
this.lsAndTsDocResolver = lsAndTsDocResolver;
this.referenceProvider = referenceProvider;
this.implementationProvider = implementationProvider;
this.configManager = configManager;
}
async getCodeLens(document) {
if (!this.anyCodeLensEnabled('typescript') && !this.anyCodeLensEnabled('javascript')) {
return null;
}
const { lang, tsDoc } = await this.lsAndTsDocResolver.getLsForSyntheticOperations(document);
const results = [];
const collectors = [];
const clientTsConfig = this.configManager.getClientTsUserConfig(tsDoc.scriptKind === typescript_1.default.ScriptKind.TS ? 'typescript' : 'javascript');
if (clientTsConfig.referencesCodeLens?.enabled) {
collectors.push({
type: 'reference',
collect: (tsDoc, item, parent) => this.extractReferenceLocation(tsDoc, item, parent, clientTsConfig)
});
if (!tsDoc.parserError) {
// always add a reference code lens for the generated component
results.push([
'reference',
{
start: { line: 0, character: 0 },
// some client refused to resolve the code lens if the start is the same as the end
end: { line: 0, character: 1 }
}
]);
}
}
if (tsDoc.scriptKind === typescript_1.default.ScriptKind.TS &&
clientTsConfig.implementationsCodeLens?.enabled) {
collectors.push({
type: 'implementation',
collect: (tsDoc, item, parent) => this.extractImplementationLocation(tsDoc, item, clientTsConfig, parent)
});
}
if (!collectors.length) {
return null;
}
const navigationTree = lang.getNavigationTree(tsDoc.filePath);
const renderFunction = navigationTree?.childItems?.find((item) => item.text === svelte2tsx_1.internalHelpers.renderName);
if (renderFunction) {
// pretty rare that there is anything to show in the template, so we skip it
const notTemplate = renderFunction.childItems?.filter((item) => item.text !== '<function>');
renderFunction.childItems = notTemplate;
}
this.walkTree(tsDoc, navigationTree, undefined, results, collectors);
const uri = document.uri;
return results.map(([type, range]) => vscode_languageserver_1.CodeLens.create(range, { type, uri }));
}
anyCodeLensEnabled(lang) {
const vscodeTsConfig = this.configManager.getClientTsUserConfig(lang);
return (vscodeTsConfig.referencesCodeLens?.enabled ||
vscodeTsConfig.implementationsCodeLens?.enabled);
}
/**
* https://github.com/microsoft/vscode/blob/062ba1ed6c2b9ff4819f4f7dad76de3fde0044ab/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts#L61
*/
extractReferenceLocation(tsDoc, item, parent, config) {
if (parent && parent.kind === typescript_1.default.ScriptElementKind.enumElement) {
return this.getSymbolRange(tsDoc, item);
}
switch (item.kind) {
case typescript_1.default.ScriptElementKind.functionElement: {
const showOnAllFunctions = config.referencesCodeLens?.showOnAllFunctions;
if (showOnAllFunctions) {
return this.getSymbolRange(tsDoc, item);
}
if (this.isExported(item, tsDoc)) {
return this.getSymbolRange(tsDoc, item);
}
break;
}
case typescript_1.default.ScriptElementKind.constElement:
case typescript_1.default.ScriptElementKind.letElement:
case typescript_1.default.ScriptElementKind.variableElement:
// Only show references for exported variables
if (this.isExported(item, tsDoc)) {
return this.getSymbolRange(tsDoc, item);
}
break;
case typescript_1.default.ScriptElementKind.classElement:
if (item.text === '<class>') {
break;
}
return this.getSymbolRange(tsDoc, item);
case typescript_1.default.ScriptElementKind.interfaceElement:
case typescript_1.default.ScriptElementKind.typeElement:
case typescript_1.default.ScriptElementKind.enumElement:
return this.getSymbolRange(tsDoc, item);
case typescript_1.default.ScriptElementKind.memberFunctionElement:
case typescript_1.default.ScriptElementKind.memberGetAccessorElement:
case typescript_1.default.ScriptElementKind.memberSetAccessorElement:
case typescript_1.default.ScriptElementKind.constructorImplementationElement:
case typescript_1.default.ScriptElementKind.memberVariableElement:
if (parent?.spans[0].start === item.spans[0].start) {
return undefined;
}
// Only show if parent is a class type object (not a literal)
switch (parent?.kind) {
case typescript_1.default.ScriptElementKind.classElement:
case typescript_1.default.ScriptElementKind.interfaceElement:
case typescript_1.default.ScriptElementKind.typeElement:
return this.getSymbolRange(tsDoc, item);
}
break;
}
return undefined;
}
isExported(item, tsDoc) {
return !tsDoc.parserError && item.kindModifiers.match(/\bexport\b/g) !== null;
}
/**
* https://github.com/microsoft/vscode/blob/062ba1ed6c2b9ff4819f4f7dad76de3fde0044ab/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts#L66
*/
extractImplementationLocation(tsDoc, item, config, parent) {
if (item.kind === typescript_1.default.ScriptElementKind.memberFunctionElement &&
parent &&
parent.kind === typescript_1.default.ScriptElementKind.interfaceElement &&
config.implementationsCodeLens?.showOnInterfaceMethods === true) {
return this.getSymbolRange(tsDoc, item);
}
switch (item.kind) {
case typescript_1.default.ScriptElementKind.interfaceElement:
return this.getSymbolRange(tsDoc, item);
case typescript_1.default.ScriptElementKind.classElement:
case typescript_1.default.ScriptElementKind.memberFunctionElement:
case typescript_1.default.ScriptElementKind.memberVariableElement:
case typescript_1.default.ScriptElementKind.memberGetAccessorElement:
case typescript_1.default.ScriptElementKind.memberSetAccessorElement:
if (item.kindModifiers.match(/\babstract\b/g)) {
return this.getSymbolRange(tsDoc, item);
}
break;
}
return undefined;
}
getSymbolRange(tsDoc, item) {
if (!item.nameSpan || (0, utils_3.isTextSpanInGeneratedCode)(tsDoc.getFullText(), item.nameSpan)) {
return;
}
const range = (0, documents_1.mapRangeToOriginal)(tsDoc, (0, utils_2.convertRange)(tsDoc, item.nameSpan));
if (range.start.line >= 0 && range.end.line >= 0) {
return (0, utils_1.isZeroLengthRange)(range) ? undefined : range;
}
}
walkTree(tsDoc, item, parent, results, collectors) {
for (const collector of collectors) {
const range = collector.collect(tsDoc, item, parent);
if (range) {
results.push([collector.type, range]);
}
}
item.childItems?.forEach((child) => this.walkTree(tsDoc, child, item, results, collectors));
}
async resolveCodeLens(textDocument, codeLensToResolve, cancellationToken) {
if (codeLensToResolve.data.type === 'reference') {
return await this.resolveReferenceCodeLens(textDocument, codeLensToResolve, cancellationToken);
}
if (codeLensToResolve.data.type === 'implementation') {
return await this.resolveImplementationCodeLens(textDocument, codeLensToResolve, cancellationToken);
}
return codeLensToResolve;
}
async resolveReferenceCodeLens(textDocument, codeLensToResolve, cancellationToken) {
const references = (await this.referenceProvider.findReferences(textDocument, codeLensToResolve.range.start, { includeDeclaration: false }, cancellationToken)) ?? [];
codeLensToResolve.command = {
title: references.length === 1 ? `1 reference` : `${references.length} references`,
// language clients need to map this to the corresponding command in each editor
// see example in svelte-vscode/src/middlewares.ts
command: '',
arguments: [textDocument.uri, codeLensToResolve.range.start, references]
};
return codeLensToResolve;
}
async resolveImplementationCodeLens(textDocument, codeLensToResolve, cancellationToken) {
const implementations = (await this.implementationProvider.getImplementation(textDocument, codeLensToResolve.range.start, cancellationToken)) ?? [];
codeLensToResolve.command = {
title: implementations.length === 1
? `1 implementation`
: `${implementations.length} implementations`,
command: '',
arguments: [textDocument.uri, codeLensToResolve.range.start, implementations]
};
return codeLensToResolve;
}
}
exports.CodeLensProviderImpl = CodeLensProviderImpl;
//# sourceMappingURL=CodeLensProvider.js.map