Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc (#9646)

* Merge from vscode 099a7622e6e90dbcc226e428d4e35a72cb19ecbc

* fix strict
This commit is contained in:
Anthony Dresser
2020-03-16 23:16:40 -07:00
committed by GitHub
parent 81e1b9a434
commit a53b78c0c8
170 changed files with 2601 additions and 2026 deletions

View File

@@ -27,7 +27,7 @@ export abstract class AbstractEditorCommandsQuickAccessProvider extends Abstract
/**
* Subclasses to provide the current active editor control.
*/
abstract activeTextEditorControl: IEditor | undefined;
protected abstract activeTextEditorControl: IEditor | undefined;
protected getCodeEditorCommandPicks(): ICommandQuickPick[] {
const activeTextEditorControl = this.activeTextEditorControl;

View File

@@ -0,0 +1,192 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration, OverviewRulerLane, ITextModel } from 'vs/editor/common/model';
import { IRange } from 'vs/editor/common/core/range';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
import { withNullAsUndefined } from 'vs/base/common/types';
import { once } from 'vs/base/common/functional';
interface IEditorLineDecoration {
rangeHighlightId: string;
overviewRulerDecorationId: string;
}
/**
* A reusable quick access provider for the editor with support
* for adding decorations for navigating in the currently active file
* (for example "Go to line", "Go to symbol").
*/
export abstract class AbstractEditorNavigationQuickAccessProvider implements IQuickAccessProvider {
//#region Provider methods
provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Disable filtering & sorting, we control the results
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
// Provide based on current active editor
let pickerDisposable = this.doProvide(picker, token);
disposables.add(toDisposable(() => pickerDisposable.dispose()));
// Re-create whenever the active editor changes
disposables.add(this.onDidActiveTextEditorControlChange(() => {
pickerDisposable.dispose();
pickerDisposable = this.doProvide(picker, token);
}));
return disposables;
}
private doProvide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// With text control
const editor = this.activeTextEditorControl;
if (editor && this.canProvideWithTextEditor(editor)) {
// Restore any view state if this picker was closed
// without actually going to a line
const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
once(token.onCancellationRequested)(() => {
if (lastKnownEditorViewState) {
editor.restoreViewState(lastKnownEditorViewState);
}
});
// Clean up decorations on dispose
disposables.add(toDisposable(() => this.clearDecorations(editor)));
// Ask subclass for entries
disposables.add(this.provideWithTextEditor(editor, picker, token));
}
// Without text control
else {
disposables.add(this.provideWithoutTextEditor(picker, token));
}
return disposables;
}
/**
* Subclasses to implement if they can operate on the text editor.
*/
protected canProvideWithTextEditor(editor: IEditor): boolean {
return true;
}
/**
* Subclasses to implement to provide picks for the picker when an editor is active.
*/
protected abstract provideWithTextEditor(editor: IEditor, picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
/**
* Subclasses to implement to provide picks for the picker when no editor is active.
*/
protected abstract provideWithoutTextEditor(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
protected gotoLocation(editor: IEditor, range: IRange, keyMods: IKeyMods, forceSideBySide?: boolean): void {
editor.setSelection(range);
editor.revealRangeInCenter(range, ScrollType.Smooth);
editor.focus();
}
protected getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
return isDiffEditor(editor) ?
editor.getModel()?.modified :
editor.getModel() as ITextModel;
}
//#endregion
//#region Editor access
/**
* Subclasses to provide an event when the active editor control changes.
*/
protected abstract readonly onDidActiveTextEditorControlChange: Event<void>;
/**
* Subclasses to provide the current active editor control.
*/
protected abstract activeTextEditorControl: IEditor | undefined;
//#endregion
//#region Decorations Utils
private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
protected addDecorations(editor: IEditor, range: IRange): void {
editor.changeDecorations(changeAccessor => {
// Reset old decorations if any
const deleteDecorations: string[] = [];
if (this.rangeHighlightDecorationId) {
deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
this.rangeHighlightDecorationId = undefined;
}
// Add new decorations for the range
const newDecorations: IModelDeltaDecoration[] = [
// highlight the entire line on the range
{
range,
options: {
className: 'rangeHighlight',
isWholeLine: true
}
},
// also add overview ruler highlight
{
range,
options: {
overviewRuler: {
color: themeColorFromId(overviewRulerRangeHighlight),
position: OverviewRulerLane.Full
}
}
}
];
const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
});
}
protected clearDecorations(editor: IEditor): void {
const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
if (rangeHighlightDecorationId) {
editor.changeDecorations(changeAccessor => {
changeAccessor.deltaDecorations([
rangeHighlightDecorationId.overviewRulerDecorationId,
rangeHighlightDecorationId.rangeHighlightId
], []);
});
this.rangeHighlightDecorationId = undefined;
}
}
//#endregion
}

