svelte-language-server
Version:
A language server for Svelte
149 lines • 7.88 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FindReferencesProviderImpl = void 0;
const typescript_1 = __importDefault(require("typescript"));
const utils_1 = require("../../../utils");
const utils_2 = require("../utils");
const utils_3 = require("./utils");
class FindReferencesProviderImpl {
constructor(lsAndTsDocResolver, componentReferencesProvider) {
this.lsAndTsDocResolver = lsAndTsDocResolver;
this.componentReferencesProvider = componentReferencesProvider;
}
async findReferences(document, position, context, cancellationToken) {
if (this.isPositionForComponentCodeLens(position) ||
this.isScriptStartOrEndTag(position, document)) {
return this.componentReferencesProvider.findComponentReferences(document.uri);
}
const { lang, tsDoc, lsContainer } = await this.getLSAndTSDoc(document);
if (cancellationToken?.isCancellationRequested) {
return null;
}
const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position));
const rawReferences = lang.findReferences(tsDoc.filePath, tsDoc.offsetAt(tsDoc.getGeneratedPosition(position)));
if (!rawReferences) {
return null;
}
const snapshots = new utils_3.SnapshotMap(this.lsAndTsDocResolver, lsContainer);
snapshots.set(tsDoc.filePath, tsDoc);
if (rawReferences.some((ref) => ref.definition.kind === typescript_1.default.ScriptElementKind.alias)) {
const componentReferences = await this.checkIfHasAliasedComponentReference(offset, tsDoc, lang);
if (componentReferences?.length) {
return componentReferences;
}
}
const references = (0, utils_1.flatten)(rawReferences.map((ref) => ref.references));
references.push(...(await this.getStoreReferences(references, tsDoc, snapshots, lang, cancellationToken)));
const locations = await Promise.all(references.map(async (ref) => this.mapReference(ref, context, snapshots, cancellationToken)));
return (locations
.filter(utils_1.isNotNullOrUndefined)
// Possible $store references are added afterwards, sort for correct order
.sort(sortLocationByFileAndRange));
}
isScriptStartOrEndTag(position, document) {
if (!document.scriptInfo) {
return false;
}
const { start, end } = document.scriptInfo.container;
const offset = document.offsetAt(position);
return ((offset >= start && offset <= start + '<script'.length) ||
(offset >= end - '</script>'.length && offset <= end));
}
isPositionForComponentCodeLens(position) {
return position.line === 0 && position.character === 0;
}
/**
* If references of a $store are searched, also find references for the corresponding store
* and vice versa.
*/
async getStoreReferences(references, tsDoc, snapshots, lang, cancellationToken) {
// If user started finding references at $store, find references for store, too
let storeReferences = [];
const storeReference = references.find((ref) => (0, utils_1.normalizePath)(ref.fileName) === tsDoc.filePath &&
(0, utils_3.isTextSpanInGeneratedCode)(tsDoc.getFullText(), ref.textSpan) &&
(0, utils_3.is$storeVariableIn$storeDeclaration)(tsDoc.getFullText(), ref.textSpan.start));
if (storeReference) {
const additionalReferences = lang.findReferences(tsDoc.filePath, (0, utils_3.getStoreOffsetOf$storeDeclaration)(tsDoc.getFullText(), storeReference.textSpan.start)) || [];
storeReferences = (0, utils_1.flatten)(additionalReferences.map((ref) => ref.references));
}
// If user started finding references at store, find references for $store, too
// If user started finding references at $store, find references for $store in other files
const $storeReferences = [];
for (const ref of [...references, ...storeReferences]) {
const snapshot = await snapshots.retrieve(ref.fileName);
if (cancellationToken?.isCancellationRequested) {
return [];
}
if (!((0, utils_3.isTextSpanInGeneratedCode)(snapshot.getFullText(), ref.textSpan) &&
(0, utils_3.isStoreVariableIn$storeDeclaration)(snapshot.getFullText(), ref.textSpan.start))) {
continue;
}
if (storeReference?.fileName === ref.fileName) {
// $store in X -> usages of store -> store in X -> we would add duplicate $store references
continue;
}
const additionalReferences = lang.findReferences(snapshot.filePath, (0, utils_3.get$storeOffsetOf$storeDeclaration)(snapshot.getFullText(), ref.textSpan.start)) || [];
$storeReferences.push(...(0, utils_1.flatten)(additionalReferences.map((ref) => ref.references)));
}
return [...storeReferences, ...$storeReferences];
}
async checkIfHasAliasedComponentReference(offset, tsDoc, lang) {
const definitions = lang.getDefinitionAtPosition(tsDoc.filePath, offset);
if (!definitions?.length) {
return null;
}
const nonAliasDefinitions = definitions.filter((definition) => (0, utils_2.isGeneratedSvelteComponentName)(definition.name));
const references = await Promise.all(nonAliasDefinitions.map((definition) => this.componentReferencesProvider.findComponentReferences((0, utils_1.pathToUrl)(definition.fileName))));
const flattened = [];
for (const ref of references) {
if (ref) {
const tmp = []; // perf optimization: we know each iteration has unique references
for (const r of ref) {
const exists = flattened.some((f) => f.uri === r.uri &&
f.range.start.line === r.range.start.line &&
f.range.start.character === r.range.start.character);
if (!exists) {
tmp.push(r);
}
}
flattened.push(...tmp);
}
}
return flattened;
}
async mapReference(ref, context, snapshots, cancellationToken) {
if (!context.includeDeclaration && ref.isDefinition) {
return null;
}
const snapshot = await snapshots.retrieve(ref.fileName);
if (cancellationToken?.isCancellationRequested) {
return null;
}
if ((0, utils_3.isTextSpanInGeneratedCode)(snapshot.getFullText(), ref.textSpan)) {
return null;
}
// TODO we should deduplicate if we support finding references from multiple language service
const location = (0, utils_2.convertToLocationForReferenceOrDefinition)(snapshot, ref.textSpan);
// Some references are in generated code but not wrapped with explicit ignore comments.
// These show up as zero-length ranges, so filter them out.
if (!(0, utils_2.hasNonZeroRange)(location)) {
return null;
}
return location;
}
async getLSAndTSDoc(document) {
return this.lsAndTsDocResolver.getLSAndTSDoc(document);
}
}
exports.FindReferencesProviderImpl = FindReferencesProviderImpl;
function sortLocationByFileAndRange(l1, l2) {
const localeCompare = l1.uri.localeCompare(l2.uri);
return localeCompare === 0
? (l1.range.start.line - l2.range.start.line) * 10000 +
(l1.range.start.character - l2.range.start.character)
: localeCompare;
}
//# sourceMappingURL=FindReferencesProvider.js.map