diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 5504bb05d4a..106e13425d0 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -78,26 +78,35 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens case PConst.Kind.let: case PConst.Kind.variable: // Only show references for exported variables - if (!item.kindModifiers.match(/\bexport\b/)) { - break; + if (/\bexport\b/.test(item.kindModifiers)) { + return getSymbolRange(document, item); } - // fallthrough + break; case PConst.Kind.class: if (item.text === '') { break; } - // fallthrough + return getSymbolRange(document, item); - case PConst.Kind.memberFunction: - case PConst.Kind.memberVariable: - case PConst.Kind.memberGetAccessor: - case PConst.Kind.memberSetAccessor: - case PConst.Kind.constructorImplementation: case PConst.Kind.interface: case PConst.Kind.type: case PConst.Kind.enum: return getSymbolRange(document, item); + + case PConst.Kind.memberFunction: + case PConst.Kind.memberGetAccessor: + case PConst.Kind.memberSetAccessor: + case PConst.Kind.constructorImplementation: + case PConst.Kind.memberVariable: + // Only show if parent is a class type object (not a literal) + switch (parent?.kind) { + case PConst.Kind.class: + case PConst.Kind.interface: + case PConst.Kind.type: + return getSymbolRange(document, item); + } + break; } return null; diff --git a/extensions/typescript-language-features/src/test/index.ts b/extensions/typescript-language-features/src/test/index.ts index 4c3a74b15b8..ba0fd6d7663 100644 --- a/extensions/typescript-language-features/src/test/index.ts +++ b/extensions/typescript-language-features/src/test/index.ts @@ -22,7 +22,8 @@ const testRunner = require('vscode/lib/testrunner'); testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), // colored output from test results (only windows cannot handle) - timeout: 60000 + timeout: 60000, + grep: 'References' }); export = testRunner; diff --git a/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts new file mode 100644 index 00000000000..7ca35ed89cc --- /dev/null +++ b/extensions/typescript-language-features/src/test/referencesCodeLens.test.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, wait } from './testUtils'; + + +type VsCodeConfiguration = { [key: string]: any }; + +async function updateConfig(newConfig: VsCodeConfiguration): Promise { + const oldConfig: VsCodeConfiguration = {}; + const config = vscode.workspace.getConfiguration(undefined); + for (const configKey of Object.keys(newConfig)) { + oldConfig[configKey] = config.get(configKey); + await new Promise((resolve, reject) => + config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) + .then(() => resolve(), reject)); + } + return oldConfig; +} + +namespace Config { + export const referencesCodeLens = 'typescript.referencesCodeLens.enabled'; +} + +suite('TypeScript References', () => { + const configDefaults: VsCodeConfiguration = Object.freeze({ + [Config.referencesCodeLens]: true, + }); + + const _disposables: vscode.Disposable[] = []; + let oldConfig: { [key: string]: any } = {}; + + setup(async () => { + await wait(100); + + // Save off config and apply defaults + oldConfig = await updateConfig(configDefaults); + }); + + teardown(async () => { + disposeAll(_disposables); + + // Restore config + await updateConfig(oldConfig); + + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Should show on basic class', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test1.ts'); + await createTestEditor(testDocumentUri, + `class Foo {}` + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 1); + assert.strictEqual(codeLenses?.[0].range.start.line, 0); + }); + + test('Should show on basic class properties', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test2.ts'); + await createTestEditor(testDocumentUri, + `class Foo {`, + ` prop: number;`, + ` meth(): void {}`, + `}` + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 3); + assert.strictEqual(codeLenses?.[0].range.start.line, 0); + assert.strictEqual(codeLenses?.[1].range.start.line, 1); + assert.strictEqual(codeLenses?.[2].range.start.line, 2); + }); + + test('Should not show on const property', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test3.ts'); + await createTestEditor(testDocumentUri, + `const foo = {`, + ` prop: 1;`, + ` meth(): void {}`, + `}` + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 0); + }); +}); + +function getCodeLenses(document: vscode.Uri): Thenable { + return vscode.commands.executeCommand('vscode.executeCodeLensProvider', document, 100); +} +