View File

@@ -4,74 +4,32 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IQuickPick, IQuickPickItem, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { DisposableStore, toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { once } from 'vs/base/common/functional';
import { IEditor, ScrollType, IDiffEditor } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { isDiffEditor } from 'vs/editor/browser/editorBrowser';
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
import { IRange } from 'vs/editor/common/core/range';
import { withNullAsUndefined } from 'vs/base/common/types';
import { AbstractEditorQuickAccessProvider } from 'vs/editor/contrib/quickAccess/quickAccess';
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
import { IPosition } from 'vs/editor/common/core/position';
interface IGotoLineQuickPickItem extends IQuickPickItem, Partial<IPosition> { }
export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorQuickAccessProvider {
export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
static PREFIX = ':';
provide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
protected provideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
const label = localize('cannotRunGotoLine', "Open a text editor first to go to a line.");
// Disable filtering & sorting, we control the results
picker.matchOnLabel = picker.matchOnDescription = picker.matchOnDetail = picker.sortByLabel = false;
// Provide based on current active editor
let pickerDisposable = this.doProvide(picker, token);
disposables.add(toDisposable(() => pickerDisposable.dispose()));
// Re-create whenever the active editor changes
disposables.add(this.onDidActiveTextEditorControlChange(() => {
pickerDisposable.dispose();
pickerDisposable = this.doProvide(picker, token);
}));
return disposables;
}
private doProvide(picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
// With text control
if (this.activeTextEditorControl) {
return this.doProvideWithTextEditor(this.activeTextEditorControl, picker, token);
}
// Without text control
return this.doProvideWithoutTextEditor(picker);
}
private doProvideWithoutTextEditor(picker: IQuickPick<IGotoLineQuickPickItem>): IDisposable {
const label = localize('cannotRunGotoLine', "Open a text file first to go to a line.");
picker.items = [{ label }];
picker.ariaLabel = label;
return Disposable.None;
}
private doProvideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
protected provideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoLineQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Restore any view state if this picker was closed
// without actually going to a line
const lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
once(token.onCancellationRequested)(() => {
if (lastKnownEditorViewState) {
editor.restoreViewState(lastKnownEditorViewState);
}
});
// Goto line once picked
disposables.add(picker.onDidAccept(() => {
const [item] = picker.selectedItems;
@@ -80,7 +38,7 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
return;
}
this.gotoLine(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
this.gotoLocation(editor, this.toRange(item.lineNumber, item.column), picker.keyMods);
picker.hide();
}
@@ -117,9 +75,6 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
updatePickerAndEditor();
disposables.add(picker.onDidChangeValue(() => updatePickerAndEditor()));
// Clean up decorations on dispose
disposables.add(toDisposable(() => this.clearDecorations(editor)));
return disposables;
}
@@ -191,16 +146,4 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
private lineCount(editor: IEditor): number {
return this.getModel(editor)?.getLineCount() ?? 0;
}
private getModel(editor: IEditor | IDiffEditor): ITextModel | undefined {
return isDiffEditor(editor) ?
editor.getModel()?.modified :
editor.getModel() as ITextModel;
}
protected gotoLine(editor: IEditor, range: IRange, keyMods: IKeyMods): void {
editor.setSelection(range);
editor.revealRangeInCenter(range, ScrollType.Smooth);
editor.focus();
}
}

View File

@@ -0,0 +1,420 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IEditor, ScrollType } from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { IRange, Range } from 'vs/editor/common/core/range';
import { AbstractEditorNavigationQuickAccessProvider } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
import { DocumentSymbol, SymbolKinds, SymbolTag, DocumentSymbolProviderRegistry, SymbolKind } from 'vs/editor/common/modes';
import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { values } from 'vs/base/common/collections';
import { trim, format } from 'vs/base/common/strings';
import { fuzzyScore, FuzzyScore, createMatches } from 'vs/base/common/filters';
interface IGotoSymbolQuickPickItem extends IQuickPickItem {
kind: SymbolKind,
index: number,
score?: FuzzyScore;
range?: { decoration: IRange, selection: IRange },
}
export interface IGotoSymbolQuickAccessProviderOptions {
openSideBySideDirection: () => undefined | 'right' | 'down'
}
export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEditorNavigationQuickAccessProvider {
static PREFIX = '@';
static SCOPE_PREFIX = ':';
static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
constructor(private options?: IGotoSymbolQuickAccessProviderOptions) {
super();
}
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
const label = localize('cannotRunGotoSymbolWithoutEditor', "Open a text editor first to go to a symbol.");
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
picker.ariaLabel = label;
return Disposable.None;
}
protected provideWithTextEditor(editor: IEditor, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
const model = this.getModel(editor);
if (!model) {
return Disposable.None;
}
// Provide symbols from model if available in registry
if (DocumentSymbolProviderRegistry.has(model)) {
return this.doProvideWithEditorSymbols(editor, model, picker, token);
}
// Otherwise show an entry for a model without registry
// But give a chance to resolve the symbols at a later
// point if possible
return this.doProvideWithoutEditorSymbols(editor, model, picker, token);
}
private doProvideWithoutEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Generic pick for not having any symbol information
const label = localize('cannotRunGotoSymbolWithoutSymbolProvider', "Open a text editor with symbol information first to go to a symbol.");
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
picker.ariaLabel = label;
// Listen to changes to the registry and see if eventually
// we do get symbols. This can happen if the picker is opened
// very early after the model has loaded but before the
// language registry is ready.
// https://github.com/microsoft/vscode/issues/70607
const symbolProviderListener = disposables.add(DocumentSymbolProviderRegistry.onDidChange(() => {
if (DocumentSymbolProviderRegistry.has(model)) {
symbolProviderListener.dispose();
disposables.add(this.doProvideWithEditorSymbols(editor, model, picker, token));
}
}));
return disposables;
}
private doProvideWithEditorSymbols(editor: IEditor, model: ITextModel, picker: IQuickPick<IGotoSymbolQuickPickItem>, token: CancellationToken): IDisposable {
const disposables = new DisposableStore();
// Goto symbol once picked
disposables.add(picker.onDidAccept(() => {
const [item] = picker.selectedItems;
if (item && item.range) {
this.gotoLocation(editor, item.range.selection, picker.keyMods);
picker.hide();
}
}));
// Goto symbol side by side if enabled
disposables.add(picker.onDidTriggerItemButton(({ item }) => {
if (item && item.range) {
this.gotoLocation(editor, item.range.selection, picker.keyMods, true);
picker.hide();
}
}));
// Resolve symbols from document once and reuse this
// request for all filtering and typing then on
const symbolsPromise = this.getDocumentSymbols(model, true, token);
// Set initial picks and update on type
let picksCts: CancellationTokenSource | undefined = undefined;
const updatePickerItems = async () => {
// Cancel any previous ask for picks and busy
picksCts?.dispose(true);
picker.busy = false;
// Create new cancellation source for this run
picksCts = new CancellationTokenSource(token);
// Collect symbol picks
picker.busy = true;
try {
const items = await this.getSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
if (token.isCancellationRequested) {
return;
}
picker.items = items;
} finally {
if (!token.isCancellationRequested) {
picker.busy = false;
}
}
};
disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
updatePickerItems();
// Reveal and decorate when active item changes
// However, ignore the very first event so that
// opening the picker is not immediately revealing
// and decorating the first entry.
let ignoreFirstActiveEvent = true;
disposables.add(picker.onDidChangeActive(() => {
const [item] = picker.activeItems;
if (item && item.range) {
if (ignoreFirstActiveEvent) {
ignoreFirstActiveEvent = false;
return;
}
// Reveal
editor.revealRangeInCenter(item.range.selection, ScrollType.Smooth);
// Decorate
this.addDecorations(editor, item.range.decoration);
}
}));
return disposables;
}
private async getSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, filter: string, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
const symbols = await symbolsPromise;
if (token.isCancellationRequested) {
return [];
}
// Normalize filter
const filterBySymbolKind = filter.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
const filterPos = filterBySymbolKind ? 1 : 0;
const [symbolFilter, containerFilter] = filter.split(' ') as [string, string | undefined];
const symbolFilterLow = symbolFilter.toLowerCase();
const containerFilterLow = containerFilter?.toLowerCase();
// Convert to symbol picks and apply filtering
const filteredSymbolPicks: IGotoSymbolQuickPickItem[] = [];
for (let index = 0; index < symbols.length; index++) {
const symbol = symbols[index];
const symbolLabel = trim(symbol.name);
const containerLabel = symbol.containerName;
let symbolScore: FuzzyScore | undefined = undefined;
let containerScore: FuzzyScore | undefined = undefined;
let includeSymbol = true;
if (filter.length > filterPos) {
// Score by symbol
symbolScore = fuzzyScore(symbolFilter, symbolFilterLow, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
includeSymbol = !!symbolScore;
// Score by container if specified
if (includeSymbol && containerFilter && containerFilterLow) {
if (containerLabel) {
containerScore = fuzzyScore(containerFilter, containerFilterLow, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
}
includeSymbol = !!containerScore;
}
}
if (includeSymbol) {
const symbolLabelWithIcon = `$(symbol-${SymbolKinds.toString(symbol.kind) || 'property'}) ${symbolLabel}`;
const deprecated = symbol.tags && symbol.tags.indexOf(SymbolTag.Deprecated) >= 0;
filteredSymbolPicks.push({
index,
kind: symbol.kind,
score: symbolScore,
label: symbolLabelWithIcon,
ariaLabel: localize('symbolsAriaLabel', "{0}, symbols picker", symbolLabel),
description: containerLabel,
highlights: deprecated ? undefined : {
label: createMatches(symbolScore, symbolLabelWithIcon.length - symbolLabel.length /* Readjust matches to account for codicons in label */),
description: createMatches(containerScore)
},
range: {
selection: Range.collapseToStart(symbol.selectionRange),
decoration: symbol.range
},
strikethrough: deprecated,
buttons: (() => {
const openSideBySideDirection = this.options?.openSideBySideDirection();
if (!openSideBySideDirection) {
return undefined;
}
return [
{
iconClass: openSideBySideDirection === 'right' ? 'codicon-split-horizontal' : 'codicon-split-vertical',
tooltip: openSideBySideDirection === 'right' ? localize('openToSide', "Open to the Side") : localize('openToBottom', "Open to the Bottom")
}
];
})()
});
}
}
// Sort by score
const sortedFilteredSymbolPicks = filteredSymbolPicks.sort((symbolA, symbolB) => filterBySymbolKind ?
this.compareByKindAndScore(symbolA, symbolB) :
this.compareByScore(symbolA, symbolB)
);
// Add separator for types
// - @ only total number of symbols
// - @: grouped by symbol kind
let symbolPicks: Array<IGotoSymbolQuickPickItem | IQuickPickSeparator> = [];
if (filterBySymbolKind) {
let lastSymbolKind: SymbolKind | undefined = undefined;
let lastSeparator: IQuickPickSeparator | undefined = undefined;
let lastSymbolKindCounter = 0;
function updateLastSeparatorLabel(): void {
if (lastSeparator && typeof lastSymbolKind === 'number' && lastSymbolKindCounter > 0) {
lastSeparator.label = format(NLS_SYMBOL_KIND_CACHE[lastSymbolKind] || FALLBACK_NLS_SYMBOL_KIND, lastSymbolKindCounter);
}
}
for (const symbolPick of sortedFilteredSymbolPicks) {
// Found new kind
if (lastSymbolKind !== symbolPick.kind) {
// Update last separator with number of symbols we found for kind
updateLastSeparatorLabel();
lastSymbolKind = symbolPick.kind;
lastSymbolKindCounter = 1;
// Add new separator for new kind
lastSeparator = { type: 'separator' };
symbolPicks.push(lastSeparator);
}
// Existing kind, keep counting
else {
lastSymbolKindCounter++;
}
// Add to final result
symbolPicks.push(symbolPick);
}
// Update last separator with number of symbols we found for kind
updateLastSeparatorLabel();
} else {
symbolPicks = [
{ label: localize('symbols', "symbols ({0})", filteredSymbolPicks.length), type: 'separator' },
...sortedFilteredSymbolPicks
];
}
return symbolPicks;
}
private compareByScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
if (!symbolA.score && symbolB.score) {
return 1;
} else if (symbolA.score && !symbolB.score) {
return -1;
}
if (symbolA.score && symbolB.score) {
if (symbolA.score[0] > symbolB.score[0]) {
return -1;
} else if (symbolA.score[0] < symbolB.score[0]) {
return 1;
}
}
if (symbolA.index < symbolB.index) {
return -1;
} else if (symbolA.index > symbolB.index) {
return 1;
}
return 0;
}
private compareByKindAndScore(symbolA: IGotoSymbolQuickPickItem, symbolB: IGotoSymbolQuickPickItem): number {
const kindA = NLS_SYMBOL_KIND_CACHE[symbolA.kind] || FALLBACK_NLS_SYMBOL_KIND;
const kindB = NLS_SYMBOL_KIND_CACHE[symbolB.kind] || FALLBACK_NLS_SYMBOL_KIND;
// Sort by type first if scoped search
const result = kindA.localeCompare(kindB);
if (result === 0) {
return this.compareByScore(symbolA, symbolB);
}
return result;
}
private async getDocumentSymbols(document: ITextModel, flatten: boolean, token: CancellationToken): Promise<DocumentSymbol[]> {
const model = await OutlineModel.create(document, token);
if (token.isCancellationRequested) {
return [];
}
const roots: DocumentSymbol[] = [];
for (const child of values(model.children)) {
if (child instanceof OutlineElement) {
roots.push(child.symbol);
} else {
roots.push(...values(child.children).map(child => child.symbol));
}
}
let flatEntries: DocumentSymbol[] = [];
if (flatten) {
this.flattenDocumentSymbols(flatEntries, roots, '');
} else {
flatEntries = roots;
}
return flatEntries.sort((symbolA, symbolB) => Range.compareRangesUsingStarts(symbolA.range, symbolB.range));
}
private flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void {
for (const entry of entries) {
bucket.push({
kind: entry.kind,
tags: entry.tags,
name: entry.name,
detail: entry.detail,
containerName: entry.containerName || overrideContainerLabel,
range: entry.range,
selectionRange: entry.selectionRange,
children: undefined, // we flatten it...
});
// Recurse over children
if (entry.children) {
this.flattenDocumentSymbols(bucket, entry.children, entry.name);
}
}
}
}
// #region NLS Helpers
const FALLBACK_NLS_SYMBOL_KIND = localize('property', "properties ({0})");
const NLS_SYMBOL_KIND_CACHE: { [type: number]: string } = {
[SymbolKind.Method]: localize('method', "methods ({0})"),
[SymbolKind.Function]: localize('function', "functions ({0})"),
[SymbolKind.Constructor]: localize('_constructor', "constructors ({0})"),
[SymbolKind.Variable]: localize('variable', "variables ({0})"),
[SymbolKind.Class]: localize('class', "classes ({0})"),
[SymbolKind.Struct]: localize('struct', "structs ({0})"),
[SymbolKind.Event]: localize('event', "events ({0})"),
[SymbolKind.Operator]: localize('operator', "operators ({0})"),
[SymbolKind.Interface]: localize('interface', "interfaces ({0})"),
[SymbolKind.Namespace]: localize('namespace', "namespaces ({0})"),
[SymbolKind.Package]: localize('package', "packages ({0})"),
[SymbolKind.TypeParameter]: localize('typeParameter', "type parameters ({0})"),
[SymbolKind.Module]: localize('modules', "modules ({0})"),
[SymbolKind.Property]: localize('property', "properties ({0})"),
[SymbolKind.Enum]: localize('enum', "enumerations ({0})"),
[SymbolKind.EnumMember]: localize('enumMember', "enumeration members ({0})"),
[SymbolKind.String]: localize('string', "strings ({0})"),
[SymbolKind.File]: localize('file', "files ({0})"),
[SymbolKind.Array]: localize('array', "arrays ({0})"),
[SymbolKind.Number]: localize('number', "numbers ({0})"),
[SymbolKind.Boolean]: localize('boolean', "booleans ({0})"),
[SymbolKind.Object]: localize('object', "objects ({0})"),
[SymbolKind.Key]: localize('key', "keys ({0})"),
[SymbolKind.Field]: localize('field', "fields ({0})"),
[SymbolKind.Constant]: localize('constant', "constants ({0})")
};
//#endregion

