From 7087316ad665338bdd399b382c4008f53819a5e6 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Fri, 24 May 2024 10:58:07 -0700 Subject: [PATCH 1/3] all search scope functionality except cell decorations --- .../editor/contrib/find/browser/findWidget.ts | 2 +- .../browser/contrib/find/findFilters.ts | 49 ++++++++++++++++-- .../browser/contrib/find/findModel.ts | 14 +++--- .../browser/contrib/find/notebookFind.ts | 13 +++-- .../contrib/find/notebookFindReplaceWidget.ts | 50 ++++++++++++++++--- .../contrib/find/notebookFindWidget.ts | 3 ++ .../notebook/browser/notebook.contribution.ts | 4 +- .../notebook/browser/notebookEditorWidget.ts | 31 +++++++++++- .../view/renderers/backLayerWebView.ts | 2 +- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 6 ++- .../viewModel/notebookViewModelImpl.ts | 15 +++++- .../contrib/notebook/common/notebookCommon.ts | 4 +- .../contrib/search/browser/searchWidget.ts | 4 +- 14 files changed, 163 insertions(+), 36 deletions(-) diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index d3949c02e726c..f536964cb8664 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -47,10 +47,10 @@ import { createInstantHoverDelegate, getDefaultHoverDelegate } from 'vs/base/bro import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IHoverService } from 'vs/platform/hover/browser/hover'; -const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); const findCollapsedIcon = registerIcon('find-collapsed', Codicon.chevronRight, nls.localize('findCollapsedIcon', 'Icon to indicate that the editor find widget is collapsed.')); const findExpandedIcon = registerIcon('find-expanded', Codicon.chevronDown, nls.localize('findExpandedIcon', 'Icon to indicate that the editor find widget is expanded.')); +export const findSelectionIcon = registerIcon('find-selection', Codicon.selection, nls.localize('findSelectionIcon', 'Icon for \'Find in Selection\' in the editor find widget.')); export const findReplaceIcon = registerIcon('find-replace', Codicon.replace, nls.localize('findReplaceIcon', 'Icon for \'Replace\' in the editor find widget.')); export const findReplaceAllIcon = registerIcon('find-replace-all', Codicon.replaceAll, nls.localize('findReplaceAllIcon', 'Icon for \'Replace All\' in the editor find widget.')); export const findPreviousMatchIcon = registerIcon('find-previous-match', Codicon.arrowUp, nls.localize('findPreviousMatchIcon', 'Icon for \'Find Previous\' in the editor find widget.')); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts index a4bf2fd2d9fec..72598a6c6b2c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts @@ -5,17 +5,19 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -export interface INotebookFindFiltersChangeEvent { +export interface INotebookFindChangeEvent { markupInput?: boolean; markupPreview?: boolean; codeInput?: boolean; codeOutput?: boolean; + searchInRanges?: boolean; } export class NotebookFindFilters extends Disposable { - private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; private _markupInput: boolean = true; @@ -68,17 +70,46 @@ export class NotebookFindFilters extends Disposable { } } + private _searchInRanges: boolean = false; + + get searchInRanges(): boolean { + return this._searchInRanges; + } + + set searchInRanges(value: boolean) { + if (this._searchInRanges !== value) { + this._searchInRanges = value; + this._onDidChange.fire({ searchInRanges: value }); + } + } + + private _selectedRanges: ICellRange[] = []; + + get selectedRanges(): ICellRange[] { + return this._selectedRanges; + } + + set selectedRanges(value: ICellRange[]) { + if (this._selectedRanges !== value) { + this._selectedRanges = value; + this._onDidChange.fire({ searchInRanges: this._searchInRanges }); + } + } + private readonly _initialMarkupInput: boolean; private readonly _initialMarkupPreview: boolean; private readonly _initialCodeInput: boolean; private readonly _initialCodeOutput: boolean; - + private readonly _initialSearchInRanges: boolean; + private readonly _initialSelectedRanges: ICellRange[]; constructor( markupInput: boolean, markupPreview: boolean, codeInput: boolean, - codeOutput: boolean + codeOutput: boolean, + searchInRanges: boolean, + selectedRanges: ICellRange[] ) { super(); @@ -86,11 +117,15 @@ export class NotebookFindFilters extends Disposable { this._markupPreview = markupPreview; this._codeInput = codeInput; this._codeOutput = codeOutput; + this._searchInRanges = searchInRanges; + this._selectedRanges = selectedRanges; this._initialMarkupInput = markupInput; this._initialMarkupPreview = markupPreview; this._initialCodeInput = codeInput; this._initialCodeOutput = codeOutput; + this._initialSearchInRanges = searchInRanges; + this._initialSelectedRanges = selectedRanges; } isModified(): boolean { @@ -99,6 +134,8 @@ export class NotebookFindFilters extends Disposable { || this._markupPreview !== this._initialMarkupPreview || this._codeInput !== this._initialCodeInput || this._codeOutput !== this._initialCodeOutput + || this._searchInRanges !== this._initialSearchInRanges + || this._selectedRanges !== this._initialSelectedRanges ); } @@ -107,5 +144,7 @@ export class NotebookFindFilters extends Disposable { this._markupPreview = v.markupPreview; this._codeInput = v.codeInput; this._codeOutput = v.codeOutput; + this._searchInRanges = v.searchInRanges; + this._selectedRanges = v.selectedRanges; } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts index 6c0cca31b7516..de522b344a5eb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -115,11 +115,7 @@ export class FindModel extends Disposable { } private _updateCellStates(e: FindReplaceStateChangedEvent) { - if (!this._state.filters?.markupInput) { - return; - } - - if (!this._state.filters?.markupPreview) { + if (!this._state.filters?.markupInput || !this._state.filters?.markupPreview || !this._state.filters?.searchInRanges || !this._state.filters?.selectedRanges) { return; } @@ -139,7 +135,9 @@ export class FindModel extends Disposable { includeMarkupInput: true, includeCodeInput: false, includeMarkupPreview: false, - includeOutput: false + includeOutput: false, + searchInRanges: this._state.filters?.searchInRanges, + selectedRanges: this._state.filters?.selectedRanges }; const contentMatches = viewModel.find(this._state.searchString, options); @@ -486,7 +484,9 @@ export class FindModel extends Disposable { includeMarkupInput: this._state.filters?.markupInput ?? true, includeCodeInput: this._state.filters?.codeInput ?? true, includeMarkupPreview: !!this._state.filters?.markupPreview, - includeOutput: !!this._state.filters?.codeOutput + includeOutput: !!this._state.filters?.codeOutput, + searchInRanges: this._state.filters?.searchInRanges, + selectedRanges: this._state.filters?.selectedRanges }; ret = await this._notebookEditor.find(val, options, token); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts index f91d2ed8863b1..fd39a06a3f720 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts @@ -25,6 +25,7 @@ import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INTERACTIVE_WINDOW_IS_ACTIVE_EDITOR, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { INotebookCommandContext, NotebookMultiCellAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; registerNotebookContribution(NotebookFindContrib.id, NotebookFindContrib); @@ -55,7 +56,7 @@ registerAction2(class extends Action2 { } }); -registerAction2(class extends Action2 { +registerAction2(class extends NotebookMultiCellAction { constructor() { super({ id: 'notebook.find', @@ -68,7 +69,7 @@ registerAction2(class extends Action2 { }); } - async run(accessor: ServicesAccessor): Promise { + async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext): Promise { const editorService = accessor.get(IEditorService); const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); @@ -77,7 +78,12 @@ registerAction2(class extends Action2 { } const controller = editor.getContribution(NotebookFindContrib.id); - controller.show(); + + if (context.selectedCells.length > 1) { + controller.show(undefined, { searchInRanges: true, selectedRanges: editor.getSelections() }); + } else { + controller.show(undefined, { searchInRanges: false, selectedRanges: [] }); + } } }); @@ -200,4 +206,3 @@ StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, codeE return false; }); - diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index d616161b30996..e12014baf849d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -13,7 +13,7 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./notebookFindReplaceWidget'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; -import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, SimpleButton } from 'vs/editor/contrib/find/browser/findWidget'; +import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, findSelectionIcon, SimpleButton } from 'vs/editor/contrib/find/browser/findWidget'; import * as nls from 'vs/nls'; import { ContextScopedReplaceInput, registerAndCreateHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -37,17 +37,21 @@ import { isSafari } from 'vs/base/common/platform'; import { ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { IToggleStyles } from 'vs/base/browser/ui/toggle/toggle'; +import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Disposable } from 'vs/base/common/lifecycle'; import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; + const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "Previous Match"); // const NLS_FILTER_BTN_LABEL = nls.localize('label.findFilterButton', "Search in View"); const NLS_NEXT_MATCH_BTN_LABEL = nls.localize('label.nextMatchButton', "Next Match"); +const NLS_FIND_IN_CELL_SELECTION_BTN_LABEL = nls.localize('label.findInCellSelectionButton', "Find in Cell Selection"); const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close"); const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace"); const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace"); @@ -294,6 +298,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { protected readonly _matchesCount!: HTMLElement; private readonly prevBtn: SimpleButton; private readonly nextBtn: SimpleButton; + private readonly inSelectionToggle: Toggle; protected readonly _replaceInput!: ReplaceInput; private readonly _innerReplaceDomNode!: HTMLElement; @@ -326,14 +331,14 @@ export abstract class SimpleFindReplaceWidget extends Widget { ) { super(); - const findScope = this._configurationService.getValue<{ + const findFilters = this._configurationService.getValue<{ markupSource: boolean; markupPreview: boolean; codeSource: boolean; codeOutput: boolean; - }>(NotebookSetting.findScope) ?? { markupSource: true, markupPreview: true, codeSource: true, codeOutput: true }; + }>(NotebookSetting.findFilters) ?? { markupSource: true, markupPreview: true, codeSource: true, codeOutput: true }; - this._filters = new NotebookFindFilters(findScope.markupSource, findScope.markupPreview, findScope.codeSource, findScope.codeOutput); + this._filters = new NotebookFindFilters(findFilters.markupSource, findFilters.markupPreview, findFilters.codeSource, findFilters.codeOutput, false, []); this._state.change({ filters: this._filters }, false); this._filters.onDidChange(() => { @@ -410,6 +415,9 @@ export abstract class SimpleFindReplaceWidget extends Widget { }); this._register(this._findInput.inputBox.onDidChange(() => { + // ! this._filters.searchInRanges = this.inSelectionToggle.checked; + // this._filters.selectedRanges = this._notebookEditor.getSelections(); + this._state.change({ searchString: this._findInput.getValue() }, true); })); @@ -430,7 +438,6 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.setWholeWords(this._state.wholeWord); this._findInput.setCaseSensitive(this._state.matchCase); this._replaceInput.setPreserveCase(this._state.preserveCase); - this.findFirst(); })); this._matchesCount = document.createElement('div'); @@ -453,6 +460,25 @@ export abstract class SimpleFindReplaceWidget extends Widget { } }, hoverService)); + this.inSelectionToggle = this._register(new Toggle({ + icon: findSelectionIcon, + title: NLS_FIND_IN_CELL_SELECTION_BTN_LABEL, + isChecked: false, + inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground), + inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder), + inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground), + })); + + this.inSelectionToggle.onChange(() => { + const checked = this.inSelectionToggle.checked; + this._filters.searchInRanges = checked; + if (checked) { + this._filters.selectedRanges = this._notebookEditor.getSelections(); + } else { + this._filters.selectedRanges = []; + } + }); + const closeBtn = this._register(new SimpleButton({ label: NLS_CLOSE_BTN_LABEL, icon: widgetClose, @@ -465,6 +491,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._innerFindDomNode.appendChild(this._matchesCount); this._innerFindDomNode.appendChild(this.prevBtn.domNode); this._innerFindDomNode.appendChild(this.nextBtn.domNode); + this._innerFindDomNode.appendChild(this.inSelectionToggle.domNode); this._innerFindDomNode.appendChild(closeBtn.domNode); // _domNode wraps _innerDomNode, ensuring that @@ -599,7 +626,6 @@ export abstract class SimpleFindReplaceWidget extends Widget { protected abstract onInputChanged(): boolean; protected abstract find(previous: boolean): void; - protected abstract findFirst(): void; protected abstract replaceOne(): void; protected abstract replaceAll(): void; protected abstract onFocusTrackerFocus(): void; @@ -686,11 +712,19 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.focus(); } - public show(initialInput?: string, options?: { focus?: boolean }): void { + public show(initialInput?: string, options?: { focus?: boolean; searchInRanges?: boolean; selectedRanges?: ICellRange[] }): void { if (initialInput) { this._findInput.setValue(initialInput); } + if (options?.searchInRanges !== undefined) { + this._filters.searchInRanges = options.searchInRanges; + this.inSelectionToggle.checked = options.searchInRanges; + if (options.selectedRanges) { + this._filters.selectedRanges = options.selectedRanges; + } + } + this._isVisible = true; setTimeout(() => { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts index 7c92a6b057669..51a075fe24a61 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts @@ -26,6 +26,7 @@ import { FindModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/fi import { SimpleFindReplaceWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { CellEditState, ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; const FIND_HIDE_TRANSITION = 'find-hide-transition'; const FIND_SHOW_TRANSITION = 'find-show-transition'; @@ -39,6 +40,8 @@ export interface IShowNotebookFindWidgetOptions { matchIndex?: number; focus?: boolean; searchStringSeededFrom?: { cell: ICellViewModel; range: Range }; + searchInRanges?: boolean; + selectedRanges?: ICellRange[]; } export class NotebookFindContrib extends Disposable implements INotebookEditorContribution { diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 67c09c2746337..7cb72c7cf5460 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1026,8 +1026,8 @@ configurationRegistry.registerConfiguration({ type: 'boolean', default: true }, - [NotebookSetting.findScope]: { - markdownDescription: nls.localize('notebook.findScope', "Customize the Find Widget behavior for searching within notebook cells. When both markup source and markup preview are enabled, the Find Widget will search either the source code or preview based on the current state of the cell."), + [NotebookSetting.findFilters]: { + markdownDescription: nls.localize('notebook.findFilters', "Customize the Find Widget behavior for searching within notebook cells. When both markup source and markup preview are enabled, the Find Widget will search either the source code or preview based on the current state of the cell."), type: 'object', properties: { markupSource: { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 05156eebb7299..31eb5bfe62df9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2518,6 +2518,26 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } + // TODO: @Yoyokrazy -- selective webview warmup to improve perf (not in accessiblility mode, and not doing full nb search) + // let findCells: ICellViewModel[] = []; + // if (cellSelections) { + // const selectedRanges = cellSelections.map(range => this.viewModel?.validateRange(range)); + // for (const range of selectedRanges.filter(range => !!range)) { + // for (let i = range.start; i < range.end; i++) { + // findCells.push(this.viewModel.viewCells[i]); + // } + // } + // } else { + // findCells = this.viewModel.viewCells; + // } + + // const requests = []; + // for (let i = 0; i < findCells.length; i++) { + // if (findCells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(findCells[i].id)) { + // requests.push(this.createMarkupPreview(findCells[i])); + // } + // } + const cells = this.viewModel.viewCells; const requests = []; @@ -2537,7 +2557,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } } - return Promise.all(requests); } @@ -2576,7 +2595,15 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return []; } - const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID }); + const selectedRanges = options.selectedRanges?.map(range => this._notebookViewModel?.validateRange(range)).filter(range => !!range); + const findIds = []; + for (const range of selectedRanges ?? []) { + for (let i = range.start; i < range.end; i++) { + findIds.push(this._notebookViewModel.viewCells[i].id); + } + } + + const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: options.searchInRanges ? findIds : [] }); if (token.isCancellationRequested) { return []; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 702182063b361..458a2b77fc2c9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1772,7 +1772,7 @@ export class BackLayerWebView extends Themable { }); } - async find(query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string }): Promise { + async find(query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string; findIds: string[] }): Promise { if (query === '') { this._sendMessageToWebview({ type: 'findStop', diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index 5d0e13ad3b25b..a59abdada13bc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -399,7 +399,7 @@ export interface ITokenizedStylesChangedMessage { export interface IFindMessage { readonly type: 'find'; readonly query: string; - readonly options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string }; + readonly options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string; findIds: string[] }; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 6e4aed1f573eb..4ded337182e92 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1425,9 +1425,9 @@ async function webviewPreloads(ctx: PreloadContext) { return offset + getSelectionOffsetRelativeTo(parentElement, currentNode.parentNode); } - const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string }) => { + const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean; ownerID: string; findIds: string[] }) => { let find = true; - const matches: IFindMatch[] = []; + let matches: IFindMatch[] = []; const range = document.createRange(); range.selectNodeContents(window.document.getElementById('findStart')!); @@ -1553,6 +1553,8 @@ async function webviewPreloads(ctx: PreloadContext) { console.log(e); } + + matches = matches.filter(match => options.findIds.length ? options.findIds.includes(match.cellId) : true); _highlighter.addHighlights(matches, options.ownerID); window.document.getSelection()?.removeAllRanges(); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index b7fa0bc7ecfe6..fcfcbc8a6fa63 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -910,7 +910,20 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD //#region Find find(value: string, options: INotebookSearchOptions): CellFindMatchWithIndex[] { const matches: CellFindMatchWithIndex[] = []; - this._viewCells.forEach((cell, index) => { + let findCells: CellViewModel[] = []; + + const selectedRanges = options.selectedRanges?.map(range => this.validateRange(range)); + if (options.searchInRanges && selectedRanges) { + for (const range of selectedRanges.filter(range => !!range)) { + for (let i = range.start; i < range.end; i++) { + findCells.push(this._viewCells[i]); + } + } + } else { + findCells = this._viewCells; + } + + findCells.forEach((cell, index) => { const cellMatches = cell.startFind(value, options); if (cellMatches) { matches.push(new CellFindMatchModel( diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 7828091e5982c..c6df049d2826c 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -827,6 +827,8 @@ export interface INotebookSearchOptions { includeMarkupPreview?: boolean; includeCodeInput?: boolean; includeOutput?: boolean; + searchInRanges?: boolean; + selectedRanges?: ICellRange[]; } export interface INotebookExclusiveDocumentFilter { @@ -951,7 +953,7 @@ export const NotebookSetting = { outputFontSize: 'notebook.output.fontSize', outputFontFamilyDeprecated: 'notebook.outputFontFamily', outputFontFamily: 'notebook.output.fontFamily', - findScope: 'notebook.find.scope', + findFilters: 'notebook.find.filters', logging: 'notebook.logging', confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell', remoteSaving: 'notebook.experimental.remoteSave', diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 766990702f80d..4bac997542dca 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -204,7 +204,9 @@ export class SearchWidget extends Widget { notebookOptions.isInNotebookMarkdownInput, notebookOptions.isInNotebookMarkdownPreview, notebookOptions.isInNotebookCellInput, - notebookOptions.isInNotebookCellOutput + notebookOptions.isInNotebookCellOutput, + false, + [] )); this._register( From 41e9cb0cd6d8dc60441bbaca3b350d98dfce4f04 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Fri, 24 May 2024 11:05:09 -0700 Subject: [PATCH 2/3] fix filter icon showing fake useage --- .../contrib/notebook/browser/contrib/find/findFilters.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts index 72598a6c6b2c0..0901d295edd06 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts @@ -100,8 +100,6 @@ export class NotebookFindFilters extends Disposable { private readonly _initialMarkupPreview: boolean; private readonly _initialCodeInput: boolean; private readonly _initialCodeOutput: boolean; - private readonly _initialSearchInRanges: boolean; - private readonly _initialSelectedRanges: ICellRange[]; constructor( markupInput: boolean, @@ -124,18 +122,15 @@ export class NotebookFindFilters extends Disposable { this._initialMarkupPreview = markupPreview; this._initialCodeInput = codeInput; this._initialCodeOutput = codeOutput; - this._initialSearchInRanges = searchInRanges; - this._initialSelectedRanges = selectedRanges; } isModified(): boolean { + // do not include searchInRanges or selectedRanges in the check. This will incorrectly mark the filter icon as modified return ( this._markupInput !== this._initialMarkupInput || this._markupPreview !== this._initialMarkupPreview || this._codeInput !== this._initialCodeInput || this._codeOutput !== this._initialCodeOutput - || this._searchInRanges !== this._initialSearchInRanges - || this._selectedRanges !== this._initialSelectedRanges ); } From a6999cf1c65b6b7685d2fa3a57f78a33a716641b Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Fri, 24 May 2024 16:24:40 -0700 Subject: [PATCH 3/3] decorations + css tweaking + PR feedback --- .../contrib/find/notebookFindReplaceWidget.ts | 56 ++++++++++++++++--- .../notebook/browser/media/notebook.css | 6 ++ .../notebook/browser/notebook.contribution.ts | 5 ++ .../notebook/browser/notebookEditorWidget.ts | 42 +++++--------- .../view/cellParts/cellFocusIndicator.ts | 2 +- .../browser/view/cellParts/markupCell.ts | 3 + .../view/renderers/backLayerWebView.ts | 8 +++ .../viewModel/notebookViewModelImpl.ts | 10 ++-- .../contrib/notebook/common/notebookCommon.ts | 1 + 9 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index e12014baf849d..7b261f5dc0e39 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -35,7 +35,7 @@ import { filterIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIc import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { isSafari } from 'vs/base/common/platform'; import { ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; -import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookDeltaDecoration, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -298,7 +298,6 @@ export abstract class SimpleFindReplaceWidget extends Widget { protected readonly _matchesCount!: HTMLElement; private readonly prevBtn: SimpleButton; private readonly nextBtn: SimpleButton; - private readonly inSelectionToggle: Toggle; protected readonly _replaceInput!: ReplaceInput; private readonly _innerReplaceDomNode!: HTMLElement; @@ -319,6 +318,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { private _filters: NotebookFindFilters; + private readonly inSelectionToggle: Toggle; + private searchInSelectionEnabled: boolean; + private selectionDecorationIds: string[] = []; + constructor( @IContextViewService private readonly _contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService, @@ -415,9 +418,6 @@ export abstract class SimpleFindReplaceWidget extends Widget { }); this._register(this._findInput.inputBox.onDidChange(() => { - // ! this._filters.searchInRanges = this.inSelectionToggle.checked; - // this._filters.selectedRanges = this._notebookEditor.getSelections(); - this._state.change({ searchString: this._findInput.getValue() }, true); })); @@ -474,8 +474,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._filters.searchInRanges = checked; if (checked) { this._filters.selectedRanges = this._notebookEditor.getSelections(); + this.setCellSelectionDecorations(); } else { this._filters.selectedRanges = []; + this.clearCellSelectionDecorations(); } }); @@ -494,6 +496,22 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._innerFindDomNode.appendChild(this.inSelectionToggle.domNode); this._innerFindDomNode.appendChild(closeBtn.domNode); + this.searchInSelectionEnabled = this._configurationService.getValue(NotebookSetting.findScope); + this.inSelectionToggle.domNode.style.display = this.searchInSelectionEnabled ? 'inline' : 'none'; + + this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(NotebookSetting.findScope)) { + this.searchInSelectionEnabled = this._configurationService.getValue(NotebookSetting.findScope); + if (this.searchInSelectionEnabled) { + this.inSelectionToggle.domNode.style.display = 'inline'; + } else { + this.inSelectionToggle.domNode.style.display = 'none'; + this.inSelectionToggle.checked = false; + this.clearCellSelectionDecorations(); + } + } + }); + // _domNode wraps _innerDomNode, ensuring that this._domNode.appendChild(this._innerFindDomNode); @@ -673,6 +691,26 @@ export abstract class SimpleFindReplaceWidget extends Widget { this.updateButtons(this.foundMatch); } + private setCellSelectionDecorations() { + const cellHandles: number[] = []; + this._notebookEditor.getSelectionViewModels().forEach(viewModel => { + cellHandles.push(viewModel.handle); + }); + + const decorations: INotebookDeltaDecoration[] = []; + for (const handle of cellHandles) { + decorations.push({ + handle: handle, + options: { className: 'nb-multiCellHighlight', outputClassName: 'nb-multiCellHighlight' } + } satisfies INotebookDeltaDecoration); + } + this.selectionDecorationIds = this._notebookEditor.deltaCellDecorations([], decorations); + } + + private clearCellSelectionDecorations() { + this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + } + protected _updateMatchesCount(): void { } @@ -717,11 +755,12 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.setValue(initialInput); } - if (options?.searchInRanges !== undefined) { + if (this.searchInSelectionEnabled && options?.searchInRanges !== undefined) { this._filters.searchInRanges = options.searchInRanges; this.inSelectionToggle.checked = options.searchInRanges; - if (options.selectedRanges) { + if (options.searchInRanges && options.selectedRanges) { this._filters.selectedRanges = options.selectedRanges; + this.setCellSelectionDecorations(); } } @@ -772,6 +811,9 @@ export abstract class SimpleFindReplaceWidget extends Widget { public hide(): void { if (this._isVisible) { + this.inSelectionToggle.checked = false; + this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + this._domNode.classList.remove('visible-transition'); this._domNode.setAttribute('aria-hidden', 'true'); // Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 93989cbc04f31..d659b3cf277de 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -446,6 +446,12 @@ background-color: var(--vscode-notebook-symbolHighlightBackground) !important; } +/** Cell Search Range selection highlight */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-multiCellHighlight .cell-focus-indicator, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-multiCellHighlight { + background-color: var(--vscode-notebook-symbolHighlightBackground) !important; +} + /** Cell focused editor border */ .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-editor-focus .cell-editor-part:before { outline: solid 1px var(--vscode-notebook-focusedEditorBorder); diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 7cb72c7cf5460..0f9026c0dde80 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1055,6 +1055,11 @@ configurationRegistry.registerConfiguration({ }, tags: ['notebookLayout'] }, + [NotebookSetting.findScope]: { + markdownDescription: nls.localize('notebook.experimental.find.scope.enabled', "Enables the user to search within a selection of cells in the notebook. When enabled, the user will see a \"Find in Cell Selection\" icon in the notebook find widget."), + type: 'boolean', + default: false, + }, [NotebookSetting.remoteSaving]: { markdownDescription: nls.localize('notebook.remoteSaving', "Enables the incremental saving of notebooks between processes and across Remote connections. When enabled, only the changes to the notebook are sent to the extension host, improving performance for large notebooks and slow network connections."), type: 'boolean', diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 31eb5bfe62df9..2f83ab690ff5a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -80,7 +80,7 @@ import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/ import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions, OutputInnerContainerTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; +import { cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { INotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; @@ -835,6 +835,18 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } `); + styleSheets.push(` + .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom { + height: ${bottomToolbarGap + cellBottomMargin}px; + background-color: var(--vscode-notebook-symbolHighlightBackground) !important; + } + + .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom { + height: ${bottomToolbarGap + cellBottomMargin - 6}px; + background-color: var(--vscode-notebook-symbolHighlightBackground) !important; + } + `); + styleSheets.push(` .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview { @@ -2518,26 +2530,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } - // TODO: @Yoyokrazy -- selective webview warmup to improve perf (not in accessiblility mode, and not doing full nb search) - // let findCells: ICellViewModel[] = []; - // if (cellSelections) { - // const selectedRanges = cellSelections.map(range => this.viewModel?.validateRange(range)); - // for (const range of selectedRanges.filter(range => !!range)) { - // for (let i = range.start; i < range.end; i++) { - // findCells.push(this.viewModel.viewCells[i]); - // } - // } - // } else { - // findCells = this.viewModel.viewCells; - // } - - // const requests = []; - // for (let i = 0; i < findCells.length; i++) { - // if (findCells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(findCells[i].id)) { - // requests.push(this.createMarkupPreview(findCells[i])); - // } - // } - const cells = this.viewModel.viewCells; const requests = []; @@ -2596,12 +2588,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } const selectedRanges = options.selectedRanges?.map(range => this._notebookViewModel?.validateRange(range)).filter(range => !!range); - const findIds = []; - for (const range of selectedRanges ?? []) { - for (let i = range.start; i < range.end; i++) { - findIds.push(this._notebookViewModel.viewCells[i].id); - } - } + const selectedIndexes = cellRangesToIndexes(selectedRanges ?? []); + const findIds: string[] = selectedIndexes.map(index => this._notebookViewModel?.viewCells[index].id ?? ''); const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: options.searchInRanges ? findIds : [] }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts index 7549fdeaab827..189ef50f1665c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator.ts @@ -77,7 +77,7 @@ export class CellFocusIndicator extends CellContentPart { override updateInternalLayoutNow(element: ICellViewModel): void { if (element.cellKind === CellKind.Markup) { const indicatorPostion = this.notebookEditor.notebookOptions.computeIndicatorPosition(element.layoutInfo.totalHeight, (element as MarkupCellViewModel).layoutInfo.foldHintHeight, this.notebookEditor.textModel?.viewType); - this.bottom.domNode.style.transform = `translateY(${indicatorPostion.bottomIndicatorTop}px)`; + this.bottom.domNode.style.transform = `translateY(${indicatorPostion.bottomIndicatorTop + 6}px)`; this.left.setHeight(indicatorPostion.verticalIndicatorHeight); this.right.setHeight(indicatorPostion.verticalIndicatorHeight); this.codeFocusIndicator.setHeight(indicatorPostion.verticalIndicatorHeight - this.getIndicatorTopMargin() * 2 - element.layoutInfo.chatHeight); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts index d334bb1db3f20..b1a3e8b3e9374 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts @@ -228,12 +228,14 @@ export class MarkupCell extends Disposable { e.added.forEach(options => { if (options.className) { this.notebookEditor.deltaCellContainerClassNames(this.viewCell.id, [options.className], []); + this.templateData.rootContainer.classList.add(options.className); } }); e.removed.forEach(options => { if (options.className) { this.notebookEditor.deltaCellContainerClassNames(this.viewCell.id, [], [options.className]); + this.templateData.rootContainer.classList.remove(options.className); } }); })); @@ -241,6 +243,7 @@ export class MarkupCell extends Disposable { this.viewCell.getCellDecorations().forEach(options => { if (options.className) { this.notebookEditor.deltaCellContainerClassNames(this.viewCell.id, [options.className], []); + this.templateData.rootContainer.classList.add(options.className); } }); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 458a2b77fc2c9..9cc82bc223a00 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -401,6 +401,14 @@ export class BackLayerWebView extends Themable { background-color: var(--theme-notebook-symbol-highlight-background); } + #container .markup > div.nb-multiCellHighlight { + background-color: var(--theme-notebook-symbol-highlight-background); + } + + #container .nb-multiCellHighlight .output_container .output { + background-color: var(--theme-notebook-symbol-highlight-background); + } + #container .nb-chatGenerationHighlight .output_container .output { background-color: var(--vscode-notebook-selectedCellBackground); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index fcfcbc8a6fa63..8a0f837901156 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -912,13 +912,11 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD const matches: CellFindMatchWithIndex[] = []; let findCells: CellViewModel[] = []; - const selectedRanges = options.selectedRanges?.map(range => this.validateRange(range)); + const selectedRanges = options.selectedRanges?.map(range => this.validateRange(range)).filter(range => !!range); + if (options.searchInRanges && selectedRanges) { - for (const range of selectedRanges.filter(range => !!range)) { - for (let i = range.start; i < range.end; i++) { - findCells.push(this._viewCells[i]); - } - } + const selectedIndexes = cellRangesToIndexes(selectedRanges); + findCells = selectedIndexes.map(index => this._viewCells[index]); } else { findCells = this._viewCells; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index c6df049d2826c..33c85b2742097 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -954,6 +954,7 @@ export const NotebookSetting = { outputFontFamilyDeprecated: 'notebook.outputFontFamily', outputFontFamily: 'notebook.output.fontFamily', findFilters: 'notebook.find.filters', + findScope: 'notebook.experimental.find.scope.enabled', logging: 'notebook.logging', confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell', remoteSaving: 'notebook.experimental.remoteSave',