Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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, '..');

describe('FindFileReferencesProvider', () => {
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();
const lsAndTsDocResolver = new LSAndTSDocResolver(docManager, [testDir], lsConfigManager);
const provider = new FindFileReferencesProviderImpl(lsAndTsDocResolver);
const document = openDoc(filename);
return { provider, document, openDoc };

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

it('finds file references', async () => {
const { provider, document, openDoc } = setup('find-file-references-child.svelte');
//Make known all the associated files
openDoc('find-file-references-parent.svelte');

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

assert.deepStrictEqual(results, expectedResults);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
const findMe = true;
if (findMe) {
findMe;
}
</script>
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
84 changes: 84 additions & 0 deletions packages/svelte-vscode/src/typescript/findFileReferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
commands,
ExtensionContext,
ProgressLocation,
Uri,
window,
workspace,
Position,
Location,
Range
} from 'vscode';
import { LanguageClient } from 'vscode-languageclient/node';
import { Location as LSLocation } from 'vscode-languageclient';

/**
* adopted from https://github.com/microsoft/vscode/blob/5f3e9c120a4407de3e55465588ce788618526eb0/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts
*/
export async function addFindFileReferencesListener(
getLS: () => LanguageClient,
context: ExtensionContext
) {
const disposable = commands.registerCommand('svelte.typescript.findAllFileReferences', handler);

context.subscriptions.push(disposable);

async function handler(resource?: Uri) {
if (!resource) {
resource = window.activeTextEditor?.document.uri;
}

if (!resource || resource.scheme !== 'file') {
return;
}

const document = await workspace.openTextDocument(resource);

await window.withProgress(
{
location: ProgressLocation.Window,
title: 'Finding file references'
},
async (_, token) => {
const lsLocations = await getLS().sendRequest<LSLocation[] | null>(
'$/getFileReferences',
document.uri.toString(),
token
);

if (!lsLocations) {
return;
}

const config = workspace.getConfiguration('references');
const existingSetting = config.inspect<string>('preferredLocation');

await config.update('preferredLocation', 'view');
try {
await commands.executeCommand(
'editor.action.showReferences',
resource,
new Position(0, 0),
lsLocations.map(
(ref) =>
new Location(
Uri.parse(ref.uri),
new Range(
ref.range.start.line,
ref.range.start.character,
ref.range.end.line,
ref.range.end.character
)
)
)
);
} finally {
await config.update(
'preferredLocation',
existingSetting?.workspaceFolderValue ?? existingSetting?.workspaceValue
);
}
}
);
}
}