View File

@@ -1,104 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IQuickAccessProvider } from 'vs/platform/quickinput/common/quickAccess';
import { IEditor } from 'vs/editor/common/editorCommon';
import { IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
import { IRange } from 'vs/editor/common/core/range';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
interface IEditorLineDecoration {
rangeHighlightId: string;
overviewRulerDecorationId: string;
}
/**
* A reusable quick access provider for the editor with support for adding decorations.
*/
export abstract class AbstractEditorQuickAccessProvider implements IQuickAccessProvider {
/**
* Subclasses to provide an event when the active editor control changes.
*/
abstract readonly onDidActiveTextEditorControlChange: Event<void>;
/**
* Subclasses to provide the current active editor control.
*/
abstract activeTextEditorControl: IEditor | undefined;
/**
* Subclasses to implement the quick access picker.
*/
abstract provide(picker: IQuickPick<IQuickPickItem>, token: CancellationToken): IDisposable;
//#region Decorations Utils
private rangeHighlightDecorationId: IEditorLineDecoration | undefined = undefined;
protected addDecorations(editor: IEditor, range: IRange): void {
editor.changeDecorations(changeAccessor => {
// Reset old decorations if any
const deleteDecorations: string[] = [];
if (this.rangeHighlightDecorationId) {
deleteDecorations.push(this.rangeHighlightDecorationId.overviewRulerDecorationId);
deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId);
this.rangeHighlightDecorationId = undefined;
}
// Add new decorations for the range
const newDecorations: IModelDeltaDecoration[] = [
// highlight the entire line on the range
{
range,
options: {
className: 'rangeHighlight',
isWholeLine: true
}
},
// also add overview ruler highlight
{
range,
options: {
overviewRuler: {
color: themeColorFromId(overviewRulerRangeHighlight),
position: OverviewRulerLane.Full
}
}
}
];
const [rangeHighlightId, overviewRulerDecorationId] = changeAccessor.deltaDecorations(deleteDecorations, newDecorations);
this.rangeHighlightDecorationId = { rangeHighlightId, overviewRulerDecorationId };
});
}
protected clearDecorations(editor: IEditor): void {
const rangeHighlightDecorationId = this.rangeHighlightDecorationId;
if (rangeHighlightDecorationId) {
editor.changeDecorations(changeAccessor => {
changeAccessor.deltaDecorations([
rangeHighlightDecorationId.overviewRulerDecorationId,
rangeHighlightDecorationId.rangeHighlightId
], []);
});
this.rangeHighlightDecorationId = undefined;
}
}
//#endregion
}