Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,16 @@ class NotebookOutlineRenderer implements ITreeRenderer<OutlineEntry, FuzzyScore,
};

const isCodeCell = node.element.cell.cellKind === CellKind.Code;
if (isCodeCell && this._themeService.getFileIconTheme().hasFileIcons && !node.element.isExecuting) {
if (node.element.level >= 8) { // symbol
template.iconClass.className = 'element-icon ' + ThemeIcon.asClassNameArray(node.element.icon).join(' ');
} else if (isCodeCell && this._themeService.getFileIconTheme().hasFileIcons && !node.element.isExecuting) {
template.iconClass.className = '';
extraClasses.push(...getIconClassesForLanguageId(node.element.cell.language ?? ''));
} else {
template.iconClass.className = 'element-icon ' + ThemeIcon.asClassNameArray(node.element.icon).join(' ');
}

template.iconLabel.setLabel(node.element.label, undefined, options);
template.iconLabel.setLabel(' ' + node.element.label, undefined, options);

const { markerInfo } = node.element;

Expand Down Expand Up @@ -377,7 +379,11 @@ export class NotebookCellOutline implements IOutline<OutlineEntry> {
}));

installSelectionListener();
const treeDataSource: IDataSource<this, OutlineEntry> = { getChildren: parent => parent instanceof NotebookCellOutline ? (this._outlineProvider?.entries ?? []) : parent.children };
const treeDataSource: IDataSource<this, OutlineEntry> = {
getChildren: parent => {
return this.getChildren(parent, _configurationService);
}
};
const delegate = new NotebookOutlineVirtualDelegate();
const renderers = [instantiationService.createInstance(NotebookOutlineRenderer, this._editor.getControl(), _target)];
const comparator = new NotebookComparator();
Expand Down Expand Up @@ -412,6 +418,24 @@ export class NotebookCellOutline implements IOutline<OutlineEntry> {
};
}

*getChildren(parent: OutlineEntry | NotebookCellOutline, configurationService: IConfigurationService): Iterable<OutlineEntry> {
for (const entry of parent instanceof NotebookCellOutline ? (this._outlineProvider?.entries ?? []) : parent.children) {
const showOutlineCodeCells = configurationService.getValue<boolean>('notebook.outline.showCodeCells');
const showMarkdownHeadersOnly = configurationService.getValue<boolean>('notebook.outline.showMarkdownHeadersOnly');

if (entry.cell.cellKind === CellKind.Markup) {
if (!showMarkdownHeadersOnly) {
yield entry;
} else if (entry.level < 7) {
yield entry;
}
}
if (showOutlineCodeCells && entry.cell.cellKind === CellKind.Code) {
yield entry;
}
}
}

