Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go to References - Where Imports Used #1491

Merged
merged 18 commits into from May 30, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/language-server/package.json
Expand Up @@ -6,6 +6,7 @@
"typings": "dist/src/index",
"scripts": {
"test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\"",
"test2": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --require ts-node/register \"test/**/FindFileReferencesProvider.test.ts\" --exclude \"test/**/*.d.ts\"",
Jojoshua marked this conversation as resolved.
Show resolved Hide resolved
"build": "tsc",
"prepublishOnly": "npm run build",
"watch": "tsc -w"
Expand Down
4 changes: 4 additions & 0 deletions packages/language-server/src/ls-config.ts
Expand Up @@ -14,6 +14,7 @@ const defaultLSConfig: LSConfig = {
completions: { enable: true },
definitions: { enable: true },
findReferences: { enable: true },
fileReferences: { enable: true },
documentSymbols: { enable: true },
codeActions: { enable: true },
rename: { enable: true },
Expand Down Expand Up @@ -97,6 +98,9 @@ export interface LSTypescriptConfig {
findReferences: {
enable: boolean;
};
fileReferences: {
enable: boolean;
};
definitions: {
enable: boolean;
};
Expand Down
4 changes: 4 additions & 0 deletions packages/language-server/src/plugins/PluginHost.ts
Expand Up @@ -389,6 +389,10 @@ export class PluginHost implements LSProvider, OnWatchFileChanges {
);
}

async fileReferences(uri: string): Promise<Location[] | null> {
return await this.execute<any>('fileReferences', [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
Expand Up @@ -143,6 +143,10 @@ export interface FindReferencesProvider {
): Promise<Location[] | null>;
}

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

export interface SignatureHelpProvider {
getSignatureHelp(
document: Document,
Expand Down Expand Up @@ -199,6 +203,7 @@ type ProviderBase = DiagnosticsProvider &
UpdateImportsProvider &
CodeActionsProvider &
FindReferencesProvider &
FileReferencesProvider &
RenameProvider &
SignatureHelpProvider &
SemanticTokensProvider &
Expand Down
Expand Up @@ -36,6 +36,7 @@ import {
DocumentSymbolsProvider,
FileRename,
FindReferencesProvider,
FileReferencesProvider,
HoverProvider,
ImplementationProvider,
OnWatchFileChanges,
Expand All @@ -54,6 +55,7 @@ import {
CompletionsProviderImpl
} from './features/CompletionProvider';
import { DiagnosticsProviderImpl } from './features/DiagnosticsProvider';
import { FindFileReferencesProviderImpl } from './features/FindFileReferencesProvider';
import { FindReferencesProviderImpl } from './features/FindReferencesProvider';
import { getDirectiveCommentCompletions } from './features/getDirectiveCommentCompletions';
import { HoverProviderImpl } from './features/HoverProvider';
Expand Down Expand Up @@ -85,6 +87,7 @@ export class TypeScriptPlugin
UpdateImportsProvider,
RenameProvider,
FindReferencesProvider,
FileReferencesProvider,
SelectionRangeProvider,
SignatureHelpProvider,
SemanticTokensProvider,
Expand All @@ -104,6 +107,8 @@ export class TypeScriptPlugin
private readonly renameProvider: RenameProviderImpl;
private readonly hoverProvider: HoverProviderImpl;
private readonly findReferencesProvider: FindReferencesProviderImpl;
private readonly findFileReferencesProvider: FindFileReferencesProviderImpl;

private readonly selectionRangeProvider: SelectionRangeProviderImpl;
private readonly signatureHelpProvider: SignatureHelpProviderImpl;
private readonly semanticTokensProvider: SemanticTokensProviderImpl;
Expand All @@ -130,6 +135,9 @@ 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.selectionRangeProvider = new SelectionRangeProviderImpl(this.lsAndTsDocResolver);
this.signatureHelpProvider = new SignatureHelpProviderImpl(this.lsAndTsDocResolver);
this.semanticTokensProvider = new SemanticTokensProviderImpl(this.lsAndTsDocResolver);
Expand Down Expand Up @@ -423,6 +431,14 @@ export class TypeScriptPlugin
return this.findReferencesProvider.findReferences(document, position, context);
}

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

return this.findFileReferencesProvider.fileReferences(uri);
}

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

Expand Down
@@ -0,0 +1,51 @@
import { Location } from 'vscode-languageserver';
import { URI } from 'vscode-uri';
import { pathToUrl } from '../../../utils';
import { FileReferencesProvider } from '../../interfaces';
import { LSAndTSDocResolver } from '../LSAndTSDocResolver';
import { convertToLocationRange, hasNonZeroRange } from '../utils';
import { SnapshotFragmentMap } from './utils';

export class FindFileReferencesProviderImpl implements FileReferencesProvider {
constructor(private readonly lsAndTsDocResolver: LSAndTSDocResolver) {}

async fileReferences(uri: string): Promise<Location[] | null> {
const u = URI.parse(uri);
const fileName = u.fsPath;

const lang = await this.getLSForPath(fileName);
const tsDoc = await this.getSnapshotForPath(fileName);
const fragment = tsDoc.getFragment();

const references = lang.getFileReferences(fileName);

if (!references) {
return null;
}

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

const locations = await Promise.all(
references.map(async (ref) => {
const defDoc = await docs.retrieveFragment(ref.fileName);

return Location.create(
pathToUrl(ref.fileName),
convertToLocationRange(defDoc, 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.
return locations.filter(hasNonZeroRange);
}

private async getLSForPath(path: string) {
return this.lsAndTsDocResolver.getLSForPath(path);
}

private async getSnapshotForPath(path: string) {
return this.lsAndTsDocResolver.getSnapshot(path);
}
}
4 changes: 4 additions & 0 deletions packages/language-server/src/server.ts
Expand Up @@ -426,6 +426,10 @@ export function startServer(options?: LSOptions) {
pluginHost.updateImports(fileRename)
);

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

connection.onRequest('$/getCompiledCode', async (uri: DocumentUri) => {
const doc = docManager.get(uri);
if (!doc) {
Expand Down
@@ -0,0 +1,68 @@
import * as assert from 'assert';
import * as path from 'path';
import ts from 'typescript';
import { Location, Position, Range } from 'vscode-languageserver';
import { Document, DocumentManager } from '../../../../src/lib/documents';
import { LSConfigManager } from '../../../../src/ls-config';
import { FindFileReferencesProviderImpl } from '../../../../src/plugins/typescript/features/FindFileReferencesProvider';
import { LSAndTSDocResolver } from '../../../../src/plugins/typescript/LSAndTSDocResolver';
import { pathToUrl } from '../../../../src/utils';

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

function test(useNewTransformation: boolean) {
return () => {
function getFullPath(filename: string) {
return path.join(testDir, 'testfiles', filename);
}
function getUri(filename: string) {
const filePath = path.join(testDir, 'testfiles', filename);
return pathToUrl(filePath);
}

function setup(filename: string) {
const docManager = new DocumentManager(
(textDocument) => new Document(textDocument.uri, textDocument.text)
);
const lsConfigManager = new LSConfigManager();
lsConfigManager.update({ svelte: { useNewTransformation } });
const lsAndTsDocResolver = new LSAndTSDocResolver(
docManager,
[testDir],
lsConfigManager
);
const provider = new FindFileReferencesProviderImpl(lsAndTsDocResolver);
Jojoshua marked this conversation as resolved.
Show resolved Hide resolved
const document = openDoc(filename);
return { provider, document };

function openDoc(filename: string) {
const filePath = getFullPath(filename);
const doc = docManager.openDocument(<any>{
uri: pathToUrl(filePath),
text: ts.sys.readFile(filePath) || ''
});
return doc;
}
}

async function test() {
const { provider, document } = setup('find-file-references-child.svelte');

const results = await provider.fileReferences(document.uri.toString());
Jojoshua marked this conversation as resolved.
Show resolved Hide resolved
const expectedResults = [
Location.create(
getUri('find-file-references-parent.svelte'),
Range.create(Position.create(1, 37), Position.create(1, 72))
)
];

assert.deepStrictEqual(results, expectedResults);
}

it('finds file references', async () => {
await test();
});
};
}

describe('FindFileReferencesProvider', test(true));
@@ -0,0 +1,6 @@
<script>
const findMe = true;
if (findMe) {
findMe;
}
</script>
@@ -0,0 +1,10 @@
<script>
import FindFileReferencesChild from "./find-file-references-child.svelte";

const findMe = true;
if (findMe) {
findMe;
}
</script>

<FindFileReferencesChild></FindFileReferencesChild>
26 changes: 26 additions & 0 deletions packages/svelte-vscode/package.json
Expand Up @@ -534,13 +534,21 @@
{
"command": "svelte.extractComponent",
"title": "Svelte: Extract Component"
},
{
"command": "svelte.typescript.findAllFileReferences",
"title": "Svelte: Find File References"
}
],
"menus": {
"commandPalette": [
{
"command": "svelte.showCompiledCodeToSide",
"when": "editorLangId == svelte"
},
{
"command": "svelte.typescript.findAllFileReferences",
"when": "editorLangId == svelte && resourceScheme == file"
}
],
"editor/title": [
Expand All @@ -550,11 +558,29 @@
"group": "navigation"
}
],
"editor/title/context": [
{
"command": "svelte.typescript.findAllFileReferences",
"when": "resourceLangId == svelte && resourceScheme == file"
}
],
"editor/context": [
{
"command": "svelte.extractComponent",
"when": "editorLangId == svelte",
"group": "1_modification"
},
{
"command": "svelte.typescript.findAllFileReferences",
"when": "editorLangId == svelte",
"group": "4_search"
}
],
"explorer/context": [
{
"command": "svelte.typescript.findAllFileReferences",
"when": "resourceLangId == svelte",
"group": "4_search"
}
]
},
Expand Down
3 changes: 3 additions & 0 deletions packages/svelte-vscode/src/extension.ts
Expand Up @@ -29,6 +29,7 @@ import CompiledCodeContentProvider from './CompiledCodeContentProvider';
import { activateTagClosing } from './html/autoClose';
import { EMPTY_ELEMENTS } from './html/htmlEmptyTagsShared';
import { TsPlugin } from './tsplugin';
import { addFindFileReferencesListener } from './typescript/findFileReferences';

namespace TagCloseRequest {
export const type: RequestType<TextDocumentPositionParams, string, any> = new RequestType(
Expand Down Expand Up @@ -223,6 +224,8 @@ export function activateSvelteLanguageServer(context: ExtensionContext) {

addDidChangeTextDocumentListener(getLS);

addFindFileReferencesListener(getLS, context);

addRenameFileListener(getLS);

addCompilePreviewCommand(getLS, context);
Expand Down