提交 074f6e0f 编写于 作者: J Johannes Rieken

add TableOfContentsProvider for none text editors, #95234

上级 cb755713
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IKeyMods, IQuickPickSeparator, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IKeyMods, IQuickPickSeparator, IQuickInputService, IQuickPick } from 'vs/platform/quickinput/common/quickInput';
import { IEditor } from 'vs/editor/common/editorCommon';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IRange } from 'vs/editor/common/core/range';
......@@ -12,16 +12,19 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IQuickAccessRegistry, Extensions as QuickaccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
import { AbstractGotoSymbolQuickAccessProvider, IGotoSymbolQuickPickItem } from 'vs/editor/contrib/quickAccess/gotoSymbolQuickAccess';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { IWorkbenchEditorConfiguration, IEditorPane } from 'vs/workbench/common/editor';
import { ITextModel } from 'vs/editor/common/model';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { timeout } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Action } from 'vs/base/common/actions';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { prepareQuery } from 'vs/base/common/fuzzyScorer';
import { SymbolKind } from 'vs/editor/common/modes';
import { fuzzyScore, createMatches } from 'vs/base/common/filters';
import { onUnexpectedError } from 'vs/base/common/errors';
export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider {
......@@ -36,6 +39,8 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
});
}
//#region DocumentSymbols (text editor required)
private get configuration() {
const editorConfig = this.configurationService.getValue<IWorkbenchEditorConfiguration>().workbench.editor;
......@@ -66,6 +71,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
}
}
//#endregion
//#region public methods to use this picker from other pickers
......@@ -98,6 +104,81 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
}
//#endregion
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
const pane = this.editorService.activeEditorPane;
if (!pane || !TableOfContentsProviderRegistry.has(pane.getId())) {
//
return super.provideWithoutTextEditor(picker);
}
const provider = TableOfContentsProviderRegistry.get(pane.getId())!;
const cts = new CancellationTokenSource();
const disposables = new DisposableStore();
disposables.add(toDisposable(() => cts.dispose(true)));
picker.busy = true;
provider.provideTableOfContents(pane, cts.token).then(entries => {
picker.busy = false;
if (cts.token.isCancellationRequested || !entries || entries.length === 0) {
return;
}
const items: IGotoSymbolQuickPickItem[] = entries.map((entry, idx) => {
return {
kind: SymbolKind.File,
index: idx,
score: 0,
label: entry.label,
detail: entry.detail,
description: entry.description,
};
});
disposables.add(picker.onDidAccept(() => {
picker.hide();
const [entry] = picker.selectedItems;
entries[entry.index]?.reveal();
}));
const updatePickerItems = () => {
const filteredItems = items.filter(item => {
if (picker.value === '@') {
// default, no filtering, scoring...
item.score = 0;
item.highlights = undefined;
return true;
}
const score = fuzzyScore(picker.value, picker.value.toLowerCase(), 1 /*@-character*/, item.label, item.label.toLowerCase(), 0, true);
if (!score) {
return false;
}
item.score = score[1];
item.highlights = { label: createMatches(score) };
return true;
});
if (filteredItems.length === 0) {
const label = localize('empty', 'No matching entries');
picker.items = [{ label, index: -1, kind: SymbolKind.String }];
picker.ariaLabel = label;
} else {
picker.items = filteredItems;
}
};
updatePickerItems();
disposables.add(picker.onDidChangeValue(updatePickerItems));
}).catch(err => {
onUnexpectedError(err);
picker.hide();
});
return disposables;
}
}
Registry.as<IQuickAccessRegistry>(QuickaccessExtensions.Quickaccess).registerQuickAccessProvider({
......@@ -132,3 +213,43 @@ export class GotoSymbolAction extends Action {
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registerWorkbenchAction(SyncActionDescriptor.from(GotoSymbolAction, {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O
}), 'Go to Symbol in Editor...');
//#region toc definition and logic
export interface ITableOfContentsEntry {
label: string;
detail?: string;
description?: string;
reveal(): any;
}
export interface ITableOfContentsProvider<T extends IEditorPane = IEditorPane> {
provideTableOfContents(editor: T, token: CancellationToken): Promise<ITableOfContentsEntry[] | undefined | null>;
}
class ProviderRegistry {
private readonly _provider = new Map<string, ITableOfContentsProvider>();
register(type: string, provider: ITableOfContentsProvider): IDisposable {
this._provider.set(type, provider);
return toDisposable(() => {
if (this._provider.get(type) === provider) {
this._provider.delete(type);
}
});
}
get(type: string): ITableOfContentsProvider | undefined {
return this._provider.get(type);
}
has(type: string): boolean {
return this._provider.has(type);
}
}
export const TableOfContentsProviderRegistry = new ProviderRegistry();
//#endregion
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TableOfContentsProviderRegistry, ITableOfContentsProvider, ITableOfContentsEntry } from 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess';
import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
TableOfContentsProviderRegistry.register(NotebookEditor.ID, new class implements ITableOfContentsProvider {
async provideTableOfContents(editor: NotebookEditor) {
if (!editor.viewModel) {
return undefined;
}
// return an entry per markdown header
const result: ITableOfContentsEntry[] = [];
for (let cell of editor.viewModel.viewCells) {
if (cell.cellKind === CellKind.Code) {
continue;
}
const content = cell.getText();
const matches = content.match(/^[ \t]*(\#+)(.+)$/gm);
if (matches && matches.length) {
for (let j = 0; j < matches.length; j++) {
result.push({
label: matches[j].replace(/^[ \t]*(\#+)/, ''),
reveal: () => editor.revealInCenterIfOutsideViewport(cell)
});
}
}
}
return result;
}
});
......@@ -42,6 +42,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import 'vs/workbench/contrib/notebook/browser/contrib/find/findController';
import 'vs/workbench/contrib/notebook/browser/contrib/fold/folding';
import 'vs/workbench/contrib/notebook/browser/contrib/format/formatting';
import 'vs/workbench/contrib/notebook/browser/contrib/toc/tocProvider';
// Output renderers registration
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册