diff --git a/src/test/typescript-service-helpers.ts b/src/test/typescript-service-helpers.ts index 74a1f1a88..899b1607c 100644 --- a/src/test/typescript-service-helpers.ts +++ b/src/test/typescript-service-helpers.ts @@ -561,8 +561,9 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor [rootUri + 'a.ts', 'class a { foo() { const i = 1;} }'], [rootUri + 'foo/b.ts', 'class b { bar: number; baz(): number { return this.bar;}}; function qux() {}'], [rootUri + 'c.ts', 'import { x } from "dep/dep";'], - [rootUri + 'package.json', '{ "name": "mypkg" }'], - [rootUri + 'node_modules/dep/dep.ts', 'export var x = 1;'] + [rootUri + 'package.json', JSON.stringify({ name: 'mypkg' })], + [rootUri + 'node_modules/dep/dep.ts', 'export var x = 1;'], + [rootUri + 'node_modules/dep/package.json', JSON.stringify({ name: 'dep' })] ]))); afterEach(shutdownService); @@ -865,7 +866,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 10, + character: 9, line: 0 } }, @@ -892,7 +893,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 10, + character: 9, line: 0 } }, @@ -905,7 +906,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor assert.deepEqual(result, []); }); it('should return all references to a symbol from a dependency', async function (this: TestContext & ITestCallbackContext) { - const result: ReferenceInformation[] = await this.service.workspaceXreferences({ query: { name: 'x', containerName: '' } }) + const result: ReferenceInformation[] = await this.service.workspaceXreferences({ query: { name: 'x' } }) .reduce(jsonpatch.applyReducer, null as any) .toPromise(); assert.deepEqual(result, [{ @@ -916,7 +917,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 9, + character: 8, line: 0 } }, @@ -931,6 +932,38 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor } }]); }); + it('should return all references to a symbol from a dependency with PackageDescriptor query', async function (this: TestContext & ITestCallbackContext) { + const result: ReferenceInformation[] = await this.service.workspaceXreferences({ query: { name: 'x', package: { name: 'dep' } } }) + .reduce(jsonpatch.applyReducer, null as any) + .toPromise(); + assert.deepEqual(result, [{ + reference: { + range: { + end: { + character: 10, + line: 0 + }, + start: { + character: 8, + line: 0 + } + }, + uri: rootUri + 'c.ts' + }, + symbol: { + filePath: 'node_modules/dep/dep.ts', + containerKind: '', + containerName: '"node_modules/dep/dep"', + kind: 'var', + name: 'x', + package: { + name: 'dep', + repoURL: undefined, + version: undefined + } + } + }]); + }); it('should return all references to all symbols if empty SymbolDescriptor query is passed', async function (this: TestContext & ITestCallbackContext) { const result: ReferenceInformation[] = await this.service.workspaceXreferences({ query: {} }) .reduce(jsonpatch.applyReducer, null as any) @@ -951,7 +984,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 6, + character: 5, line: 0 } }, @@ -973,7 +1006,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 10, + character: 9, line: 0 } }, @@ -995,7 +1028,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 24, + character: 23, line: 0 } }, @@ -1010,7 +1043,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 9, + character: 8, line: 0 } }, @@ -1039,7 +1072,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 6, + character: 5, line: 0 } }, @@ -1061,7 +1094,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 10, + character: 9, line: 0 } }, @@ -1083,7 +1116,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 23, + character: 22, line: 0 } }, @@ -1105,7 +1138,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 52, + character: 51, line: 0 } }, @@ -1127,7 +1160,7 @@ export function describeTypeScriptService(createService: TypeScriptServiceFactor line: 0 }, start: { - character: 68, + character: 67, line: 0 } }, diff --git a/src/typescript-service.ts b/src/typescript-service.ts index 061c6bef8..f210c7fee 100644 --- a/src/typescript-service.ts +++ b/src/typescript-service.ts @@ -44,6 +44,7 @@ import { InitializeResult, PackageDescriptor, PackageInformation, + ReferenceInformation, SymbolDescriptor, SymbolLocationInformation, WorkspaceReferenceParams, @@ -53,7 +54,6 @@ import { defInfoToSymbolDescriptor, getMatchingPropertyCount, getPropertyCount, - isSymbolDescriptorMatch, JSONPTR, normalizeUri, observableFromIterable, @@ -761,14 +761,21 @@ export class TypeScriptService { */ workspaceXreferences(params: WorkspaceReferenceParams, span = new Span()): Observable { const queryWithoutPackage = omit(params.query, 'package'); - return Observable.from(this.projectManager.ensureAllFiles(span)) - .mergeMap(() => { + const minScore = Math.min(4.75, getPropertyCount(queryWithoutPackage)); + return this.isDefinitelyTyped + .mergeMap(isDefinitelyTyped => { + if (isDefinitelyTyped) { + throw new Error('workspace/xreferences not supported in DefinitelyTyped'); + } + return this.projectManager.ensureAllFiles(span); + }) + .mergeMap(() => { // if we were hinted that we should only search a specific package, find it and only search the owning tsconfig.json if (params.hints && params.hints.dependeePackageName) { return observableFromIterable(this.packageManager.packageJsonUris()) .filter(uri => (JSON.parse(this.inMemoryFileSystem.getContent(uri)) as PackageJson).name === params.hints!.dependeePackageName) .take(1) - .mergeMap(uri => { + .mergeMap(uri => { const config = this.projectManager.getParentConfiguration(uri); if (!config) { return observableFromIterable(this.projectManager.configurations()); @@ -796,35 +803,36 @@ export class TypeScriptService { .filter((node): node is ts.Identifier => node.kind === ts.SyntaxKind.Identifier) .mergeMap(node => { try { - // Get DefinitionInformations at the node + // Find definition for node return Observable.from(config.getService().getDefinitionAtPosition(source.fileName, node.pos + 1) || []) .mergeMap(definition => { const symbol = defInfoToSymbolDescriptor(definition, this.root); // Check if SymbolDescriptor without PackageDescriptor matches - if (!isSymbolDescriptorMatch(queryWithoutPackage, symbol)) { + const score = getMatchingPropertyCount(queryWithoutPackage, symbol); + if (score < minScore || (params.query.package && !definition.fileName.includes(params.query.package.name))) { return []; } + span.log({ event: 'match', score }); // If no PackageDescriptor query, return match if (!params.query.package || !params.query.package) { return [symbol]; } // If SymbolDescriptor matched and the query contains a PackageDescriptor, get package.json and match PackageDescriptor name - // TODO match full PackageDescriptor (version) + // TODO match full PackageDescriptor (version) and fill out the symbol.package field const uri = path2uri(definition.fileName); - return this._getPackageDescriptor(uri) - .mergeMap(packageDescriptor => { + return Observable.from(this._getPackageDescriptor(uri, span)) + .filter(packageDescriptor => !!(packageDescriptor && packageDescriptor.name === params.query.package!.name!)) + .map(packageDescriptor => { symbol.package = packageDescriptor; - return packageDescriptor && packageDescriptor.name === params.query.package!.name! - ? [symbol] - : []; + return symbol; }); }) - .map(symbol => ({ + .map((symbol: SymbolDescriptor): ReferenceInformation => ({ symbol, reference: { uri: locationUri(source.fileName), range: { - start: ts.getLineAndCharacterOfPosition(source, node.pos + 1), + start: ts.getLineAndCharacterOfPosition(source, node.pos), end: ts.getLineAndCharacterOfPosition(source, node.end) } }