diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 490e1c38beb..7aceffd2368 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -28,7 +28,7 @@ import * as modes from 'vs/editor/common/modes'; import {IResourceEdit} from 'vs/editor/common/services/bulkEdit'; import {IPickOpenEntry, IPickOptions} from 'vs/workbench/services/quickopen/common/quickOpenService'; -import {ITypeBearing} from 'vs/workbench/parts/search/common/search'; +import {IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import {TextEditorRevealType, ITextEditorConfigurationUpdate, IResolvedTextEditorConfiguration, ISelectionChangeEvent} from './mainThreadEditorsTracker'; import {EndOfLine} from './extHostTypes'; @@ -263,7 +263,8 @@ export abstract class ExtHostLanguageFeaturesShape { $provideDocumentFormattingEdits(handle: number, resource: URI, options: modes.FormattingOptions): TPromise { throw ni(); } $provideDocumentRangeFormattingEdits(handle: number, resource: URI, range: editorCommon.IRange, options: modes.FormattingOptions): TPromise { throw ni(); } $provideOnTypeFormattingEdits(handle: number, resource: URI, position: editorCommon.IPosition, ch: string, options: modes.FormattingOptions): TPromise { throw ni(); } - $getNavigateToItems(handle: number, search: string): TPromise { throw ni(); } + $provideWorkspaceSymbols(handle: number, search: string): TPromise { throw ni(); } + $resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbol): TPromise { throw ni(); } $provideRenameEdits(handle: number, resource: URI, position: editorCommon.IPosition, newName: string): TPromise { throw ni(); } $provideCompletionItems(handle: number, resource: URI, position: editorCommon.IPosition): TPromise { throw ni(); } $resolveCompletionItem(handle: number, resource: URI, position: editorCommon.IPosition, suggestion: modes.ISuggestion): TPromise { throw ni(); } diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 4f5c1625e33..3b82ceedcc0 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -16,7 +16,7 @@ import {ICommandHandlerDescription} from 'vs/platform/commands/common/commands'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {IQuickFix2} from 'vs/editor/contrib/quickFix/common/quickFix'; import {IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; -import {ITypeBearing} from 'vs/workbench/parts/search/common/search'; +import {IWorkspaceSymbolProvider, IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import {ICodeLensData} from 'vs/editor/contrib/codelens/common/codelens'; export function registerApiCommands(commands:ExtHostCommands) { @@ -240,10 +240,14 @@ class ExtHostApiCommands { * @return A promise that resolves to an array of symbol information. */ private _executeWorkspaceSymbolProvider(query: string): Thenable { - return this._commands.executeCommand('_executeWorkspaceSymbolProvider', { query }).then(value => { + return this._commands.executeCommand<[IWorkspaceSymbolProvider, IWorkspaceSymbol[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => { + const result: types.SymbolInformation[] = []; if (Array.isArray(value)) { - return value.map(typeConverters.toSymbolInformation); + for (const tuple of value) { + result.push(...tuple[1].map(typeConverters.toSymbolInformation)); + } } + return result; }); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index d2a45eb6a94..6cd60ce7acf 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -16,7 +16,7 @@ import * as modes from 'vs/editor/common/modes'; import {ExtHostDocuments} from 'vs/workbench/api/node/extHostDocuments'; import {ExtHostCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostDiagnostics} from 'vs/workbench/api/node/extHostDiagnostics'; -import {INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search'; +import {IWorkspaceSymbolProvider, IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import {asWinJsPromise, ShallowCancelThenPromise} from 'vs/base/common/async'; import {MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape} from './extHost.protocol'; import {regExpLeadsToEndlessLoop} from 'vs/base/common/strings'; @@ -394,7 +394,7 @@ class OnTypeFormattingAdapter { } } -class NavigateTypeAdapter implements INavigateTypesSupport { +class NavigateTypeAdapter implements IWorkspaceSymbolProvider { private _provider: vscode.WorkspaceSymbolProvider; @@ -402,13 +402,17 @@ class NavigateTypeAdapter implements INavigateTypesSupport { this._provider = provider; } - getNavigateToItems(search: string): TPromise { + provideWorkspaceSymbols(search: string): TPromise { return asWinJsPromise(token => this._provider.provideWorkspaceSymbols(search, token)).then(value => { if (Array.isArray(value)) { return value.map(TypeConverters.fromSymbolInformation); } }); } + + resolveWorkspaceSymbol(item: IWorkspaceSymbol): TPromise { + return TPromise.as(item); + } } class RenameAdapter { @@ -795,8 +799,12 @@ export class ExtHostLanguageFeatures extends ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $getNavigateToItems(handle: number, search: string): TPromise { - return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.getNavigateToItems(search)); + $provideWorkspaceSymbols(handle: number, search: string): TPromise { + return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search)); + } + + $resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbol): TPromise { + return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol)); } // --- rename diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 777667e6842..7923943d4b4 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -12,7 +12,7 @@ import * as modes from 'vs/editor/common/modes'; import * as types from './extHostTypes'; import {Position as EditorPosition} from 'vs/platform/editor/common/editor'; import {IPosition, ISelection, IRange, IDecorationOptions, ISingleEditOperation} from 'vs/editor/common/editorCommon'; -import {ITypeBearing} from 'vs/workbench/parts/search/common/search'; +import {IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import * as vscode from 'vscode'; import URI from 'vs/base/common/uri'; @@ -190,22 +190,21 @@ export namespace SymbolInformation { } } -export function fromSymbolInformation(info: vscode.SymbolInformation): ITypeBearing { - return { +export function fromSymbolInformation(info: vscode.SymbolInformation): IWorkspaceSymbol { + return { name: info.name, type: types.SymbolKind[info.kind || types.SymbolKind.Property].toLowerCase(), range: fromRange(info.location.range), - resourceUri: info.location.uri, - containerName: info.containerName, - parameters: '', + resource: info.location.uri, + containerName: info.containerName }; } -export function toSymbolInformation(bearing: ITypeBearing): types.SymbolInformation { +export function toSymbolInformation(bearing: IWorkspaceSymbol): types.SymbolInformation { return new types.SymbolInformation(bearing.name, types.SymbolKind[bearing.type.charAt(0).toUpperCase() + bearing.type.substr(1)], toRange(bearing.range), - bearing.resourceUri, + bearing.resource, bearing.containerName); } diff --git a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts index d6c87d52e3c..ae821aab07c 100644 --- a/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/node/mainThreadLanguageFeatures.ts @@ -10,7 +10,7 @@ import {IThreadService} from 'vs/workbench/services/thread/common/threadService' import * as vscode from 'vscode'; import {IReadOnlyModel, ISingleEditOperation} from 'vs/editor/common/editorCommon'; import * as modes from 'vs/editor/common/modes'; -import {NavigateTypesSupportRegistry, INavigateTypesSupport, ITypeBearing} from 'vs/workbench/parts/search/common/search'; +import {WorkspaceSymbolProviderRegistry, IWorkspaceSymbolProvider, IWorkspaceSymbol} from 'vs/workbench/parts/search/common/search'; import {wireCancellationToken} from 'vs/base/common/async'; import {CancellationToken} from 'vs/base/common/cancellation'; import {Position as EditorPosition} from 'vs/editor/common/core/position'; @@ -152,9 +152,12 @@ export class MainThreadLanguageFeatures extends MainThreadLanguageFeaturesShape // --- navigate type $registerNavigateTypeSupport(handle: number): TPromise { - this._registrations[handle] = NavigateTypesSupportRegistry.register({ - getNavigateToItems: (search: string): TPromise => { - return this._proxy.$getNavigateToItems(handle, search); + this._registrations[handle] = WorkspaceSymbolProviderRegistry.register({ + provideWorkspaceSymbols: (search: string): TPromise => { + return this._proxy.$provideWorkspaceSymbols(handle, search); + }, + resolveWorkspaceSymbol: (item: IWorkspaceSymbol): TPromise => { + return this._proxy.$resolveWorkspaceSymbol(handle, item); } }); return undefined; diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts index 1593d2e1151..eb599f2819e 100644 --- a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {TPromise} from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); +import URI from 'vs/base/common/uri'; +import {TPromise} from 'vs/base/common/winjs.base'; +import {onUnexpectedError} from 'vs/base/common/errors'; import {ThrottledDelayer} from 'vs/base/common/async'; import {QuickOpenHandler, EditorQuickOpenEntry} from 'vs/workbench/browser/quickopen'; import {QuickOpenModel, QuickOpenEntry} from 'vs/base/parts/quickopen/browser/quickOpenModel'; -import {IAutoFocus/*, Mode, IEntryRunContext*/} from 'vs/base/parts/quickopen/common/quickOpen'; +import {IAutoFocus, Mode, IEntryRunContext} from 'vs/base/parts/quickopen/common/quickOpen'; import filters = require('vs/base/common/filters'); import {Range} from 'vs/editor/common/core/range'; import {EditorInput, IWorkbenchEditorConfiguration} from 'vs/workbench/common/editor'; @@ -18,18 +20,19 @@ import {IResourceInput} from 'vs/platform/editor/common/editor'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; -import {IModeService} from 'vs/editor/common/services/modeService'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; -import {ITypeBearing, INavigateTypesSupport, getNavigateToItems} from 'vs/workbench/parts/search/common/search'; +import {IWorkspaceSymbol, IWorkspaceSymbolProvider, getWorkspaceSymbols} from 'vs/workbench/parts/search/common/search'; class SymbolEntry extends EditorQuickOpenEntry { + private _bearingResolve: TPromise; + constructor( - private _bearing: ITypeBearing, - private _provider: INavigateTypesSupport, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, + private _bearing: IWorkspaceSymbol, + private _provider: IWorkspaceSymbolProvider, @IConfigurationService private _configurationService: IConfigurationService, - @IWorkspaceContextService private _contextService: IWorkspaceContextService + @IWorkspaceContextService private _contextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService ) { super(editorService); } @@ -45,7 +48,7 @@ class SymbolEntry extends EditorQuickOpenEntry { public getDescription(): string { let result = this._bearing.containerName; if (!result) { - result = labels.getPathLabel(this._bearing.resourceUri, this._contextService); + result = labels.getPathLabel(this._bearing.resource, this._contextService); } return result; } @@ -54,9 +57,34 @@ class SymbolEntry extends EditorQuickOpenEntry { return this._bearing.type; } + public getResource(): URI { + return this._bearing.resource; + } + + public run(mode: Mode, context: IEntryRunContext): boolean { + + // resolve this type bearing if neccessary + if (!this._bearingResolve + && typeof this._provider.resolveWorkspaceSymbol === 'function' + && !this._bearing.range + ) { + + this._bearingResolve = this._provider.resolveWorkspaceSymbol(this._bearing).then(result => { + this._bearing = result || this._bearing; + return this; + }, onUnexpectedError); + } + + TPromise.as(this._bearingResolve) + .then(_ => super.run(mode, context)) + .done(undefined, onUnexpectedError); + + return true; + } + public getInput(): IResourceInput | EditorInput { let input: IResourceInput = { - resource: this._bearing.resourceUri, + resource: this._bearing.resource, options: { pinned: !this._configurationService.getConfiguration().workbench.editor.enablePreviewFromQuickOpen } @@ -97,12 +125,7 @@ export class OpenSymbolHandler extends QuickOpenHandler { private delayer: ThrottledDelayer; private options: IOpenSymbolOptions; - constructor( - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IModeService private modeService: IModeService, - @IInstantiationService private instantiationService: IInstantiationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { + constructor(@IInstantiationService private instantiationService: IInstantiationService) { super(); this.delayer = new ThrottledDelayer(OpenSymbolHandler.SEARCH_DELAY); @@ -135,13 +158,24 @@ export class OpenSymbolHandler extends QuickOpenHandler { } private doGetResults(searchValue: string): TPromise { - return getNavigateToItems(searchValue).then(bearings => { - return this.toQuickOpenEntries(bearings, searchValue); + return getWorkspaceSymbols(searchValue).then(tuples => { + const result: SymbolEntry[] = []; + for (const tuple of tuples) { + const [provider, bearings] = tuple; + this.fillInSymbolEntries(result, provider, bearings, searchValue); + } + + // Sort (Standalone only) + if (!this.options.skipSorting) { + searchValue = searchValue.toLowerCase(); + return result.sort((a, b) => SymbolEntry.compare(a, b, searchValue)); + } else { + return result; + } }); } - private toQuickOpenEntries(types: ITypeBearing[], searchValue: string): SymbolEntry[] { - const results: SymbolEntry[] = []; + private fillInSymbolEntries(bucket: SymbolEntry[], provider: IWorkspaceSymbolProvider, types: IWorkspaceSymbol[], searchValue: string): void { // Convert to Entries for (const element of types) { @@ -150,18 +184,10 @@ export class OpenSymbolHandler extends QuickOpenHandler { continue; // ignore local symbols if we are told so } - const entry = this.instantiationService.createInstance(SymbolEntry, element, undefined); + const entry = this.instantiationService.createInstance(SymbolEntry, element, provider); entry.setHighlights(filters.matchesFuzzy(searchValue, entry.getLabel())); - results.push(entry); + bucket.push(entry); } - - // Sort (Standalone only) - if (!this.options.skipSorting) { - searchValue = searchValue.toLowerCase(); - return results.sort((a, b) => SymbolEntry.compare(a, b, searchValue)); - } - - return results; } public getGroupLabel(): string { diff --git a/src/vs/workbench/parts/search/common/search.ts b/src/vs/workbench/parts/search/common/search.ts index b315d74e59b..585137639df 100644 --- a/src/vs/workbench/parts/search/common/search.ts +++ b/src/vs/workbench/parts/search/common/search.ts @@ -15,24 +15,24 @@ import URI from 'vs/base/common/uri'; /** * Interface used to navigate to types by value. */ -export interface ITypeBearing { - containerName: string; +export interface IWorkspaceSymbol { name: string; - parameters: string; type: string; + containerName: string; range: IRange; - resourceUri: URI; + resource: URI; } -export interface INavigateTypesSupport { - getNavigateToItems: (search: string) => TPromise; +export interface IWorkspaceSymbolProvider { + provideWorkspaceSymbols(search: string): TPromise; + resolveWorkspaceSymbol?: (item: IWorkspaceSymbol) => TPromise; } -export namespace NavigateTypesSupportRegistry { +export namespace WorkspaceSymbolProviderRegistry { - const _supports: INavigateTypesSupport[] = []; + const _supports: IWorkspaceSymbolProvider[] = []; - export function register(support: INavigateTypesSupport): IDisposable { + export function register(support: IWorkspaceSymbolProvider): IDisposable { if (support) { _supports.push(support); @@ -51,26 +51,24 @@ export namespace NavigateTypesSupportRegistry { }; } - export function all(): INavigateTypesSupport[] { + export function all(): IWorkspaceSymbolProvider[] { return _supports.slice(0); } } -export function getNavigateToItems(query: string): TPromise { +export function getWorkspaceSymbols(query: string): TPromise<[IWorkspaceSymbolProvider, IWorkspaceSymbol[]][]> { - const promises = NavigateTypesSupportRegistry.all().map(support => { - return support.getNavigateToItems(query).then(value => value, onUnexpectedError); - }); + const result: [IWorkspaceSymbolProvider, IWorkspaceSymbol[]][] = []; - return TPromise.join(promises).then(all => { - const result: ITypeBearing[] = []; - for (let bearings of all) { - if (Array.isArray(bearings)) { - result.push(...bearings); + const promises = WorkspaceSymbolProviderRegistry.all().map(support => { + return support.provideWorkspaceSymbols(query).then(value => { + if (Array.isArray(value)) { + result.push([support, value]); } - } - return result; + }, onUnexpectedError); }); + + return TPromise.join(promises).then(_ => result); } CommonEditorRegistry.registerLanguageCommand('_executeWorkspaceSymbolProvider', function (accessor, args: { query: string; }) { @@ -78,5 +76,5 @@ CommonEditorRegistry.registerLanguageCommand('_executeWorkspaceSymbolProvider', if (typeof query !== 'string') { throw illegalArgument(); } - return getNavigateToItems(query); + return getWorkspaceSymbols(query); }); diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index e2da57a4192..a926d755727 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -30,7 +30,7 @@ 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'; import {getCodeActions} from 'vs/editor/contrib/quickFix/common/quickFix'; -import {getNavigateToItems} from 'vs/workbench/parts/search/common/search'; +import {getWorkspaceSymbols} from 'vs/workbench/parts/search/common/search'; import {rename} from 'vs/editor/contrib/rename/common/rename'; import {provideSignatureHelp} from 'vs/editor/contrib/parameterHints/common/parameterHints'; import {provideSuggestionItems} from 'vs/editor/contrib/suggest/common/suggest'; @@ -649,8 +649,12 @@ suite('ExtHostLanguageFeatures', function() { return threadService.sync().then(() => { - return getNavigateToItems('').then(value => { + return getWorkspaceSymbols('').then(value => { assert.equal(value.length, 1); + const [first] = value; + const [, symbols] = first; + assert.equal(symbols.length, 1); + assert.equal(symbols[0].name, 'testing'); }); }); });