diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index e27b7ce7530..4ac1a024910 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -427,21 +427,127 @@ export interface DefinitionProvider { provideDefinition(model:editorCommon.IReadOnlyModel, position:editorCommon.IEditorPosition, token:CancellationToken): Definition | Thenable; } - -/** - * Interface used to compute an outline - */ -export interface IOutlineEntry { - label: string; - containerLabel?: string; - type: string; - icon?: string; // icon class or null to use the default images based on the type - range: editorCommon.IRange; - children?: IOutlineEntry[]; +export enum SymbolKind { + File, + Module, + Namespace, + Package, + Class, + Method, + Property, + Field, + Constructor, + Enum, + Interface, + Function, + Variable, + Constant, + String, + Number, + Boolean, + Array, + Object, + Key, + Null } +export namespace SymbolKind { -export interface IOutlineSupport { - getOutline(resource:URI):TPromise; + export function from(kind: number | SymbolKind): string { + switch (kind) { + case SymbolKind.Method: + return 'method'; + case SymbolKind.Function: + return 'function'; + case SymbolKind.Constructor: + return 'constructor'; + case SymbolKind.Variable: + return 'variable'; + case SymbolKind.Class: + return 'class'; + case SymbolKind.Interface: + return 'interface'; + case SymbolKind.Namespace: + return 'namespace'; + case SymbolKind.Package: + return 'package'; + case SymbolKind.Module: + return 'module'; + case SymbolKind.Property: + return 'property'; + case SymbolKind.Enum: + return 'enum'; + case SymbolKind.String: + return 'string'; + case SymbolKind.File: + return 'file'; + case SymbolKind.Array: + return 'array'; + case SymbolKind.Number: + return 'number'; + case SymbolKind.Boolean: + return 'boolean'; + case SymbolKind.Object: + return 'object'; + case SymbolKind.Key: + return 'key'; + case SymbolKind.Null: + return 'null'; + } + return 'property'; + } + + export function to(type: string): SymbolKind { + switch (type) { + case 'method': + return SymbolKind.Method; + case 'function': + return SymbolKind.Function; + case 'constructor': + return SymbolKind.Constructor; + case 'variable': + return SymbolKind.Variable; + case 'class': + return SymbolKind.Class; + case 'interface': + return SymbolKind.Interface; + case 'namespace': + return SymbolKind.Namespace; + case 'package': + return SymbolKind.Package; + case 'module': + return SymbolKind.Module; + case 'property': + return SymbolKind.Property; + case 'enum': + return SymbolKind.Enum; + case 'string': + return SymbolKind.String; + case 'file': + return SymbolKind.File; + case 'array': + return SymbolKind.Array; + case 'number': + return SymbolKind.Number; + case 'boolean': + return SymbolKind.Boolean; + case 'object': + return SymbolKind.Object; + case 'key': + return SymbolKind.Key; + case 'null': + return SymbolKind.Null; + } + return SymbolKind.Property; + } +} +export interface SymbolInformation { + name: string; + containerName?: string; + kind: SymbolKind; + location: Location; +} +export interface DocumentSymbolProvider { + provideDocumentSymbols(model:editorCommon.IReadOnlyModel, token: CancellationToken): SymbolInformation[] | Thenable; } /** @@ -691,7 +797,7 @@ export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry(); -export const OutlineRegistry = new LanguageFeatureRegistry(); +export const DocumentSymbolProviderRegistry = new LanguageFeatureRegistry(); export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); diff --git a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts b/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts index 1121ccc19a5..2bea5b95ef3 100644 --- a/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts +++ b/src/vs/editor/contrib/outlineMarker/browser/outlineMarker.ts @@ -12,10 +12,10 @@ import {IDisposable, dispose} from 'vs/base/common/lifecycle'; import {TPromise} from 'vs/base/common/winjs.base'; import {Range} from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import {IOutlineEntry, OutlineRegistry} from 'vs/editor/common/modes'; +import {SymbolInformation, SymbolKind, DocumentSymbolProviderRegistry} from 'vs/editor/common/modes'; import {ICodeEditor, IViewZone, IViewZoneChangeAccessor} from 'vs/editor/browser/editorBrowser'; import {EditorBrowserRegistry} from 'vs/editor/browser/editorBrowserExtensions'; -import {getOutlineEntries, IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import {getDocumentSymbols, IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; class OutlineViewZone implements IViewZone { @@ -25,15 +25,14 @@ class OutlineViewZone implements IViewZone { public domNode:HTMLElement; - constructor(range:editorCommon.IRange, outlineType:string) - { + constructor(range:editorCommon.IRange, outlineType:SymbolKind) { this.afterLineNumber = range.startLineNumber-1; this.heightInPx = 4; this.suppressMouseDown = true; this.domNode = document.createElement('div'); var hr = document.createElement('hr'); - hr.className = 'outlineRule ' + outlineType; + hr.className = 'outlineRule ' + SymbolKind.from(outlineType); this.domNode.appendChild(hr); } } @@ -78,8 +77,7 @@ class OutlineMarker { private _editor:ICodeEditor; - public constructor(range:editorCommon.IRange, outlineType:string, _editor:ICodeEditor, helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor) - { + public constructor(range:editorCommon.IRange, outlineType:SymbolKind, _editor:ICodeEditor, helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor) { this._editor = _editor; this._viewZone = new OutlineViewZone(range, outlineType); this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); @@ -177,7 +175,7 @@ export class OutlineMarkerContribution implements editorCommon.IEditorContributi return; } - if (!OutlineRegistry.has(model)) { + if (!DocumentSymbolProviderRegistry.has(model)) { return; } @@ -186,7 +184,7 @@ export class OutlineMarkerContribution implements editorCommon.IEditorContributi this._currentOutlinePromise.cancel(); } - this._currentOutlinePromise = getOutlineEntries(model); + this._currentOutlinePromise = getDocumentSymbols(model); this._currentOutlinePromise.then((result) => { this.renderOutlines(result.entries); @@ -224,7 +222,7 @@ export class OutlineMarkerContribution implements editorCommon.IEditorContributi scheduler.schedule(); } - private renderOutlines(entries: IOutlineEntry[]): void { + private renderOutlines(entries: SymbolInformation[]): void { var centeredRange = this._editor.getCenteredRangeInViewport(); var oldMarkersCount = this._markers.length; this._editor.changeDecorations((decorationsAccessor) => { @@ -244,18 +242,16 @@ export class OutlineMarkerContribution implements editorCommon.IEditorContributi } } - private renderOutlinesRecursive(entries: IOutlineEntry[], helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor): void { + private renderOutlinesRecursive(entries: SymbolInformation[], helper:OutlineMarkerHelper, viewZoneChangeAccessor:IViewZoneChangeAccessor): void { if (entries) { entries.forEach((outline) => { - if (outline.type === 'class' || outline.type === 'method' || outline.type === 'function') { - var range = Range.lift(outline.range); + if (outline.kind === SymbolKind.Class || outline.kind === SymbolKind.Method || outline.kind === SymbolKind.Function) { + var range = Range.lift(outline.location.range); if (!this.alreadyHasMarkerAtRange(range)) { - var marker = new OutlineMarker(range, outline.type, this._editor, helper, viewZoneChangeAccessor); + var marker = new OutlineMarker(range, outline.kind, this._editor, helper, viewZoneChangeAccessor); this._markers.push(marker); } } - - this.renderOutlinesRecursive(outline.children, helper, viewZoneChangeAccessor); }); } } diff --git a/src/vs/editor/contrib/quickOpen/browser/quickOutline.ts b/src/vs/editor/contrib/quickOpen/browser/quickOutline.ts index 511d3abd6b6..2882708006c 100644 --- a/src/vs/editor/contrib/quickOpen/browser/quickOutline.ts +++ b/src/vs/editor/contrib/quickOpen/browser/quickOutline.ts @@ -8,7 +8,6 @@ import 'vs/css!./quickOutline'; import * as nls from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; import {onUnexpectedError} from 'vs/base/common/errors'; import {matchesFuzzy} from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; @@ -17,9 +16,9 @@ import {IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel} from 'vs/base import {IAutoFocus, Mode} from 'vs/base/parts/quickopen/common/quickOpen'; import {Behaviour} from 'vs/editor/common/editorActionEnablement'; import {ICommonCodeEditor, IEditorActionDescriptorData, IRange} from 'vs/editor/common/editorCommon'; -import {IOutlineEntry, OutlineRegistry} from 'vs/editor/common/modes'; +import {SymbolInformation, SymbolKind, DocumentSymbolProviderRegistry} from 'vs/editor/common/modes'; import {BaseEditorQuickOpenAction, IDecorator} from './editorQuickOpen'; -import {getOutlineEntries, IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import {getDocumentSymbols, IOutline} from 'vs/editor/contrib/quickOpen/common/quickOpen'; let SCOPE_PREFIX = ':'; @@ -108,19 +107,11 @@ class SymbolEntry extends QuickOpenEntryGroup { } } -interface OutlineNode { - label: string; - type: string; - range: IRange; - children?: OutlineNode[]; - parentScope?: string[]; -} - export class QuickOutlineAction extends BaseEditorQuickOpenAction { public static ID = 'editor.action.quickOutline'; - private cachedResult: IOutlineEntry[]; + private cachedResult: SymbolInformation[]; constructor(descriptor: IEditorActionDescriptorData, editor: ICommonCodeEditor) { super(descriptor, editor, nls.localize('QuickOutlineAction.label', "Go to Symbol..."), Behaviour.WidgetFocus | Behaviour.ShowInContextMenu); @@ -131,18 +122,18 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { } public isSupported(): boolean { - return (OutlineRegistry.has(this.editor.getModel()) && super.isSupported()); + return (DocumentSymbolProviderRegistry.has(this.editor.getModel()) && super.isSupported()); } public run(): TPromise { let model = this.editor.getModel(); - if (!OutlineRegistry.has(model)) { + if (!DocumentSymbolProviderRegistry.has(model)) { return null; } // Resolve outline - let promise = getOutlineEntries(model); + let promise = getDocumentSymbols(model); return promise.then((result: IOutline) => { if (result.entries.length > 0) { @@ -180,15 +171,9 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { return nls.localize('quickOutlineActionInput', "Type the name of an identifier you wish to navigate to"); } - private toQuickOpenEntries(outline: OutlineNode[], searchValue: string): SymbolEntry[] { + private toQuickOpenEntries(flattened: SymbolInformation[], searchValue: string): SymbolEntry[] { let results: SymbolEntry[] = []; - // Flatten - let flattened: OutlineNode[] = []; - if (outline) { - this.flatten(outline, flattened); - } - // Convert to Entries let normalizedSearchValue = searchValue; if (searchValue.indexOf(SCOPE_PREFIX) === 0) { @@ -197,7 +182,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { for (let i = 0; i < flattened.length; i++) { let element = flattened[i]; - let label = strings.trim(element.label); + let label = strings.trim(element.name); // Check for meatch let highlights = matchesFuzzy(normalizedSearchValue, label); @@ -205,12 +190,12 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { // Show parent scope as description let description: string = null; - if (element.parentScope) { - description = arrays.tail(element.parentScope); + if (element.containerName) { + description = element.containerName; } // Add - results.push(new SymbolEntry(label, element.type, description, element.range, highlights, this.editor, this)); + results.push(new SymbolEntry(label, SymbolKind.from(element.kind), description, element.location.range, highlights, this.editor, this)); } } @@ -284,27 +269,6 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { return type; } - private flatten(outline: OutlineNode[], flattened: OutlineNode[], parentScope?: string[]): void { - for (let i = 0; i < outline.length; i++) { - let element = outline[i]; - flattened.push(element); - - if (parentScope) { - element.parentScope = parentScope; - } - - if (element.children) { - let elementScope: string[] = []; - if (parentScope) { - elementScope = parentScope.slice(0); - } - elementScope.push(element.label); - - this.flatten(element.children, flattened, elementScope); - } - } - } - private sortNormal(searchValue: string, elementA: SymbolEntry, elementB: SymbolEntry): number { let elementAName = elementA.getLabel().toLowerCase(); let elementBName = elementB.getLabel().toLowerCase(); diff --git a/src/vs/editor/contrib/quickOpen/common/quickOpen.ts b/src/vs/editor/contrib/quickOpen/common/quickOpen.ts index 5fd567a488b..f44b0ff6669 100644 --- a/src/vs/editor/contrib/quickOpen/common/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/common/quickOpen.ts @@ -11,20 +11,23 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {Range} from 'vs/editor/common/core/range'; import {IModel} from 'vs/editor/common/editorCommon'; import {CommonEditorRegistry} from 'vs/editor/common/editorCommonExtensions'; -import {IOutlineEntry, OutlineRegistry} from 'vs/editor/common/modes'; +import {SymbolInformation, DocumentSymbolProviderRegistry} from 'vs/editor/common/modes'; import {IModelService} from 'vs/editor/common/services/modelService'; +import {asWinJsPromise} from 'vs/base/common/async'; export interface IOutline { - entries: IOutlineEntry[]; + entries: SymbolInformation[]; } -export function getOutlineEntries(model: IModel): TPromise { +export function getDocumentSymbols(model: IModel): TPromise { - let entries: IOutlineEntry[] = []; + let entries: SymbolInformation[] = []; - let promises = OutlineRegistry.all(model).map(support => { + let promises = DocumentSymbolProviderRegistry.all(model).map(support => { - return support.getOutline(model.getAssociatedResource()).then(result => { + return asWinJsPromise((token) => { + return support.provideDocumentSymbols(model, token); + }).then(result => { if (Array.isArray(result)) { entries.push(...result); } @@ -34,7 +37,7 @@ export function getOutlineEntries(model: IModel): TPromise { }); return TPromise.join(promises).then(() => { - let flatEntries: IOutlineEntry[] = []; + let flatEntries: SymbolInformation[] = []; flatten(flatEntries, entries, ''); flatEntries.sort(compareEntriesUsingStart); @@ -44,22 +47,18 @@ export function getOutlineEntries(model: IModel): TPromise { }); } -function compareEntriesUsingStart(a: IOutlineEntry, b: IOutlineEntry): number{ - return Range.compareRangesUsingStarts(Range.lift(a.range), Range.lift(b.range)); +function compareEntriesUsingStart(a: SymbolInformation, b: SymbolInformation): number{ + return Range.compareRangesUsingStarts(Range.lift(a.location.range), Range.lift(b.location.range)); } -function flatten(bucket: IOutlineEntry[], entries: IOutlineEntry[], overrideContainerLabel: string): void { +function flatten(bucket: SymbolInformation[], entries: SymbolInformation[], overrideContainerLabel: string): void { for (let entry of entries) { bucket.push({ - type: entry.type, - range: entry.range, - label: entry.label, - icon: entry.icon, - containerLabel: entry.containerLabel || overrideContainerLabel + kind: entry.kind, + location: entry.location, + name: entry.name, + containerName: entry.containerName || overrideContainerLabel }); - if (entry.children) { - flatten(bucket, entry.children, entry.label); - } } } @@ -73,5 +72,5 @@ CommonEditorRegistry.registerLanguageCommand('_executeDocumentSymbolProvider', f if (!model) { throw illegalArgument('resource'); } - return getOutlineEntries(model); + return getDocumentSymbols(model); }); \ No newline at end of file diff --git a/src/vs/languages/css/common/css.ts b/src/vs/languages/css/common/css.ts index 7da37536070..68c6b1a7d35 100644 --- a/src/vs/languages/css/common/css.ts +++ b/src/vs/languages/css/common/css.ts @@ -345,7 +345,11 @@ export class CSSMode extends AbstractMode { // } // }); - modes.OutlineRegistry.register(this.getId(), this); + modes.DocumentSymbolProviderRegistry.register(this.getId(), { + provideDocumentSymbols: (model, token): Thenable => { + return wireCancellationToken(token, this._provideDocumentSymbols(model.getAssociatedResource())); + } + }); // modes.DefinitionProviderRegistry.register(this.getId(), { // provideDefinition: (model, position, token): Thenable => { @@ -423,9 +427,9 @@ export class CSSMode extends AbstractMode { return this._worker((w) => w.provideReferences(resource, position)); } - static $getOutline = OneWorkerAttr(CSSMode, CSSMode.prototype.getOutline); - public getOutline(resource:URI):WinJS.TPromise { - return this._worker((w) => w.getOutline(resource)); + static $_provideDocumentSymbols = OneWorkerAttr(CSSMode, CSSMode.prototype._provideDocumentSymbols); + private _provideDocumentSymbols(resource:URI):WinJS.TPromise { + return this._worker((w) => w.provideDocumentSymbols(resource)); } static $findColorDeclarations = OneWorkerAttr(CSSMode, CSSMode.prototype.findColorDeclarations); diff --git a/src/vs/languages/css/common/cssWorker.ts b/src/vs/languages/css/common/cssWorker.ts index e0d7b5fb41a..bd8ffd1c97f 100644 --- a/src/vs/languages/css/common/cssWorker.ts +++ b/src/vs/languages/css/common/cssWorker.ts @@ -211,42 +211,44 @@ export class CSSWorker { } - public getOutline(resource:URI):winjs.TPromise { + public provideDocumentSymbols(resource:URI):winjs.TPromise { return this.languageService.join().then(() => { let model = this.resourceService.get(resource), stylesheet = this.languageService.getStylesheet(resource), - result:modes.IOutlineEntry[] = []; + result:modes.SymbolInformation[] = []; stylesheet.accept((node) => { - let entry:modes.IOutlineEntry = { - label: null, - type: 'rule', - range: null, - children: [] + let entry:modes.SymbolInformation = { + name: null, + kind: modes.SymbolKind.Class, // TODO@Martin: find a good SymbolKind + location: null }; if(node instanceof nodes.Selector) { - entry.label = node.getText(); + entry.name = node.getText(); } else if(node instanceof nodes.VariableDeclaration) { - entry.label = ( node).getName(); - entry.type = 'letiable'; + entry.name = ( node).getName(); + entry.kind = modes.SymbolKind.Variable; } else if(node instanceof nodes.MixinDeclaration) { - entry.label = ( node).getName(); - entry.type = 'method'; + entry.name = ( node).getName(); + entry.kind = modes.SymbolKind.Method; } else if(node instanceof nodes.FunctionDeclaration) { - entry.label = ( node).getName(); - entry.type = 'function'; + entry.name = ( node).getName(); + entry.kind = modes.SymbolKind.Function; } else if(node instanceof nodes.Keyframe) { - entry.label = nls.localize('literal.keyframes', "@keyframes {0}", ( node).getName()); + entry.name = nls.localize('literal.keyframes', "@keyframes {0}", ( node).getName()); } else if(node instanceof nodes.FontFace) { - entry.label = nls.localize('literal.fontface', "@font-face"); + entry.name = nls.localize('literal.fontface', "@font-face"); } - if(entry.label) { - entry.range = this._range(node, model, true); + if(entry.name) { + entry.location = { + uri: resource, + range: this._range(node, model, true) + }; result.push(entry); } diff --git a/src/vs/languages/json/common/json.ts b/src/vs/languages/json/common/json.ts index 20ab5d44084..cbf976e67ad 100644 --- a/src/vs/languages/json/common/json.ts +++ b/src/vs/languages/json/common/json.ts @@ -19,7 +19,7 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat import {RichEditSupport} from 'vs/editor/common/modes/supports/richEditSupport'; import {wireCancellationToken} from 'vs/base/common/async'; -export class JSONMode extends AbstractMode implements modes.IOutlineSupport { +export class JSONMode extends AbstractMode { public tokenizationSupport: modes.ITokenizationSupport; public richEditSupport: modes.IRichEditSupport; @@ -74,7 +74,11 @@ export class JSONMode extends AbstractMode implements modes.IOutlineSupport { this.configSupport = this; // Initialize Outline support - modes.OutlineRegistry.register(this.getId(), this); + modes.DocumentSymbolProviderRegistry.register(this.getId(), { + provideDocumentSymbols: (model, token): Thenable => { + return wireCancellationToken(token, this._provideDocumentSymbols(model.getAssociatedResource())); + } + }); modes.FormatRegistry.register(this.getId(), this); @@ -150,9 +154,9 @@ export class JSONMode extends AbstractMode implements modes.IOutlineSupport { return this._worker((w) => w.provideHover(resource, position)); } - static $getOutline = OneWorkerAttr(JSONMode, JSONMode.prototype.getOutline); - public getOutline(resource:URI):WinJS.TPromise { - return this._worker((w) => w.getOutline(resource)); + static $_provideDocumentSymbols = OneWorkerAttr(JSONMode, JSONMode.prototype._provideDocumentSymbols); + private _provideDocumentSymbols(resource:URI):WinJS.TPromise { + return this._worker((w) => w.provideDocumentSymbols(resource)); } static $formatDocument = OneWorkerAttr(JSONMode, JSONMode.prototype.formatDocument); diff --git a/src/vs/languages/json/common/jsonWorker.ts b/src/vs/languages/json/common/jsonWorker.ts index 5a52057f78a..935fcaff422 100644 --- a/src/vs/languages/json/common/jsonWorker.ts +++ b/src/vs/languages/json/common/jsonWorker.ts @@ -344,7 +344,7 @@ export class JSONWorker { } - public getOutline(resource:URI):WinJS.TPromise { + public provideDocumentSymbols(resource:URI):WinJS.TPromise { var modelMirror = this.resourceService.get(resource); var parser = new Parser.JSONParser(); @@ -358,13 +358,20 @@ export class JSONWorker { var resourceString = resource.toString(); if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) { if (root.type === 'array') { - var result : Modes.IOutlineEntry[] = []; + var result : Modes.SymbolInformation[] = []; ( root).items.forEach((item) => { if (item.type === 'object') { var property = ( item).getFirstProperty('key'); if (property && property.value) { var range = modelMirror.getRangeFromOffsetAndLength(item.start, item.end - item.start); - result.push({ label: property.value.getValue(), icon: 'function', type: 'string', range: range, children: []}); + result.push({ + name: property.value.getValue(), + kind: Modes.SymbolKind.String, + location: { + uri: resource, + range: range + } + }); } } }); @@ -372,10 +379,10 @@ export class JSONWorker { } } - function collectOutlineEntries(result: Modes.IOutlineEntry[], node: Parser.ASTNode): Modes.IOutlineEntry[] { + function collectOutlineEntries(result: Modes.SymbolInformation[], node: Parser.ASTNode, containerName: string): Modes.SymbolInformation[] { if (node.type === 'array') { (node).items.forEach((node:Parser.ASTNode) => { - collectOutlineEntries(result, node); + collectOutlineEntries(result, node, containerName); }); } else if (node.type === 'object') { var objectNode = node; @@ -384,15 +391,23 @@ export class JSONWorker { var range = modelMirror.getRangeFromOffsetAndLength(property.start, property.end - property.start); var valueNode = property.value; if (valueNode) { - var children = collectOutlineEntries([], valueNode); - var icon = valueNode.type === 'object' ? 'module' : valueNode.type; - result.push({ label: property.key.getValue(), icon: icon, type: valueNode.type, range: range, children: children}); + let childContainerName = containerName ? containerName + '.' + property.key.name : property.key.name; + result.push({ + name: property.key.getValue(), + kind: getSymbolKind(valueNode.type), + location: { + uri: resource, + range: range, + }, + containerName: containerName + }); + collectOutlineEntries(result, valueNode, childContainerName); } }); } return result; } - var result = collectOutlineEntries([], root); + var result = collectOutlineEntries([], root, void 0); return WinJS.TPromise.as(result); } @@ -401,3 +416,20 @@ export class JSONWorker { return WinJS.TPromise.as(JSONFormatter.format(model, range, options)); } } + +function getSymbolKind(nodeType: string): Modes.SymbolKind { + switch (nodeType) { + case 'object': + return Modes.SymbolKind.Module; + case 'string': + return Modes.SymbolKind.String; + case 'number': + return Modes.SymbolKind.Number; + case 'array': + return Modes.SymbolKind.Array; + case 'boolean': + return Modes.SymbolKind.Boolean; + default: // 'null' + return Modes.SymbolKind.Variable; + } +} diff --git a/src/vs/languages/json/test/common/jsonworker.test.ts b/src/vs/languages/json/test/common/jsonworker.test.ts index 537973e674f..f653d370633 100644 --- a/src/vs/languages/json/test/common/jsonworker.test.ts +++ b/src/vs/languages/json/test/common/jsonworker.test.ts @@ -18,18 +18,24 @@ import EditorCommon = require('vs/editor/common/editorCommon'); import Modes = require('vs/editor/common/modes'); import WinJS = require('vs/base/common/winjs.base'); +interface RelaxedSymbolInformation { + name: string; + containerName?: string; + kind: Modes.SymbolKind; +} suite('JSON - Worker', () => { - var assertOutline:any = function(actual: Modes.IOutlineEntry[], expected: any[], message: string) { - assert.equal(actual.length, expected.length, message); - for (var i = 0; i < expected.length; i++) { - assert.equal(actual[i].label, expected[i].label, message); - assert.equal(actual[i].type, expected[i].type, message); - var childrenExpected = expected[i].children || []; - var childrenActual = actual[i].children || []; - assertOutline(childrenExpected, childrenActual, message); - } + function toRelaxedSymbolInformation(a: RelaxedSymbolInformation): RelaxedSymbolInformation { + return { + name: a.name, + containerName: a.containerName, + kind: a.kind + }; + } + + var assertOutline = function(actual: Modes.SymbolInformation[], expected: RelaxedSymbolInformation[], message?: string) { + assert.deepEqual(actual.map(toRelaxedSymbolInformation), expected.map(toRelaxedSymbolInformation), message); }; function mockWorkerEnv(url: URI, content: string): { worker: jsonworker.JSONWorker; model: EditorCommon.IMirrorModel; } { @@ -80,10 +86,10 @@ suite('JSON - Worker', () => { return env.worker.navigateValueSet(url, range, up); }; - function getOutline(content: string):WinJS.TPromise { + function getOutline(content: string):WinJS.TPromise { var url = URI.parse('test'); var workerEnv = mockWorkerEnv(url, content); - return workerEnv.worker.getOutline(url); + return workerEnv.worker.provideDocumentSymbols(url); }; var assertSuggestion= function(completion:Modes.ISuggestResult, label:string, documentationLabel?: string) { @@ -96,13 +102,13 @@ suite('JSON - Worker', () => { test('JSON outline - base types', function(testDone) { var content= '{ "key1": 1, "key2": "foo", "key3" : true }'; - var expected= [ - { label: 'key1', type: 'number'}, - { label: 'key2', type: 'string'}, - { label: 'key3', type: 'boolean'}, + var expected = [ + { name: 'key1', kind: Modes.SymbolKind.Number}, + { name: 'key2', kind: Modes.SymbolKind.String}, + { name: 'key3', kind: Modes.SymbolKind.Boolean}, ]; - getOutline(content).then((entries: Modes.IOutlineEntry[]) => { + getOutline(content).then((entries: Modes.SymbolInformation[]) => { assertOutline(entries, expected); }).done(() => testDone(), (error) => { testDone(error); @@ -113,12 +119,14 @@ suite('JSON - Worker', () => { var content= '{ "key1": 1, "key2": [ 1, 2, 3 ], "key3" : [ { "k1": 1 }, {"k2": 2 } ] }'; var expected= [ - { label: 'key1', type: 'number'}, - { label: 'key2', type: 'array'}, - { label: 'key3', type: 'array', children : [ { label: 'k1', type: 'number'}, { label: 'k2', type: 'number'} ]}, + { name: 'key1', kind: Modes.SymbolKind.Number}, + { name: 'key2', kind: Modes.SymbolKind.Array}, + { name: 'key3', kind: Modes.SymbolKind.Array}, + { name: 'k1', kind: Modes.SymbolKind.Number, containerName: 'key3'}, + { name: 'k2', kind: Modes.SymbolKind.Number, containerName: 'key3'}, ]; - getOutline(content).then((entries: Modes.IOutlineEntry[]) => { + getOutline(content).then((entries: Modes.SymbolInformation[]) => { assertOutline(entries, expected); }).done(() => testDone(), (error) => { testDone(error); @@ -129,11 +137,13 @@ suite('JSON - Worker', () => { var content= '{ "key1": { "key2": true }, "key3" : { "k1": { } }'; var expected= [ - { label: 'key1', type: 'object', children : [ { label: 'key2', type: 'boolean'} ] }, - { label: 'key3', type: 'object', children : [ { label: 'k1', type: 'object'} ] } + { name: 'key1', kind: Modes.SymbolKind.Module}, + { name: 'key2', kind: Modes.SymbolKind.Boolean, containerName: 'key1' }, + { name: 'key3', kind: Modes.SymbolKind.Module}, + { name: 'k1', kind: Modes.SymbolKind.Module, containerName: 'key3'} ]; - getOutline(content).then((entries: Modes.IOutlineEntry[]) => { + getOutline(content).then((entries: Modes.SymbolInformation[]) => { assertOutline(entries, expected); }).done(() => testDone(), (error) => { testDone(error); @@ -144,10 +154,12 @@ suite('JSON - Worker', () => { var content= '{ "key1": { "key2": true, "key3":, "key4": false } }'; var expected= [ - { label: 'key1', type: 'object', children : [ { label: 'key2', type: 'boolean'}, { label: 'key4', type: 'boolean'} ] }, + { name: 'key1', kind: Modes.SymbolKind.Module }, + { name: 'key2', kind: Modes.SymbolKind.Boolean, containerName: 'key1'}, + { name: 'key4', kind: Modes.SymbolKind.Boolean, containerName: 'key1'}, ]; - getOutline(content).then((entries: Modes.IOutlineEntry[]) => { + getOutline(content).then((entries: Modes.SymbolInformation[]) => { assertOutline(entries, expected); }).done(() => testDone(), (error) => { testDone(error); diff --git a/src/vs/languages/less/common/less.ts b/src/vs/languages/less/common/less.ts index e5f3a38369f..0eb1b7bf6e5 100644 --- a/src/vs/languages/less/common/less.ts +++ b/src/vs/languages/less/common/less.ts @@ -176,7 +176,7 @@ export var language: Types.ILanguage = { } }; -export class LESSMode extends AbstractMode implements modes.IOutlineSupport { +export class LESSMode extends AbstractMode { public inplaceReplaceSupport:modes.IInplaceReplaceSupport; public configSupport:modes.IConfigurationSupport; @@ -225,7 +225,11 @@ export class LESSMode extends AbstractMode implements modes.IOutlineSupport { } }); - modes.OutlineRegistry.register(this.getId(), this); + modes.DocumentSymbolProviderRegistry.register(this.getId(), { + provideDocumentSymbols: (model, token): Thenable => { + return wireCancellationToken(token, this._provideDocumentSymbols(model.getAssociatedResource())); + } + }); modes.SuggestRegistry.register(this.getId(), { triggerCharacters: [], @@ -289,9 +293,9 @@ export class LESSMode extends AbstractMode implements modes.IOutlineSupport { return this._worker((w) => w.provideHover(resource, position)); } - static $getOutline = OneWorkerAttr(LESSMode, LESSMode.prototype.getOutline); - public getOutline(resource:URI):winjs.TPromise { - return this._worker((w) => w.getOutline(resource)); + static $_provideDocumentSymbols = OneWorkerAttr(LESSMode, LESSMode.prototype._provideDocumentSymbols); + private _provideDocumentSymbols(resource:URI):winjs.TPromise { + return this._worker((w) => w.provideDocumentSymbols(resource)); } static $_provideDefinition = OneWorkerAttr(LESSMode, LESSMode.prototype._provideDefinition); diff --git a/src/vs/languages/sass/common/sass.ts b/src/vs/languages/sass/common/sass.ts index cbdba35ed82..58984ef3899 100644 --- a/src/vs/languages/sass/common/sass.ts +++ b/src/vs/languages/sass/common/sass.ts @@ -278,7 +278,7 @@ export var language = { } }; -export class SASSMode extends AbstractMode implements modes.IOutlineSupport { +export class SASSMode extends AbstractMode { public inplaceReplaceSupport:modes.IInplaceReplaceSupport; public configSupport:modes.IConfigurationSupport; @@ -326,14 +326,19 @@ export class SASSMode extends AbstractMode implements modes.IOutlineSupport { } }); - modes.OutlineRegistry.register(this.getId(), this); + modes.DocumentSymbolProviderRegistry.register(this.getId(), { + provideDocumentSymbols: (model, token): Thenable => { + return wireCancellationToken(token, this._provideDocumentSymbols(model.getAssociatedResource())); + } + }); modes.SuggestRegistry.register(this.getId(), { triggerCharacters: [], shouldAutotriggerSuggest: true, provideCompletionItems: (model, position, token): Thenable => { return wireCancellationToken(token, this._provideCompletionItems(model.getAssociatedResource(), position)); - } }); + } + }); this.tokenizationSupport = createTokenizationSupport(modeService, this, lexer); @@ -389,9 +394,9 @@ export class SASSMode extends AbstractMode implements modes.IOutlineSupport { return this._worker((w) => w.provideHover(resource, position)); } - static $getOutline = OneWorkerAttr(SASSMode, SASSMode.prototype.getOutline); - public getOutline(resource:URI):winjs.TPromise { - return this._worker((w) => w.getOutline(resource)); + static $_provideDocumentSymbols = OneWorkerAttr(SASSMode, SASSMode.prototype._provideDocumentSymbols); + private _provideDocumentSymbols(resource:URI):winjs.TPromise { + return this._worker((w) => w.provideDocumentSymbols(resource)); } static $_provideDefinition = OneWorkerAttr(SASSMode, SASSMode.prototype._provideDefinition); diff --git a/src/vs/languages/typescript/common/languageFeatures.ts b/src/vs/languages/typescript/common/languageFeatures.ts index f1daffbc201..ffd0e24b0a9 100644 --- a/src/vs/languages/typescript/common/languageFeatures.ts +++ b/src/vs/languages/typescript/common/languageFeatures.ts @@ -28,7 +28,7 @@ export function register(modelService: IModelService, markerService: IMarkerServ disposables.push(modes.DocumentHighlightProviderRegistry.register(selector, new OccurrencesAdapter(modelService, worker))); disposables.push(modes.DefinitionProviderRegistry.register(selector, new DefinitionAdapter(modelService, worker))); disposables.push(modes.ReferenceProviderRegistry.register(selector, new ReferenceAdapter(modelService, worker))); - disposables.push(modes.OutlineRegistry.register(selector, new OutlineAdapter(modelService, worker))); + disposables.push(modes.DocumentSymbolProviderRegistry.register(selector, new OutlineAdapter(modelService, worker))); disposables.push(modes.FormatRegistry.register(selector, new FormatAdapter(modelService, worker))); disposables.push(modes.FormatOnTypeRegistry.register(selector, new FormatAdapter(modelService, worker))); disposables.push(new DiagnostcsAdapter(defaults, selector, markerService, modelService, worker)); @@ -394,28 +394,90 @@ class ReferenceAdapter extends Adapter implements modes.ReferenceProvider { // --- outline ------ -class OutlineAdapter extends Adapter implements modes.IOutlineSupport { +class OutlineAdapter extends Adapter implements modes.DocumentSymbolProvider { - getOutline(resource: URI): TPromise { - return this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => { + public provideDocumentSymbols(model:editorCommon.IReadOnlyModel, token: CancellationToken): Thenable { + const resource = model.getAssociatedResource(); + + return wireCancellationToken(token, this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => { if (!items) { return; } - const convert = (item: ts.NavigationBarItem): modes.IOutlineEntry => { - return { - label: item.text, - type: item.kind, - range: this._textSpanToRange(resource, item.spans[0]), - children: item.childItems && item.childItems.map(convert) + function convert(bucket: modes.SymbolInformation[], item: ts.NavigationBarItem, containerLabel?: string): void { + let result: modes.SymbolInformation = { + name: item.text, + kind: outlineTypeTable[item.kind] || modes.SymbolKind.Variable, + location: { + uri: resource, + range: this._textSpanToRange(resource, item.spans[0]) + }, + containerName: containerLabel }; - }; - return items.map(convert); - }); + if (item.childItems && item.childItems.length > 0) { + for (let child of item.childItems) { + convert(bucket, child, result.name); + } + } + + bucket.push(result); + } + + let result: modes.SymbolInformation[] = []; + items.forEach(item => convert(result, item)); + return result; + })); } } +export class Kind { + public static unknown:string = ''; + public static keyword:string = 'keyword'; + public static script:string = 'script'; + public static module:string = 'module'; + public static class:string = 'class'; + public static interface:string = 'interface'; + public static type:string = 'type'; + public static enum:string = 'enum'; + public static variable:string = 'var'; + public static localVariable:string = 'local var'; + public static function:string = 'function'; + public static localFunction:string = 'local function'; + public static memberFunction:string = 'method'; + public static memberGetAccessor:string = 'getter'; + public static memberSetAccessor:string = 'setter'; + public static memberVariable:string = 'property'; + public static constructorImplementation:string = 'constructor'; + public static callSignature:string = 'call'; + public static indexSignature:string = 'index'; + public static constructSignature:string = 'construct'; + public static parameter:string = 'parameter'; + public static typeParameter:string = 'type parameter'; + public static primitiveType:string = 'primitive type'; + public static label:string = 'label'; + public static alias:string = 'alias'; + public static const:string = 'const'; + public static let:string = 'let'; + public static warning:string = 'warning'; +} + +let outlineTypeTable: { [kind: string]: modes.SymbolKind } = Object.create(null); +outlineTypeTable[Kind.module] = modes.SymbolKind.Module; +outlineTypeTable[Kind.class] = modes.SymbolKind.Class; +outlineTypeTable[Kind.enum] = modes.SymbolKind.Enum; +outlineTypeTable[Kind.interface] = modes.SymbolKind.Interface; +outlineTypeTable[Kind.memberFunction] = modes.SymbolKind.Method; +outlineTypeTable[Kind.memberVariable] = modes.SymbolKind.Property; +outlineTypeTable[Kind.memberGetAccessor] = modes.SymbolKind.Property; +outlineTypeTable[Kind.memberSetAccessor] = modes.SymbolKind.Property; +outlineTypeTable[Kind.variable] = modes.SymbolKind.Variable; +outlineTypeTable[Kind.const] = modes.SymbolKind.Variable; +outlineTypeTable[Kind.localVariable] = modes.SymbolKind.Variable; +outlineTypeTable[Kind.variable] = modes.SymbolKind.Variable; +outlineTypeTable[Kind.function] = modes.SymbolKind.Function; +outlineTypeTable[Kind.localFunction] = modes.SymbolKind.Function; + // --- formatting ---- class FormatAdapter extends Adapter implements modes.IFormattingSupport { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 4b63f539aae..a8c5b599599 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -22,7 +22,7 @@ import {CancellationToken} from 'vs/base/common/cancellation'; // --- adapter -class OutlineAdapter implements modes.IOutlineSupport { +class OutlineAdapter { private _documents: ExtHostModelService; private _provider: vscode.DocumentSymbolProvider; @@ -32,7 +32,7 @@ class OutlineAdapter implements modes.IOutlineSupport { this._provider = provider; } - getOutline(resource: URI): TPromise { + provideDocumentSymbols(resource: URI): TPromise { let doc = this._documents.getDocumentData(resource).document; return asWinJsPromise(token => this._provider.provideDocumentSymbols(doc, token)).then(value => { if (Array.isArray(value)) { @@ -643,8 +643,8 @@ export class ExtHostLanguageFeatures { return this._createDisposable(handle); } - $getOutline(handle: number, resource: URI): TPromise { - return this._withAdapter(handle, OutlineAdapter, adapter => adapter.getOutline(resource)); + $provideDocumentSymbols(handle: number, resource: URI): TPromise { + return this._withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(resource)); } // --- code lens @@ -847,9 +847,9 @@ export class MainThreadLanguageFeatures { // --- outline $registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise { - this._registrations[handle] = modes.OutlineRegistry.register(selector, { - getOutline: (resource: URI): TPromise => { - return this._proxy.$getOutline(handle, resource); + this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(selector, { + provideDocumentSymbols: (model:IReadOnlyModel, token: CancellationToken): Thenable => { + return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.getAssociatedResource())); } }); return undefined; diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 259f14e88c2..450db22f7c0 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -15,6 +15,7 @@ import {IPosition, ISelection, IRange, IRangeWithMessage, ISingleEditOperation} import {IHTMLContentElement} from 'vs/base/common/htmlContent'; import {ITypeBearing} from 'vs/workbench/parts/search/common/search'; import * as vscode from 'vscode'; +import URI from 'vs/base/common/uri'; export interface PositionLike { line: number; @@ -194,114 +195,27 @@ export const TextEdit = { } }; -export namespace SymbolKind { - - export function from(kind: number | types.SymbolKind): string { - switch (kind) { - case types.SymbolKind.Method: - return 'method'; - case types.SymbolKind.Function: - return 'function'; - case types.SymbolKind.Constructor: - return 'constructor'; - case types.SymbolKind.Variable: - return 'variable'; - case types.SymbolKind.Class: - return 'class'; - case types.SymbolKind.Interface: - return 'interface'; - case types.SymbolKind.Namespace: - return 'namespace'; - case types.SymbolKind.Package: - return 'package'; - case types.SymbolKind.Module: - return 'module'; - case types.SymbolKind.Property: - return 'property'; - case types.SymbolKind.Enum: - return 'enum'; - case types.SymbolKind.String: - return 'string'; - case types.SymbolKind.File: - return 'file'; - case types.SymbolKind.Array: - return 'array'; - case types.SymbolKind.Number: - return 'number'; - case types.SymbolKind.Boolean: - return 'boolean'; - case types.SymbolKind.Object: - return 'object'; - case types.SymbolKind.Key: - return 'key'; - case types.SymbolKind.Null: - return 'null'; - } - return 'property'; - } - - export function to(type: string): types.SymbolKind { - switch (type) { - case 'method': - return types.SymbolKind.Method; - case 'function': - return types.SymbolKind.Function; - case 'constructor': - return types.SymbolKind.Constructor; - case 'variable': - return types.SymbolKind.Variable; - case 'class': - return types.SymbolKind.Class; - case 'interface': - return types.SymbolKind.Interface; - case 'namespace': - return types.SymbolKind.Namespace; - case 'package': - return types.SymbolKind.Package; - case 'module': - return types.SymbolKind.Module; - case 'property': - return types.SymbolKind.Property; - case 'enum': - return types.SymbolKind.Enum; - case 'string': - return types.SymbolKind.String; - case 'file': - return types.SymbolKind.File; - case 'array': - return types.SymbolKind.Array; - case 'number': - return types.SymbolKind.Number; - case 'boolean': - return types.SymbolKind.Boolean; - case 'object': - return types.SymbolKind.Object; - case 'key': - return types.SymbolKind.Key; - case 'null': - return types.SymbolKind.Null; - } - return types.SymbolKind.Property; - } -} - export namespace SymbolInformation { - export function fromOutlineEntry(entry: modes.IOutlineEntry): types.SymbolInformation { - return new types.SymbolInformation(entry.label, - SymbolKind.to(entry.type), - toRange(entry.range), - undefined, - entry.containerLabel); + export function fromOutlineEntry(entry: modes.SymbolInformation): types.SymbolInformation { + return new types.SymbolInformation( + entry.name, + entry.kind, + toRange(entry.location.range), + entry.location.uri, + entry.containerName + ); } - export function toOutlineEntry(symbol: vscode.SymbolInformation): modes.IOutlineEntry { - return { - type: SymbolKind.from(symbol.kind), - range: fromRange(symbol.location.range), - containerLabel: symbol.containerName, - label: symbol.name, - icon: undefined, + export function toOutlineEntry(symbol: vscode.SymbolInformation): modes.SymbolInformation { + return { + name: symbol.name, + kind: symbol.kind, + containerName: symbol.containerName, + location: { + uri: symbol.location.uri, + range: fromRange(symbol.location.range) + } }; } } diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 9fc0fd686c2..eaa860e956d 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -8,7 +8,6 @@ import 'vs/css!./media/gotoSymbolHandler'; import {TPromise} from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); -import arrays = require('vs/base/common/arrays'); import errors = require('vs/base/common/errors'); import types = require('vs/base/common/types'); import strings = require('vs/base/common/strings'); @@ -24,8 +23,8 @@ import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/edito import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpenService'; import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace'; import {Position} from 'vs/platform/editor/common/editor'; -import {getOutlineEntries} from 'vs/editor/contrib/quickOpen/common/quickOpen'; -import {OutlineRegistry} from 'vs/editor/common/modes'; +import {getDocumentSymbols} from 'vs/editor/contrib/quickOpen/common/quickOpen'; +import {DocumentSymbolProviderRegistry, SymbolInformation, SymbolKind} from 'vs/editor/common/modes'; export const GOTO_SYMBOL_PREFIX = '@'; export const SCOPE_PREFIX = ':'; @@ -205,7 +204,7 @@ class OutlineModel extends QuickOpenModel { private renderGroupLabel(type: string, count: number, outline: Outline): string { - let pattern = outline.outlineGroupLabel[type] || OutlineModel.getDefaultGroupLabelPatterns()[type]; + let pattern = OutlineModel.getDefaultGroupLabelPatterns()[type]; if (pattern) { return strings.format(pattern, count); } @@ -361,18 +360,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { } interface Outline { - entries: OutlineNode[]; - outlineGroupLabel?: { [name: string]: string; }; -} - -interface OutlineNode { - label: string; - containerLabel?: string; - type: string; - icon?: string; - range: IRange; - children?: OutlineNode[]; - parentScope?: string[]; + entries: SymbolInformation[]; } interface IEditorLineDecoration { @@ -439,7 +427,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { } if (model && types.isFunction((model).getMode)) { - canRun = OutlineRegistry.has(model); + canRun = DocumentSymbolProviderRegistry.has(model); } } @@ -460,54 +448,24 @@ export class GotoSymbolHandler extends QuickOpenHandler { }; } - private toQuickOpenEntries(outline: Outline): SymbolEntry[] { + private toQuickOpenEntries(flattened: SymbolInformation[]): SymbolEntry[] { let results: SymbolEntry[] = []; - // Flatten - let flattened: OutlineNode[] = []; - if (outline) { - this.flatten(outline.entries, flattened); - } - for (let i = 0; i < flattened.length; i++) { let element = flattened[i]; - let label = strings.trim(element.label); + let label = strings.trim(element.name); // Show parent scope as description - let description: string = element.containerLabel; - if (element.parentScope) { - description = arrays.tail(element.parentScope); - } + let description: string = element.containerName; // Add - let icon = element.icon || element.type; - results.push(new SymbolEntry(i, label, element.type, description, icon, element.range, null, this.editorService, this)); + let icon = SymbolKind.from(element.kind); + results.push(new SymbolEntry(i, label, SymbolKind.from(element.kind), description, icon, element.location.range, null, this.editorService, this)); } return results; } - private flatten(outline: OutlineNode[], flattened: OutlineNode[], parentScope?: string[]): void { - for (let i = 0; i < outline.length; i++) { - let element = outline[i]; - flattened.push(element); - - if (parentScope) { - element.parentScope = parentScope; - } - - if (element.children) { - let elementScope: string[] = []; - if (parentScope) { - elementScope = parentScope.slice(0); - } - elementScope.push(element.label); - - this.flatten(element.children, flattened, elementScope); - } - } - } - private getActiveOutline(): TPromise { if (!this.activeOutlineRequest) { this.activeOutlineRequest = this.doGetActiveOutline(); @@ -533,9 +491,9 @@ export class GotoSymbolHandler extends QuickOpenHandler { return TPromise.as(this.outlineToModelCache[modelId]); } - return getOutlineEntries(model).then(outline => { + return getDocumentSymbols(model).then(outline => { - let model = new OutlineModel(outline, this.toQuickOpenEntries(outline)); + let model = new OutlineModel(outline, this.toQuickOpenEntries(outline.entries)); this.outlineToModelCache = {}; // Clear cache, only keep 1 outline this.outlineToModelCache[modelId] = model; diff --git a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts index 63f7b44b044..5c86f97e76c 100644 --- a/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/node/api/extHostLanguageFeatures.test.ts @@ -21,8 +21,8 @@ import {IThreadService} from 'vs/platform/thread/common/thread'; import {ExtHostLanguageFeatures, MainThreadLanguageFeatures} from 'vs/workbench/api/node/extHostLanguageFeatures'; import {ExtHostCommands, MainThreadCommands} from 'vs/workbench/api/node/extHostCommands'; import {ExtHostModelService} from 'vs/workbench/api/node/extHostDocuments'; -import {getOutlineEntries} from 'vs/editor/contrib/quickOpen/common/quickOpen'; -import {OutlineRegistry, DocumentHighlightKind} from 'vs/editor/common/modes'; +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 {provideHover} from 'vs/editor/contrib/hover/common/hover'; @@ -105,7 +105,7 @@ suite('ExtHostLanguageFeatures', function() { // --- outline test('DocumentSymbols, register/deregister', function() { - assert.equal(OutlineRegistry.all(model).length, 0); + assert.equal(DocumentSymbolProviderRegistry.all(model).length, 0); let d1 = extHost.registerDocumentSymbolProvider(defaultSelector, { provideDocumentSymbols() { return []; @@ -113,7 +113,7 @@ suite('ExtHostLanguageFeatures', function() { }); return threadService.sync().then(() => { - assert.equal(OutlineRegistry.all(model).length, 1); + assert.equal(DocumentSymbolProviderRegistry.all(model).length, 1); d1.dispose(); return threadService.sync(); }); @@ -134,7 +134,7 @@ suite('ExtHostLanguageFeatures', function() { return threadService.sync().then(() => { - return getOutlineEntries(model).then(value => { + return getDocumentSymbols(model).then(value => { assert.equal(value.entries.length, 1); }); }); @@ -149,12 +149,12 @@ suite('ExtHostLanguageFeatures', function() { return threadService.sync().then(() => { - return getOutlineEntries(model).then(value => { + return getDocumentSymbols(model).then(value => { assert.equal(value.entries.length, 1); let entry = value.entries[0]; - assert.equal(entry.label, 'test'); - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + assert.equal(entry.name, 'test'); + assert.deepEqual(entry.location.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); }); });