async setFullSymbols(cancelToken: CancellationToken) {
await this._outlineProvider?.setFullSymbols(cancelToken);
}
Expand Down Expand Up @@ -524,10 +548,14 @@ export class NotebookOutlineCreator implements IOutlineCreator<NotebookEditor, O
async createOutline(editor: NotebookEditor, target: OutlineTarget, cancelToken: CancellationToken): Promise<IOutline<OutlineEntry> | undefined> {
const outline = this._instantiationService.createInstance(NotebookCellOutline, editor, target);

const showAllSymbols = this._configurationService.getValue<boolean>(NotebookSetting.gotoSymbolsAllSymbols);
if (target === OutlineTarget.QuickPick && showAllSymbols) {
const showAllGotoSymbols = this._configurationService.getValue<boolean>(NotebookSetting.gotoSymbolsAllSymbols);
const showAllOutlineSymbols = this._configurationService.getValue<boolean>('notebook.outline.showCodeCellSymbols');
if (target === OutlineTarget.QuickPick && showAllGotoSymbols) {
await outline.setFullSymbols(cancelToken);
} else if (target === OutlineTarget.OutlinePane && showAllOutlineSymbols) {
await outline.setFullSymbols(cancelToken);
}

return outline;
}
}
Expand All @@ -547,25 +575,30 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
order: 100,
type: 'object',
'properties': {
'notebook.outline.showMarkdownHeadersOnly': {
type: 'boolean',
default: true,
markdownDescription: localize('outline.showMarkdownHeadersOnly', "When enabled, notebook outline will show only markdown cells containing a header.")
},
'notebook.outline.showCodeCells': {
type: 'boolean',
default: false,
markdownDescription: localize('outline.showCodeCells', "When enabled notebook outline shows code cells.")
markdownDescription: localize('outline.showCodeCells', "When enabled, notebook outline shows code cells.")
},
'notebook.outline.showNonHeaderMarkdownCells': {
'notebook.outline.showCodeCellSymbols': {
type: 'boolean',
default: false,
markdownDescription: localize('outline.showNonHeaderMarkdownCells', "When enabled, notebook outline will show non-header markdown cell entries. When disabled, only markdown cells containing a header are shown.")
default: true,
markdownDescription: localize('outline.showCodeCellSymbols', "When enabled, notebook outline shows code cell symbols. Relies on `notebook.outline.showCodeCells` being enabled.")
},
'notebook.breadcrumbs.showCodeCells': {
type: 'boolean',
default: true,
markdownDescription: localize('breadcrumbs.showCodeCells', "When enabled notebook breadcrumbs contain code cells.")
markdownDescription: localize('breadcrumbs.showCodeCells', "When enabled, notebook breadcrumbs contain code cells.")
},
[NotebookSetting.gotoSymbolsAllSymbols]: {
type: 'boolean',
default: true,
markdownDescription: localize('notebook.gotoSymbols.showAllSymbols', "When enabled the Go to Symbol Quick Pick will display full code symbols from the notebook, as well as Markdown headers.")
markdownDescription: localize('notebook.gotoSymbols.showAllSymbols', "When enabled, the Go to Symbol Quick Pick will display full code symbols from the notebook, as well as Markdown headers.")
},
}
});
Expand All @@ -579,18 +612,43 @@ MenuRegistry.appendMenuItem(MenuId.ViewTitle, {
when: ContextKeyExpr.and(ContextKeyExpr.equals('view', IOutlinePane.Id), NOTEBOOK_IS_ACTIVE_EDITOR),
});

registerAction2(class ToggleShowMarkdownHeadersOnly extends Action2 {
constructor() {
super({
id: 'notebook.outline.toggleShowMarkdownHeadersOnly',
title: localize('toggleShowMarkdownHeadersOnly', "Markdown Headers Only"),
f1: false,
toggled: {
condition: ContextKeyExpr.equals('config.notebook.outline.showMarkdownHeadersOnly', true)
},
menu: {
id: MenuId.NotebookOutlineFilter,
group: '0_markdown_cells',
}
});
}

run(accessor: ServicesAccessor, ...args: any[]) {
const configurationService = accessor.get(IConfigurationService);
const showMarkdownHeadersOnly = configurationService.getValue<boolean>('notebook.outline.showMarkdownHeadersOnly');
configurationService.updateValue('notebook.outline.showMarkdownHeadersOnly', !showMarkdownHeadersOnly);
}
});


