提交 d2ee88ba 编写于 作者: J Johannes Rieken

add config per UX (breadcrumb, tree, quick pick)

上级 1922771f
......@@ -299,7 +299,7 @@ export class OutlineFilter implements ITreeFilter<OutlineItem> {
});
constructor(
private readonly _prefix: string,
private readonly _prefix: 'breadcrumbs' | 'outline',
@ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService,
) { }
......
......@@ -78,8 +78,8 @@ class OutlineItem extends BreadcrumbsItem {
return;
}
const templateId = outline.config.delegate.getTemplateId(element);
const renderer = outline.config.renderers.find(renderer => renderer.templateId === templateId);
const templateId = outline.breadcrumbsConfig.delegate.getTemplateId(element);
const renderer = outline.breadcrumbsConfig.renderers.find(renderer => renderer.templateId === templateId);
if (!renderer) {
container.innerText = '<<NO RENDERER>>';
return;
......
......@@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { FileKind } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IOutline, IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
import { IOutline, IOutlineService } from 'vs/workbench/services/outline/browser/outline';
import { IEditorPane } from 'vs/workbench/common/editor';
export class FileElement {
......@@ -104,7 +104,7 @@ export class BreadcrumbsModel {
}
let didAddOutlineElement = false;
for (let element of this._currentOutline.config.breadcrumbsDataSource.getBreadcrumbElements()) {
for (let element of this._currentOutline.breadcrumbsConfig.breadcrumbsDataSource.getBreadcrumbElements()) {
result.push(new OutlineElement2(element, this._currentOutline));
didAddOutlineElement = true;
}
......@@ -154,7 +154,7 @@ export class BreadcrumbsModel {
this._outlineDisposables.clear();
this._outlineDisposables.add(toDisposable(() => newCts.dispose(true)));
this._outlineService.createOutline(editor, OutlineTarget.Breadcrumbs, newCts.token).then(outline => {
this._outlineService.createOutline(editor, newCts.token).then(outline => {
if (newCts.token.isCancellationRequested) {
// cancelled: dispose new outline and reset
outline?.dispose();
......
......@@ -452,7 +452,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
protected _createTree(container: HTMLElement, input: OutlineElement2) {
const { config } = input.outline;
const { breadcrumbsConfig: config } = input.outline;
return <WorkbenchDataTree<IOutline<any>, any, FuzzyScore>>this._instantiationService.createInstance(
WorkbenchDataTree,
......
......@@ -5,7 +5,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IBreadcrumbsDataSource, IOutline, IOutlineCreator, IOutlineService, OutlineTarget, OutlineTreeConfiguration } from 'vs/workbench/services/outline/browser/outline';
import { IBreadcrumbsDataSource, IOutline, IOutlineBreadcrumbsConfig, IOutlineCreator, IOutlineQuickPickConfig, IOutlineService, IOutlineTreeConfig, } from 'vs/workbench/services/outline/browser/outline';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
......@@ -28,6 +28,8 @@ import { Range } from 'vs/editor/common/core/range';
import { IEditorOptions, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { IDataSource } from 'vs/base/browser/ui/tree/tree';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
type DocumentSymbolItem = OutlineGroup | OutlineElement;
......@@ -124,69 +126,92 @@ class DocumentSymbolsOutline implements IOutline<DocumentSymbolItem> {
readonly onDidChange: Event<this> = this._onDidChange.event;
readonly onDidChangeActive: Event<void> = this._onDidChangeActive.event;
readonly config: OutlineTreeConfiguration<DocumentSymbolItem>;
private _outlineModel?: OutlineModel;
private _outlineDisposables = new DisposableStore();
private readonly _breadcrumbsDataSource: DocumentSymbolBreadcrumbsSource;
readonly breadcrumbsConfig: IOutlineBreadcrumbsConfig<DocumentSymbolItem>;
readonly treeConfig: IOutlineTreeConfig<DocumentSymbolItem>;
readonly quickPickConfig: IOutlineQuickPickConfig<DocumentSymbolItem>;
constructor(
private readonly _editor: ICodeEditor,
target: OutlineTarget,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
// @IConfigurationService private readonly _configurationService: IConfigurationService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IInstantiationService instantiationService: IInstantiationService,
) {
this._breadcrumbsDataSource = new DocumentSymbolBreadcrumbsSource(_editor, textResourceConfigurationService);
const sorter = new OutlineItemComparator();
this.config = new OutlineTreeConfiguration(
this._breadcrumbsDataSource,
{ getQuickPickElements: () => { throw new Error('not implemented'); } },
{
getChildren: (parent) => {
if (parent instanceof OutlineElement || parent instanceof OutlineGroup) {
return parent.children.values();
}
if (parent === this && this._outlineModel) {
return this._outlineModel.children.values();
}
return [];
const delegate = new OutlineVirtualDelegate();
const renderers = [new OutlineGroupRenderer(), instantiationService.createInstance(OutlineElementRenderer)];
const treeDataSource: IDataSource<this, DocumentSymbolItem> = {
getChildren: (parent) => {
if (parent instanceof OutlineElement || parent instanceof OutlineGroup) {
return parent.children.values();
}
if (parent === this && this._outlineModel) {
return this._outlineModel.children.values();
}
},
new OutlineVirtualDelegate(),
[new OutlineGroupRenderer(), instantiationService.createInstance(OutlineElementRenderer)],
{
collapseByDefault: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
accessibilityProvider: new OutlineAccessibilityProvider(target === OutlineTarget.Breadcrumbs ? 'breadcrumbs' : 'outline'),
identityProvider: new OutlineIdentityProvider(),
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(),
filter: instantiationService.createInstance(OutlineFilter, target === OutlineTarget.Breadcrumbs ? 'breadcrumbs' : 'outline'),
sorter
return [];
}
);
};
const options = {
collapseByDefault: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
identityProvider: new OutlineIdentityProvider(),
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(),
};
const breadcrumbsSorter = new OutlineItemComparator();
this.breadcrumbsConfig = {
breadcrumbsDataSource: this._breadcrumbsDataSource,
delegate,
renderers,
treeDataSource,
options: {
...options,
sorter: breadcrumbsSorter,
filter: instantiationService.createInstance(OutlineFilter, 'breadcrumbs'),
accessibilityProvider: new OutlineAccessibilityProvider('breadcrumbs'),
}
};
this.treeConfig = {
delegate,
renderers,
treeDataSource,
options: {
...options,
// sorter: breadcrumbsSorter, //todo@jrieken
filter: instantiationService.createInstance(OutlineFilter, 'outline'),
accessibilityProvider: new OutlineAccessibilityProvider('outline'),
}
};
this.quickPickConfig = {
quickPickDataSource: { getQuickPickElements: () => { throw new Error('not implemented'); } }
};
// special sorting for breadcrumbs
if (target === OutlineTarget.Breadcrumbs) {
const updateSort = () => {
const uri = this._outlineModel?.uri;
const value = textResourceConfigurationService.getValue(uri, `breadcrumbs.symbolSortOrder`);
if (value === 'name') {
sorter.type = OutlineSortOrder.ByName;
} else if (value === 'type') {
sorter.type = OutlineSortOrder.ByKind;
} else {
sorter.type = OutlineSortOrder.ByPosition;
}
};
this._disposables.add(textResourceConfigurationService.onDidChangeConfiguration(() => updateSort()));
updateSort();
}
const updateSort = () => {
const uri = this._outlineModel?.uri;
const value = textResourceConfigurationService.getValue(uri, `breadcrumbs.symbolSortOrder`);
if (value === 'name') {
breadcrumbsSorter.type = OutlineSortOrder.ByName;
} else if (value === 'type') {
breadcrumbsSorter.type = OutlineSortOrder.ByKind;
} else {
breadcrumbsSorter.type = OutlineSortOrder.ByPosition;
}
};
this._disposables.add(textResourceConfigurationService.onDidChangeConfiguration(() => updateSort()));
updateSort();
// update as language, model, providers changes
......@@ -196,22 +221,22 @@ class DocumentSymbolsOutline implements IOutline<DocumentSymbolItem> {
// TODO@jrieken
// update when config changes (re-render)
// this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
// if (e.affectsConfiguration('breadcrumbs')) {
// this._createOutline(true);
// return;
// }
// if (this._editor && this._editor.getModel()) {
// const editorModel = this._editor.getModel() as ITextModel;
// const languageName = editorModel.getLanguageIdentifier().language;
// // Checking for changes in the current language override config.
// // We can't be more specific than this because the ConfigurationChangeEvent(e) only includes the first part of the root path
// if (e.affectsConfiguration(`[${languageName}]`)) {
// this._createOutline(true);
// }
// }
// }));
this._disposables.add(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('breadcrumbs')) {
this._updateOutlineModel(this._outlineModel);
return;
}
if (this._editor && this._editor.getModel()) {
const editorModel = this._editor.getModel() as ITextModel;
const languageName = editorModel.getLanguageIdentifier().language;
// Checking for changes in the current language override config.
// We can't be more specific than this because the ConfigurationChangeEvent(e) only includes the first part of the root path
if (e.affectsConfiguration(`[${languageName}]`)) {
this._updateOutlineModel(this._outlineModel);
}
}
}));
// update soon'ish as model content change
const updateSoon = new TimeoutTimer();
......@@ -385,7 +410,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
return isCodeEditor(ctrl) || isDiffEditor(ctrl);
}
async createOutline(pane: IEditorPane, target: OutlineTarget, token: CancellationToken): Promise<IOutline<DocumentSymbolItem> | undefined> {
async createOutline(pane: IEditorPane, token: CancellationToken): Promise<IOutline<DocumentSymbolItem> | undefined> {
const control = pane.getControl();
let editor: ICodeEditor | undefined;
if (isCodeEditor(control)) {
......@@ -396,7 +421,7 @@ class DocumentSymbolsOutlineCreator implements IOutlineCreator<IEditorPane, Docu
if (!editor) {
return undefined;
}
return this._instantiationService.createInstance(DocumentSymbolsOutline, editor, target);
return this._instantiationService.createInstance(DocumentSymbolsOutline, editor);
}
}
......
......@@ -26,7 +26,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IQuickAccessTextEditorContext } from 'vs/editor/contrib/quickAccess/editorNavigationQuickAccess';
import { IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
import { IOutlineService } from 'vs/workbench/services/outline/browser/outline';
import { isCompositeEditor } from 'vs/editor/browser/editorBrowser';
export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
......@@ -138,7 +138,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
picker.busy = true;
this.outlineService.createOutline(pane, OutlineTarget.QuickPick, cts.token).then(outline => {
this.outlineService.createOutline(pane, cts.token).then(outline => {
if (!outline) {
return;
......@@ -150,7 +150,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
disposables.add(outline);
const entries = Array.from(outline.config.quickPickDataSource.getQuickPickElements());
const entries = Array.from(outline.quickPickConfig.quickPickDataSource.getQuickPickElements());
const items: IGotoSymbolQuickPickItem[] = entries.map((entry, idx) => {
return {
......
......@@ -12,13 +12,13 @@ import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'
import { ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IOutline, IOutlineCreator, IOutlineService, IQuickPickDataSource, OutlineTreeConfiguration } from 'vs/workbench/services/outline/browser/outline';
import { IOutline, IOutlineBreadcrumbsConfig, IOutlineCreator, IOutlineQuickPickConfig, IOutlineService, IOutlineTreeConfig, IQuickPickDataSource } from 'vs/workbench/services/outline/browser/outline';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IEditorPane } from 'vs/workbench/common/editor';
import { IKeyboardNavigationLabelProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { IDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
......@@ -28,6 +28,7 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { getIconClassesForModeId } from 'vs/editor/common/services/getIconClasses';
import { SymbolKind } from 'vs/editor/common/modes';
import { IWorkbenchDataTreeOptions } from 'vs/platform/list/browser/listService';
export class OutlineEntry {
constructor(
......@@ -135,7 +136,9 @@ class NotebookCellOutline implements IOutline<OutlineEntry> {
private _activeEntry: number = -1;
private readonly _entriesDisposables = new DisposableStore();
readonly config: OutlineTreeConfiguration<OutlineEntry>;
readonly breadcrumbsConfig: IOutlineBreadcrumbsConfig<OutlineEntry>;
readonly treeConfig: IOutlineTreeConfig<OutlineEntry>;
readonly quickPickConfig: IOutlineQuickPickConfig<OutlineEntry>;
constructor(
private readonly _editor: NotebookEditor,
......@@ -164,21 +167,37 @@ class NotebookCellOutline implements IOutline<OutlineEntry> {
this._recomputeState();
installSelectionListener();
this.config = new OutlineTreeConfiguration<OutlineEntry>(
{ getBreadcrumbElements: () => this._activeEntry >= 0 ? Iterable.single(this._entries[this._activeEntry]) : Iterable.empty() },
instantiationService.createInstance(NotebookQuickPickProvider, () => this._entries),
{ getChildren: parent => parent === this ? this._entries : [] },
new NotebookOutlineVirtualDelegate(),
[instantiationService.createInstance(NotebookOutlineRenderer)],
{
collapseByDefault: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
accessibilityProvider: new NotebookOutlineAccessibility(),
identityProvider: { getId: element => element.cell.handle },
keyboardNavigationLabelProvider: new NotebookNavigationLabelProvider()
}
);
const options: IWorkbenchDataTreeOptions<OutlineEntry, FuzzyScore> = {
collapseByDefault: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
accessibilityProvider: new NotebookOutlineAccessibility(),
identityProvider: { getId: element => element.cell.handle },
keyboardNavigationLabelProvider: new NotebookNavigationLabelProvider()
};
const treeDataSource: IDataSource<this, OutlineEntry> = { getChildren: parent => parent === this ? this._entries : [] };
const delegate = new NotebookOutlineVirtualDelegate();
const renderers = [instantiationService.createInstance(NotebookOutlineRenderer)];
this.breadcrumbsConfig = {
breadcrumbsDataSource: { getBreadcrumbElements: () => this._activeEntry >= 0 ? Iterable.single(this._entries[this._activeEntry]) : Iterable.empty() },
treeDataSource,
delegate,
renderers,
options
};
this.treeConfig = {
treeDataSource,
delegate,
renderers,
options
};
this.quickPickConfig = {
quickPickDataSource: instantiationService.createInstance(NotebookQuickPickProvider, () => this._entries),
};
}
dispose(): void {
......
......@@ -17,33 +17,46 @@ import { IEditorPane } from 'vs/workbench/common/editor';
export const IOutlineService = createDecorator<IOutlineService>('IOutlineService');
export const enum OutlineTarget {
OutlinePane,
Breadcrumbs,
QuickPick
}
export interface IOutlineService {
_serviceBrand: undefined;
onDidChange: Event<void>;
canCreateOutline(editor: IEditorPane): boolean;
createOutline(editor: IEditorPane, target: OutlineTarget, token: CancellationToken): Promise<IOutline<any> | undefined>;
createOutline(editor: IEditorPane, token: CancellationToken): Promise<IOutline<any> | undefined>;
registerOutlineCreator(creator: IOutlineCreator<any, any>): IDisposable;
}
export interface IOutlineCreator<P extends IEditorPane, E> {
matches(candidate: IEditorPane): candidate is P;
createOutline(editor: P, target: OutlineTarget, token: CancellationToken): Promise<IOutline<E> | undefined>;
createOutline(editor: P, token: CancellationToken): Promise<IOutline<E> | undefined>;
}
export interface IBreadcrumbsDataSource<E> {
getBreadcrumbElements(): Iterable<E>;
}
export interface IOutlineBreadcrumbsConfig<E> {
readonly breadcrumbsDataSource: IBreadcrumbsDataSource<E>;
readonly treeDataSource: IDataSource<IOutline<E>, E>;
readonly delegate: IListVirtualDelegate<E>;
readonly renderers: ITreeRenderer<E, FuzzyScore, any>[];
readonly options: IWorkbenchDataTreeOptions<E, FuzzyScore>;
}
export interface IOutlineTreeConfig<E> {
readonly treeDataSource: IDataSource<IOutline<E>, E>;
readonly delegate: IListVirtualDelegate<E>;
readonly renderers: ITreeRenderer<E, FuzzyScore, any>[];
readonly options: IWorkbenchDataTreeOptions<E, FuzzyScore>;
}
export interface IQuickPickDataSource<E> {
getQuickPickElements(): Iterable<{ element: E, kind?: SymbolKind, label: string, iconClasses?: string[], ariaLabel?: string, description?: string }>;
}
export interface IOutlineQuickPickConfig<E> {
readonly quickPickDataSource: IQuickPickDataSource<E>,
}
export class OutlineTreeConfiguration<E> {
constructor(
readonly breadcrumbsDataSource: IBreadcrumbsDataSource<E>,
......@@ -59,7 +72,9 @@ export interface IOutline<E> {
dispose(): void;
readonly config: OutlineTreeConfiguration<E>
readonly breadcrumbsConfig: IOutlineBreadcrumbsConfig<E>;
readonly treeConfig: IOutlineTreeConfig<E>;
readonly quickPickConfig: IOutlineQuickPickConfig<E>;
readonly onDidChange: Event<this>;
readonly onDidChangeActive: Event<void>;
......
......@@ -8,7 +8,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IEditorPane } from 'vs/workbench/common/editor';
import { IOutline, IOutlineCreator, IOutlineService, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
import { IOutline, IOutlineCreator, IOutlineService } from 'vs/workbench/services/outline/browser/outline';
import { Event, Emitter } from 'vs/base/common/event';
class OutlineService implements IOutlineService {
......@@ -29,10 +29,10 @@ class OutlineService implements IOutlineService {
return false;
}
async createOutline(pane: IEditorPane, target: OutlineTarget, token: CancellationToken): Promise<IOutline<any> | undefined> {
async createOutline(pane: IEditorPane, token: CancellationToken): Promise<IOutline<any> | undefined> {
for (let factory of this._factories) {
if (factory.matches(pane)) {
return await factory.createOutline(pane, target, token);
return await factory.createOutline(pane, token);
}
}
return undefined;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册