diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index 4305c75e301..d93af0a7a1c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -26,7 +26,7 @@ import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/co import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; 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 { NotebookCellOutlineProvider } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider'; 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 { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { Range } from 'vs/editor/common/core/range'; class NotebookOutlineTemplate { @@ -303,12 +305,49 @@ export class NotebookCellOutline implements IOutline { if (!widget) { 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([], [{ handle: entry.cell.handle, 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, []); + }); + } + }); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts index 9fd1c4f7160..398f676c876 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts @@ -8,10 +8,9 @@ import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/marke import { ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; 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'; - export interface IOutlineMarkerInfo { readonly count: number; readonly topSev: MarkerSeverity; @@ -38,7 +37,7 @@ export class OutlineEntry { readonly label: string, readonly isExecuting: boolean, readonly isPaused: boolean, - readonly position?: Range, + readonly range?: IRange, readonly symbolKind?: SymbolKind, ) { } @@ -59,6 +58,13 @@ export class OutlineEntry { return this._markerInfo; } + get position() { + if (this.range) { + return { startLineNumber: this.range.startLineNumber, startColumn: this.range.startColumn }; + } + return undefined; + } + updateMarkers(markerService: IMarkerService): void { if (this.cell.cellKind === CellKind.Code) { // a code cell can have marker diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts index fdcd175c354..54335576ac9 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts @@ -12,12 +12,12 @@ import { getMarkdownHeadersInCell } from 'vs/workbench/contrib/notebook/browser/ import { OutlineEntry } from './OutlineEntry'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; 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'; type entryDesc = { name: string; - position: Range; + range: IRange; level: number; 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. if (cachedEntries) { 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[number]; function createOutlineEntries(symbols: documentSymbol[], level: number): entryDesc[] { const entries: entryDesc[] = []; symbols.forEach(symbol => { - const position = new Range(symbol.selectionRange.startLineNumber, - symbol.selectionRange.startColumn, - symbol.selectionRange.startLineNumber, - symbol.selectionRange.startColumn); - entries.push({ name: symbol.name, position, level, kind: symbol.kind }); + entries.push({ name: symbol.name, range: symbol.range, level, kind: symbol.kind }); if (symbol.children) { entries.push(...createOutlineEntries(symbol.children, level + 1)); } diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts index 94ae1e47523..8826eb3dda7 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts @@ -16,7 +16,7 @@ import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/co suite('Notebook Symbols', function () { ensureNoDisposablesAreLeakedInTestSuite(); - type textSymbol = { name: string; selectionRange: {}; children?: textSymbol[] }; + type textSymbol = { name: string; range: {}; children?: textSymbol[] }; const symbolsPerTextModel: Record = {}; function setSymbolsForTextModel(symbols: textSymbol[], textmodelId = 'textId') { symbolsPerTextModel[textmodelId] = symbols; @@ -64,7 +64,7 @@ suite('Notebook Symbols', function () { } test('Cell without symbols cache', function () { - setSymbolsForTextModel([{ name: 'var', selectionRange: {} }]); + setSymbolsForTextModel([{ name: 'var', range: {} }]); const entryFactory = new NotebookOutlineEntryFactory(executionService); const entries = entryFactory.getOutlineEntries(createCellViewModel(), 0); @@ -73,7 +73,7 @@ suite('Notebook Symbols', 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 cell = createCellViewModel(); @@ -92,8 +92,8 @@ suite('Notebook Symbols', function () { test('Cell with nested symbols', async function () { setSymbolsForTextModel([ - { name: 'root1', selectionRange: {}, children: [{ name: 'nested1', selectionRange: {} }, { name: 'nested2', selectionRange: {} }] }, - { name: 'root2', selectionRange: {}, children: [{ name: 'nested1', selectionRange: {} }] } + { name: 'root1', range: {}, children: [{ name: 'nested1', range: {} }, { name: 'nested2', range: {} }] }, + { name: 'root2', range: {}, children: [{ name: 'nested1', range: {} }] } ]); const entryFactory = new NotebookOutlineEntryFactory(executionService); const cell = createCellViewModel(); @@ -115,8 +115,8 @@ suite('Notebook Symbols', function () { }); test('Multiple Cells with symbols', async function () { - setSymbolsForTextModel([{ name: 'var1', selectionRange: {} }], '$1'); - setSymbolsForTextModel([{ name: 'var2', selectionRange: {} }], '$2'); + setSymbolsForTextModel([{ name: 'var1', range: {} }], '$1'); + setSymbolsForTextModel([{ name: 'var2', range: {} }], '$2'); const entryFactory = new NotebookOutlineEntryFactory(executionService); const cell1 = createCellViewModel(1, '$1');