From 9d41b1cefcbacb7d3fa4b253ce56ac0cf01257c0 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 24 Jun 2024 02:30:46 -0700 Subject: [PATCH] Support Textual Selections in notebook find widget (#216840) * support textual selections in notebook find widget * add textual selection decorations * union type * fix passed ranges, clear find scope text range decs * organize imports pass * remove auto find explorations --- .../browser/contrib/find/findFilters.ts | 43 ++--- .../browser/contrib/find/findModel.ts | 24 ++- .../contrib/find/media/notebookFind.css | 4 + .../browser/contrib/find/notebookFind.ts | 13 +- .../contrib/find/notebookFindReplaceWidget.ts | 158 +++++++++++------- .../contrib/find/notebookFindWidget.ts | 5 +- .../notebook/browser/notebook.contribution.ts | 5 - .../notebook/browser/notebookBrowser.ts | 4 +- .../notebook/browser/notebookEditorWidget.ts | 16 +- .../browser/viewModel/baseCellViewModel.ts | 17 +- .../browser/viewModel/codeCellViewModel.ts | 18 +- .../browser/viewModel/markupCellViewModel.ts | 4 +- .../viewModel/notebookViewModelImpl.ts | 19 +-- .../contrib/notebook/common/notebookCommon.ts | 33 ++-- .../test/browser/testNotebookEditor.ts | 4 +- .../contrib/search/browser/searchWidget.ts | 6 +- 16 files changed, 201 insertions(+), 172 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 0901d295edd..cf98120913c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; +import { INotebookFindScope, NotebookFindScopeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export interface INotebookFindChangeEvent { markupInput?: boolean; markupPreview?: boolean; codeInput?: boolean; codeOutput?: boolean; - searchInRanges?: boolean; + findScope?: boolean; } export class NotebookFindFilters extends Disposable { @@ -70,31 +70,19 @@ export class NotebookFindFilters extends Disposable { } } - private _searchInRanges: boolean = false; + private _findScope: INotebookFindScope = { findScopeType: NotebookFindScopeType.None }; - get searchInRanges(): boolean { - return this._searchInRanges; + get findScope(): INotebookFindScope { + return this._findScope; } - set searchInRanges(value: boolean) { - if (this._searchInRanges !== value) { - this._searchInRanges = value; - this._onDidChange.fire({ searchInRanges: value }); + set findScope(value: INotebookFindScope) { + if (this._findScope !== value) { + this._findScope = value; + this._onDidChange.fire({ findScope: true }); } } - 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; @@ -106,8 +94,7 @@ export class NotebookFindFilters extends Disposable { markupPreview: boolean, codeInput: boolean, codeOutput: boolean, - searchInRanges: boolean, - selectedRanges: ICellRange[] + findScope: INotebookFindScope ) { super(); @@ -115,8 +102,7 @@ export class NotebookFindFilters extends Disposable { this._markupPreview = markupPreview; this._codeInput = codeInput; this._codeOutput = codeOutput; - this._searchInRanges = searchInRanges; - this._selectedRanges = selectedRanges; + this._findScope = findScope; this._initialMarkupInput = markupInput; this._initialMarkupPreview = markupPreview; @@ -125,7 +111,7 @@ export class NotebookFindFilters extends Disposable { } isModified(): boolean { - // do not include searchInRanges or selectedRanges in the check. This will incorrectly mark the filter icon as modified + // do not include findInSelection or either selectedRanges in the check. This will incorrectly mark the filter icon as modified return ( this._markupInput !== this._initialMarkupInput || this._markupPreview !== this._initialMarkupPreview @@ -139,7 +125,6 @@ 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; + this._findScope = v.findScope; } } 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 de522b344a5..0ca2d422f53 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; -import { INotebookEditor, CellEditState, CellFindMatchWithIndex, CellWebviewFindMatch, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch } from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; -import { CellKind, INotebookSearchOptions, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { FindMatchDecorationModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel'; +import { CellEditState, CellFindMatchWithIndex, CellWebviewFindMatch, ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { CellKind, INotebookFindOptions, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class CellFindMatchModel implements CellFindMatchWithIndex { readonly cell: ICellViewModel; @@ -115,7 +115,7 @@ export class FindModel extends Disposable { } private _updateCellStates(e: FindReplaceStateChangedEvent) { - if (!this._state.filters?.markupInput || !this._state.filters?.markupPreview || !this._state.filters?.searchInRanges || !this._state.filters?.selectedRanges) { + if (!this._state.filters?.markupInput || !this._state.filters?.markupPreview || !this._state.filters?.findScope) { return; } @@ -127,7 +127,7 @@ export class FindModel extends Disposable { } // search markup sources first to decide if a markup cell should be in editing mode const wordSeparators = this._configurationService.inspect('editor.wordSeparators').value; - const options: INotebookSearchOptions = { + const options: INotebookFindOptions = { regex: this._state.isRegex, wholeWord: this._state.wholeWord, caseSensitive: this._state.matchCase, @@ -136,8 +136,7 @@ export class FindModel extends Disposable { includeCodeInput: false, includeMarkupPreview: false, includeOutput: false, - searchInRanges: this._state.filters?.searchInRanges, - selectedRanges: this._state.filters?.selectedRanges + findScope: this._state.filters?.findScope, }; const contentMatches = viewModel.find(this._state.searchString, options); @@ -476,7 +475,7 @@ export class FindModel extends Disposable { const val = this._state.searchString; const wordSeparators = this._configurationService.inspect('editor.wordSeparators').value; - const options: INotebookSearchOptions = { + const options: INotebookFindOptions = { regex: this._state.isRegex, wholeWord: this._state.wholeWord, caseSensitive: this._state.matchCase, @@ -485,8 +484,7 @@ export class FindModel extends Disposable { includeCodeInput: this._state.filters?.codeInput ?? true, includeMarkupPreview: !!this._state.filters?.markupPreview, includeOutput: !!this._state.filters?.codeOutput, - searchInRanges: this._state.filters?.searchInRanges, - selectedRanges: this._state.filters?.selectedRanges + findScope: this._state.filters?.findScope, }; ret = await this._notebookEditor.find(val, options, token); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css index fe115e23081..d61cc797497 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css @@ -19,3 +19,7 @@ padding: 0 2px; box-sizing: border-box; } + +.monaco-workbench .nb-findScope { + background-color: var(--vscode-editor-findRangeHighlightBackground); +} 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 fd39a06a3f7..fb3d93d9d5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts @@ -10,6 +10,7 @@ import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; import { FindStartFocusAction, getSelectionSearchString, IFindStartOptions, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/browser/findController'; @@ -19,13 +20,12 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IShowNotebookFindWidgetOptions, NotebookFindContrib } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget'; +import { INotebookCommandContext, NotebookMultiCellAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; -import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, NotebookFindScopeType } 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); @@ -78,12 +78,7 @@ registerAction2(class extends NotebookMultiCellAction { } const controller = editor.getContribution(NotebookFindContrib.id); - - if (context.selectedCells.length > 1) { - controller.show(undefined, { searchInRanges: true, selectedRanges: editor.getSelections() }); - } else { - controller.show(undefined, { searchInRanges: false, selectedRanges: [] }); - } + controller.show(undefined, { findScope: { findScopeType: NotebookFindScopeType.None } }); } }); 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 c8cc9eb2187..8c4b59a688e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -3,46 +3,48 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import 'vs/css!./notebookFindReplaceWidget'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; +import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Widget } from 'vs/base/browser/ui/widget'; +import { Action, ActionRunner, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; import { KeyCode } from 'vs/base/common/keyCodes'; -import 'vs/css!./notebookFindReplaceWidget'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isSafari } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { Range } from 'vs/editor/common/core/range'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; 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 { parseReplaceString, ReplacePattern } from 'vs/editor/contrib/find/browser/replacePattern'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ContextScopedReplaceInput, registerAndCreateHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { parseReplaceString, ReplacePattern } from 'vs/editor/contrib/find/browser/replacePattern'; -import { Codicon } from 'vs/base/common/codicons'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Action, ActionRunner, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IMenu } from 'vs/platform/actions/common/actions'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { filterIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; 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 { 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'; -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 { IShowNotebookFindWidgetOptions } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget'; +import { ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, INotebookDeltaDecoration, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookFindScopeType, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -318,8 +320,8 @@ export abstract class SimpleFindReplaceWidget extends Widget { private _filters: NotebookFindFilters; private readonly inSelectionToggle: Toggle; - private searchInSelectionEnabled: boolean; - private selectionDecorationIds: string[] = []; + private cellSelectionDecorationIds: string[] = []; + private textSelectionDecorationIds: ICellModelDecorations[] = []; constructor( @IContextViewService private readonly _contextViewService: IContextViewService, @@ -340,7 +342,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { codeOutput: boolean; }>(NotebookSetting.findFilters) ?? { markupSource: true, markupPreview: true, codeSource: true, codeOutput: true }; - this._filters = new NotebookFindFilters(findFilters.markupSource, findFilters.markupPreview, findFilters.codeSource, findFilters.codeOutput, false, []); + this._filters = new NotebookFindFilters(findFilters.markupSource, findFilters.markupPreview, findFilters.codeSource, findFilters.codeOutput, { findScopeType: NotebookFindScopeType.None }); this._state.change({ filters: this._filters }, false); this._filters.onDidChange(() => { @@ -468,16 +470,48 @@ export abstract class SimpleFindReplaceWidget extends Widget { inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder), inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground), })); + this.inSelectionToggle.domNode.style.display = 'inline'; this.inSelectionToggle.onChange(() => { const checked = this.inSelectionToggle.checked; - this._filters.searchInRanges = checked; if (checked) { - this._filters.selectedRanges = this._notebookEditor.getSelections(); - this.setCellSelectionDecorations(); + // selection logic: + // 1. if there are multiple cells, do that. + // 2. if there is only one cell, do the following: + // - if there is a multi-line range highlighted, textual in selection + // - if there is no range, cell in selection for that cell + + const cellSelection: ICellRange[] = this._notebookEditor.getSelections(); + const textSelection: Range[] = this._notebookEditor.getSelectionViewModels()[0].getSelections(); + + if (cellSelection.length > 1 || cellSelection.some(range => range.end - range.start > 1)) { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Cells, + selectedCellRanges: cellSelection + }; + this.setCellSelectionDecorations(); + + } else if (textSelection.length > 1 || textSelection.some(range => range.endLineNumber - range.startLineNumber >= 1)) { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Text, + selectedCellRanges: cellSelection, + selectedTextRanges: textSelection + }; + this.setTextSelectionDecorations(textSelection, this._notebookEditor.getSelectionViewModels()[0]); + + } else { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Cells, + selectedCellRanges: cellSelection + }; + this.setCellSelectionDecorations(); + } } else { - this._filters.selectedRanges = []; + this._filters.findScope = { + findScopeType: NotebookFindScopeType.None + }; this.clearCellSelectionDecorations(); + this.clearTextSelectionDecorations(); } }); @@ -496,22 +530,6 @@ 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); @@ -704,11 +722,37 @@ export abstract class SimpleFindReplaceWidget extends Widget { options: { className: 'nb-multiCellHighlight', outputClassName: 'nb-multiCellHighlight' } } satisfies INotebookDeltaDecoration); } - this.selectionDecorationIds = this._notebookEditor.deltaCellDecorations([], decorations); + this.cellSelectionDecorationIds = this._notebookEditor.deltaCellDecorations([], decorations); } private clearCellSelectionDecorations() { - this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + this._notebookEditor.deltaCellDecorations(this.cellSelectionDecorationIds, []); + } + + private setTextSelectionDecorations(textRanges: Range[], cell: ICellViewModel) { + this._notebookEditor.changeModelDecorations(changeAccessor => { + const decorations: ICellModelDeltaDecorations[] = []; + for (const range of textRanges) { + decorations.push({ + ownerId: cell.handle, + decorations: [{ + range: range, + options: { + description: 'text search range for notebook search scope', + isWholeLine: true, + className: 'nb-findScope' + } + }] + }); + } + this.textSelectionDecorationIds = changeAccessor.deltaDecorations([], decorations); + }); + } + + private clearTextSelectionDecorations() { + this._notebookEditor.changeModelDecorations(changeAccessor => { + changeAccessor.deltaDecorations(this.textSelectionDecorationIds, []); + }); } protected _updateMatchesCount(): void { @@ -748,20 +792,11 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.focus(); } - public show(initialInput?: string, options?: { focus?: boolean; searchInRanges?: boolean; selectedRanges?: ICellRange[] }): void { + public show(initialInput?: string, options?: IShowNotebookFindWidgetOptions): void { if (initialInput) { this._findInput.setValue(initialInput); } - if (this.searchInSelectionEnabled && options?.searchInRanges !== undefined) { - this._filters.searchInRanges = options.searchInRanges; - this.inSelectionToggle.checked = options.searchInRanges; - if (options.searchInRanges && options.selectedRanges) { - this._filters.selectedRanges = options.selectedRanges; - this.setCellSelectionDecorations(); - } - } - this._isVisible = true; setTimeout(() => { @@ -810,7 +845,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { public hide(): void { if (this._isVisible) { this.inSelectionToggle.checked = false; - this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + this._notebookEditor.deltaCellDecorations(this.cellSelectionDecorationIds, []); + this._notebookEditor.changeModelDecorations(changeAccessor => { + changeAccessor.deltaDecorations(this.textSelectionDecorationIds, []); + }); this._domNode.classList.remove('visible-transition'); this._domNode.setAttribute('aria-hidden', 'true'); 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 5e67785a00b..15954bc2b81 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts @@ -25,8 +25,8 @@ import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contr import { FindModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; import { SimpleFindReplaceWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { CellEditState, ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookFindScope } from 'vs/workbench/contrib/notebook/common/notebookCommon'; 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'; @@ -40,8 +40,7 @@ export interface IShowNotebookFindWidgetOptions { matchIndex?: number; focus?: boolean; searchStringSeededFrom?: { cell: ICellViewModel; range: Range }; - searchInRanges?: boolean; - selectedRanges?: ICellRange[]; + findScope?: INotebookFindScope; } 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 423c6165f73..4e174318c11 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1055,11 +1055,6 @@ 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/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index dce4811e235..18e44f2a663 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -22,7 +22,7 @@ import { IEditorPane, IEditorPaneWithSelection } from 'vs/workbench/common/edito import { CellViewModelStateChangeEvent, NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID, REPL_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookFindOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID, REPL_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; @@ -738,7 +738,7 @@ export interface INotebookEditor { getCellIndex(cell: ICellViewModel): number | undefined; getNextVisibleCellIndex(index: number): number | undefined; getPreviousVisibleCellIndex(index: number): number | undefined; - find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup?: boolean, shouldGetSearchPreviewInfo?: boolean, ownerID?: string): Promise; + find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup?: boolean, shouldGetSearchPreviewInfo?: boolean, ownerID?: string): Promise; findHighlightCurrent(matchIndex: number, ownerID?: string): Promise; findUnHighlightCurrent(matchIndex: number, ownerID?: string): Promise; findStop(ownerID?: string): void; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ea03755d75d..1e59c4cc138 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -74,7 +74,7 @@ import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser import { NotebookOverviewRuler } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler'; import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellEditType, CellKind, INotebookSearchOptions, RENDERER_NOT_AVAILABLE, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -2567,7 +2567,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return Promise.all(requests); } - async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise { + async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise { if (!this._notebookViewModel) { return []; } @@ -2578,7 +2578,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const findMatches = this._notebookViewModel.find(query, options).filter(match => match.length > 0); - if (!options.includeMarkupPreview && !options.includeOutput) { + if ((!options.includeMarkupPreview && !options.includeOutput) || options.findScope?.findScopeType === NotebookFindScopeType.Text) { this._webview?.findStop(ownerID); return findMatches; } @@ -2602,11 +2602,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return []; } - const selectedRanges = options.selectedRanges?.map(range => this._notebookViewModel?.validateRange(range)).filter(range => !!range); - const selectedIndexes = cellRangesToIndexes(selectedRanges ?? []); - const findIds: string[] = selectedIndexes.map(index => this._notebookViewModel?.viewCells[index].id ?? ''); + let findIds: string[] = []; + if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) { + const selectedIndexes = cellRangesToIndexes(options.findScope.selectedCellRanges); + findIds = 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 : [] }); + const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: findIds }); if (token.isCancellationRequested) { return []; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index f1c1ab7b7c8..b0751fc5b96 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -9,7 +9,7 @@ import { Mimes } from 'vs/base/common/mime'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IPosition } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; @@ -24,7 +24,7 @@ import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browse import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookCellStatusBarItem, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookCellStatusBarItem, INotebookFindOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export abstract class BaseCellViewModel extends Disposable { @@ -650,20 +650,21 @@ export abstract class BaseCellViewModel extends Disposable { protected abstract onDidChangeTextModelContent(): void; - protected cellStartFind(value: string, options: INotebookSearchOptions): model.FindMatch[] | null { + protected cellStartFind(value: string, options: INotebookFindOptions): model.FindMatch[] | null { let cellMatches: model.FindMatch[] = []; + const lineCount = this.textBuffer.getLineCount(); + const findRange: IRange[] = options.findScope?.selectedTextRanges ?? [new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1)]; + if (this.assertTextModelAttached()) { cellMatches = this.textModel!.findMatches( value, - false, + findRange, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null, options.regex || false); } else { - const lineCount = this.textBuffer.getLineCount(); - const fullRange = new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1); const searchParams = new SearchParams(value, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null,); const searchData = searchParams.parseSearchRequest(); @@ -671,7 +672,9 @@ export abstract class BaseCellViewModel extends Disposable { return null; } - cellMatches = this.textBuffer.findMatchesLineByLine(fullRange, searchData, options.regex || false, 1000); + findRange.forEach(range => { + cellMatches.push(...this.textBuffer.findMatchesLineByLine(new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn), searchData, options.regex || false, 1000)); + }); } return cellMatches; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 3f5a6c3e68d..06c8c851f3b 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -5,25 +5,25 @@ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { dispose } from 'vs/base/common/lifecycle'; +import { observableValue } from 'vs/base/common/observable'; import * as UUID from 'vs/base/common/uuid'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CellLayoutState, ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatch, CellLayoutState, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; +import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; +import { CellKind, INotebookFindOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellExecutionError, ICellExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { BaseCellViewModel } from './baseCellViewModel'; -import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; -import { ICellExecutionError, ICellExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { observableValue } from 'vs/base/common/observable'; export const outputDisplayLimit = 500; @@ -518,7 +518,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + startFind(value: string, options: INotebookFindOptions): CellFindMatch | null { const matches = super.cellStartFind(value, options); if (matches === null) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts index 0f255cc20db..ee1b0d37d9f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts @@ -10,7 +10,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CellEditState, CellFindMatch, CellFoldingState, CellLayoutContext, CellLayoutState, EditorFoldingStateDelegate, ICellOutputViewModel, ICellViewModel, MarkupCellLayoutChangeEvent, MarkupCellLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookFindOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -295,7 +295,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + startFind(value: string, options: INotebookFindOptions): CellFindMatch | null { const matches = super.cellStartFind(value, options); if (matches === null) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 7ea020f145c..60a3626484f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -13,27 +13,27 @@ import { URI } from 'vs/base/common/uri'; import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IWorkspaceTextEdit } from 'vs/editor/common/languages'; import { FindMatch, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { MultiModelEditStackElement, SingleModelEditStackElement } from 'vs/editor/common/model/editStack'; import { IntervalNode, IntervalTree } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IWorkspaceTextEdit } from 'vs/editor/common/languages'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FoldingRegions } from 'vs/editor/contrib/folding/browser/foldingRanges'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellViewModel, INotebookDeltaCellStatusBarItems, INotebookDeltaDecoration, ICellModelDecorations, ICellModelDeltaDecorations, IModelDecorationsChangeAccessor, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatchModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; +import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, IModelDecorationsChangeAccessor, INotebookDeltaCellStatusBarItems, INotebookDeltaDecoration, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICell, INotebookSearchOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; -import { CellFindMatchModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; +import { CellKind, ICell, INotebookFindOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookFindScopeType, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; @@ -910,13 +910,12 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } //#region Find - find(value: string, options: INotebookSearchOptions): CellFindMatchWithIndex[] { + find(value: string, options: INotebookFindOptions): CellFindMatchWithIndex[] { const matches: CellFindMatchWithIndex[] = []; let findCells: CellViewModel[] = []; - const selectedRanges = options.selectedRanges?.map(range => this.validateRange(range)).filter(range => !!range); - - if (options.searchInRanges && selectedRanges) { + if (options.findScope && (options.findScope.findScopeType === NotebookFindScopeType.Cells || options.findScope.findScopeType === NotebookFindScopeType.Text)) { + const selectedRanges = options.findScope.selectedCellRanges?.map(range => this.validateRange(range)).filter(range => !!range) ?? []; const selectedIndexes = cellRangesToIndexes(selectedRanges); findCells = selectedIndexes.map(index => this._viewCells[index]); } else { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index bb08370538b..aea2596da48 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -8,33 +8,34 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Iterable } from 'vs/base/common/iterator'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { ISplice } from 'vs/base/common/sequence'; +import { ThemeColor } from 'vs/base/common/themables'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Command, WorkspaceEditMetadata } from 'vs/editor/common/languages'; import { IReadonlyTextBuffer } from 'vs/editor/common/model'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ThemeColor } from 'vs/base/common/themables'; +import { IFileReadLimits } from 'vs/platform/files/common/files'; import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { IFileReadLimits } from 'vs/platform/files/common/files'; -import { parse as parseUri, generate as generateUri } from 'vs/workbench/services/notebook/common/notebookDocumentService'; import { ICellExecutionError } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { generate as generateUri, parse as parseUri } from 'vs/workbench/services/notebook/common/notebookDocumentService'; +import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook'; export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor'; @@ -828,7 +829,7 @@ export enum NotebookEditorPriority { option = 'option', } -export interface INotebookSearchOptions { +export interface INotebookFindOptions { regex?: boolean; wholeWord?: boolean; caseSensitive?: boolean; @@ -837,8 +838,19 @@ export interface INotebookSearchOptions { includeMarkupPreview?: boolean; includeCodeInput?: boolean; includeOutput?: boolean; - searchInRanges?: boolean; - selectedRanges?: ICellRange[]; + findScope?: INotebookFindScope; +} + +export interface INotebookFindScope { + findScopeType: NotebookFindScopeType; + selectedCellRanges?: ICellRange[]; + selectedTextRanges?: Range[]; +} + +export enum NotebookFindScopeType { + Cells = 'cells', + Text = 'text', + None = 'none' } export interface INotebookExclusiveDocumentFilter { @@ -964,7 +976,6 @@ 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', diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index ef524745b9d..830ac41e6a8 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -52,7 +52,7 @@ import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/vie import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { CellKind, CellUri, ICellDto2, INotebookDiffEditorModel, INotebookEditorModel, INotebookSearchOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellExecutionState, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, ICellDto2, INotebookDiffEditorModel, INotebookEditorModel, INotebookFindOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellExecutionState, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookCellExecution, INotebookExecution, INotebookExecutionStateService, INotebookFailStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -311,7 +311,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic override get onDidChangeSelection() { return viewModel.onDidChangeSelection as Event; } override get onDidChangeOptions() { return viewModel.onDidChangeOptions; } override get onDidChangeViewCells() { return viewModel.onDidChangeViewCells; } - override async find(query: string, options: INotebookSearchOptions): Promise { + override async find(query: string, options: INotebookFindOptions): Promise { const findMatches = viewModel.find(query, options).filter(match => match.length > 0); return findMatches; } diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 905ea80a71c..f8beab04b54 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -16,7 +17,6 @@ import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/browser/findModel'; -import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -44,6 +44,7 @@ import { GroupModelChangeKind } from 'vs/workbench/common/editor'; import { SearchFindInput } from 'vs/workbench/contrib/search/browser/searchFindInput'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { NotebookFindScopeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -205,8 +206,7 @@ export class SearchWidget extends Widget { notebookOptions.isInNotebookMarkdownPreview, notebookOptions.isInNotebookCellInput, notebookOptions.isInNotebookCellOutput, - false, - [] + { findScopeType: NotebookFindScopeType.None } )); this._register(