registerAction2(class ToggleCodeCellEntries extends Action2 {
constructor() {
super({
id: 'notebook.outline.toggleCodeCells',
title: localize('toggleCodeCells', "Toggle Code Cells"),
title: localize('toggleCodeCells', "Code Cells"),
f1: false,
toggled: {
condition: ContextKeyExpr.equals('config.notebook.outline.showCodeCells', true)
},
menu: {
id: MenuId.NotebookOutlineFilter,
group: 'filter',
order: 1,
group: '1_code_cells',
}
});
}
Expand All @@ -602,25 +660,26 @@ registerAction2(class ToggleCodeCellEntries extends Action2 {
}
});

registerAction2(class ToggleNonHeaderMarkdownCells extends Action2 {
registerAction2(class ToggleCodeCellSymbolEntries extends Action2 {
constructor() {
super({
id: 'notebook.outline.toggleNonHeaderMarkdownCells',
title: localize('toggleNonHeaderMarkdownCells', "Toggle Non-Header Markdown Cells"),
id: 'notebook.outline.toggleCodeCellSymbols',
title: localize('toggleCodeCellSymbols', "Code Cell Symbols"),
f1: false,
toggled: {
condition: ContextKeyExpr.equals('config.notebook.outline.showNonHeaderMarkdownCells', true)
condition: ContextKeyExpr.equals('config.notebook.outline.showCodeCellSymbols', true)
},
menu: {
id: MenuId.NotebookOutlineFilter,
group: 'filter',
order: 2,
group: '1_code_cells',
}
});
}

run(accessor: ServicesAccessor, ...args: any[]) {
const configurationService = accessor.get(IConfigurationService);
const showNonHeaderMarkdownCells = configurationService.getValue<boolean>('notebook.outline.showNonHeaderMarkdownCells');
configurationService.updateValue('notebook.outline.showNonHeaderMarkdownCells', !showNonHeaderMarkdownCells);
const showCodeCellSymbols = configurationService.getValue<boolean>('notebook.outline.showCodeCellSymbols');
configurationService.updateValue('notebook.outline.showCodeCellSymbols', !showCodeCellSymbols);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ export class NotebookOutlineEntryFactory {
}

if (!hasHeader) {
const exeState = !isMarkdown && this.executionStateService.getCellExecution(cell.uri);
let preview = content.trim();
if (preview.length === 0) {
// empty or just whitespace
preview = localize('empty', "empty cell");
}
entries.push(new OutlineEntry(index++, 7, cell, preview, !!exeState, exeState ? exeState.isPaused : false));

if (!isMarkdown && cell.model.textModel) {
const cachedEntries = this.cellOutlineEntryCache[cell.model.textModel.id];

Expand All @@ -76,17 +84,6 @@ export class NotebookOutlineEntryFactory {
});
}
}

const exeState = !isMarkdown && this.executionStateService.getCellExecution(cell.uri);
if (entries.length === 0) {
let preview = content.trim();
if (preview.length === 0) {
// empty or just whitespace
preview = localize('empty', "empty cell");
}

entries.push(new OutlineEntry(index++, 7, cell, preview, !!exeState, exeState ? exeState.isPaused : false));
}
}

return entries;
Expand All @@ -95,7 +92,7 @@ export class NotebookOutlineEntryFactory {
public async cacheSymbols(cell: ICellViewModel, outlineModelService: IOutlineModelService, cancelToken: CancellationToken) {
const textModel = await cell.resolveTextModel();
const outlineModel = await outlineModelService.getOrCreate(textModel, cancelToken);
const entries = createOutlineEntries(outlineModel.getTopLevelSymbols(), 7);
const entries = createOutlineEntries(outlineModel.getTopLevelSymbols(), 8);
this.cellOutlineEntryCache[textModel.id] = entries;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IActiveNotebookEditor, INotebookEditor, INotebookViewCellsUpdateEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { IActiveNotebookEditor, ICellViewModel, INotebookEditor, INotebookViewCellsUpdateEvent } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
import { OutlineChangeEvent, OutlineConfigKeys, OutlineTarget } from 'vs/workbench/services/outline/browser/outline';
Expand Down Expand Up @@ -70,7 +70,11 @@ export class NotebookCellOutlineProvider {
);

this._dispoables.add(_configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('notebook.outline.showCodeCells') || e.affectsConfiguration('notebook.outline.showNonHeaderMarkdownCells')) {
if (e.affectsConfiguration('notebook.outline.showMarkdownHeadersOnly') ||
e.affectsConfiguration('notebook.outline.showCodeCells') ||
e.affectsConfiguration('notebook.outline.showCodeCellSymbols') ||
e.affectsConfiguration('notebook.breadcrumbs.showCodeCells')
) {
this._recomputeState();
}
}));
Expand Down Expand Up @@ -136,15 +140,16 @@ export class NotebookCellOutlineProvider {
}

let includeCodeCells = true;
if (this._target === OutlineTarget.OutlinePane) {
includeCodeCells = this._configurationService.getValue<boolean>('notebook.outline.showCodeCells');
} else if (this._target === OutlineTarget.Breadcrumbs) {
if (this._target === OutlineTarget.Breadcrumbs) {
includeCodeCells = this._configurationService.getValue<boolean>('notebook.breadcrumbs.showCodeCells');
}

const showNonHeaderMarkdownCells = this._configurationService.getValue<boolean>('notebook.outline.showNonHeaderMarkdownCells');

const notebookCells = notebookEditorWidget.getViewModel().viewCells.filter((cell) => cell.cellKind === CellKind.Markup || includeCodeCells);
let notebookCells: ICellViewModel[];
if (this._target === OutlineTarget.Breadcrumbs) {
notebookCells = notebookEditorWidget.getViewModel().viewCells.filter((cell) => cell.cellKind === CellKind.Markup || includeCodeCells);
} else {
notebookCells = notebookEditorWidget.getViewModel().viewCells;
}

const entries: OutlineEntry[] = [];
for (const cell of notebookCells) {
Expand All @@ -164,11 +169,6 @@ export class NotebookCellOutlineProvider {
for (let i = 1; i < entries.length; i++) {
const entry = entries[i];

if (!showNonHeaderMarkdownCells && entry.cell.cellKind === CellKind.Markup && entry.level === 7) {
// skip plain text markdown cells
continue;
}

while (true) {
const len = parentStack.length;
if (len === 0) {
Expand Down Expand Up @@ -269,8 +269,6 @@ export class NotebookCellOutlineProvider {
}
}



get isEmpty(): boolean {
return this._entries.length === 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ export class NotebookStickyScroll extends Disposable {

static computeStickyHeight(entry: OutlineEntry) {
let height = 0;
if (entry.cell.cellKind === CellKind.Markup && entry.level !== 7) {
if (entry.cell.cellKind === CellKind.Markup && entry.level < 7) {
height += 22;
}
while (entry.parent) {
Expand All @@ -297,8 +297,8 @@ export class NotebookStickyScroll extends Disposable {

const elementsToRender = [];
while (currentEntry) {
if (currentEntry.level === 7) {
// level 7 represents a non-header entry, which we don't want to render
if (currentEntry.level >= 7) {
// level 7+ represents a non-header entry, which we don't want to render
currentEntry = currentEntry.parent;
continue;
}
Expand Down Expand Up @@ -382,7 +382,7 @@ export function computeContent(notebookEditor: INotebookEditor, notebookCellList
if (visibleRange.start === 0) {
const firstCell = notebookEditor.cellAt(0);
const firstCellEntry = NotebookStickyScroll.getVisibleOutlineEntry(0, notebookOutlineEntries);
if (firstCell && firstCellEntry && firstCell.cellKind === CellKind.Markup && firstCellEntry.level !== 7) {
if (firstCell && firstCellEntry && firstCell.cellKind === CellKind.Markup && firstCellEntry.level < 7) {
if (notebookEditor.scrollTop > 22) {
const newMap = NotebookStickyScroll.checkCollapsedStickyLines(firstCellEntry, 100, notebookEditor);
return newMap;
Expand Down Expand Up @@ -418,7 +418,7 @@ export function computeContent(notebookEditor: INotebookEditor, notebookCellList
}

// check next cell, if markdown with non level 7 entry, that means this is the end of the section (new header) ---------------------
if (nextCell.cellKind === CellKind.Markup && nextCellEntry.level !== 7) {
if (nextCell.cellKind === CellKind.Markup && nextCellEntry.level < 7) {
const sectionBottom = notebookCellList.getCellViewScrollTop(nextCell);
const currentSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(cellEntry);
const nextSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(nextCellEntry);
Expand Down