Skip to content

Commit

Permalink
(feat) find component references (sveltejs#1509)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jojoshua committed Jun 14, 2022
1 parent c364414 commit bc93baf
Show file tree
Hide file tree
Showing 14 changed files with 398 additions and 22 deletions.
4 changes: 4 additions & 0 deletions packages/language-server/src/ls-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const defaultLSConfig: LSConfig = {
definitions: { enable: true },
findReferences: { enable: true },
fileReferences: { enable: true },
findComponentReferences: { enable: true },
documentSymbols: { enable: true },
codeActions: { enable: true },
rename: { enable: true },
Expand Down Expand Up @@ -101,6 +102,9 @@ export interface LSTypescriptConfig {
fileReferences: {
enable: boolean;
};
findComponentReferences: {
enable: boolean;
};
definitions: {
enable: boolean;
};
Expand Down
9 changes: 9 additions & 0 deletions packages/language-server/src/plugins/PluginHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
return await this.execute<any>('fileReferences', [uri], ExecuteMode.FirstNonNull, 'high');
}

async findComponentReferences(uri: string): Promise<Location[] | null> {
return await this.execute<any>(
'findComponentReferences',
[uri],
ExecuteMode.FirstNonNull,
'high'
);
}

async getSignatureHelp(
textDocument: TextDocumentIdentifier,
position: Position,
Expand Down
5 changes: 5 additions & 0 deletions packages/language-server/src/plugins/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ export interface FileReferencesProvider {
fileReferences(uri: string): Promise<Location[] | null>;
}

export interface FindComponentReferencesProvider {
findComponentReferences(uri: string): Promise<Location[] | null>;
}

export interface SignatureHelpProvider {
getSignatureHelp(
document: Document,
Expand Down Expand Up @@ -204,6 +208,7 @@ type ProviderBase = DiagnosticsProvider &
CodeActionsProvider &
FindReferencesProvider &
FileReferencesProvider &
FindComponentReferencesProvider &
RenameProvider &
SignatureHelpProvider &
SemanticTokensProvider &
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ export class SvelteDocumentSnapshot implements DocumentSnapshot {
return this.parent.getFilePath() || '';
}

getOriginalText(range?: Range) {
return this.parent.getText(range);
}

getText(start: number, end: number) {
return this.text.substring(start, end);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
FileRename,
FindReferencesProvider,
FileReferencesProvider,
FindComponentReferencesProvider,
HoverProvider,
ImplementationProvider,
OnWatchFileChanges,
Expand All @@ -56,6 +57,7 @@ import {
} from './features/CompletionProvider';
import { DiagnosticsProviderImpl } from './features/DiagnosticsProvider';
import { FindFileReferencesProviderImpl } from './features/FindFileReferencesProvider';
import { FindComponentReferencesProviderImpl } from './features/FindComponentReferencesProvider';
import { FindReferencesProviderImpl } from './features/FindReferencesProvider';
import { getDirectiveCommentCompletions } from './features/getDirectiveCommentCompletions';
import { HoverProviderImpl } from './features/HoverProvider';
Expand Down Expand Up @@ -88,6 +90,7 @@ export class TypeScriptPlugin
RenameProvider,
FindReferencesProvider,
FileReferencesProvider,
FindComponentReferencesProvider,
SelectionRangeProvider,
SignatureHelpProvider,
SemanticTokensProvider,
Expand All @@ -108,6 +111,7 @@ export class TypeScriptPlugin
private readonly hoverProvider: HoverProviderImpl;
private readonly findReferencesProvider: FindReferencesProviderImpl;
private readonly findFileReferencesProvider: FindFileReferencesProviderImpl;
private readonly findComponentReferencesProvider: FindComponentReferencesProviderImpl;

private readonly selectionRangeProvider: SelectionRangeProviderImpl;
private readonly signatureHelpProvider: SignatureHelpProviderImpl;
Expand Down Expand Up @@ -138,6 +142,9 @@ export class TypeScriptPlugin
this.findFileReferencesProvider = new FindFileReferencesProviderImpl(
this.lsAndTsDocResolver
);
this.findComponentReferencesProvider = new FindComponentReferencesProviderImpl(
this.lsAndTsDocResolver
);
this.selectionRangeProvider = new SelectionRangeProviderImpl(this.lsAndTsDocResolver);
this.signatureHelpProvider = new SignatureHelpProviderImpl(this.lsAndTsDocResolver);
this.semanticTokensProvider = new SemanticTokensProviderImpl(this.lsAndTsDocResolver);
Expand Down Expand Up @@ -439,6 +446,14 @@ export class TypeScriptPlugin
return this.findFileReferencesProvider.fileReferences(uri);
}

async findComponentReferences(uri: string): Promise<Location[] | null> {
if (!this.featureEnabled('findComponentReferences')) {
return null;
}

return this.findComponentReferencesProvider.findComponentReferences(uri);
}

async onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesPara[]): Promise<void> {
let doneUpdateProjectFiles = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Location, Position, Range } from 'vscode-languageserver';
import { flatten, isNotNullOrUndefined, pathToUrl, urlToPath } from '../../../utils';
import { FindComponentReferencesProvider } from '../../interfaces';
import {
DocumentSnapshot,
SvelteDocumentSnapshot,
SvelteSnapshotFragment
} from '../DocumentSnapshot';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertToLocationRange, hasNonZeroRange } from '../utils';
import { isNoTextSpanInGeneratedCode, SnapshotFragmentMap } from './utils';

const COMPONENT_SUFFIX = '__SvelteComponent_';

export class FindComponentReferencesProviderImpl implements FindComponentReferencesProvider {
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}

async findComponentReferences(uri: string): Promise<Location[] | null> {
// No document available, just the uri, because it could be called on an unopened file
const fileName = urlToPath(uri);
if (!fileName) {
return null;
}

const lang = await this.lsAndTsDocResolver.getLSForPath(fileName);
const tsDoc = await this.lsAndTsDocResolver.getSnapshot(fileName);
const fragment = tsDoc.getFragment();
if (!(fragment instanceof SvelteSnapshotFragment)) {
return null;
}

const references = lang.findReferences(
tsDoc.filePath,
this.offsetOfComponentExport(fragment)
);
if (!references) {
return null;
}

const docs = new SnapshotFragmentMap(this.lsAndTsDocResolver);
docs.set(tsDoc.filePath, { fragment, snapshot: tsDoc });

const locations = await Promise.all(
flatten(references.map((ref) => ref.references)).map(async (ref) => {
if (ref.isDefinition) {
return null;
}

const { fragment, snapshot } = await docs.retrieve(ref.fileName);

if (!isNoTextSpanInGeneratedCode(snapshot.getFullText(), ref.textSpan)) {
return null;
}

const refLocation = Location.create(
pathToUrl(ref.fileName),
convertToLocationRange(fragment, ref.textSpan)
);

//Only report starting tags
if (this.isEndTag(refLocation, snapshot)) {
return null;
}

// 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 (!hasNonZeroRange(refLocation)) {
return null;
}

return refLocation;
})
);

return locations.filter(isNotNullOrUndefined);
}

private offsetOfComponentExport(fragment: SvelteSnapshotFragment) {
return fragment.text.lastIndexOf(COMPONENT_SUFFIX);
}

private isEndTag(element: Location, snapshot: DocumentSnapshot) {
if (!(snapshot instanceof SvelteDocumentSnapshot)) {
return false;
}

const testEndTagRange = Range.create(
Position.create(element.range.start.line, element.range.start.character - 1),
element.range.end
);

const text = snapshot.getOriginalText(testEndTagRange);
if (text.substring(0, 1) == '/') {
return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ts from 'typescript';
import { Location, Position, ReferenceContext } from 'vscode-languageserver';
import { Document } from '../../../lib/documents';
import { flatten, pathToUrl } from '../../../utils';
import { flatten, isNotNullOrUndefined, pathToUrl } from '../../../utils';
import { FindReferencesProvider } from '../../interfaces';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertToLocationRange, hasNonZeroRange } from '../utils';
Expand Down Expand Up @@ -30,30 +29,36 @@ export class FindReferencesProviderImpl implements FindReferencesProvider {
docs.set(tsDoc.filePath, { fragment, snapshot: tsDoc });

const locations = await Promise.all(
flatten(references.map((ref) => ref.references))
.filter((ref) => context.includeDeclaration || !ref.isDefinition)
.filter(notInGeneratedCode(tsDoc.getFullText()))
.map(async (ref) => {
const defDoc = await docs.retrieveFragment(ref.fileName);

return Location.create(
pathToUrl(ref.fileName),
convertToLocationRange(defDoc, ref.textSpan)
);
})
flatten(references.map((ref) => ref.references)).map(async (ref) => {
if (!context.includeDeclaration && ref.isDefinition) {
return null;
}

const { fragment, snapshot } = await docs.retrieve(ref.fileName);

if (!isNoTextSpanInGeneratedCode(snapshot.getFullText(), ref.textSpan)) {
return null;
}

const location = Location.create(
pathToUrl(ref.fileName),
convertToLocationRange(fragment, 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 (!hasNonZeroRange(location)) {
return null;
}

return location;
})
);
// Some references are in generated code but not wrapped with explicit ignore comments.
// These show up as zero-length ranges, so filter them out.
return locations.filter(hasNonZeroRange);

return locations.filter(isNotNullOrUndefined);
}

private async getLSAndTSDoc(document: Document) {
return this.lsAndTsDocResolver.getLSAndTSDoc(document);
}
}

function notInGeneratedCode(text: string) {
return (ref: ts.ReferenceEntry) => {
return isNoTextSpanInGeneratedCode(text, ref.textSpan);
};
}
4 changes: 4 additions & 0 deletions packages/language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,10 @@ export function startServer(options?: LSOptions) {
return pluginHost.fileReferences(uri);
});

connection.onRequest('$/getComponentReferences', async (uri: string) => {
return pluginHost.findComponentReferences(uri);
});

connection.onRequest('$/getCompiledCode', async (uri: DocumentUri) => {
const doc = docManager.get(uri);
if (!doc) {
Expand Down

0 comments on commit bc93baf

Please sign in to comment.