mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-20 02:08:47 +00:00
highlight symbol row in notebooks (#193845)
* highlight symbol row in notebooks * fix test * better range usage
This commit is contained in:
@@ -26,7 +26,7 @@ import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/co
|
|||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||||
import { IEditorPane } from 'vs/workbench/common/editor';
|
import { IEditorPane } from 'vs/workbench/common/editor';
|
||||||
import { CellRevealType, INotebookEditorOptions, INotebookEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
import { CellRevealType, ICellModelDecorations, ICellModelDeltaDecorations, INotebookEditorOptions, INotebookEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||||
import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
|
import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor';
|
||||||
import { NotebookCellOutlineProvider } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider';
|
import { NotebookCellOutlineProvider } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider';
|
||||||
import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
import { CellKind, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||||
@@ -35,6 +35,8 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle
|
|||||||
import { IOutline, IOutlineComparator, IOutlineCreator, IOutlineListConfig, IOutlineService, IQuickPickDataSource, IQuickPickOutlineElement, OutlineChangeEvent, OutlineConfigCollapseItemsValues, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
|
import { IOutline, IOutlineComparator, IOutlineCreator, IOutlineListConfig, IOutlineService, IQuickPickDataSource, IQuickPickOutlineElement, OutlineChangeEvent, OutlineConfigCollapseItemsValues, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
|
||||||
import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry';
|
import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
|
import { IModelDeltaDecoration } from 'vs/editor/common/model';
|
||||||
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
|
|
||||||
class NotebookOutlineTemplate {
|
class NotebookOutlineTemplate {
|
||||||
|
|
||||||
@@ -303,12 +305,49 @@ export class NotebookCellOutline implements IOutline<OutlineEntry> {
|
|||||||
if (!widget) {
|
if (!widget) {
|
||||||
return Disposable.None;
|
return Disposable.None;
|
||||||
}
|
}
|
||||||
widget.revealInCenterIfOutsideViewport(entry.cell);
|
|
||||||
|
|
||||||
|
if (entry.range) {
|
||||||
|
const range = Range.lift(entry.range);
|
||||||
|
widget.revealRangeInCenterIfOutsideViewportAsync(entry.cell, range);
|
||||||
|
} else {
|
||||||
|
widget.revealInCenterIfOutsideViewport(entry.cell);
|
||||||
|
}
|
||||||
|
|
||||||
const ids = widget.deltaCellDecorations([], [{
|
const ids = widget.deltaCellDecorations([], [{
|
||||||
handle: entry.cell.handle,
|
handle: entry.cell.handle,
|
||||||
options: { className: 'nb-symbolHighlight', outputClassName: 'nb-symbolHighlight' }
|
options: { className: 'nb-symbolHighlight', outputClassName: 'nb-symbolHighlight' }
|
||||||
}]);
|
}]);
|
||||||
return toDisposable(() => { widget.deltaCellDecorations(ids, []); });
|
|
||||||
|
let editorDecorations: ICellModelDecorations[];
|
||||||
|
widget.changeModelDecorations(accessor => {
|
||||||
|
if (entry.range) {
|
||||||
|
const decorations: IModelDeltaDecoration[] = [
|
||||||
|
{
|
||||||
|
range: entry.range, options: {
|
||||||
|
description: 'document-symbols-outline-range-highlight',
|
||||||
|
className: 'rangeHighlight',
|
||||||
|
isWholeLine: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const deltaDecoration: ICellModelDeltaDecorations = {
|
||||||
|
ownerId: entry.cell.handle,
|
||||||
|
decorations: decorations
|
||||||
|
};
|
||||||
|
|
||||||
|
editorDecorations = accessor.deltaDecorations([], [deltaDecoration]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return toDisposable(() => {
|
||||||
|
widget.deltaCellDecorations(ids, []);
|
||||||
|
if (editorDecorations?.length) {
|
||||||
|
widget.changeModelDecorations(accessor => {
|
||||||
|
accessor.deltaDecorations(editorDecorations, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/marke
|
|||||||
import { ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
import { ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||||
import { executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
import { executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { IRange } from 'vs/editor/common/core/range';
|
||||||
import { SymbolKind, SymbolKinds } from 'vs/editor/common/languages';
|
import { SymbolKind, SymbolKinds } from 'vs/editor/common/languages';
|
||||||
|
|
||||||
|
|
||||||
export interface IOutlineMarkerInfo {
|
export interface IOutlineMarkerInfo {
|
||||||
readonly count: number;
|
readonly count: number;
|
||||||
readonly topSev: MarkerSeverity;
|
readonly topSev: MarkerSeverity;
|
||||||
@@ -38,7 +37,7 @@ export class OutlineEntry {
|
|||||||
readonly label: string,
|
readonly label: string,
|
||||||
readonly isExecuting: boolean,
|
readonly isExecuting: boolean,
|
||||||
readonly isPaused: boolean,
|
readonly isPaused: boolean,
|
||||||
readonly position?: Range,
|
readonly range?: IRange,
|
||||||
readonly symbolKind?: SymbolKind,
|
readonly symbolKind?: SymbolKind,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
@@ -59,6 +58,13 @@ export class OutlineEntry {
|
|||||||
return this._markerInfo;
|
return this._markerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get position() {
|
||||||
|
if (this.range) {
|
||||||
|
return { startLineNumber: this.range.startLineNumber, startColumn: this.range.startColumn };
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
updateMarkers(markerService: IMarkerService): void {
|
updateMarkers(markerService: IMarkerService): void {
|
||||||
if (this.cell.cellKind === CellKind.Code) {
|
if (this.cell.cellKind === CellKind.Code) {
|
||||||
// a code cell can have marker
|
// a code cell can have marker
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ import { getMarkdownHeadersInCell } from 'vs/workbench/contrib/notebook/browser/
|
|||||||
import { OutlineEntry } from './OutlineEntry';
|
import { OutlineEntry } from './OutlineEntry';
|
||||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { IRange } from 'vs/editor/common/core/range';
|
||||||
import { SymbolKind } from 'vs/editor/common/languages';
|
import { SymbolKind } from 'vs/editor/common/languages';
|
||||||
|
|
||||||
type entryDesc = {
|
type entryDesc = {
|
||||||
name: string;
|
name: string;
|
||||||
position: Range;
|
range: IRange;
|
||||||
level: number;
|
level: number;
|
||||||
kind: SymbolKind;
|
kind: SymbolKind;
|
||||||
};
|
};
|
||||||
@@ -72,9 +72,8 @@ export class NotebookOutlineEntryFactory {
|
|||||||
// So symbols need to be precached before this function is called to get the full list.
|
// So symbols need to be precached before this function is called to get the full list.
|
||||||
if (cachedEntries) {
|
if (cachedEntries) {
|
||||||
cachedEntries.forEach((cached) => {
|
cachedEntries.forEach((cached) => {
|
||||||
entries.push(new OutlineEntry(index++, cached.level, cell, cached.name, false, false, cached.position, cached.kind));
|
entries.push(new OutlineEntry(index++, cached.level, cell, cached.name, false, false, cached.range, cached.kind));
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,11 +106,7 @@ type documentSymbol = ReturnType<outlineModel['getTopLevelSymbols']>[number];
|
|||||||
function createOutlineEntries(symbols: documentSymbol[], level: number): entryDesc[] {
|
function createOutlineEntries(symbols: documentSymbol[], level: number): entryDesc[] {
|
||||||
const entries: entryDesc[] = [];
|
const entries: entryDesc[] = [];
|
||||||
symbols.forEach(symbol => {
|
symbols.forEach(symbol => {
|
||||||
const position = new Range(symbol.selectionRange.startLineNumber,
|
entries.push({ name: symbol.name, range: symbol.range, level, kind: symbol.kind });
|
||||||
symbol.selectionRange.startColumn,
|
|
||||||
symbol.selectionRange.startLineNumber,
|
|
||||||
symbol.selectionRange.startColumn);
|
|
||||||
entries.push({ name: symbol.name, position, level, kind: symbol.kind });
|
|
||||||
if (symbol.children) {
|
if (symbol.children) {
|
||||||
entries.push(...createOutlineEntries(symbol.children, level + 1));
|
entries.push(...createOutlineEntries(symbol.children, level + 1));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/co
|
|||||||
suite('Notebook Symbols', function () {
|
suite('Notebook Symbols', function () {
|
||||||
ensureNoDisposablesAreLeakedInTestSuite();
|
ensureNoDisposablesAreLeakedInTestSuite();
|
||||||
|
|
||||||
type textSymbol = { name: string; selectionRange: {}; children?: textSymbol[] };
|
type textSymbol = { name: string; range: {}; children?: textSymbol[] };
|
||||||
const symbolsPerTextModel: Record<string, textSymbol[]> = {};
|
const symbolsPerTextModel: Record<string, textSymbol[]> = {};
|
||||||
function setSymbolsForTextModel(symbols: textSymbol[], textmodelId = 'textId') {
|
function setSymbolsForTextModel(symbols: textSymbol[], textmodelId = 'textId') {
|
||||||
symbolsPerTextModel[textmodelId] = symbols;
|
symbolsPerTextModel[textmodelId] = symbols;
|
||||||
@@ -64,7 +64,7 @@ suite('Notebook Symbols', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('Cell without symbols cache', function () {
|
test('Cell without symbols cache', function () {
|
||||||
setSymbolsForTextModel([{ name: 'var', selectionRange: {} }]);
|
setSymbolsForTextModel([{ name: 'var', range: {} }]);
|
||||||
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
||||||
const entries = entryFactory.getOutlineEntries(createCellViewModel(), 0);
|
const entries = entryFactory.getOutlineEntries(createCellViewModel(), 0);
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ suite('Notebook Symbols', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Cell with simple symbols', async function () {
|
test('Cell with simple symbols', async function () {
|
||||||
setSymbolsForTextModel([{ name: 'var1', selectionRange: {} }, { name: 'var2', selectionRange: {} }]);
|
setSymbolsForTextModel([{ name: 'var1', range: {} }, { name: 'var2', range: {} }]);
|
||||||
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
||||||
const cell = createCellViewModel();
|
const cell = createCellViewModel();
|
||||||
|
|
||||||
@@ -92,8 +92,8 @@ suite('Notebook Symbols', function () {
|
|||||||
|
|
||||||
test('Cell with nested symbols', async function () {
|
test('Cell with nested symbols', async function () {
|
||||||
setSymbolsForTextModel([
|
setSymbolsForTextModel([
|
||||||
{ name: 'root1', selectionRange: {}, children: [{ name: 'nested1', selectionRange: {} }, { name: 'nested2', selectionRange: {} }] },
|
{ name: 'root1', range: {}, children: [{ name: 'nested1', range: {} }, { name: 'nested2', range: {} }] },
|
||||||
{ name: 'root2', selectionRange: {}, children: [{ name: 'nested1', selectionRange: {} }] }
|
{ name: 'root2', range: {}, children: [{ name: 'nested1', range: {} }] }
|
||||||
]);
|
]);
|
||||||
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
||||||
const cell = createCellViewModel();
|
const cell = createCellViewModel();
|
||||||
@@ -115,8 +115,8 @@ suite('Notebook Symbols', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Multiple Cells with symbols', async function () {
|
test('Multiple Cells with symbols', async function () {
|
||||||
setSymbolsForTextModel([{ name: 'var1', selectionRange: {} }], '$1');
|
setSymbolsForTextModel([{ name: 'var1', range: {} }], '$1');
|
||||||
setSymbolsForTextModel([{ name: 'var2', selectionRange: {} }], '$2');
|
setSymbolsForTextModel([{ name: 'var2', range: {} }], '$2');
|
||||||
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
const entryFactory = new NotebookOutlineEntryFactory(executionService);
|
||||||
|
|
||||||
const cell1 = createCellViewModel(1, '$1');
|
const cell1 = createCellViewModel(1, '$1');
|
||||||
|
|||||||
Reference in New Issue
Block a user