Path: blob/main/extensions/copilot/test/simulation/language/simulationLanguageFeatureService.ts
13394 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import type * as vscode from 'vscode';5import { ILanguageFeaturesService, NoopLanguageFeaturesService } from '../../../src/platform/languages/common/languageFeaturesService';6import { SimulationWorkspace } from '../../../src/platform/test/node/simulationWorkspace';7import { getLanguageForResource } from '../../../src/util/common/languages';8import { URI } from '../../../src/util/vs/base/common/uri';9import { Range, Uri } from '../../../src/vscodeTypes';10import { computeSHA256 } from '../../base/hash';11import { TestingCacheSalts } from '../../base/salts';12import { CacheScope, ICachingResourceFetcher } from '../../base/simulationContext';13import { TSServerClient } from './tsServerClient';141516export class SimulationLanguageFeaturesService implements ILanguageFeaturesService {17_serviceBrand: undefined;18private readonly _tsService: TSServerLanguageFeaturesService;19private readonly _noOpService: NoopLanguageFeaturesService;2021constructor(22readonly _workspace: SimulationWorkspace,23@ICachingResourceFetcher _cachingResourceFetcher: ICachingResourceFetcher24) {25this._tsService = new TSServerLanguageFeaturesService(_workspace, _cachingResourceFetcher);26this._noOpService = new NoopLanguageFeaturesService();27}282930private getLanguageFeatures(uri: vscode.Uri) {31const language = getLanguageForResource(uri);32switch (language?.languageId) {33case 'javascript':34case 'javascriptreact':35case 'typescript':36case 'typescriptreact':37return this._tsService;38default:39return this._noOpService;40}41}4243getDocumentSymbols(uri: vscode.Uri): Promise<vscode.DocumentSymbol[]> {44return this.getLanguageFeatures(uri).getDocumentSymbols(uri);45}46getDefinitions(uri: vscode.Uri, position: vscode.Position): Promise<(vscode.LocationLink | vscode.Location)[]> {47return this.getLanguageFeatures(uri).getDefinitions(uri, position);48}49getImplementations(uri: vscode.Uri, position: vscode.Position): Promise<(vscode.LocationLink | vscode.Location)[]> {50return this.getLanguageFeatures(uri).getImplementations(uri, position);51}52getReferences(uri: vscode.Uri, position: vscode.Position): Promise<vscode.Location[]> {53return this.getLanguageFeatures(uri).getReferences(uri, position);54}55getDiagnostics(uri: vscode.Uri): vscode.Diagnostic[] {56return this.getLanguageFeatures(uri).getDiagnostics(uri);57}58getWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> {59return Promise.resolve([]);60}61dispose(): void {62this._tsService.teardown().catch(err => {63console.error(err);64});65}66teardown(): Promise<void> {67return this._tsService.teardown();68}69}7071class TSServerLanguageFeaturesService implements ILanguageFeaturesService {72_serviceBrand: undefined;7374private _tsServerClient: TSServerClient | undefined;7576constructor(77private _workspace: SimulationWorkspace,78@ICachingResourceFetcher private readonly _cachingResourceFetcher: ICachingResourceFetcher79) {80}8182public async teardown() {83try {84await this._tsServerClient?.teardown();85} catch {86// ignored87}88}8990public async getDefinitions(uri: vscode.Uri, position: vscode.Position): Promise<vscode.LocationLink[]> {91return (await this.cachedGetFromTSServer(uri, position, 'def', async (tsserver, currentFile, position) => {92const definitions = await tsserver.findDefinitions(currentFile, position);93return definitions.map(def => {94return {95targetUri: this._workspace.getUriFromFilePath(def.fileName),96targetRange: def.range97};98});99})).map((def: any) => {100return {101...def,102targetUri: URI.isUri(def.targetUri) ? def.targetUri : Uri.file(def.targetUri.path),103targetRange: def.targetRange instanceof Range ? def.targetRange : new Range(def.targetRange[0].line, def.targetRange[0].character, def.targetRange[1].line, def.targetRange[1].character),104};105});106}107108public async getReferences(uri: vscode.Uri, position: vscode.Position): Promise<vscode.Location[]> {109return (await this.cachedGetFromTSServer(uri, position, 'ref', async (tsserver, currentFile, position) => {110const references = await tsserver.findReferences(currentFile, position);111return references.map(ref => {112return {113uri: this._workspace.getUriFromFilePath(ref.fileName),114range: ref.range115};116});117})).map((ref: any) => {118return {119...ref,120uri: URI.isUri(ref.uri) ? ref.uri : Uri.file(ref.uri.path),121range: ref.range instanceof Range ? ref.range : new Range(ref.range[0].line, ref.range[0].character, ref.range[1].line, ref.range[1].character),122};123});124}125126private async cachedGetFromTSServer<T extends vscode.LocationLink | vscode.Location>(127uri: vscode.Uri,128position: vscode.Position,129target: 'ref' | 'def',130f: (tsserver: TSServerClient, currentFile: string, pos: vscode.Position) => Promise<T[]>): Promise<T[]> {131const currentFile = this._workspace.getFilePath(uri);132const files = this._workspace.documents.map(d => ({ fileName: this._workspace.getFilePath(d.document.uri), fileContents: d.getText() }));133const serializablePosition = { line: position.line, character: position.character };134135const cacheKey = computeSHA256(`${TSServerClient.id}-v${TSServerClient.cacheVersion}-${target}-${JSON.stringify({ files, currentFile, serializablePosition })}`);136137const getFromTSServer = async () => {138try {139if (this._tsServerClient === undefined) {140this._tsServerClient = new TSServerClient(files);141}142return f(this._tsServerClient, currentFile, position);143} catch (error) {144console.error(error);145return [];146}147};148return this._cachingResourceFetcher.invokeWithCache(CacheScope.TSC, undefined, TestingCacheSalts.tscCacheSalt, cacheKey, getFromTSServer);149}150151getImplementations(uri: vscode.Uri, position: vscode.Position): Promise<(vscode.Location | vscode.LocationLink)[]> {152return Promise.resolve([]);153}154getWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> {155return Promise.resolve([]);156}157getDocumentSymbols(uri: vscode.Uri): Promise<vscode.DocumentSymbol[]> {158return Promise.resolve([]);159}160getDiagnostics(uri: vscode.Uri): vscode.Diagnostic[] {161return [];162}163}164165166