diff --git a/extensions/typescript/src/features/definitionProvider.ts b/extensions/typescript/src/features/definitionProvider.ts index 73d87374783..ee364a63614 100644 --- a/extensions/typescript/src/features/definitionProvider.ts +++ b/extensions/typescript/src/features/definitionProvider.ts @@ -5,50 +5,18 @@ 'use strict'; -import { DefinitionProvider, TextDocument, Position, Range, CancellationToken, Definition, Location } from 'vscode'; +import { DefinitionProvider, TextDocument, Position, CancellationToken, Definition } from 'vscode'; -import * as Proto from '../protocol'; import { ITypescriptServiceClient } from '../typescriptService'; +import DefinitionProviderBase from './definitionProviderBase'; -export default class TypeScriptDefinitionProvider implements DefinitionProvider { - - private client: ITypescriptServiceClient; - - public tokens: string[] = []; +export default class TypeScriptDefinitionProvider extends DefinitionProviderBase implements DefinitionProvider { constructor(client: ITypescriptServiceClient) { - this.client = client; + super(client); } - public provideDefinition(document: TextDocument, position: Position, token: CancellationToken): Promise { - const filepath = this.client.asAbsolutePath(document.uri); - if (!filepath) { - return Promise.resolve(null); - } - let args: Proto.FileLocationRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1 - }; - if (!args.file) { - return Promise.resolve(null); - } - return this.client.execute('definition', args, token).then(response => { - let locations: Proto.FileSpan[] = response.body || []; - if (!locations || locations.length === 0) { - return [] as Definition; - } - return locations.map(location => { - let resource = this.client.asUrl(location.file); - if (resource === null) { - return null; - } else { - return new Location(resource, new Range(location.start.line - 1, location.start.offset - 1, location.end.line - 1, location.end.offset - 1)); - } - }).filter(x => x !== null) as Location[]; - }, (error) => { - this.client.error(`'definition' request failed with error.`, error); - return [] as Definition; - }); + public provideDefinition(document: TextDocument, position: Position, token: CancellationToken | boolean): Promise { + return this.getSymbolLocations('definition', document, position, token); } } \ No newline at end of file diff --git a/extensions/typescript/src/features/definitionProviderBase.ts b/extensions/typescript/src/features/definitionProviderBase.ts new file mode 100644 index 00000000000..94977e55076 --- /dev/null +++ b/extensions/typescript/src/features/definitionProviderBase.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TextDocument, Position, Range, CancellationToken, Location } from 'vscode'; + +import * as Proto from '../protocol'; +import { ITypescriptServiceClient } from '../typescriptService'; + +export default class TypeScriptDefinitionProviderBase { + + private client: ITypescriptServiceClient; + + public tokens: string[] = []; + + constructor(client: ITypescriptServiceClient) { + this.client = client; + } + + protected getSymbolLocations(definitionType: 'definition' | 'implementation', document: TextDocument, position: Position, token: CancellationToken | boolean): Promise { + const filepath = this.client.asAbsolutePath(document.uri); + if (!filepath) { + return Promise.resolve(null); + } + let args: Proto.FileLocationRequestArgs = { + file: filepath, + line: position.line + 1, + offset: position.character + 1 + }; + if (!args.file) { + return Promise.resolve(null); + } + return this.client.execute(definitionType, args, token).then(response => { + let locations: Proto.FileSpan[] = (response && response.body) || []; + if (!locations || locations.length === 0) { + return []; + } + return locations.map(location => { + let resource = this.client.asUrl(location.file); + if (resource === null) { + return null; + } else { + return new Location(resource, new Range(location.start.line - 1, location.start.offset - 1, location.end.line - 1, location.end.offset - 1)); + } + }).filter(x => x !== null) as Location[]; + }, (error) => { + this.client.error(`'${definitionType}' request failed with error.`, error); + return []; + }); + } +} \ No newline at end of file diff --git a/extensions/typescript/src/features/typeDefinitionProvider.ts b/extensions/typescript/src/features/typeDefinitionProvider.ts new file mode 100644 index 00000000000..49c45283689 --- /dev/null +++ b/extensions/typescript/src/features/typeDefinitionProvider.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TypeDefinitionProvider, TextDocument, Position, CancellationToken, Definition } from 'vscode'; + +import { ITypescriptServiceClient } from '../typescriptService'; +import DefinitionProviderBase from './definitionProviderBase'; + +export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements TypeDefinitionProvider { + + constructor(client: ITypescriptServiceClient) { + super(client); + } + + public provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken | boolean): Promise { + return this.getSymbolLocations('implementation', document, position, token); + } +} \ No newline at end of file diff --git a/extensions/typescript/src/typescriptMain.ts b/extensions/typescript/src/typescriptMain.ts index 9b53a6bd255..97c8e8b3140 100644 --- a/extensions/typescript/src/typescriptMain.ts +++ b/extensions/typescript/src/typescriptMain.ts @@ -25,6 +25,7 @@ import { ITypescriptServiceClientHost } from './typescriptService'; import HoverProvider from './features/hoverProvider'; import DefinitionProvider from './features/definitionProvider'; +import TypeDefinitionProvider from './features/TypeDefinitionProvider'; import DocumentHighlightProvider from './features/documentHighlightProvider'; import ReferenceProvider from './features/referenceProvider'; import DocumentSymbolProvider from './features/documentSymbolProvider'; @@ -147,6 +148,7 @@ class LanguageProvider { let hoverProvider = new HoverProvider(client); let definitionProvider = new DefinitionProvider(client); + let typeDefinitionProvider = new TypeDefinitionProvider(client); let documentHighlightProvider = new DocumentHighlightProvider(client); let referenceProvider = new ReferenceProvider(client); let documentSymbolProvider = new DocumentSymbolProvider(client); @@ -169,6 +171,7 @@ class LanguageProvider { languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.'); languages.registerHoverProvider(selector, hoverProvider); languages.registerDefinitionProvider(selector, definitionProvider); + languages.registerTypeDefinitionProvider(selector, typeDefinitionProvider); languages.registerDocumentHighlightProvider(selector, documentHighlightProvider); languages.registerReferenceProvider(selector, referenceProvider); languages.registerDocumentSymbolProvider(selector, documentSymbolProvider); diff --git a/extensions/typescript/src/typescriptService.ts b/extensions/typescript/src/typescriptService.ts index 14a8b65f86a..8f05acaaa6e 100644 --- a/extensions/typescript/src/typescriptService.ts +++ b/extensions/typescript/src/typescriptService.ts @@ -87,6 +87,7 @@ export interface ITypescriptServiceClient { execute(commant: 'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken): Promise; execute(commant: 'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken): Promise; execute(command: 'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; + execute(command: 'implementation', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; execute(command: 'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken): Promise; execute(command: 'navbar', args: Proto.FileRequestArgs, token?: CancellationToken): Promise; diff --git a/src/vs/editor/browser/standalone/standaloneLanguages.ts b/src/vs/editor/browser/standalone/standaloneLanguages.ts index 0a9810e6b05..14750f8b094 100644 --- a/src/vs/editor/browser/standalone/standaloneLanguages.ts +++ b/src/vs/editor/browser/standalone/standaloneLanguages.ts @@ -276,6 +276,13 @@ export function registerDefinitionProvider(languageId: string, provider: modes.D return modes.DefinitionProviderRegistry.register(languageId, provider); } +/** + * Register a type definition provider (used by e.g. go to implementation). + */ +export function registerTypeDefinitionProvider(languageId: string, provider: modes.TypeDefinitionProvider): IDisposable { + return modes.TypeDefinitionProviderRegistry.register(languageId, provider); +} + /** * Register a code lens provider (used by e.g. inline code lenses). */ @@ -633,6 +640,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages { registerDocumentSymbolProvider: registerDocumentSymbolProvider, registerDocumentHighlightProvider: registerDocumentHighlightProvider, registerDefinitionProvider: registerDefinitionProvider, + registerTypeDefinitionProvider: registerTypeDefinitionProvider, registerCodeLensProvider: registerCodeLensProvider, registerCodeActionProvider: registerCodeActionProvider, registerDocumentFormattingEditProvider: registerDocumentFormattingEditProvider, diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 1932b5795ac..1bc532e7c83 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -3064,6 +3064,10 @@ export namespace ModeContextKeys { * @internal */ export const hasDefinitionProvider = new RawContextKey('editorHasDefinitionProvider', undefined); + /** + * @internal + */ + export const hasTypeDefinitionProvider = new RawContextKey('editorHasTypeDefinitionProvider', undefined); /** * @internal */ diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 9e4f4bbf394..8bee5352016 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -418,6 +418,7 @@ export interface Location { * defined. */ export type Definition = Location | Location[]; + /** * The definition provider interface defines the contract between extensions and * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) @@ -430,6 +431,16 @@ export interface DefinitionProvider { provideDefinition(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; } +/** + * The type definition provider interface defines the contract between extensions and + * the go to implementation feature. + */ +export interface TypeDefinitionProvider { + /** + * Provide the implementation of the symbol at the given position and document. + */ + provideTypeDefinition(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; +} /** * A symbol kind. @@ -738,6 +749,11 @@ export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const TypeDefinitionProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/common/modes/editorModeContext.ts b/src/vs/editor/common/modes/editorModeContext.ts index 1bcac6ebf21..b406ca7c8d0 100644 --- a/src/vs/editor/common/modes/editorModeContext.ts +++ b/src/vs/editor/common/modes/editorModeContext.ts @@ -19,6 +19,7 @@ export class EditorModeContext { private _hasCodeActionsProvider: IContextKey; private _hasCodeLensProvider: IContextKey; private _hasDefinitionProvider: IContextKey; + private _hasTypeDefinitionProvider: IContextKey; private _hasHoverProvider: IContextKey; private _hasDocumentHighlightProvider: IContextKey; private _hasDocumentSymbolProvider: IContextKey; @@ -39,6 +40,7 @@ export class EditorModeContext { this._hasCodeActionsProvider = ModeContextKeys.hasCodeActionsProvider.bindTo(contextKeyService); this._hasCodeLensProvider = ModeContextKeys.hasCodeLensProvider.bindTo(contextKeyService); this._hasDefinitionProvider = ModeContextKeys.hasDefinitionProvider.bindTo(contextKeyService); + this._hasTypeDefinitionProvider = ModeContextKeys.hasTypeDefinitionProvider.bindTo(contextKeyService); this._hasHoverProvider = ModeContextKeys.hasHoverProvider.bindTo(contextKeyService); this._hasDocumentHighlightProvider = ModeContextKeys.hasDocumentHighlightProvider.bindTo(contextKeyService); this._hasDocumentSymbolProvider = ModeContextKeys.hasDocumentSymbolProvider.bindTo(contextKeyService); @@ -57,6 +59,7 @@ export class EditorModeContext { modes.CodeActionProviderRegistry.onDidChange(this._update, this, this._disposables); modes.CodeLensProviderRegistry.onDidChange(this._update, this, this._disposables); modes.DefinitionProviderRegistry.onDidChange(this._update, this, this._disposables); + modes.TypeDefinitionProviderRegistry.onDidChange(this._update, this, this._disposables); modes.HoverProviderRegistry.onDidChange(this._update, this, this._disposables); modes.DocumentHighlightProviderRegistry.onDidChange(this._update, this, this._disposables); modes.DocumentSymbolProviderRegistry.onDidChange(this._update, this, this._disposables); @@ -79,6 +82,7 @@ export class EditorModeContext { this._hasCodeActionsProvider.reset(); this._hasCodeLensProvider.reset(); this._hasDefinitionProvider.reset(); + this._hasTypeDefinitionProvider.reset(); this._hasHoverProvider.reset(); this._hasDocumentHighlightProvider.reset(); this._hasDocumentSymbolProvider.reset(); @@ -100,6 +104,7 @@ export class EditorModeContext { this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model)); this._hasCodeLensProvider.set(modes.CodeLensProviderRegistry.has(model)); this._hasDefinitionProvider.set(modes.DefinitionProviderRegistry.has(model)); + this._hasTypeDefinitionProvider.set(modes.TypeDefinitionProviderRegistry.has(model)); this._hasHoverProvider.set(modes.HoverProviderRegistry.has(model)); this._hasDocumentHighlightProvider.set(modes.DocumentHighlightProviderRegistry.has(model)); this._hasDocumentSymbolProvider.set(modes.DocumentSymbolProviderRegistry.has(model)); diff --git a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts index 10e0304f3a2..d4b1246ba76 100644 --- a/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/browser/goToDeclaration.ts @@ -26,13 +26,14 @@ import { editorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes'; import { ICodeEditor, IEditorMouseEvent, IMouseTarget } from 'vs/editor/browser/editorBrowser'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; -import { getDeclarationsAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; +import { getDeclarationsAtPosition, getTypeDefinitionAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/browser/referencesController'; import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/browser/referencesModel'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { PeekContext } from 'vs/editor/contrib/zoneWidget/browser/peekViewWidget'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; +import * as corePosition from 'vs/editor/common/core/position'; import ModeContextKeys = editorCommon.ModeContextKeys; import EditorContextKeys = editorCommon.EditorContextKeys; @@ -64,7 +65,7 @@ export class DefinitionAction extends EditorAction { let model = editor.getModel(); let pos = editor.getPosition(); - return getDeclarationsAtPosition(model, pos).then(references => { + return this.getDeclarationsAtPosition(model, pos).then(references => { if (!references) { return; @@ -104,6 +105,10 @@ export class DefinitionAction extends EditorAction { }); } + protected getDeclarationsAtPosition(model: editorCommon.IModel, position: corePosition.Position): TPromise { + return getDeclarationsAtPosition(model, position); + } + private _onResult(editorService: IEditorService, editor: editorCommon.ICommonCodeEditor, model: ReferencesModel) { if (this._configuration.openInPeek) { this._openInPeek(editorService, editor, model); @@ -217,6 +222,35 @@ export class PeekDefinitionAction extends DefinitionAction { } } + +@editorAction +export class GoToImplementationAction extends DefinitionAction { + + public static ID = 'editor.action.goToImplementation'; + + constructor() { + super(new DefinitionActionConfig(), { + id: GoToImplementationAction.ID, + label: nls.localize('actions.goToImplementation.label', "Go to Implementation"), + alias: 'Go to Implementation', + precondition: ModeContextKeys.hasTypeDefinitionProvider, + kbOpts: { + kbExpr: EditorContextKeys.TextFocus, + primary: KeyMod.CtrlCmd | KeyCode.F12 + }, + menuOpts: { + group: 'navigation', + order: 1.3 + } + }); + } + + protected getDeclarationsAtPosition(model: editorCommon.IModel, position: corePosition.Position): TPromise { + return getTypeDefinitionAtPosition(model, position); + } +} + + // --- Editor Contribution to goto definition using the mouse and a modifier key @editorContribution diff --git a/src/vs/editor/contrib/goToDeclaration/common/goToDeclaration.ts b/src/vs/editor/contrib/goToDeclaration/common/goToDeclaration.ts index 0d076deae36..11037d438da 100644 --- a/src/vs/editor/contrib/goToDeclaration/common/goToDeclaration.ts +++ b/src/vs/editor/contrib/goToDeclaration/common/goToDeclaration.ts @@ -9,10 +9,24 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { TPromise } from 'vs/base/common/winjs.base'; import { IReadOnlyModel } from 'vs/editor/common/editorCommon'; import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions'; -import { DefinitionProviderRegistry, Location } from 'vs/editor/common/modes'; +import { DefinitionProviderRegistry, TypeDefinitionProviderRegistry, Location } from 'vs/editor/common/modes'; import { asWinJsPromise } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; +function outputResults(promises: TPromise[]) { + return TPromise.join(promises).then(allReferences => { + let result: Location[] = []; + for (let references of allReferences) { + if (Array.isArray(references)) { + result.push(...references); + } else if (references) { + result.push(references); + } + } + return result; + }); +} + export function getDeclarationsAtPosition(model: IReadOnlyModel, position: Position): TPromise { const provider = DefinitionProviderRegistry.ordered(model); @@ -27,18 +41,25 @@ export function getDeclarationsAtPosition(model: IReadOnlyModel, position: Posit onUnexpectedExternalError(err); }); }); - - return TPromise.join(promises).then(allReferences => { - let result: Location[] = []; - for (let references of allReferences) { - if (Array.isArray(references)) { - result.push(...references); - } else if (references) { - result.push(references); - } - } - return result; - }); + return outputResults(promises); } -CommonEditorRegistry.registerDefaultLanguageCommand('_executeDefinitionProvider', getDeclarationsAtPosition); \ No newline at end of file +export function getTypeDefinitionAtPosition(model: IReadOnlyModel, position: Position): TPromise { + + const provider = TypeDefinitionProviderRegistry.ordered(model); + + // get results + const promises = provider.map((provider, idx) => { + return asWinJsPromise((token) => { + return provider.provideTypeDefinition(model, position, token); + }).then(result => { + return result; + }, err => { + onUnexpectedExternalError(err); + }); + }); + return outputResults(promises); +} + +CommonEditorRegistry.registerDefaultLanguageCommand('_executeDefinitionProvider', getDeclarationsAtPosition); +CommonEditorRegistry.registerDefaultLanguageCommand('_executeTypeDefinitionProvider', getTypeDefinitionAtPosition); \ No newline at end of file diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 76745062237..b236973e1a6 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3960,6 +3960,11 @@ declare module monaco.languages { */ export function registerDefinitionProvider(languageId: string, provider: DefinitionProvider): IDisposable; + /** + * Register a type definition provider (used by e.g. go to implementation). + */ + export function registerTypeDefinitionProvider(languageId: string, provider: TypeDefinitionProvider): IDisposable; + /** * Register a code lens provider (used by e.g. inline code lenses). */ @@ -4539,6 +4544,17 @@ declare module monaco.languages { provideDefinition(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; } + /** + * The type definition provider interface defines the contract between extensions and + * the go to implementation feature. + */ + export interface TypeDefinitionProvider { + /** + * Provide the implementation of the symbol at the given position and document. + */ + provideTypeDefinition(model: editor.IReadOnlyModel, position: Position, token: CancellationToken): Definition | Thenable; + } + /** * A symbol kind. */ diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index cbd1ad7f30c..82be13ee78d 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1632,6 +1632,24 @@ declare module 'vscode' { provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } + /** + * The type definition provider interface defines the contract between extensions and + * the go to implementation feature. + */ + export interface TypeDefinitionProvider { + + /** + * Provide the implementations of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @return A definition or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + /** * MarkedString can be used to render human readable text. It is either a markdown string * or a code-block that provides a language and a code snippet. Note that @@ -4117,6 +4135,18 @@ declare module 'vscode' { */ export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; + /** + * Register an type definition provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their [score](#languages.match) and the best-matching provider is used. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider An implementation provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; + /** * Register a hover provider. * diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index e4433369948..43cd604ebf8 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -180,6 +180,9 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { return languageFeatures.registerDefinitionProvider(selector, provider); }, + registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { + return languageFeatures.registerTypeDefinitionProvider(selector, provider); + }, registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { return languageFeatures.registerHoverProvider(selector, provider); }, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index f67c5c241f3..55f053f9f29 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -153,6 +153,7 @@ export abstract class MainThreadLanguageFeaturesShape { $registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise { throw ni(); } $emitCodeLensEvent(eventHandle: number, event?: any): TPromise { throw ni(); } $registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } + $registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } $registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise { throw ni(); } @@ -329,6 +330,7 @@ export abstract class ExtHostLanguageFeaturesShape { $provideCodeLenses(handle: number, resource: URI): TPromise { throw ni(); } $resolveCodeLens(handle: number, resource: URI, symbol: modes.ICodeLensSymbol): TPromise { throw ni(); } $provideDefinition(handle: number, resource: URI, position: editorCommon.IPosition): TPromise { throw ni(); } + $provideTypeDefinition(handle: number, resource: URI, position: editorCommon.IPosition): TPromise { throw ni(); } $provideHover(handle: number, resource: URI, position: editorCommon.IPosition): TPromise { throw ni(); } $provideDocumentHighlights(handle: number, resource: URI, position: editorCommon.IPosition): TPromise { throw ni(); } $provideReferences(handle: number, resource: URI, position: editorCommon.IPosition, context: modes.ReferenceContext): TPromise { throw ni(); } diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 5fca8b0a243..31b63b2fe0e 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -46,6 +46,14 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of Location-instances.' }); + this._register('vscode.executeTypeDefinitionProvider', this._executeTypeDefinitionProvider, { + description: 'Execute all implementation providers.', + args: [ + { name: 'uri', description: 'Uri of a text document', constraint: URI }, + { name: 'position', description: 'Position of a symbol', constraint: types.Position } + ], + returns: 'A promise that resolves to an array of Location-instance.' + }); this._register('vscode.executeHoverProvider', this._executeHoverProvider, { description: 'Execute all hover provider.', args: [ @@ -265,6 +273,18 @@ export class ExtHostApiCommands { }); } + private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Thenable { + const args = { + resource, + position: position && typeConverters.fromPosition(position) + }; + return this._commands.executeCommand('_executeTypeDefinitionProvider', args).then(value => { + if (Array.isArray(value)) { + return value.map(typeConverters.location.to); + } + }); + } + private _executeHoverProvider(resource: URI, position: types.Position): Thenable { const args = { resource, diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 6128e8165c2..ad4b7245e45 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -122,6 +122,29 @@ class DefinitionAdapter { } } +class ImplementationAdapter { + + private _documents: ExtHostDocuments; + private _provider: vscode.TypeDefinitionProvider; + + constructor(documents: ExtHostDocuments, provider: vscode.TypeDefinitionProvider) { + this._documents = documents; + this._provider = provider; + } + + provideTypeDefinition(resource: URI, position: IPosition): TPromise { + let doc = this._documents.getDocumentData(resource).document; + let pos = TypeConverters.toPosition(position); + return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(value => { + if (Array.isArray(value)) { + return value.map(TypeConverters.location.from); + } else if (value) { + return TypeConverters.location.from(value); + } + }); + } +} + class HoverAdapter { private _documents: ExtHostDocuments; @@ -614,7 +637,7 @@ class LinkProviderAdapter { type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | QuickFixAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter - | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter; + | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter; export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { @@ -713,6 +736,17 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(resource, position)); } + registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { + const handle = this._nextHandle(); + this._adapter.set(handle, new ImplementationAdapter(this._documents, provider)); + this._proxy.$registerImplementationSupport(handle, selector); + return this._createDisposable(handle); + } + + $provideTypeDefinition(handle: number, resource: URI, position: IPosition): TPromise { + return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideTypeDefinition(resource, position)); + } + // --- extra info registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index c1cc2c93809..5b12ce8f47d 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -102,6 +102,15 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape return undefined; } + $registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): TPromise { + this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(selector, { + provideTypeDefinition: (model, position, token): Thenable => { + return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)); + } + }); + return undefined; + } + // --- extra info $registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise { diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index de736d04d87..a915bf3b3c4 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -27,7 +27,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; import { DocumentSymbolProviderRegistry, DocumentHighlightKind } from 'vs/editor/common/modes'; import { getCodeLensData } from 'vs/editor/contrib/codelens/common/codelens'; -import { getDeclarationsAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; +import { getDeclarationsAtPosition, getTypeDefinitionAtPosition } from 'vs/editor/contrib/goToDeclaration/common/goToDeclaration'; import { getHover } from 'vs/editor/contrib/hover/common/hover'; import { getOccurrencesAtPosition } from 'vs/editor/contrib/wordHighlighter/common/wordHighlighter'; import { provideReferences } from 'vs/editor/contrib/referenceSearch/common/referenceSearch'; @@ -351,6 +351,26 @@ suite('ExtHostLanguageFeatures', function () { }); }); + // --- type definition + + test('TypeDefinition, data conversion', function () { + + disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + provideTypeDefinition(): any { + return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; + } + })); + + return threadService.sync().then(() => { + return getTypeDefinitionAtPosition(model, new EditorPosition(1, 1)).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); + }); + }); + }); + // --- extra info test('HoverProvider, word range at pos', function () {