提交 265803b0 编写于 作者: M Matt Bierner 提交者: GitHub

Add TypeScript References Code Lens Provider (#18205)

Fixes #18054

Adds an references code lens provide for JS and TS
上级 f14e8ed4
......@@ -99,6 +99,11 @@
"default": true,
"description": "%typescript.check.tscVersion%"
},
"typescript.referencesCodeLens.enabled": {
"type": "boolean",
"default": false,
"description": "%typescript.referencesCodeLens.enabled%"
},
"typescript.tsserver.trace": {
"type": "string",
"enum": [
......
......@@ -24,5 +24,6 @@
"format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": "Defines space handling after opening and before closing JSX expression braces. Requires TypeScript >= 2.0.6.",
"format.placeOpenBraceOnNewLineForFunctions": "Defines whether an open brace is put onto a new line for functions or not.",
"format.placeOpenBraceOnNewLineForControlBlocks": "Defines whether an open brace is put onto a new line for control blocks or not.",
"javascript.validate.enable": "Enable/disable JavaScript validation."
"javascript.validate.enable": "Enable/disable JavaScript validation.",
"typescript.referencesCodeLens.enabled": "Enable/disable the references code lens"
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { CodeLensProvider, CodeLens, CancellationToken, TextDocument, Range, Uri, Location, Position, workspace, WorkspaceConfiguration } from 'vscode';
import * as Proto from '../protocol';
import * as PConst from '../protocol.const';
import { ITypescriptServiceClient } from '../typescriptService';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
class ReferencesCodeLens extends CodeLens {
public document: Uri;
public file: string;
constructor(document: Uri, file: string, range: Range) {
super(range);
this.document = document;
this.file = file;
}
}
export default class TypeScriptReferencesCodeLensProvider implements CodeLensProvider {
private client: ITypescriptServiceClient;
private enabled = false;
constructor(client: ITypescriptServiceClient) {
this.client = client;
}
public updateConfiguration(config: WorkspaceConfiguration): void {
let typeScriptConfig = workspace.getConfiguration('typescript');
this.enabled = typeScriptConfig.get('referencesCodeLens.enabled', false);
}
provideCodeLenses(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
if (!this.enabled) {
return Promise.resolve([]);
}
const filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve([]);
}
return this.client.execute('navtree', { file: filepath }, token).then(response => {
const tree = response.body;
const referenceableSpans: Range[] = [];
if (tree && tree.childItems) {
tree.childItems.forEach(item => this.extractReferenceableSymbols(document, item, referenceableSpans));
}
return Promise.resolve(referenceableSpans.map(span => new ReferencesCodeLens(document.uri, filepath, span)));
});
}
resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise<CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
if (!codeLens.document) {
return Promise.reject<CodeLens>(codeLens);
}
const args: Proto.FileLocationRequestArgs = {
file: codeLens.file,
line: codeLens.range.start.line + 1,
offset: codeLens.range.start.character + 1
};
return this.client.execute('references', args, token).then(response => {
if (response && response.body) {
const referenceCount = Math.max(0, response.body.refs.length - 1);
const locations = response.body.refs.map(reference =>
new Location(Uri.file(reference.file),
new Range(
new Position(reference.start.line - 1, reference.start.offset - 1),
new Position(reference.end.line - 1, reference.end.offset - 1))));
codeLens.command = {
title: referenceCount + ' ' + (referenceCount === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')),
command: 'editor.action.showReferences',
arguments: [codeLens.document, codeLens.range.start, locations]
};
return Promise.resolve(codeLens);
}
return Promise.reject(codeLens);
}).catch(() => {
codeLens.command = {
title: localize('referenceErrorLabel', 'Could not determine references'),
command: ''
};
return Promise.resolve(codeLens);
});
}
private extractReferenceableSymbols(document: TextDocument, item: Proto.NavigationTree, results: Range[]) {
if (!item) {
return;
}
const span = item.spans && item.spans[0];
if (span) {
const range = new Range(
new Position(span.start.line - 1, span.start.offset - 1),
new Position(span.end.line - 1, span.end.offset - 1));
// TODO: TS currently requires the position for 'references 'to be inside of the identifer
// Massage the range to make sure this is the case
const text = document.getText(range);
switch (item.kind) {
case PConst.Kind.const:
case PConst.Kind.let:
case PConst.Kind.variable:
case PConst.Kind.function:
// Only show references for exported variables
if (!item.kindModifiers.match(/\bexport\b/)) {
break;
}
// fallthrough
case PConst.Kind.memberFunction:
case PConst.Kind.memberVariable:
case PConst.Kind.memberGetAccessor:
case PConst.Kind.memberSetAccessor:
case PConst.Kind.constructorImplementation:
case PConst.Kind.class:
case PConst.Kind.interface:
case PConst.Kind.type:
case PConst.Kind.enum:
const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${item.text}`, 'g');
const match = identifierMatch.exec(text);
const start = match ? match.index + match[1].length : 0;
results.push(new Range(
new Position(range.start.line, range.start.character + start),
new Position(range.start.line, range.start.character + start + item.text.length)));
break;
}
}
(item.childItems || []).forEach(item => this.extractReferenceableSymbols(document, item, results));
}
};
\ No newline at end of file
......@@ -35,6 +35,7 @@ import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
......@@ -107,6 +108,7 @@ class LanguageProvider {
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus;
private referenceCodeLensProvider: ReferenceCodeLensProvider;
private _validate: boolean;
......@@ -156,6 +158,12 @@ class LanguageProvider {
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
}
this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
this.referenceCodeLensProvider.updateConfiguration(config);
if (client.apiVersion.has206Features()) {
languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
}
this.description.modeIds.forEach(modeId => {
let selector: DocumentFilter = { scheme: 'file', language: modeId };
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
......@@ -171,6 +179,7 @@ class LanguageProvider {
if (client.apiVersion.has213Features()) {
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
}
languages.setLanguageConfiguration(modeId, {
indentationRules: {
// ^(.*\*/)?\s*\}.*$
......@@ -217,6 +226,9 @@ class LanguageProvider {
if (this.completionItemProvider) {
this.completionItemProvider.updateConfiguration(config);
}
if (this.referenceCodeLensProvider) {
this.referenceCodeLensProvider.updateConfiguration(config);
}
if (this.formattingProvider) {
this.formattingProvider.updateConfiguration(config);
if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册