Skip to content

Commit

Permalink
feat: enhance component references in find references (#2157)
Browse files Browse the repository at this point in the history
#2130

This allows for finding current component references from the script tag. The advantage of this is that you can now use the "Find All References" command for component references. This will always open in the sidebar without setting the references.preferredLocation config.

While we're on this topic, I also map the reference for a component to "find component references". Currently, these are only the references for the file that was requested. The reason is that the name of the generated component is suffixed, so any import will be an aliased default import. Thus, TypeScript only searches within the same file.
  • Loading branch information
jasonlyu123 committed Sep 19, 2023
1 parent 54f921a commit bf99a93
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,16 @@ export class TypeScriptPlugin
);
this.renameProvider = new RenameProviderImpl(this.lsAndTsDocResolver, configManager);
this.hoverProvider = new HoverProviderImpl(this.lsAndTsDocResolver);
this.findReferencesProvider = new FindReferencesProviderImpl(this.lsAndTsDocResolver);
this.findFileReferencesProvider = new FindFileReferencesProviderImpl(
this.lsAndTsDocResolver
);
this.findComponentReferencesProvider = new FindComponentReferencesProviderImpl(
this.lsAndTsDocResolver
);
this.findReferencesProvider = new FindReferencesProviderImpl(
this.lsAndTsDocResolver,
this.findComponentReferencesProvider
);
this.selectionRangeProvider = new SelectionRangeProviderImpl(this.lsAndTsDocResolver);
this.signatureHelpProvider = new SignatureHelpProviderImpl(this.lsAndTsDocResolver);
this.semanticTokensProvider = new SemanticTokensProviderImpl(this.lsAndTsDocResolver);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type ts from 'typescript';
import ts from 'typescript';
import { Location, Position, ReferenceContext } from 'vscode-languageserver';
import { Document } from '../../../lib/documents';
import { flatten, isNotNullOrUndefined } from '../../../utils';
import { FindReferencesProvider } from '../../interfaces';
import { flatten, isNotNullOrUndefined, pathToUrl } from '../../../utils';
import { FindComponentReferencesProvider, FindReferencesProvider } from '../../interfaces';
import { SvelteDocumentSnapshot } from '../DocumentSnapshot';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertToLocationForReferenceOrDefinition, hasNonZeroRange } from '../utils';
import {
convertToLocationForReferenceOrDefinition,
hasNonZeroRange,
isGeneratedSvelteComponentName
} from '../utils';
import {
get$storeOffsetOf$storeDeclaration,
getStoreOffsetOf$storeDeclaration,
Expand All @@ -16,14 +20,22 @@ import {
} from './utils';

export class FindReferencesProviderImpl implements FindReferencesProvider {
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}
constructor(
private readonly lsAndTsDocResolver: LSAndTSDocResolver,
private readonly componentReferencesProvider: FindComponentReferencesProvider
) {}

async findReferences(
document: Document,
position: Position,
context: ReferenceContext
): Promise<Location[] | null> {
if (this.isScriptStartOrEndTag(position, document)) {
return this.componentReferencesProvider.findComponentReferences(document.uri);
}

const { lang, tsDoc } = await this.getLSAndTSDoc(document);
const offset = tsDoc.offsetAt(tsDoc.getGeneratedPosition(position));

const rawReferences = lang.findReferences(
tsDoc.filePath,
Expand All @@ -36,7 +48,19 @@ export class FindReferencesProviderImpl implements FindReferencesProvider {
const snapshots = new SnapshotMap(this.lsAndTsDocResolver);
snapshots.set(tsDoc.filePath, tsDoc);

if (rawReferences.some((ref) => ref.definition.kind === ts.ScriptElementKind.alias)) {
const componentReferences = await this.checkIfHasAliasedComponentReference(
offset,
tsDoc,
lang
);

if (componentReferences?.length) {
return componentReferences;
}
}
const references = flatten(rawReferences.map((ref) => ref.references));

references.push(...(await this.getStoreReferences(references, tsDoc, snapshots, lang)));

const locations = await Promise.all(
Expand All @@ -51,6 +75,19 @@ export class FindReferencesProviderImpl implements FindReferencesProvider {
);
}

private isScriptStartOrEndTag(position: Position, document: 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)
);
}

/**
* If references of a $store are searched, also find references for the corresponding store
* and vice versa.
Expand Down Expand Up @@ -110,6 +147,30 @@ export class FindReferencesProviderImpl implements FindReferencesProvider {
return [...storeReferences, ...$storeReferences];
}

private async checkIfHasAliasedComponentReference(
offset: number,
tsDoc: SvelteDocumentSnapshot,
lang: ts.LanguageService
) {
const definitions = lang.getDefinitionAtPosition(tsDoc.filePath, offset);
if (!definitions?.length) {
return null;
}

const nonAliasDefinitions = definitions.filter((definition) =>
isGeneratedSvelteComponentName(definition.name)
);
const references = await Promise.all(
nonAliasDefinitions.map((definition) =>
this.componentReferencesProvider.findComponentReferences(
pathToUrl(definition.fileName)
)
)
);

return flatten(references.filter(isNotNullOrUndefined));
}

private async mapReference(
ref: ts.ReferencedSymbolEntry,
context: ReferenceContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('FindComponentReferencesProvider', function () {
const { provider, document, openDoc } = setup('find-component-references-child.svelte');
//Make known all the associated files
openDoc('find-component-references-parent.svelte');
openDoc('find-component-references-parent2.svelte');

const results = await provider.findComponentReferences(document.uri.toString());

Expand Down Expand Up @@ -105,6 +106,19 @@ describe('FindComponentReferencesProvider', function () {
}
},
uri: getUri('find-component-references-parent.svelte')
},
{
range: {
start: {
line: 1,
character: 9
},
end: {
line: 1,
character: 19
}
},
uri: getUri('find-component-references-parent2.svelte')
}
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LSAndTSDocResolver } from '../../../../src/plugins/typescript/LSAndTSDo
import { __resetCache } from '../../../../src/plugins/typescript/service';
import { pathToUrl } from '../../../../src/utils';
import { serviceWarmup } from '../test-utils';
import { FindComponentReferencesProviderImpl } from '../../../../src/plugins/typescript/features/FindComponentReferencesProvider';

const testDir = path.join(__dirname, '..');

Expand All @@ -29,7 +30,10 @@ describe('FindReferencesProvider', function () {
);
const lsConfigManager = new LSConfigManager();
const lsAndTsDocResolver = new LSAndTSDocResolver(docManager, [testDir], lsConfigManager);
const provider = new FindReferencesProviderImpl(lsAndTsDocResolver);
const provider = new FindReferencesProviderImpl(
lsAndTsDocResolver,
new FindComponentReferencesProviderImpl(lsAndTsDocResolver)
);
const document = openDoc(filename);
return { provider, document, openDoc };

Expand Down Expand Up @@ -76,7 +80,7 @@ describe('FindReferencesProvider', function () {
await test(Position.create(1, 11), true);
});

it('finds references, exluding definition', async () => {
it('finds references, excluding definition', async () => {
await test(Position.create(1, 11), false);
});

Expand Down Expand Up @@ -330,6 +334,99 @@ describe('FindReferencesProvider', function () {
]);
});

const componentReferences = [
{
range: {
start: {
line: 8,
character: 15
},
end: {
line: 8,
character: 22
}
},
uri: getUri('find-component-references-parent.svelte')
},
{
range: {
start: {
line: 1,
character: 9
},
end: {
line: 1,
character: 19
}
},
uri: getUri('find-component-references-parent.svelte')
},
{
range: {
start: {
line: 18,
character: 1
},
end: {
line: 18,
character: 11
}
},
uri: getUri('find-component-references-parent.svelte')
},
{
range: {
start: {
line: 20,
character: 1
},
end: {
line: 20,
character: 11
}
},
uri: getUri('find-component-references-parent.svelte')
},
{
range: {
start: {
line: 1,
character: 9
},
end: {
line: 1,
character: 19
}
},
uri: getUri('find-component-references-parent2.svelte')
}
];

it('can find component references from script tag', async () => {
const { provider, document, openDoc } = setup('find-component-references-child.svelte');

openDoc('find-component-references-parent.svelte');
openDoc('find-component-references-parent2.svelte');

const results = await provider.findReferences(document, Position.create(0, 1), {
includeDeclaration: true
});

assert.deepStrictEqual(results, componentReferences);
});

it('can find all component references', async () => {
const { provider, document, openDoc } = setup('find-component-references-parent.svelte');

openDoc('find-component-references-parent2.svelte');

const results = await provider.findReferences(document, Position.create(18, 1), {
includeDeclaration: true
});

assert.deepStrictEqual(results, componentReferences);
});

// Hacky, but it works. Needed due to testing both new and old transformation
after(() => {
__resetCache();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script script lang="ts">
import Component1 from "./find-component-references-child.svelte";
</script>

0 comments on commit bf99a93

Please sign in to comment.