From f895929a825b86e0584a0069bcbfead62cc0cedd Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 19 Oct 2022 12:22:37 -0700 Subject: [PATCH 01/45] initial marks --- .../workbench/contrib/notebook/browser/notebookEditorWidget.ts | 1 + src/vs/workbench/contrib/search/common/searchModel.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index f54262aa0b926..1143d9b628e93 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2384,6 +2384,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false): Promise { + // here if (!this._notebookViewModel) { return []; } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index f5d7b7b8bd007..ccecc6dd3128c 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -31,6 +31,7 @@ import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +// import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -1067,6 +1068,7 @@ export class SearchResult extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + // @IEditorService private readonly editorService: IEditorService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); From 03ad46750156acb92aca6429a772f0f39f74b404 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 21 Oct 2022 12:46:08 -0700 Subject: [PATCH 02/45] initial changes to support notebook editor binding --- .../search/{common => browser}/replace.ts | 2 +- .../search/browser/replaceContributions.ts | 2 +- .../contrib/search/browser/replaceService.ts | 4 +- .../search/browser/search.contribution.ts | 2 +- .../contrib/search/browser/searchActions.ts | 4 +- .../search/{common => browser}/searchModel.ts | 106 +++++++++++++++++- .../search/browser/searchResultsView.ts | 2 +- .../contrib/search/browser/searchView.ts | 5 +- .../search/test/browser/mockSearchTree.ts | 2 +- .../search/test/browser/searchActions.test.ts | 2 +- .../{common => browser}/searchModel.test.ts | 2 +- .../{common => browser}/searchResult.test.ts | 4 +- .../search/test/browser/searchViewlet.test.ts | 2 +- .../textsearch.perf.integrationTest.ts | 2 +- .../searchEditor/browser/searchEditor.ts | 2 +- .../browser/searchEditorActions.ts | 2 +- .../browser/searchEditorSerialization.ts | 2 +- 17 files changed, 121 insertions(+), 26 deletions(-) rename src/vs/workbench/contrib/search/{common => browser}/replace.ts (97%) rename src/vs/workbench/contrib/search/{common => browser}/searchModel.ts (94%) rename src/vs/workbench/contrib/search/test/{common => browser}/searchModel.test.ts (99%) rename src/vs/workbench/contrib/search/test/{common => browser}/searchResult.test.ts (99%) diff --git a/src/vs/workbench/contrib/search/common/replace.ts b/src/vs/workbench/contrib/search/browser/replace.ts similarity index 97% rename from src/vs/workbench/contrib/search/common/replace.ts rename to src/vs/workbench/contrib/search/browser/replace.ts index 8c1320c1f9cc3..4c9949259ec85 100644 --- a/src/vs/workbench/contrib/search/common/replace.ts +++ b/src/vs/workbench/contrib/search/browser/replace.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; diff --git a/src/vs/workbench/contrib/search/browser/replaceContributions.ts b/src/vs/workbench/contrib/search/browser/replaceContributions.ts index 42176f2aeecb5..1fbf571ff2bbc 100644 --- a/src/vs/workbench/contrib/search/browser/replaceContributions.ts +++ b/src/vs/workbench/contrib/search/browser/replaceContributions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { ReplaceService, ReplacePreviewContentProvider } from 'vs/workbench/contrib/search/browser/replaceService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index d653f37720370..9d7dced2e8b2f 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -7,11 +7,11 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as network from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel'; +import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/contrib/search/browser/searchModel'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 8efed0ce080cf..c8fe860bc22b1 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -47,7 +47,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { resolveResourcesForSearchIncludes } from 'vs/workbench/services/search/common/queryBuilder'; import { getWorkspaceSymbols, IWorkspaceSymbol, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, ISearchWorkbenchService, Match, RenderableMatch, SearchWorkbenchService } from 'vs/workbench/contrib/search/browser/searchModel'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 87ccd82a90a84..e2001a808ce8a 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -21,9 +21,9 @@ import { IViewsService } from 'vs/workbench/common/views'; import { searchRemoveIcon, searchReplaceAllIcon, searchReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { ISearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { arrayContainsElementOrParent, FileMatch, FolderMatch, FolderMatchNoRoot, FolderMatchWithResource, FolderMatchWorkspaceRoot, Match, RenderableMatch, searchComparer, searchMatchComparer, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { arrayContainsElementOrParent, FileMatch, FolderMatch, FolderMatchNoRoot, FolderMatchWithResource, FolderMatchWorkspaceRoot, Match, RenderableMatch, searchComparer, searchMatchComparer, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { OpenEditorCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts similarity index 94% rename from src/vs/workbench/contrib/search/common/searchModel.ts rename to src/vs/workbench/contrib/search/browser/searchModel.ts index ccecc6dd3128c..c8c779bb6d117 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -30,8 +30,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; -// import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -207,6 +209,7 @@ export class FileMatch extends Disposable implements IFileMatch { private _resource: URI; private _fileStat?: IFileStatWithPartialMetadata; private _model: ITextModel | null = null; + private _notebookEditorWidget: NotebookEditorWidget | null = null; private _modelListener: IDisposable | null = null; private _matches: Map; private _removedMatches: Set; @@ -271,6 +274,16 @@ export class FileMatch extends Disposable implements IFileMatch { this.updateHighlights(); } + bindEditorWidget(widget: NotebookEditorWidget) { + this._notebookEditorWidget = widget; + console.log(`added widget ${this._notebookEditorWidget.textModel?.uri}`); + } + + unbindEditorWidget(widget: NotebookEditorWidget) { + this._notebookEditorWidget = null; + console.log(`removed widget ${widget.textModel?.uri}`); + } + private onModelWillDispose(): void { // Update matches because model might have some dirty changes this.updateMatchesForModel(); @@ -573,6 +586,33 @@ export class FolderMatch extends Disposable { } } + bindEditorWidget(editor: NotebookEditorWidget, resource: URI) { + const fileMatch = this._fileMatches.get(resource); + + if (fileMatch) { + fileMatch.bindEditorWidget(editor); + } else { + const folderMatches = this.folderMatchesIterator(); + for (const elem of folderMatches) { + elem.bindEditorWidget(editor, resource); + } + } + } + + unbindEditorWidget(editor: NotebookEditorWidget, resource: URI) { + const fileMatch = this._fileMatches.get(resource); + + if (fileMatch) { + fileMatch.unbindEditorWidget(editor); + } else { + const folderMatches = this.folderMatchesIterator(); + for (const elem of folderMatches) { + elem.unbindEditorWidget(editor, resource); + } + } + + } + public createIntermediateFolderMatch(resource: URI, id: string, index: number, query: ITextQuery, baseWorkspaceFolder: FolderMatchWorkspaceRoot): FolderMatchWithResource { const folderMatch = this.instantiationService.createInstance(FolderMatchWithResource, resource, id, index, query, this, this._searchModel, baseWorkspaceFolder); this.configureIntermediateMatch(folderMatch); @@ -1056,10 +1096,9 @@ export class SearchResult extends Disposable { private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forUris(key => this.uriIdentityService.extUri.ignorePathCasing(key)); private _showHighlights: boolean = false; private _query: ITextQuery | null = null; - private _rangeHighlightDecorations: RangeHighlightDecorations; private disposePastResults: () => void = () => { }; - + private _notebookEditors: Set; private _isDirty = false; constructor( @@ -1068,18 +1107,23 @@ export class SearchResult extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - // @IEditorService private readonly editorService: IEditorService, + @IEditorService private readonly editorService: IEditorService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); this._register(this.modelService.onModelAdded(model => this.onModelAdded(model))); - + this._register(this.editorService.onDidVisibleEditorsChange(() => { + this.onDidVisibleEditorsChange(); + })); this._register(this.onChange(e => { if (e.removed) { this._isDirty = !this.isEmpty(); } })); + + + this._notebookEditors = new Set(); } async batchReplace(elementsToReplace: RenderableMatch[]) { @@ -1170,11 +1214,61 @@ export class SearchResult extends Disposable { return retEvent; } + + private onDidVisibleEditorsChange(): void { + const visibleNotebookEditors = this.editorService.visibleEditorPanes + .map((editor) => ((editor instanceof NotebookEditor) ? (editor as NotebookEditor) : null)) + .filter((editor2): editor2 is NotebookEditor => (NotebookEditor !== null)); + + if (visibleNotebookEditors.length === 0) { + return; + } + + const oldEditors = this._notebookEditors.values(); + + const oldEditorsToUnbind = []; + const oldEditorsToKeepBound = []; + for (const editor of oldEditors) { + if (visibleNotebookEditors.includes(editor)) { + oldEditorsToKeepBound.push(editor); + } else { + oldEditorsToUnbind.push(editor); + } + } + + for (const editor of oldEditorsToUnbind) { + const widget = editor.getControl(); + // doesn't currently work because these fields are usually undefined by now on the editor + if (editor.input && editor.input.resource && widget) { + this.onNotebookEditorRemoved(widget, editor.input.resource); + this._notebookEditors.delete(editor); + } + } + + for (const editor of visibleNotebookEditors) { + const widget = editor.getControl(); + if (editor.input && editor.input.resource && widget) { + this.onNotebookEditorAdded(widget, editor.input.resource); + this._notebookEditors.add(editor); + } + } + } + private onModelAdded(model: ITextModel): void { const folderMatch = this._folderMatchesMap.findSubstr(model.uri); folderMatch?.bindModel(model); } + private onNotebookEditorAdded(editor: NotebookEditorWidget, resource: URI): void { + const folderMatch = this._folderMatchesMap.findSubstr(resource); + folderMatch?.bindEditorWidget(editor, resource); + } + + private onNotebookEditorRemoved(editor: NotebookEditorWidget, resource: URI): void { + const folderMatch = this._folderMatchesMap.findSubstr(resource); + folderMatch?.unbindEditorWidget(editor, resource); + } + private _createBaseFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { let folderMatch; if (resource) { diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 265e89bc965c0..9f6fac711d6cc 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -24,7 +24,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; -import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot } from 'vs/workbench/contrib/search/browser/searchModel'; import { isEqual } from 'vs/base/common/resources'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 30454a8e98887..6e74ab2fb3fc7 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -68,10 +68,10 @@ import { renderSearchMessage } from 'vs/workbench/contrib/search/browser/searchM import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate } from 'vs/workbench/contrib/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { getOutOfWorkspaceEditorResources, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -537,6 +537,7 @@ export class SearchView extends ViewPane { } refreshTree(event?: IChangeEvent): void { + // animation frame and debounce const collapseResults = this.searchConfig.collapseResults; if (!event || event.added || event.removed) { // Refresh whole tree diff --git a/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts b/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts index 681dae8164cc8..31effdd2358f1 100644 --- a/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts +++ b/src/vs/workbench/contrib/search/test/browser/mockSearchTree.ts @@ -6,7 +6,7 @@ import { ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; import { Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { RenderableMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { RenderableMatch } from 'vs/workbench/contrib/search/browser/searchModel'; const someEvent = new Emitter().event; diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index 975e022550b71..6a1c09ed5659a 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -16,7 +16,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { IFileMatch } from 'vs/workbench/services/search/common/search'; import { getElementToFocusAfterRemoved, getLastNodeFromSameType } from 'vs/workbench/contrib/search/browser/searchActions'; -import { FileMatch, FileMatchOrMatch, Match } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FileMatchOrMatch, Match } from 'vs/workbench/contrib/search/browser/searchModel'; import { MockObjectTree } from 'vs/workbench/contrib/search/test/browser/mockSearchTree'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts similarity index 99% rename from src/vs/workbench/contrib/search/test/common/searchModel.test.ts rename to src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index c62b157915d93..ed36923b1dbd5 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -16,7 +16,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchMatch, OneLineRange, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; +import { SearchModel } from 'vs/workbench/contrib/search/browser/searchModel'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { FileService } from 'vs/platform/files/common/fileService'; diff --git a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts similarity index 99% rename from src/vs/workbench/contrib/search/test/common/searchResult.test.ts rename to src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index b7c72c0c44c03..44a1f266cdc08 100644 --- a/src/vs/workbench/contrib/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { Match, FileMatch, SearchResult, SearchModel, FolderMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { Match, FileMatch, SearchResult, SearchModel, FolderMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { URI } from 'vs/base/common/uri'; import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch, QueryType } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ModelService } from 'vs/editor/common/services/modelService'; import { IModelService } from 'vs/editor/common/services/model'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index a51ea982326dd..402da91997506 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -21,7 +21,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; -import { FileMatch, FolderMatch, Match, searchComparer, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FolderMatch, Match, searchComparer, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { MockLabelService } from 'vs/workbench/services/label/test/common/mockLabelService'; import { IFileMatch, ITextSearchMatch, OneLineRange, QueryType, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; diff --git a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts index 3b73a35ecb3c1..a96a9e886ca54 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts @@ -34,7 +34,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import 'vs/workbench/contrib/search/browser/search.contribution'; // load contributions import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; -import { SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; +import { SearchModel } from 'vs/workbench/contrib/search/browser/searchModel'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index a187de8b635f0..b42d54e41e696 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -44,7 +44,7 @@ import { SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import { InputBoxFocusedKey } from 'vs/workbench/contrib/search/common/constants'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; -import { SearchModel, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { InSearchEditor, SearchEditorFindMatchClass, SearchEditorID, SearchEditorInputTypeId } from 'vs/workbench/contrib/searchEditor/browser/constants'; import type { SearchConfiguration, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { serializeSearchResultForEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts index e1d2e65956d0e..e5cff3fd0fc7b 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorActions.ts @@ -17,7 +17,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { EditorsOrder } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; import { getSearchView } from 'vs/workbench/contrib/search/browser/searchActions'; -import { SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; import { getOrMakeSearchEditorInput, SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index a931eb0a9070c..de40b0c6636b4 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -10,7 +10,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import type { ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; -import { FileMatch, Match, searchMatchComparer, SearchResult, FolderMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, Match, searchMatchComparer, SearchResult, FolderMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import type { SearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { ITextQuery, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; From cc1a7788fa742e430c0bb4c292ca471bb1de2b7d Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 8 Nov 2022 08:40:04 -0800 Subject: [PATCH 03/45] change notebook track qualifications --- .../notebook/browser/notebookEditorWidget.ts | 4 ++ .../contrib/search/browser/searchModel.ts | 54 ++++++++++--------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1143d9b628e93..35aa79030ce2b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -146,6 +146,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event; + private readonly _onDidDispose = this._register(new Emitter()); + readonly onDidDispose = this._onDidDispose.event; + //#endregion private _overlayContainer!: HTMLElement; private _notebookTopToolbarContainer!: HTMLElement; @@ -2926,6 +2929,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._baseCellEditorOptions.forEach(v => v.dispose()); this._baseCellEditorOptions.clear(); + this._onDidDispose.fire(); super.dispose(); // unref diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index c8c779bb6d117..f5e0da024cab7 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1098,7 +1098,7 @@ export class SearchResult extends Disposable { private _query: ITextQuery | null = null; private _rangeHighlightDecorations: RangeHighlightDecorations; private disposePastResults: () => void = () => { }; - private _notebookEditors: Set; + // private _notebookEditors: Set; private _isDirty = false; constructor( @@ -1123,7 +1123,7 @@ export class SearchResult extends Disposable { })); - this._notebookEditors = new Set(); + // this._notebookEditors = new Set(); } async batchReplace(elementsToReplace: RenderableMatch[]) { @@ -1218,38 +1218,42 @@ export class SearchResult extends Disposable { private onDidVisibleEditorsChange(): void { const visibleNotebookEditors = this.editorService.visibleEditorPanes .map((editor) => ((editor instanceof NotebookEditor) ? (editor as NotebookEditor) : null)) - .filter((editor2): editor2 is NotebookEditor => (NotebookEditor !== null)); + .filter((editor2): editor2 is NotebookEditor => (editor2 !== null)); if (visibleNotebookEditors.length === 0) { return; } - const oldEditors = this._notebookEditors.values(); - - const oldEditorsToUnbind = []; - const oldEditorsToKeepBound = []; - for (const editor of oldEditors) { - if (visibleNotebookEditors.includes(editor)) { - oldEditorsToKeepBound.push(editor); - } else { - oldEditorsToUnbind.push(editor); - } - } - - for (const editor of oldEditorsToUnbind) { - const widget = editor.getControl(); - // doesn't currently work because these fields are usually undefined by now on the editor - if (editor.input && editor.input.resource && widget) { - this.onNotebookEditorRemoved(widget, editor.input.resource); - this._notebookEditors.delete(editor); - } - } + // const oldEditors = this._notebookEditors.values(); + + // const oldEditorsToUnbind = []; + // const oldEditorsToKeepBound = []; + // for (const editor of oldEditors) { + // if (visibleNotebookEditors.includes(editor)) { + // oldEditorsToKeepBound.push(editor); + // } + // // else { + // // oldEditorsToUnbind.push(editor); + // // } + // } + + // for (const editor of oldEditorsToUnbind) { + // const widget = editor.getControl(); + // // doesn't currently work because these fields are usually undefined by now on the editor + // if (editor.input && editor.input.resource && widget) { + // this.onNotebookEditorRemoved(widget, editor.input.resource); + // this._notebookEditors.delete(editor); + // } + // } for (const editor of visibleNotebookEditors) { const widget = editor.getControl(); if (editor.input && editor.input.resource && widget) { - this.onNotebookEditorAdded(widget, editor.input.resource); - this._notebookEditors.add(editor); + const uri = editor.input.resource; + if (uri) { + this.onNotebookEditorAdded(widget, uri); + widget.onDidDispose(() => this.onNotebookEditorRemoved(widget, uri)); + } } } } From 6bba961e550447af7112475989c1b07a43707cc4 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 15 Nov 2022 09:15:19 -0800 Subject: [PATCH 04/45] needs to read into dom --- .../notebook/browser/notebookEditorWidget.ts | 7 +- .../browser/services/notebookEditorService.ts | 4 + .../services/notebookEditorServiceImpl.ts | 15 ++ .../contrib/search/browser/searchModel.ts | 177 +++++++++++------- .../search/browser/searchNotebookHelpers.ts | 108 +++++++++++ .../services/search/common/search.ts | 6 +- 6 files changed, 249 insertions(+), 68 deletions(-) create mode 100644 src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 35aa79030ce2b..7ff88c26cb1d6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -112,6 +112,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD readonly onDidChangeCellState = this._onDidChangeCellState.event; private readonly _onDidChangeViewCells = this._register(new Emitter()); readonly onDidChangeViewCells: Event = this._onDidChangeViewCells.event; + private readonly _onWillChangeModel = this._register(new Emitter()); + readonly onWillChangeModel: Event = this._onWillChangeModel.event; private readonly _onDidChangeModel = this._register(new Emitter()); readonly onDidChangeModel: Event = this._onDidChangeModel.event; private readonly _onDidChangeOptions = this._register(new Emitter()); @@ -144,8 +146,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private readonly onDidRenderOutput = this._onDidRenderOutput.event; private readonly _onDidResizeOutputEmitter = this._register(new Emitter()); readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event; - - private readonly _onDidDispose = this._register(new Emitter()); readonly onDidDispose = this._onDidDispose.event; @@ -196,6 +196,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } set viewModel(newModel: NotebookViewModel | undefined) { + this._onWillChangeModel.fire(this._notebookViewModel?.notebookDocument); this._notebookViewModel = newModel; this._onDidChangeModel.fire(newModel?.notebookDocument); } @@ -2387,7 +2388,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false): Promise { - // here if (!this._notebookViewModel) { return []; } @@ -2924,6 +2924,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._overlayContainer.remove(); this.viewModel?.dispose(); + this.viewModel = undefined; this._renderedEditors.clear(); this._baseCellEditorOptions.forEach(v => v.dispose()); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts index ac80184de3e8b..2e7b1453f42b4 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts @@ -9,6 +9,8 @@ import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebo import { INotebookEditor, INotebookEditorCreationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { Event } from 'vs/base/common/event'; import { Dimension } from 'vs/base/browser/dom'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { URI } from 'vs/base/common/uri'; export const INotebookEditorService = createDecorator('INotebookEditorWidgetService'); @@ -20,9 +22,11 @@ export interface INotebookEditorService { _serviceBrand: undefined; retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, dimension?: Dimension): IBorrowValue; + retrieveExistingWidgetFromURI(resource: URI): IBorrowValue | undefined; onDidAddNotebookEditor: Event; onDidRemoveNotebookEditor: Event; + onDidAddNotebookEditorWidget: Event; addNotebookEditor(editor: INotebookEditor): void; removeNotebookEditor(editor: INotebookEditor): void; getNotebookEditor(editorId: string): INotebookEditor | undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index d83e19338cb01..c9069cf5a8100 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -14,6 +14,7 @@ import { INotebookEditor, INotebookEditorCreationOptions } from 'vs/workbench/co import { Emitter } from 'vs/base/common/event'; import { GroupIdentifier } from 'vs/workbench/common/editor'; import { Dimension } from 'vs/base/browser/dom'; +import { URI } from 'vs/base/common/uri'; export class NotebookEditorWidgetService implements INotebookEditorService { @@ -29,6 +30,9 @@ export class NotebookEditorWidgetService implements INotebookEditorService { readonly onDidAddNotebookEditor = this._onNotebookEditorAdd.event; readonly onDidRemoveNotebookEditor = this._onNotebookEditorsRemove.event; + private readonly _onDidAddNotebookEditorWidget = new Emitter(); + readonly onDidAddNotebookEditorWidget = this._onDidAddNotebookEditorWidget.event; + private readonly _borrowableEditors = new Map>(); constructor( @@ -129,6 +133,16 @@ export class NotebookEditorWidgetService implements INotebookEditorService { targetMap.set(input.resource, widget); } + retrieveExistingWidgetFromURI(resource: URI): IBorrowValue | undefined { + for (const widgetInfo of this._borrowableEditors.values()) { + const widget = widgetInfo.get(resource); + if (widget) { + return this._createBorrowValue(widget.token!, widget); + } + } + return undefined; + } + retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, initialDimension?: Dimension): IBorrowValue { let value = this._borrowableEditors.get(group.id)?.get(input.resource); @@ -137,6 +151,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService { // NEW widget const instantiationService = accessor.get(IInstantiationService); const widget = instantiationService.createInstance(NotebookEditorWidget, creationOptions ?? getDefaultNotebookCreationOptions(), initialDimension); + this._onDidAddNotebookEditorWidget.fire(widget); const token = this._tokenPool++; value = { widget, token }; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index f5e0da024cab7..16c66e2d35233 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { RunOnceScheduler } from 'vs/base/common/async'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { compareFileExtensions, compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { memoize } from 'vs/base/common/decorators'; import * as errors from 'vs/base/common/errors'; @@ -30,10 +30,9 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -46,11 +45,10 @@ export class Match { private _range: Range; private _oneLinePreviewText: string; private _rangeInPreviewText: ISearchRange; - // For replace private _fullPreviewRange: ISearchRange; - constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange) { + constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange, private _cellFragment?: string) { this._oneLinePreviewText = _fullPreviewLines[_fullPreviewRange.startLineNumber]; const adjustedEndCol = _fullPreviewRange.startLineNumber === _fullPreviewRange.endLineNumber ? _fullPreviewRange.endColumn : @@ -84,6 +82,10 @@ export class Match { return this._range; } + fragment(): string | undefined { + return this._cellFragment; + } + @memoize preview(): { before: string; inside: string; after: string } { let before = this._oneLinePreviewText.substring(0, this._rangeInPreviewText.startColumn - 1), @@ -211,6 +213,7 @@ export class FileMatch extends Disposable implements IFileMatch { private _model: ITextModel | null = null; private _notebookEditorWidget: NotebookEditorWidget | null = null; private _modelListener: IDisposable | null = null; + private _editorWidgetListener: IDisposable | null = null; private _matches: Map; private _removedMatches: Set; private _selectedMatch: Match | null = null; @@ -234,6 +237,7 @@ export class FileMatch extends Disposable implements IFileMatch { @IModelService private readonly modelService: IModelService, @IReplaceService private readonly replaceService: IReplaceService, @ILabelService readonly labelService: ILabelService, + @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); this._resource = this.rawMatch.resource; @@ -248,9 +252,13 @@ export class FileMatch extends Disposable implements IFileMatch { return this._closestRoot; } - private createMatches(): void { + private async createMatches(): Promise { const model = this.modelService.getModel(this._resource); - if (model) { + const notebookEditorWidgetBorrow = this.notebookEditorService.retrieveExistingWidgetFromURI(this._resource); + if (notebookEditorWidgetBorrow?.value) { + this.bindEditorWidget(notebookEditorWidgetBorrow.value); + await this.updateMatchesForEditorWidget(); + } else if (model) { this.bindModel(model); this.updateMatchesForModel(); } else { @@ -276,10 +284,20 @@ export class FileMatch extends Disposable implements IFileMatch { bindEditorWidget(widget: NotebookEditorWidget) { this._notebookEditorWidget = widget; + this._editorWidgetListener = this._notebookEditorWidget.viewModel?.onDidChangeViewCells(() => { + this._updateScheduler.schedule(); + }) ?? null; + this.updateHighlights(); console.log(`added widget ${this._notebookEditorWidget.textModel?.uri}`); } unbindEditorWidget(widget: NotebookEditorWidget) { + this.updateMatchesForModel(); + if (this._notebookEditorWidget) { + this._updateScheduler.cancel(); + this._model = null; + this._editorWidgetListener!.dispose(); + } this._notebookEditorWidget = null; console.log(`removed widget ${widget.textModel?.uri}`); } @@ -313,14 +331,48 @@ export class FileMatch extends Disposable implements IFileMatch { const matches = this._model .findMatches(this._query.pattern, this._model.getFullModelRange(), !!this._query.isRegExp, !!this._query.isCaseSensitive, wordSeparators, false, this._maxResults ?? Number.MAX_SAFE_INTEGER); - this.updateMatches(matches, true); + this.updateMatches(matches, true, this._model); + } + + private async updateMatchesForEditorWidget(): Promise { + // this is called from a timeout and might fire + // after the model has been disposed + if (!this._notebookEditorWidget) { + return; + } + this._matches = new Map(); + + const wordSeparators = this._query.isWordMatch && this._query.wordSeparators ? this._query.wordSeparators : null; + const allMatches = await this._notebookEditorWidget + .find(this._query.pattern, { + regex: this._query.isRegExp, + wholeWord: this._query.isWordMatch, + caseSensitive: this._query.isCaseSensitive, + wordSeparators: wordSeparators ?? undefined, + includeMarkupInput: true, + includeCodeInput: true + }, CancellationToken.None); + // const matches = allMatches.map((elem) => elem.matches) + // .flat() + // .map((elem) => { + // if (elem instanceof FindMatch) { + // return elem; + // } else { + // return undefined; + // } + // }).filter((e): e is FindMatch => !!FindMatch); + console.log(allMatches); + const model = this._notebookEditorWidget.textModel; + if (!model) { + return; + } + // this.updateNotebookMatches(allMatches, true); } private updatesMatchesForLineAfterReplace(lineNumber: number, modelChange: boolean): void { if (!this._model) { return; } - const range = { startLineNumber: lineNumber, startColumn: this._model.getLineMinColumn(lineNumber), @@ -332,15 +384,38 @@ export class FileMatch extends Disposable implements IFileMatch { const wordSeparators = this._query.isWordMatch && this._query.wordSeparators ? this._query.wordSeparators : null; const matches = this._model.findMatches(this._query.pattern, range, !!this._query.isRegExp, !!this._query.isCaseSensitive, wordSeparators, false, this._maxResults ?? Number.MAX_SAFE_INTEGER); - this.updateMatches(matches, modelChange); - } - - private updateMatches(matches: FindMatch[], modelChange: boolean): void { - if (!this._model) { - return; - } - - const textSearchResults = editorMatchesToTextSearchResults(matches, this._model, this._previewOptions); + this.updateMatches(matches, modelChange, this._model); + } + + // private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { + // if (!this._notebookEditorWidget) { + // return; + // } + + // const textSearchResults = matches.forEach((m) => notebookEditorMatchesToTextSearchResults(m.matches, this._notebookEditorWidget, this._previewOptions)); + // textSearchResults.forEach(textSearchResult => { + // textSearchResultToMatches(textSearchResult, this).forEach(match => { + // if (!this._removedMatches.has(match.id())) { + // this.add(match); + // if (this.isMatchSelected(match)) { + // this._selectedMatch = match; + // } + // } + // }); + // }); + + // this.addContext( + // addContextToNotebookEditorMatches(textSearchResults, this._notebookEditorWidget, this.parent().parent().query!) + // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) + // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); + + // this._onChange.fire({ forceUpdateModel: modelChange }); + // this.updateHighlights(); + // } + + private updateMatches(matches: FindMatch[], modelChange: boolean, model: ITextModel): void { + + const textSearchResults = editorMatchesToTextSearchResults(matches, model, this._previewOptions); textSearchResults.forEach(textSearchResult => { textSearchResultToMatches(textSearchResult, this).forEach(match => { if (!this._removedMatches.has(match.id())) { @@ -353,7 +428,7 @@ export class FileMatch extends Disposable implements IFileMatch { }); this.addContext( - addContextToEditorMatches(textSearchResults, this._model, this.parent().parent().query!) + addContextToEditorMatches(textSearchResults, model, this.parent().parent().query!) .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); @@ -1098,7 +1173,6 @@ export class SearchResult extends Disposable { private _query: ITextQuery | null = null; private _rangeHighlightDecorations: RangeHighlightDecorations; private disposePastResults: () => void = () => { }; - // private _notebookEditors: Set; private _isDirty = false; constructor( @@ -1107,15 +1181,14 @@ export class SearchResult extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @IModelService private readonly modelService: IModelService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - @IEditorService private readonly editorService: IEditorService, + @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); this._register(this.modelService.onModelAdded(model => this.onModelAdded(model))); - this._register(this.editorService.onDidVisibleEditorsChange(() => { - this.onDidVisibleEditorsChange(); - })); + this._register(this.notebookEditorService.onDidAddNotebookEditorWidget(widget => this.onDidAddNotebookEditorWidget(widget))); + this._register(this.onChange(e => { if (e.removed) { this._isDirty = !this.isEmpty(); @@ -1215,47 +1288,23 @@ export class SearchResult extends Disposable { return retEvent; } - private onDidVisibleEditorsChange(): void { - const visibleNotebookEditors = this.editorService.visibleEditorPanes - .map((editor) => ((editor instanceof NotebookEditor) ? (editor as NotebookEditor) : null)) - .filter((editor2): editor2 is NotebookEditor => (editor2 !== null)); - - if (visibleNotebookEditors.length === 0) { - return; - } + private onDidAddNotebookEditorWidget(widget: NotebookEditorWidget): void { + widget.onWillChangeModel( + (model) => { + if (model) { + this.onNotebookEditorWidgetRemoved(widget, model?.uri); + } + } + ); - // const oldEditors = this._notebookEditors.values(); - - // const oldEditorsToUnbind = []; - // const oldEditorsToKeepBound = []; - // for (const editor of oldEditors) { - // if (visibleNotebookEditors.includes(editor)) { - // oldEditorsToKeepBound.push(editor); - // } - // // else { - // // oldEditorsToUnbind.push(editor); - // // } - // } - - // for (const editor of oldEditorsToUnbind) { - // const widget = editor.getControl(); - // // doesn't currently work because these fields are usually undefined by now on the editor - // if (editor.input && editor.input.resource && widget) { - // this.onNotebookEditorRemoved(widget, editor.input.resource); - // this._notebookEditors.delete(editor); - // } - // } - - for (const editor of visibleNotebookEditors) { - const widget = editor.getControl(); - if (editor.input && editor.input.resource && widget) { - const uri = editor.input.resource; - if (uri) { - this.onNotebookEditorAdded(widget, uri); - widget.onDidDispose(() => this.onNotebookEditorRemoved(widget, uri)); + widget.onDidChangeModel( + (model) => { + if (model) { + this.onNotebookEditorWidgetAdded(widget, model?.uri); } } - } + ); + } private onModelAdded(model: ITextModel): void { @@ -1263,12 +1312,12 @@ export class SearchResult extends Disposable { folderMatch?.bindModel(model); } - private onNotebookEditorAdded(editor: NotebookEditorWidget, resource: URI): void { + private onNotebookEditorWidgetAdded(editor: NotebookEditorWidget, resource: URI): void { const folderMatch = this._folderMatchesMap.findSubstr(resource); folderMatch?.bindEditorWidget(editor, resource); } - private onNotebookEditorRemoved(editor: NotebookEditorWidget, resource: URI): void { + private onNotebookEditorWidgetRemoved(editor: NotebookEditorWidget, resource: URI): void { const folderMatch = this._folderMatchesMap.findSubstr(resource); folderMatch?.unbindEditorWidget(editor, resource); } diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts new file mode 100644 index 0000000000000..4f6c54146ef70 --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// import { FindMatch } from 'vs/editor/common/model'; +// import { CellFindMatchWithIndex, ICellViewModel, OutputFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +// import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +// import { ITextSearchPreviewOptions, ITextSearchResult, TextSearchMatch } from 'vs/workbench/services/search/common/search'; + +// interface CellFindMatchInfoForTextModel { +// cell: ICellViewModel; +// matches: FindMatch[] | OutputFindMatch; +// } + +// function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch { +// // what do I want here? +// // I want to take all of my matches and group them +// const matches: FindMatch[] | OutputFindMatch = cellInfo.matches; +// if (matches instanceof OutputFindMatch) { + +// return new TextSearchMatch( +// lineTexts.join('\n') + '\n', +// matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), +// previewOptions); +// } else { +// const firstLine = matches[0].range.startLineNumber; +// const lastLine = matches[matches.length - 1].range.endLineNumber; +// const lineTexts: string[] = []; +// for (let i = firstLine; i <= lastLine; i++) { +// lineTexts.push(editorWidget.textModel?.getLineContent(i)); +// } + +// return new TextSearchMatch( +// lineTexts.join('\n') + '\n', +// matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), +// previewOptions); +// } + +// } + +// export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { +// let previousEndLine = -1; +// const groupedMatches: CellFindMatchInfoForTextModel[] = []; +// let currentMatches: FindMatch[] = []; + + +// cellFindMatches.forEach((cellFindMatch) => { +// cellFindMatch.matches.forEach((match) => { +// if (match instanceof FindMatch) { + +// if (match.range.startLineNumber !== previousEndLine) { +// currentMatches = []; +// groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); +// } + +// currentMatches.push(match); +// previousEndLine = match.range.endLineNumber; +// } else { +// groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); +// } +// }); + +// currentMatches = []; +// groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); +// }); + +// return groupedMatches.map(sameLineMatches => { + +// return notebookEditorMatchToTextSearchResult(sameLineMatches, editorWidget, previewOptions); +// }); +// } + +// export function addContextToNotebookEditorMatches(matches: ITextSearchMatch[], editorWidget: NotebookEditorWidget, query: ITextQuery): ITextSearchResult[] { +// const results: ITextSearchResult[] = []; + +// let prevLine = -1; +// for (let i = 0; i < matches.length; i++) { +// const { start: matchStartLine, end: matchEndLine } = getMatchStartEnd(matches[i]); +// if (typeof query.beforeContext === 'number' && query.beforeContext > 0) { +// const beforeContextStartLine = Math.max(prevLine + 1, matchStartLine - query.beforeContext); +// for (let b = beforeContextStartLine; b < matchStartLine; b++) { +// results.push({ +// text: model.getLineContent(b + 1), +// lineNumber: b +// }); +// } +// } + +// results.push(matches[i]); + +// const nextMatch = matches[i + 1]; +// const nextMatchStartLine = nextMatch ? getMatchStartEnd(nextMatch).start : Number.MAX_VALUE; +// if (typeof query.afterContext === 'number' && query.afterContext > 0) { +// const afterContextToLine = Math.min(nextMatchStartLine - 1, matchEndLine + query.afterContext, model.getLineCount() - 1); +// for (let a = matchEndLine + 1; a <= afterContextToLine; a++) { +// results.push({ +// text: model.getLineContent(a + 1), +// lineNumber: a +// }); +// } +// } + +// prevLine = matchEndLine; +// } + +// return results; +// } diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 2ed83f3be21e3..1d0cdb843fc1c 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -174,12 +174,14 @@ export interface ISearchRange { export interface ITextSearchResultPreview { text: string; matches: ISearchRange | ISearchRange[]; + cellFragment?: string; } export interface ITextSearchMatch { uri?: URI; ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; + cellFragment?: string; } export interface ITextSearchContext { @@ -273,9 +275,11 @@ export class FileMatch implements IFileMatch { export class TextSearchMatch implements ITextSearchMatch { ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; + cellFragment?: string; - constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) { + constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions, cellFragment?: string) { this.ranges = range; + this.cellFragment = cellFragment; // Trim preview if this is one match and a single-line match with a preview requested. // Otherwise send the full text, like for replace or for showing multiple previews. From 75dddd2f4065fb2236cc4460fb3bb8665e575dbf Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 17 Nov 2022 09:54:02 -0800 Subject: [PATCH 05/45] random logs --- .../browser/view/renderers/webviewPreloads.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) 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 fe811a05ac961..bf68ccb546700 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -983,6 +983,24 @@ async function webviewPreloads(ctx: PreloadContext) { } } + function extractSelectionLine(selection: Selection) { + + const range = selection.getRangeAt(0); + const oldRange = document.createRange(); + oldRange.setStart(range.startContainer, range.startOffset); + oldRange.setEnd(range.endContainer, range.endOffset); + + selection.modify('move', 'backward', 'lineboundary'); + selection.modify('extend', 'forward', 'lineboundary'); + + const line = selection.toString(); + + // re-add the old range so that the selection is restored + selection.removeAllRanges(); + selection.addRange(oldRange); + + return line; + } const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { let find = true; const matches: IFindMatch[] = []; @@ -1020,6 +1038,7 @@ async function webviewPreloads(ctx: PreloadContext) { const root = preview.shadowRoot as ShadowRoot & { getSelection: () => Selection }; const shadowSelection = root?.getSelection ? root?.getSelection() : null; if (shadowSelection && shadowSelection.anchorNode) { + console.log(extractSelectionLine(shadowSelection)); matches.push({ type: 'preview', id: preview.id, @@ -1039,6 +1058,7 @@ async function webviewPreloads(ctx: PreloadContext) { const root = outputNode.shadowRoot as ShadowRoot & { getSelection: () => Selection }; const shadowSelection = root?.getSelection ? root?.getSelection() : null; if (shadowSelection && shadowSelection.anchorNode) { + console.log(extractSelectionLine(shadowSelection)); matches.push({ type: 'output', id: outputNode.id, @@ -1056,6 +1076,8 @@ async function webviewPreloads(ctx: PreloadContext) { const lastEl: any = matches.length ? matches[matches.length - 1] : null; if (lastEl && lastEl.container.contains(anchorNode) && options.includeOutput) { + console.log('here1'); + console.log(lastEl); matches.push({ type: lastEl.type, id: lastEl.id, @@ -1075,6 +1097,8 @@ async function webviewPreloads(ctx: PreloadContext) { // inside output const cellId = node.parentElement?.parentElement?.id; if (cellId) { + console.log('here2'); + console.log(node); matches.push({ type: 'output', id: node.id, From b7f40ea4ed09e4ab40b8392398de8706d4209427 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 22 Nov 2022 10:25:10 -0800 Subject: [PATCH 06/45] some progress on notebook search --- .../notebook/browser/notebookBrowser.ts | 9 ++ .../browser/view/renderers/webviewMessages.ts | 9 ++ .../browser/view/renderers/webviewPreloads.ts | 29 +++- .../contrib/search/browser/searchModel.ts | 60 ++++---- .../search/browser/searchNotebookHelpers.ts | 144 ++++++++++-------- 5 files changed, 151 insertions(+), 100 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 95df7696197bb..f7e0cf11af0de 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -692,8 +692,17 @@ export interface IActiveNotebookEditorDelegate extends INotebookEditorDelegate { getNextVisibleCellIndex(index: number): number; } +export interface ISearchPreviewInfo { + line: string; + range: { + start: number; + end: number; + }; +} + export interface OutputFindMatch { readonly index: number; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export interface CellFindMatch { 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 be3698df4bf76..2d70a2d120d3a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -411,11 +411,20 @@ export interface IFindStopMessage { readonly type: 'findStop'; } +export interface ISearchPreviewInfo { + line: string; + range: { + start: number; + end: number; + }; +} + export interface IFindMatch { readonly type: 'preview' | 'output'; readonly cellId: string; readonly id: string; readonly index: number; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export interface IDidFindMessage extends BaseToWebviewMessage { 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 bf68ccb546700..b7156534b65a4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -857,6 +857,15 @@ async function webviewPreloads(ctx: PreloadContext) { originalRange: Range; isShadow: boolean; highlightResult?: IHighlightResult; + searchPreviewInfo?: ISearchPreviewInfo; + } + + interface ISearchPreviewInfo { + line: string; + range: { + start: number; + end: number; + }; } interface IHighlighter { @@ -983,7 +992,7 @@ async function webviewPreloads(ctx: PreloadContext) { } } - function extractSelectionLine(selection: Selection) { + function extractSelectionLine(selection: Selection): ISearchPreviewInfo { const range = selection.getRangeAt(0); const oldRange = document.createRange(); @@ -993,14 +1002,20 @@ async function webviewPreloads(ctx: PreloadContext) { selection.modify('move', 'backward', 'lineboundary'); selection.modify('extend', 'forward', 'lineboundary'); + const newRange = selection.getRangeAt(0); const line = selection.toString(); + const lineRange = { + start: oldRange.startOffset - newRange.startOffset, + end: oldRange.endOffset - newRange.startOffset, + }; // re-add the old range so that the selection is restored selection.removeAllRanges(); selection.addRange(oldRange); - return line; + return { line, range: lineRange }; } + const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { let find = true; const matches: IFindMatch[] = []; @@ -1038,14 +1053,14 @@ async function webviewPreloads(ctx: PreloadContext) { const root = preview.shadowRoot as ShadowRoot & { getSelection: () => Selection }; const shadowSelection = root?.getSelection ? root?.getSelection() : null; if (shadowSelection && shadowSelection.anchorNode) { - console.log(extractSelectionLine(shadowSelection)); matches.push({ type: 'preview', id: preview.id, cellId: preview.id, container: preview, isShadow: true, - originalRange: shadowSelection.getRangeAt(0) + originalRange: shadowSelection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(shadowSelection), }); } } @@ -1065,7 +1080,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: outputNode, isShadow: true, - originalRange: shadowSelection.getRangeAt(0) + originalRange: shadowSelection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(shadowSelection) }); } } @@ -1141,7 +1157,8 @@ async function webviewPreloads(ctx: PreloadContext) { type: match.type, id: match.id, cellId: match.cellId, - index + index, + searchPreviewInfo: match.searchPreviewInfo, })) }); }; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 16c66e2d35233..41c129da17bc2 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -30,9 +30,13 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +// import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; +import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; +// import { addContextToNotebookEditorMatches, notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -350,7 +354,9 @@ export class FileMatch extends Disposable implements IFileMatch { caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, includeMarkupInput: true, - includeCodeInput: true + includeMarkupPreview: true, + includeCodeInput: true, + includeOutput: true, }, CancellationToken.None); // const matches = allMatches.map((elem) => elem.matches) // .flat() @@ -366,7 +372,7 @@ export class FileMatch extends Disposable implements IFileMatch { if (!model) { return; } - // this.updateNotebookMatches(allMatches, true); + this.updateNotebookMatches(allMatches, true); } private updatesMatchesForLineAfterReplace(lineNumber: number, modelChange: boolean): void { @@ -387,31 +393,31 @@ export class FileMatch extends Disposable implements IFileMatch { this.updateMatches(matches, modelChange, this._model); } - // private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { - // if (!this._notebookEditorWidget) { - // return; - // } - - // const textSearchResults = matches.forEach((m) => notebookEditorMatchesToTextSearchResults(m.matches, this._notebookEditorWidget, this._previewOptions)); - // textSearchResults.forEach(textSearchResult => { - // textSearchResultToMatches(textSearchResult, this).forEach(match => { - // if (!this._removedMatches.has(match.id())) { - // this.add(match); - // if (this.isMatchSelected(match)) { - // this._selectedMatch = match; - // } - // } - // }); - // }); - - // this.addContext( - // addContextToNotebookEditorMatches(textSearchResults, this._notebookEditorWidget, this.parent().parent().query!) - // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) - // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); - - // this._onChange.fire({ forceUpdateModel: modelChange }); - // this.updateHighlights(); - // } + private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { + if (!this._notebookEditorWidget) { + return; + } + + const textSearchResults = notebookEditorMatchesToTextSearchResults(matches, this._notebookEditorWidget, this._previewOptions); + textSearchResults.forEach(textSearchResult => { + textSearchResultToMatches(textSearchResult, this).forEach(match => { + if (!this._removedMatches.has(match.id())) { + this.add(match); + if (this.isMatchSelected(match)) { + this._selectedMatch = match; + } + } + }); + }); + + // this.addContext( + // addContextToNotebookEditorMatches(textSearchResults, this._notebookEditorWidget, this.parent().parent().query!) + // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) + // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); + + this._onChange.fire({ forceUpdateModel: modelChange }); + // this.updateHighlights(); + } private updateMatches(matches: FindMatch[], modelChange: boolean, model: ITextModel): void { diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index 4f6c54146ef70..6efd5fd56ce24 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -3,73 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// import { FindMatch } from 'vs/editor/common/model'; -// import { CellFindMatchWithIndex, ICellViewModel, OutputFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -// import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; -// import { ITextSearchPreviewOptions, ITextSearchResult, TextSearchMatch } from 'vs/workbench/services/search/common/search'; - -// interface CellFindMatchInfoForTextModel { -// cell: ICellViewModel; -// matches: FindMatch[] | OutputFindMatch; -// } - -// function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch { -// // what do I want here? -// // I want to take all of my matches and group them -// const matches: FindMatch[] | OutputFindMatch = cellInfo.matches; -// if (matches instanceof OutputFindMatch) { - -// return new TextSearchMatch( -// lineTexts.join('\n') + '\n', -// matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), -// previewOptions); -// } else { -// const firstLine = matches[0].range.startLineNumber; -// const lastLine = matches[matches.length - 1].range.endLineNumber; -// const lineTexts: string[] = []; -// for (let i = firstLine; i <= lastLine; i++) { -// lineTexts.push(editorWidget.textModel?.getLineContent(i)); -// } - -// return new TextSearchMatch( -// lineTexts.join('\n') + '\n', -// matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), -// previewOptions); -// } - -// } - -// export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { -// let previousEndLine = -1; -// const groupedMatches: CellFindMatchInfoForTextModel[] = []; -// let currentMatches: FindMatch[] = []; - - -// cellFindMatches.forEach((cellFindMatch) => { -// cellFindMatch.matches.forEach((match) => { -// if (match instanceof FindMatch) { - -// if (match.range.startLineNumber !== previousEndLine) { -// currentMatches = []; -// groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); -// } - -// currentMatches.push(match); -// previousEndLine = match.range.endLineNumber; -// } else { -// groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); -// } -// }); - -// currentMatches = []; -// groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); -// }); - -// return groupedMatches.map(sameLineMatches => { - -// return notebookEditorMatchToTextSearchResult(sameLineMatches, editorWidget, previewOptions); -// }); -// } +import { FindMatch } from 'vs/editor/common/model'; +import { CellFindMatchWithIndex, ICellViewModel, OutputFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { ITextSearchPreviewOptions, SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search'; +import { Range } from 'vs/editor/common/core/range'; + +interface CellFindMatchInfoForTextModel { + cell: ICellViewModel; + matches: FindMatch[] | OutputFindMatch; +} + +function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch | undefined { + const matches = cellInfo.matches; + + + if (Array.isArray(matches)) { + + const firstLine = matches[0].range.startLineNumber; + const lastLine = matches[matches.length - 1].range.endLineNumber; + const lineTexts: string[] = []; + for (let i = firstLine; i <= lastLine; i++) { + if (cellInfo.cell.textModel) { + lineTexts.push(cellInfo.cell.textModel?.getLineContent(i)); + } + } + + return new TextSearchMatch( + lineTexts.join('\n') + '\n', + matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), + previewOptions); + + } + else if (matches.searchPreviewInfo) { + return new TextSearchMatch( + matches.searchPreviewInfo.line, + new Range(0, matches.searchPreviewInfo.range.start, 1, matches.searchPreviewInfo.range.end), + previewOptions); + } + + + const ranges = new SearchRange(0, 0, 1, 1); + return new TextSearchMatch( + '\n', + ranges, + previewOptions); +} +export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { + let previousEndLine = -1; + const groupedMatches: CellFindMatchInfoForTextModel[] = []; + let currentMatches: FindMatch[] = []; + + + cellFindMatches.forEach((cellFindMatch) => { + cellFindMatch.matches.forEach((match) => { + if (match instanceof FindMatch) { + + if (match.range.startLineNumber !== previousEndLine) { + currentMatches = []; + groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); + } + + currentMatches.push(match); + previousEndLine = match.range.endLineNumber; + } else { + groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); + } + }); + + currentMatches = []; + groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); + }); + + return groupedMatches.map(sameLineMatches => { + + return notebookEditorMatchToTextSearchResult(sameLineMatches, editorWidget, previewOptions); + }).filter((item): item is TextSearchMatch => !!item); +} // export function addContextToNotebookEditorMatches(matches: ITextSearchMatch[], editorWidget: NotebookEditorWidget, query: ITextQuery): ITextSearchResult[] { // const results: ITextSearchResult[] = []; From 90763cc033af22bcc9678b351f62ddb9fd6a9b52 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 23 Nov 2022 14:15:35 -0800 Subject: [PATCH 07/45] some rendered content showing in search viewlet --- .../notebook/browser/notebookBrowser.ts | 2 +- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 8 +- .../test/browser/contrib/find.test.ts | 18 ++++- .../search/browser/searchActionsBase.ts | 2 +- .../search/browser/searchActionsCopy.ts | 2 +- .../search/browser/searchActionsFind.ts | 2 +- .../search/browser/searchActionsNav.ts | 2 +- .../browser/searchActionsRemoveReplace.ts | 4 +- .../search/browser/searchActionsTopBar.ts | 2 +- .../contrib/search/browser/searchModel.ts | 33 ++------ .../search/browser/searchNotebookHelpers.ts | 78 +++++++++---------- 12 files changed, 73 insertions(+), 82 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 892f3dc94a0a1..44d318c17086d 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -711,7 +711,7 @@ export interface ISearchPreviewInfo { export interface CellWebviewFindMatch { readonly index: number; - readonly searchPreviewInfo?: ISearchPreviewInfo; + readonly searchPreviewInfo: ISearchPreviewInfo; } export type CellContentFindMatch = FindMatch; 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 3e46646d23e05..9f01237fa5378 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -424,7 +424,7 @@ export interface IFindMatch { readonly cellId: string; readonly id: string; readonly index: number; - readonly searchPreviewInfo?: ISearchPreviewInfo; + readonly searchPreviewInfo: ISearchPreviewInfo; } export interface IDidFindMessage extends BaseToWebviewMessage { 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 10348dc85ff69..0315144e1dc32 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -862,8 +862,8 @@ async function webviewPreloads(ctx: PreloadContext) { container: Node; originalRange: Range; isShadow: boolean; + searchPreviewInfo: ISearchPreviewInfo; highlightResult?: IHighlightResult; - searchPreviewInfo?: ISearchPreviewInfo; } interface ISearchPreviewInfo { @@ -1110,7 +1110,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: lastEl.cellId, container: lastEl.container, isShadow: false, - originalRange: selection.getRangeAt(0) + originalRange: selection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(selection), }); } else { @@ -1132,7 +1133,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: node, isShadow: false, - originalRange: selection.getRangeAt(0) + originalRange: selection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(selection), }); } break; diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts index 60d80fcd37d36..b941e70675e6f 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts @@ -277,9 +277,23 @@ suite('Notebook Find', () => { mdModel.contentMatches.push(new FindMatch(new Range(1, 1, 1, 2), [])); assert.strictEqual(mdModel.length, 1); mdModel.webviewMatches.push({ - index: 0 + index: 0, + searchPreviewInfo: { + line: '', + range: { + start: 0, + end: 0, + } + } }, { - index: 1 + index: 1, + searchPreviewInfo: { + line: '', + range: { + start: 0, + end: 0, + } + } }); assert.strictEqual(mdModel.length, 3); diff --git a/src/vs/workbench/contrib/search/browser/searchActionsBase.ts b/src/vs/workbench/contrib/search/browser/searchActionsBase.ts index 932b714e3cecc..8c74c35c72e6c 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsBase.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsBase.ts @@ -11,7 +11,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { IViewsService } from 'vs/workbench/common/views'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; -import { RenderableMatch, searchComparer } from 'vs/workbench/contrib/search/common/searchModel'; +import { RenderableMatch, searchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; import { ISearchConfigurationProperties, VIEW_ID } from 'vs/workbench/services/search/common/search'; export const category = { value: nls.localize('search', "Search"), original: 'Search' }; diff --git a/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts b/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts index 58db3b759d526..7191dc4b41ba8 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsCopy.ts @@ -8,7 +8,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { ILabelService } from 'vs/platform/label/common/label'; import { IViewsService } from 'vs/workbench/common/views'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; -import { FileMatch, FolderMatch, FolderMatchWithResource, Match, RenderableMatch, searchMatchComparer } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FolderMatch, FolderMatchWithResource, Match, RenderableMatch, searchMatchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; diff --git a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts index 2d8cf8d99294b..c76be757abde7 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts @@ -11,7 +11,7 @@ import { IListService, WorkbenchCompressibleObjectTree } from 'vs/platform/list/ import { IViewsService, ViewContainerLocation } from 'vs/workbench/common/views'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; -import { FileMatch, FolderMatchWithResource, Match, RenderableMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FolderMatchWithResource, Match, RenderableMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { OpenSearchEditorArgs } from 'vs/workbench/contrib/searchEditor/browser/searchEditor.contribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfiguration, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; diff --git a/src/vs/workbench/contrib/search/browser/searchActionsNav.ts b/src/vs/workbench/contrib/search/browser/searchActionsNav.ts index 77a6b79b1f1da..e49950c8eb239 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsNav.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsNav.ts @@ -12,7 +12,7 @@ import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listSe import { IViewsService } from 'vs/workbench/common/views'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import * as SearchEditorConstants from 'vs/workbench/contrib/searchEditor/browser/constants'; -import { FileMatchOrMatch, FolderMatch, RenderableMatch } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatchOrMatch, FolderMatch, RenderableMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { SearchEditor } from 'vs/workbench/contrib/searchEditor/browser/searchEditor'; import { SearchEditorInput } from 'vs/workbench/contrib/searchEditor/browser/searchEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts index 857f868b810c9..e318b33266a64 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts @@ -12,8 +12,8 @@ import { IViewsService } from 'vs/workbench/common/views'; import { searchRemoveIcon, searchReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; -import { IReplaceService } from 'vs/workbench/contrib/search/common/replace'; -import { arrayContainsElementOrParent, FileMatch, FolderMatch, Match, RenderableMatch, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; +import { arrayContainsElementOrParent, FileMatch, FolderMatch, Match, RenderableMatch, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfiguration, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; diff --git a/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts b/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts index 9c9d8c92057f9..857c4f712665e 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsTopBar.ts @@ -11,7 +11,7 @@ import { IViewsService } from 'vs/workbench/common/views'; import { searchClearIcon, searchCollapseAllIcon, searchExpandAllIcon, searchRefreshIcon, searchShowAsList, searchShowAsTree, searchStopIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { ISearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot, Match, SearchResult } from 'vs/workbench/contrib/search/common/searchModel'; +import { FileMatch, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot, Match, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { VIEW_ID } from 'vs/workbench/services/search/common/search'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index e5e92b1cd28ba..98a34d2ea8c4d 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -51,7 +51,7 @@ export class Match { // For replace private _fullPreviewRange: ISearchRange; - constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange, private _cellFragment?: string) { + constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange) { this._oneLinePreviewText = _fullPreviewLines[_fullPreviewRange.startLineNumber]; const adjustedEndCol = _fullPreviewRange.startLineNumber === _fullPreviewRange.endLineNumber ? _fullPreviewRange.endColumn : @@ -85,10 +85,6 @@ export class Match { return this._range; } - fragment(): string | undefined { - return this._cellFragment; - } - @memoize preview(): { before: string; inside: string; after: string } { let before = this._oneLinePreviewText.substring(0, this._rangeInPreviewText.startColumn - 1), @@ -352,25 +348,12 @@ export class FileMatch extends Disposable implements IFileMatch { wholeWord: this._query.isWordMatch, caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, - includeMarkupInput: true, + includeMarkupInput: false, includeMarkupPreview: true, includeCodeInput: true, includeOutput: true, }, CancellationToken.None); - // const matches = allMatches.map((elem) => elem.matches) - // .flat() - // .map((elem) => { - // if (elem instanceof FindMatch) { - // return elem; - // } else { - // return undefined; - // } - // }).filter((e): e is FindMatch => !!FindMatch); - console.log(allMatches); - const model = this._notebookEditorWidget.textModel; - if (!model) { - return; - } + this.updateNotebookMatches(allMatches, true); } @@ -397,7 +380,7 @@ export class FileMatch extends Disposable implements IFileMatch { return; } - const textSearchResults = notebookEditorMatchesToTextSearchResults(matches, this._notebookEditorWidget, this._previewOptions); + const textSearchResults = notebookEditorMatchesToTextSearchResults(matches, this._previewOptions); textSearchResults.forEach(textSearchResult => { textSearchResultToMatches(textSearchResult, this).forEach(match => { if (!this._removedMatches.has(match.id())) { @@ -409,10 +392,10 @@ export class FileMatch extends Disposable implements IFileMatch { }); }); - // this.addContext( - // addContextToNotebookEditorMatches(textSearchResults, this._notebookEditorWidget, this.parent().parent().query!) - // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) - // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); + // this.addContext( + // addContextToEditorMatches(textSearchResults, model, this.parent().parent().query!) + // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) + // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); this._onChange.fire({ forceUpdateModel: modelChange }); // this.updateHighlights(); diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index 6efd5fd56ce24..24823acfdae48 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -4,81 +4,73 @@ *--------------------------------------------------------------------------------------------*/ import { FindMatch } from 'vs/editor/common/model'; -import { CellFindMatchWithIndex, ICellViewModel, OutputFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; -import { ITextSearchPreviewOptions, SearchRange, TextSearchMatch } from 'vs/workbench/services/search/common/search'; +import { CellFindMatchWithIndex, ICellViewModel, CellWebviewFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; + +import { ITextSearchPreviewOptions, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { Range } from 'vs/editor/common/core/range'; interface CellFindMatchInfoForTextModel { cell: ICellViewModel; - matches: FindMatch[] | OutputFindMatch; + matches: FindMatch[] | CellWebviewFindMatch; } -function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch | undefined { +function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch | undefined { const matches = cellInfo.matches; if (Array.isArray(matches)) { - - const firstLine = matches[0].range.startLineNumber; - const lastLine = matches[matches.length - 1].range.endLineNumber; - const lineTexts: string[] = []; - for (let i = firstLine; i <= lastLine; i++) { - if (cellInfo.cell.textModel) { - lineTexts.push(cellInfo.cell.textModel?.getLineContent(i)); + if (matches.length > 0) { + const lineTexts: string[] = []; + const firstLine = matches[0].range.startLineNumber; + const lastLine = matches[matches.length - 1].range.endLineNumber; + for (let i = firstLine; i <= lastLine; i++) { + if (cellInfo.cell.textModel) { + lineTexts.push(cellInfo.cell.textModel?.getLineContent(i)); + } } - } - - return new TextSearchMatch( - lineTexts.join('\n') + '\n', - matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), - previewOptions); + return new TextSearchMatch( + lineTexts.join('\n') + '\n', + matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), + previewOptions); + } } - else if (matches.searchPreviewInfo) { + else { return new TextSearchMatch( matches.searchPreviewInfo.line, - new Range(0, matches.searchPreviewInfo.range.start, 1, matches.searchPreviewInfo.range.end), + new Range(0, matches.searchPreviewInfo.range.start, 0, matches.searchPreviewInfo.range.end), previewOptions); } - - - const ranges = new SearchRange(0, 0, 1, 1); - return new TextSearchMatch( - '\n', - ranges, - previewOptions); + return undefined; } -export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], editorWidget: NotebookEditorWidget, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { +export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { let previousEndLine = -1; const groupedMatches: CellFindMatchInfoForTextModel[] = []; let currentMatches: FindMatch[] = []; cellFindMatches.forEach((cellFindMatch) => { - cellFindMatch.matches.forEach((match) => { - if (match instanceof FindMatch) { - - if (match.range.startLineNumber !== previousEndLine) { - currentMatches = []; - groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); - } - - currentMatches.push(match); - previousEndLine = match.range.endLineNumber; - } else { - groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); + cellFindMatch.contentMatches.forEach((match) => { + if (match.range.startLineNumber !== previousEndLine) { + currentMatches = []; + groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); } + + currentMatches.push(match); + previousEndLine = match.range.endLineNumber; }); currentMatches = []; groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); + + cellFindMatch.webviewMatches.forEach((match) => { + groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); + }); }); return groupedMatches.map(sameLineMatches => { - - return notebookEditorMatchToTextSearchResult(sameLineMatches, editorWidget, previewOptions); - }).filter((item): item is TextSearchMatch => !!item); + return notebookEditorMatchToTextSearchResult(sameLineMatches, previewOptions); + }).filter((elem): elem is TextSearchMatch => !!elem); } // export function addContextToNotebookEditorMatches(matches: ITextSearchMatch[], editorWidget: NotebookEditorWidget, query: ITextQuery): ITextSearchResult[] { From e37722ed071d783db33970ac0f42e0a97453ee43 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 29 Nov 2022 16:33:25 -0800 Subject: [PATCH 08/45] rough draft of notebook search --- .../browser/contrib/find/findModel.ts | 2 +- .../notebook/browser/notebookBrowser.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/services/notebookEditorService.ts | 2 +- .../services/notebookEditorServiceImpl.ts | 10 + .../browser/view/renderers/webviewPreloads.ts | 6 +- .../contrib/search/browser/searchModel.ts | 218 ++++++++++++++++-- .../search/browser/searchNotebookHelpers.ts | 91 ++++---- .../contrib/search/browser/searchView.ts | 16 +- .../browser/links/terminalLinkOpeners.test.ts | 2 +- .../services/search/browser/searchService.ts | 5 +- .../services/search/common/search.ts | 5 +- .../services/search/common/searchService.ts | 44 +++- 13 files changed, 323 insertions(+), 82 deletions(-) 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 0419aa607fc88..9a6147ea54202 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -491,7 +491,7 @@ export class FindModel extends Disposable { this.clearCurrentFindMatchDecoration(); const match = this._findMatches[cellIndex].getMatch(matchIndex) as CellWebviewFindMatch; - const offset = await this._notebookEditor.highlightFind(cell, match.index); + const offset = await this._notebookEditor.highlightFind(match.index); this._currentMatchDecorations = { kind: 'output', index: match.index }; this._currentMatchCellDecorations = this._notebookEditor.deltaCellDecorations(this._currentMatchCellDecorations, [{ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 44d318c17086d..f6019312b2b9b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -637,7 +637,7 @@ export interface INotebookEditor { getNextVisibleCellIndex(index: number): number | undefined; getPreviousVisibleCellIndex(index: number): number | undefined; find(query: string, options: INotebookSearchOptions, token: CancellationToken): Promise; - highlightFind(cell: ICellViewModel, matchIndex: number): Promise; + highlightFind(matchIndex: number): Promise; unHighlightFind(matchIndex: number): Promise; findStop(): void; showProgress(): void; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 9cc761c679116..61c29f7435b56 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2464,7 +2464,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return ret; } - async highlightFind(cell: CodeCellViewModel, matchIndex: number): Promise { + async highlightFind(matchIndex: number): Promise { if (!this._webview) { return 0; } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts index 2e7b1453f42b4..97bd92d99856b 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts @@ -23,7 +23,7 @@ export interface INotebookEditorService { retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, dimension?: Dimension): IBorrowValue; retrieveExistingWidgetFromURI(resource: URI): IBorrowValue | undefined; - + retrieveAllExistingWidgets(): IBorrowValue[]; onDidAddNotebookEditor: Event; onDidRemoveNotebookEditor: Event; onDidAddNotebookEditorWidget: Event; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index c9069cf5a8100..a1fbf03208a9c 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -143,6 +143,16 @@ export class NotebookEditorWidgetService implements INotebookEditorService { return undefined; } + retrieveAllExistingWidgets(): IBorrowValue[] { + const ret: IBorrowValue[] = []; + for (const widgetInfo of this._borrowableEditors.values()) { + for (const widget of widgetInfo.values()) { + ret.push(this._createBorrowValue(widget.token!, widget)); + } + } + return ret; + } + retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, initialDimension?: Dimension): IBorrowValue { let value = this._borrowableEditors.get(group.id)?.get(input.resource); 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 0315144e1dc32..8ac1c97a597d8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -973,6 +973,7 @@ async function webviewPreloads(ctx: PreloadContext) { if (match) { let offset = 0; try { + console.log(match.id); const outputOffset = document.getElementById(match.id)!.getBoundingClientRect().top; const rangeOffset = match.originalRange.getBoundingClientRect().top; offset = rangeOffset - outputOffset; @@ -1082,7 +1083,6 @@ async function webviewPreloads(ctx: PreloadContext) { const root = outputNode.shadowRoot as ShadowRoot & { getSelection: () => Selection }; const shadowSelection = root?.getSelection ? root?.getSelection() : null; if (shadowSelection && shadowSelection.anchorNode) { - console.log(extractSelectionLine(shadowSelection)); matches.push({ type: 'output', id: outputNode.id, @@ -1102,8 +1102,6 @@ async function webviewPreloads(ctx: PreloadContext) { // Optimization: avoid searching for the output container if (lastEl && lastEl.container.contains(anchorNode) && options.includeOutput) { - console.log('here1'); - console.log(lastEl); matches.push({ type: lastEl.type, id: lastEl.id, @@ -1125,8 +1123,6 @@ async function webviewPreloads(ctx: PreloadContext) { // inside output const cellId = node.parentElement?.parentElement?.id; if (cellId) { - console.log('here2'); - console.log(node); matches.push({ type: 'output', id: node.id, diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 98a34d2ea8c4d..37d601c5b7693 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -20,21 +20,22 @@ import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/model'; +import { FindDecorations } from 'vs/editor/contrib/find/browser/findDecorations'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; +import { minimapFindMatch, overviewRulerFindMatchForeground, overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; // import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; -import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; +import { notebookEditorMatchesToTextSearchResults, NotebookMatchInfo, NotebookTextSearchMatch } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; // import { addContextToNotebookEditorMatches, notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; @@ -43,15 +44,15 @@ import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/ export class Match { private static readonly MAX_PREVIEW_CHARS = 250; - - private _id: string; - private _range: Range; + // need to make sure that IDs are different for same-line-different-cell stuff + protected _id: string; + protected _range: Range; private _oneLinePreviewText: string; private _rangeInPreviewText: ISearchRange; // For replace private _fullPreviewRange: ISearchRange; - constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange) { + constructor(protected _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange) { this._oneLinePreviewText = _fullPreviewLines[_fullPreviewRange.startLineNumber]; const adjustedEndCol = _fullPreviewRange.startLineNumber === _fullPreviewRange.endLineNumber ? _fullPreviewRange.endColumn : @@ -166,6 +167,47 @@ export class Match { } } +export class NotebookMatch extends Match { + constructor(_parent: FileMatch, _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange, private _notebookMatchInfo: NotebookMatchInfo) { + super(_parent, _fullPreviewLines, _fullPreviewRange, _documentRange); + + this._id = this._parent.id() + '>' + this._notebookMatchInfo.cellIndex + '_' + this.notebookMatchTypeString() + this.getRangeString() + this._range + this.getMatchString(); + } + + private notebookMatchTypeString(): string { + return this.isWebviewMatch() ? 'webview' : 'content'; + } + + private getRangeString(): string { + return `[${this._notebookMatchInfo.matchStartIndex},${this._notebookMatchInfo.matchStartIndex}]`; + } + + public isWebviewMatch() { + return this._notebookMatchInfo.webviewMatchInfo !== undefined; + } + + get cellIndex() { + return this._notebookMatchInfo.cellIndex; + } + + get matchStartIndex() { + return this._notebookMatchInfo.matchStartIndex; + } + + get matchEndIndex() { + return this._notebookMatchInfo.matchEndIndex; + } + + get webviewIndex() { + return this._notebookMatchInfo.webviewMatchInfo?.index; + } + + get cell() { + return this._notebookMatchInfo.cell; + } +} + + export class FileMatch extends Disposable implements IFileMatch { private static readonly _CURRENT_FIND_MATCH = ModelDecorationOptions.register({ @@ -220,6 +262,8 @@ export class FileMatch extends Disposable implements IFileMatch { private _updateScheduler: RunOnceScheduler; private _modelDecorations: string[] = []; + private _currentMatchCellDecorations: string[] = []; + private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; private _context: Map = new Map(); public get context(): Map { @@ -376,13 +420,9 @@ export class FileMatch extends Disposable implements IFileMatch { } private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { - if (!this._notebookEditorWidget) { - return; - } - const textSearchResults = notebookEditorMatchesToTextSearchResults(matches, this._previewOptions); textSearchResults.forEach(textSearchResult => { - textSearchResultToMatches(textSearchResult, this).forEach(match => { + textSearchResultToNotebookMatches(textSearchResult, this).forEach(match => { if (!this._removedMatches.has(match.id())) { this.add(match); if (this.isMatchSelected(match)) { @@ -451,7 +491,9 @@ export class FileMatch extends Disposable implements IFileMatch { } matches(): Match[] { - return Array.from(this._matches.values()); + const vals = this._matches.values(); + const arr = Array.from(vals); + return arr; } remove(matches: Match | Match[]): void { @@ -551,6 +593,113 @@ export class FileMatch extends Disposable implements IFileMatch { this._onDispose.fire(); super.dispose(); } + + public async showMatch(match: NotebookMatch) { + const offset = await this.highlightCurrentFindMatchDecoration(match); + await this.revealCellRange(match, offset); + } + + private async highlightCurrentFindMatchDecoration(match: NotebookMatch): Promise { + if (!this._notebookEditorWidget) { + return Promise.resolve(null); + } + // needs + // notebook cell + // only cellmatch: + // match range + // only webviewmatch: + // + + if (!match.webviewIndex) { + this.clearCurrentFindMatchDecoration(); + // match is an editor FindMatch, we update find match decoration in the editor + // we will highlight the match in the webview + this._notebookEditorWidget.changeModelDecorations(accessor => { + const findMatchesOptions: ModelDecorationOptions = FindDecorations._CURRENT_FIND_MATCH_DECORATION; + + const decorations: IModelDeltaDecoration[] = [ + { range: match.range(), options: findMatchesOptions } + ]; + const deltaDecoration: ICellModelDeltaDecorations = { + ownerId: match.cell.handle, + decorations: decorations + }; + + this._currentMatchDecorations = { + kind: 'input', + decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], [deltaDecoration]) + }; + }); + + this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, [{ + ownerId: match.cell.handle, + handle: match.cell.handle, + options: { + overviewRuler: { + color: overviewRulerSelectionHighlightForeground, + modelRanges: [match.range()], + includeOutput: false + } + } + } as INotebookDeltaDecoration]); + + return null; + } else { + this.clearCurrentFindMatchDecoration(); + const offset = await this._notebookEditorWidget.highlightFind(match.webviewIndex); + this._currentMatchDecorations = { kind: 'output', index: match.webviewIndex }; + + this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, [{ + ownerId: match.cell.handle, + handle: match.cell.handle, + options: { + overviewRuler: { + color: overviewRulerSelectionHighlightForeground, + modelRanges: [], + includeOutput: true + } + } + } as INotebookDeltaDecoration]); + + return offset; + } + } + + private clearCurrentFindMatchDecoration() { + if (!this._notebookEditorWidget) { + return; + } + + if (this._currentMatchDecorations?.kind === 'input') { + this._notebookEditorWidget.changeModelDecorations(accessor => { + accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []); + this._currentMatchDecorations = null; + }); + } else if (this._currentMatchDecorations?.kind === 'output') { + this._notebookEditorWidget.unHighlightFind(this._currentMatchDecorations.index); + } + + this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, []); + } + + private revealCellRange(match: NotebookMatch, outputOffset: number | null) { + if (!this._notebookEditorWidget) { + return; + } + if (!match.webviewIndex) { + // reveal output range + this._notebookEditorWidget.focusElement(match.cell); + const index = this._notebookEditorWidget.getCellIndex(match.cell); + if (index !== undefined) { + // const range: ICellRange = { start: index, end: index + 1 }; + this._notebookEditorWidget.revealCellOffsetInCenterAsync(match.cell, outputOffset ?? 0); + } + } else { + this._notebookEditorWidget.focusElement(match.cell); + this._notebookEditorWidget.setCellEditorSelection(match.cell, match.range()); + this._notebookEditorWidget.revealRangeInCenterIfOutsideViewportAsync(match.cell, match.range()); + } + } } export interface IChangeEvent { @@ -1098,6 +1247,10 @@ export function searchMatchComparer(elementA: RenderableMatch, elementB: Rendera } } + if (elementA instanceof NotebookMatch && elementB instanceof NotebookMatch) { + return compareNotebookPos(elementA, elementB); + } + if (elementA instanceof Match && elementB instanceof Match) { return Range.compareRangesUsingStarts(elementA.range(), elementB.range()); } @@ -1105,6 +1258,27 @@ export function searchMatchComparer(elementA: RenderableMatch, elementB: Rendera return 0; } +export function compareNotebookPos(match1: NotebookMatch, match2: NotebookMatch): number { + if (match1.cellIndex === match2.cellIndex) { + if (match1.matchStartIndex === match2.matchStartIndex) { + if (match1.matchEndIndex === match2.matchEndIndex) { + return 0; + } else if (match1.matchEndIndex < match2.matchEndIndex) { + return -1; + } else { + return 1; + } + } else if (match1.matchStartIndex < match2.matchStartIndex) { + return -1; + } else { + return 1; + } + } else if (match1.cellIndex < match2.cellIndex) { + return -1; + } else { + return 1; + } +} export function searchComparer(elementA: RenderableMatch, elementB: RenderableMatch, sortOrder: SearchSortOrder = SearchSortOrder.Default): number { const elemAParents = createParentList(elementA); const elemBParents = createParentList(elementB); @@ -1174,9 +1348,6 @@ export class SearchResult extends Disposable { this._isDirty = !this.isEmpty(); } })); - - - // this._notebookEditors = new Set(); } async batchReplace(elementsToReplace: RenderableMatch[]) { @@ -1830,6 +2001,21 @@ function textSearchResultToMatches(rawMatch: ITextSearchMatch, fileMatch: FileMa } } +function textSearchResultToNotebookMatches(rawMatch: NotebookTextSearchMatch, fileMatch: FileMatch): NotebookMatch[] { + const previewLines = rawMatch.preview.text.split('\n'); + if (Array.isArray(rawMatch.ranges)) { + + return rawMatch.ranges.map((r, i) => { + const previewRange: ISearchRange = (rawMatch.preview.matches)[i]; + return new NotebookMatch(fileMatch, previewLines, previewRange, r, rawMatch.notebookMatchInfo); + }); + } else { + const previewRange = rawMatch.preview.matches; + const match = new NotebookMatch(fileMatch, previewLines, previewRange, rawMatch.ranges, rawMatch.notebookMatchInfo); + return [match]; + } +} + export function arrayContainsElementOrParent(element: RenderableMatch, testArray: RenderableMatch[]): boolean { do { if (testArray.includes(element)) { diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index 24823acfdae48..dad92876e1d5a 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -6,17 +6,32 @@ import { FindMatch } from 'vs/editor/common/model'; import { CellFindMatchWithIndex, ICellViewModel, CellWebviewFindMatch } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { ITextSearchPreviewOptions, TextSearchMatch } from 'vs/workbench/services/search/common/search'; +import { ISearchRange, ITextSearchPreviewOptions, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { Range } from 'vs/editor/common/core/range'; -interface CellFindMatchInfoForTextModel { +export interface NotebookMatchInfo { + cellIndex: number; + matchStartIndex: number; + matchEndIndex: number; cell: ICellViewModel; + webviewMatchInfo?: { + index: number; + }; +} + +interface CellFindMatchInfoForTextModel { + notebookMatchInfo: NotebookMatchInfo; matches: FindMatch[] | CellWebviewFindMatch; } -function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch | undefined { - const matches = cellInfo.matches; +export class NotebookTextSearchMatch extends TextSearchMatch { + constructor(text: string, range: ISearchRange | ISearchRange[], public notebookMatchInfo: NotebookMatchInfo, previewOptions?: ITextSearchPreviewOptions) { + super(text, range, previewOptions); + } +} +function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTextModel, previewOptions?: ITextSearchPreviewOptions): NotebookTextSearchMatch | undefined { + const matches = cellInfo.matches; if (Array.isArray(matches)) { if (matches.length > 0) { @@ -24,36 +39,39 @@ function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTex const firstLine = matches[0].range.startLineNumber; const lastLine = matches[matches.length - 1].range.endLineNumber; for (let i = firstLine; i <= lastLine; i++) { - if (cellInfo.cell.textModel) { - lineTexts.push(cellInfo.cell.textModel?.getLineContent(i)); - } + lineTexts.push(cellInfo.notebookMatchInfo.cell.textBuffer.getLineContent(i)); } - return new TextSearchMatch( + return new NotebookTextSearchMatch( lineTexts.join('\n') + '\n', matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), + cellInfo.notebookMatchInfo, previewOptions); } } else { - return new TextSearchMatch( + return new NotebookTextSearchMatch( matches.searchPreviewInfo.line, new Range(0, matches.searchPreviewInfo.range.start, 0, matches.searchPreviewInfo.range.end), + cellInfo.notebookMatchInfo, previewOptions); } return undefined; } -export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { +export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFindMatchWithIndex[], previewOptions?: ITextSearchPreviewOptions): NotebookTextSearchMatch[] { let previousEndLine = -1; const groupedMatches: CellFindMatchInfoForTextModel[] = []; let currentMatches: FindMatch[] = []; + let startIndexOfCurrentMatches = 0; cellFindMatches.forEach((cellFindMatch) => { - cellFindMatch.contentMatches.forEach((match) => { + const cellIndex = cellFindMatch.index; + cellFindMatch.contentMatches.forEach((match, index) => { if (match.range.startLineNumber !== previousEndLine) { currentMatches = []; - groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); + groupedMatches.push({ matches: currentMatches, notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: index, cell: cellFindMatch.cell } }); + startIndexOfCurrentMatches = cellIndex + 1; } currentMatches.push(match); @@ -61,50 +79,31 @@ export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFi }); currentMatches = []; - groupedMatches.push({ cell: cellFindMatch.cell, matches: currentMatches }); + groupedMatches.push({ matches: currentMatches, notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: cellFindMatch.contentMatches.length - 1, cell: cellFindMatch.cell } }); - cellFindMatch.webviewMatches.forEach((match) => { - groupedMatches.push({ cell: cellFindMatch.cell, matches: match }); + cellFindMatch.webviewMatches.forEach((match, index) => { + groupedMatches.push({ matches: match, notebookMatchInfo: { cellIndex, matchStartIndex: index, matchEndIndex: index, cell: cellFindMatch.cell, webviewMatchInfo: { index: match.index } } }); }); }); return groupedMatches.map(sameLineMatches => { return notebookEditorMatchToTextSearchResult(sameLineMatches, previewOptions); - }).filter((elem): elem is TextSearchMatch => !!elem); + }).filter((elem): elem is NotebookTextSearchMatch => !!elem); } -// export function addContextToNotebookEditorMatches(matches: ITextSearchMatch[], editorWidget: NotebookEditorWidget, query: ITextQuery): ITextSearchResult[] { +// export function addContextToNotebookEditorMatches(cellFindMatches: CellFindMatchWithIndex[], query: ITextQuery): ITextSearchResult[] { // const results: ITextSearchResult[] = []; -// let prevLine = -1; -// for (let i = 0; i < matches.length; i++) { -// const { start: matchStartLine, end: matchEndLine } = getMatchStartEnd(matches[i]); -// if (typeof query.beforeContext === 'number' && query.beforeContext > 0) { -// const beforeContextStartLine = Math.max(prevLine + 1, matchStartLine - query.beforeContext); -// for (let b = beforeContextStartLine; b < matchStartLine; b++) { -// results.push({ -// text: model.getLineContent(b + 1), -// lineNumber: b -// }); -// } -// } - -// results.push(matches[i]); - -// const nextMatch = matches[i + 1]; -// const nextMatchStartLine = nextMatch ? getMatchStartEnd(nextMatch).start : Number.MAX_VALUE; -// if (typeof query.afterContext === 'number' && query.afterContext > 0) { -// const afterContextToLine = Math.min(nextMatchStartLine - 1, matchEndLine + query.afterContext, model.getLineCount() - 1); -// for (let a = matchEndLine + 1; a <= afterContextToLine; a++) { -// results.push({ -// text: model.getLineContent(a + 1), -// lineNumber: a -// }); -// } -// } - -// prevLine = matchEndLine; -// } +// // we need a cell and all of its corresponding textmatches +// // ^ we need this split by webview and content matches + +// // +// cellFindMatches.forEach(findMatch => { +// findMatch.contentMatches.forEach(contentMatch => { +// const result = addContextToEditorMatches() +// }) +// }) + // return results; // } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index e03faef512fe6..03aaae8497ca9 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -73,7 +73,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { getOutOfWorkspaceEditorResources, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, NotebookMatch, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -1797,8 +1797,18 @@ export class SearchView extends ViewPane { if (editor instanceof NotebookEditor) { const controller = editor.getControl()?.getContribution(NotebookFindContrib.id); - const matchIndex = element instanceof Match ? element.parent().matches().findIndex(e => e.id() === element.id()) : undefined; - controller?.show(this.searchWidget.searchInput.getValue(), { matchIndex, focus: false }); + if (element instanceof Match) { + const matchIndex = element.parent().matches().findIndex(e => e.id() === element.id()); + if (element instanceof NotebookMatch) { + + // no op for now! + element.parent().showMatch(element); + } else { + + controller?.show(this.searchWidget.searchInput.getValue(), { matchIndex, focus: false }); + } + } + } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index 528ca7290a2f0..d45523d993920 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -77,7 +77,7 @@ suite('Workbench - TerminalLinkOpeners', () => { setup(() => { instantiationService = new TestInstantiationService(); fileService = new TestFileService(new NullLogService()); - searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!); + searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!, null!); instantiationService.set(IFileService, fileService); instantiationService.set(ILogService, new NullLogService()); instantiationService.set(ISearchService, searchService); diff --git a/src/vs/workbench/services/search/browser/searchService.ts b/src/vs/workbench/services/search/browser/searchService.ts index bea85f71a437d..4b6affdfce96d 100644 --- a/src/vs/workbench/services/search/browser/searchService.ts +++ b/src/vs/workbench/services/search/browser/searchService.ts @@ -27,6 +27,8 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; + export class RemoteSearchService extends SearchService { constructor( @IModelService modelService: IModelService, @@ -37,8 +39,9 @@ export class RemoteSearchService extends SearchService { @IFileService fileService: IFileService, @IInstantiationService readonly instantiationService: IInstantiationService, @IUriIdentityService uriIdentityService: IUriIdentityService, + @INotebookEditorService notebookEditorService: INotebookEditorService ) { - super(modelService, editorService, telemetryService, logService, extensionService, fileService, uriIdentityService); + super(modelService, editorService, telemetryService, logService, extensionService, fileService, uriIdentityService, notebookEditorService); const searchProvider = this.instantiationService.createInstance(LocalFileSearchWorkerClient); this.registerSearchResultProvider(Schemas.file, SearchProviderType.file, searchProvider); this.registerSearchResultProvider(Schemas.file, SearchProviderType.text, searchProvider); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 1d0cdb843fc1c..a12c62f5e4817 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -181,7 +181,6 @@ export interface ITextSearchMatch { uri?: URI; ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; - cellFragment?: string; } export interface ITextSearchContext { @@ -275,11 +274,9 @@ export class FileMatch implements IFileMatch { export class TextSearchMatch implements ITextSearchMatch { ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; - cellFragment?: string; - constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions, cellFragment?: string) { + constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) { this.ranges = range; - this.cellFragment = cellFragment; // Trim preview if this is one match and a single-line match with a preview requested. // Otherwise send the full text, like for replace or for showing multiple previews. diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 874de93dad5e7..de72198944c5d 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -19,6 +19,10 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; + +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; + import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, isFileMatch, isProgressMessage, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; @@ -44,6 +48,7 @@ export class SearchService extends Disposable implements ISearchService { @IExtensionService private readonly extensionService: IExtensionService, @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); } @@ -75,7 +80,7 @@ export class SearchService extends Disposable implements ISearchService { async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { // Get local results from dirty/untitled - const localResults = this.getLocalResults(query); + const localResults = await this.getLocalResults(query); if (onProgress) { arrays.coalesce([...localResults.results.values()]).forEach(onProgress); @@ -404,7 +409,7 @@ export class SearchService extends Disposable implements ISearchService { } } - private getLocalResults(query: ITextQuery): { results: ResourceMap; limitHit: boolean } { + private async getLocalResults(query: ITextQuery): Promise<{ results: ResourceMap; limitHit: boolean }> { const localResults = new ResourceMap(uri => this.uriIdentityService.extUri.getComparisonKey(uri)); let limitHit = false; @@ -473,6 +478,41 @@ export class SearchService extends Disposable implements ISearchService { localResults.set(originalResource, null); } }); + + + const notebookWidgets = this.notebookEditorService.retrieveAllExistingWidgets(); + for (const borrowWidget of notebookWidgets) { + const widget = borrowWidget.value; + if (!widget || !widget.viewModel) { + continue; + } + const askMax = isNumber(query.maxResults) ? query.maxResults + 1 : Number.MAX_SAFE_INTEGER; + let matches = await widget + .find(query.contentPattern.pattern, { + regex: query.contentPattern.isRegExp, + wholeWord: query.contentPattern.isWordMatch, + caseSensitive: query.contentPattern.isCaseSensitive, + includeMarkupInput: false, + includeMarkupPreview: true, + includeCodeInput: true, + includeOutput: true, + }, CancellationToken.None); + + + if (matches.length) { + if (askMax && matches.length >= askMax) { + limitHit = true; + matches = matches.slice(0, askMax - 1); + } + + const fileMatch = new FileMatch(widget.viewModel.uri); + localResults.set(widget.viewModel.uri, fileMatch); + + fileMatch.results = notebookEditorMatchesToTextSearchResults(matches, query.previewOptions); + } else { + localResults.set(widget.viewModel.uri, null); + } + } } return { From d20ea1d394b0d571ba20f508b12dbeb991c39c72 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 2 Dec 2022 13:15:47 -0800 Subject: [PATCH 09/45] move local notebook search out of searchservice --- .../contrib/search/browser/searchModel.ts | 99 ++++++++++++++++--- .../browser/links/terminalLinkOpeners.test.ts | 2 +- .../services/search/browser/searchService.ts | 6 +- .../services/search/common/search.ts | 2 +- .../services/search/common/searchService.ts | 78 +++++++-------- 5 files changed, 130 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 37d601c5b7693..27e56b0768096 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as arrays from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { compareFileExtensions, compareFileNames, comparePaths } from 'vs/base/common/comparers'; @@ -11,10 +12,11 @@ import * as errors from 'vs/base/common/errors'; import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { ResourceMap } from 'vs/base/common/map'; +import { ResourceMap, ResourceSet } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { lcut } from 'vs/base/common/strings'; import { TernarySearchTree } from 'vs/base/common/ternarySearchTree'; +import { isNumber } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; @@ -38,7 +40,7 @@ import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { notebookEditorMatchesToTextSearchResults, NotebookMatchInfo, NotebookTextSearchMatch } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; // import { addContextToNotebookEditorMatches, notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, QueryType, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class Match { @@ -1693,7 +1695,9 @@ export class SearchModel extends Disposable { @ISearchService private readonly searchService: ISearchService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); this._searchResult = this.instantiationService.createInstance(SearchResult, this); @@ -1735,6 +1739,84 @@ export class SearchModel extends Disposable { return this._searchResult; } + private async getLocalNotebookResults(query: ITextQuery): Promise<{ results: ResourceMap; limitHit: boolean }> { + const localResults = new ResourceMap(uri => this.uriIdentityService.extUri.getComparisonKey(uri)); + let limitHit = false; + + if (query.type === QueryType.Text) { + const notebookWidgets = this.notebookEditorService.retrieveAllExistingWidgets(); + for (const borrowWidget of notebookWidgets) { + const widget = borrowWidget.value; + if (!widget || !widget.viewModel) { + continue; + } + + const askMax = isNumber(query.maxResults) ? query.maxResults + 1 : Number.MAX_SAFE_INTEGER; + let matches = await widget + .find(query.contentPattern.pattern, { + regex: query.contentPattern.isRegExp, + wholeWord: query.contentPattern.isWordMatch, + caseSensitive: query.contentPattern.isCaseSensitive, + includeMarkupInput: false, + includeMarkupPreview: true, + includeCodeInput: true, + includeOutput: true, + }, CancellationToken.None); + + + if (matches.length) { + if (askMax && matches.length >= askMax) { + limitHit = true; + matches = matches.slice(0, askMax - 1); + } + const fileMatch = { resource: widget.viewModel.uri, results: notebookEditorMatchesToTextSearchResults(matches, query.previewOptions) }; + localResults.set(widget.viewModel.uri, fileMatch); + } else { + localResults.set(widget.viewModel.uri, null); + } + } + } + + return { + results: localResults, + limitHit + }; + } + + async notebookSearch(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { + const localResults = await this.getLocalNotebookResults(query); + + if (onProgress) { + arrays.coalesce([...localResults.results.values()]).forEach(onProgress); + } + + return { + messages: [], + limitHit: localResults.limitHit, + results: arrays.coalesce([...localResults.results.values()]) + }; + } + + + private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, onProgress?: (result: ISearchProgressItem) => void) { + const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); + const onProgressCall = (p: ISearchProgressItem) => { + progressEmitter.fire(); + this.onSearchProgress(p); + + onProgress?.(p); + }; + const notebookResult = await this.notebookSearch(query, onProgressCall); + const currentResult = await this.searchService.textSearch( + searchQuery, + this.currentCancelTokenSource.token, onProgressCall, + new ResourceSet(notebookResult.results.map(r => r.resource, this.uriIdentityService.extUri.ignorePathCasing), uri => this.uriIdentityService.extUri.getComparisonKey(uri)) + ); + tokenSource.dispose(); + return { ...currentResult, ...notebookResult }; + + } + async search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { this.cancelSearch(true); @@ -1751,16 +1833,7 @@ export class SearchModel extends Disposable { // In search on type case, delay the streaming of results just a bit, so that we don't flash the only "local results" fast path this._startStreamDelay = new Promise(resolve => setTimeout(resolve, this.searchConfig.searchOnType ? 150 : 0)); - const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); - const currentRequest = this.searchService.textSearch(this._searchQuery, this.currentCancelTokenSource.token, p => { - progressEmitter.fire(); - this.onSearchProgress(p); - - onProgress?.(p); - }); - - const dispose = () => tokenSource.dispose(); - currentRequest.then(dispose, dispose); + const currentRequest = this.doSearch(query, progressEmitter, this._searchQuery, onProgress); const start = Date.now(); diff --git a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts index d45523d993920..528ca7290a2f0 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/links/terminalLinkOpeners.test.ts @@ -77,7 +77,7 @@ suite('Workbench - TerminalLinkOpeners', () => { setup(() => { instantiationService = new TestInstantiationService(); fileService = new TestFileService(new NullLogService()); - searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!, null!); + searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!); instantiationService.set(IFileService, fileService); instantiationService.set(ILogService, new NullLogService()); instantiationService.set(ISearchService, searchService); diff --git a/src/vs/workbench/services/search/browser/searchService.ts b/src/vs/workbench/services/search/browser/searchService.ts index 4b6affdfce96d..74a704e331a43 100644 --- a/src/vs/workbench/services/search/browser/searchService.ts +++ b/src/vs/workbench/services/search/browser/searchService.ts @@ -27,7 +27,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; -import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +// import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; export class RemoteSearchService extends SearchService { constructor( @@ -39,9 +39,9 @@ export class RemoteSearchService extends SearchService { @IFileService fileService: IFileService, @IInstantiationService readonly instantiationService: IInstantiationService, @IUriIdentityService uriIdentityService: IUriIdentityService, - @INotebookEditorService notebookEditorService: INotebookEditorService + // @INotebookEditorService notebookEditorService: INotebookEditorService ) { - super(modelService, editorService, telemetryService, logService, extensionService, fileService, uriIdentityService, notebookEditorService); + super(modelService, editorService, telemetryService, logService, extensionService, fileService, uriIdentityService); const searchProvider = this.instantiationService.createInstance(LocalFileSearchWorkerClient); this.registerSearchResultProvider(Schemas.file, SearchProviderType.file, searchProvider); this.registerSearchResultProvider(Schemas.file, SearchProviderType.text, searchProvider); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index a12c62f5e4817..27f3b8a1a75ee 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -41,7 +41,7 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { readonly _serviceBrand: undefined; - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: Set): Promise; fileSearch(query: IFileQuery, token?: CancellationToken): Promise; clearCache(cacheKey: string): Promise; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index de72198944c5d..74441e3cf08b4 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -8,7 +8,7 @@ import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationError } from 'vs/base/common/errors'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ResourceMap } from 'vs/base/common/map'; +import { ResourceMap, ResourceSet } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { StopWatch } from 'vs/base/common/stopwatch'; import { isNumber } from 'vs/base/common/types'; @@ -20,8 +20,8 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; -import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; -import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; +// import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +// import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -48,7 +48,7 @@ export class SearchService extends Disposable implements ISearchService { @IExtensionService private readonly extensionService: IExtensionService, @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, + // @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); } @@ -78,7 +78,7 @@ export class SearchService extends Disposable implements ISearchService { }); } - async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { + async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { // Get local results from dirty/untitled const localResults = await this.getLocalResults(query); @@ -89,7 +89,7 @@ export class SearchService extends Disposable implements ISearchService { const onProviderProgress = (progress: ISearchProgressItem) => { if (isFileMatch(progress)) { // Match - if (!localResults.results.has(progress.resource) && onProgress) { // don't override local results + if (!localResults.results.has(progress.resource) && !(notebookURIs && notebookURIs.has(progress.resource)) && onProgress) { // don't override local results onProgress(progress); } } else if (onProgress) { @@ -480,39 +480,39 @@ export class SearchService extends Disposable implements ISearchService { }); - const notebookWidgets = this.notebookEditorService.retrieveAllExistingWidgets(); - for (const borrowWidget of notebookWidgets) { - const widget = borrowWidget.value; - if (!widget || !widget.viewModel) { - continue; - } - const askMax = isNumber(query.maxResults) ? query.maxResults + 1 : Number.MAX_SAFE_INTEGER; - let matches = await widget - .find(query.contentPattern.pattern, { - regex: query.contentPattern.isRegExp, - wholeWord: query.contentPattern.isWordMatch, - caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: false, - includeMarkupPreview: true, - includeCodeInput: true, - includeOutput: true, - }, CancellationToken.None); - - - if (matches.length) { - if (askMax && matches.length >= askMax) { - limitHit = true; - matches = matches.slice(0, askMax - 1); - } - - const fileMatch = new FileMatch(widget.viewModel.uri); - localResults.set(widget.viewModel.uri, fileMatch); - - fileMatch.results = notebookEditorMatchesToTextSearchResults(matches, query.previewOptions); - } else { - localResults.set(widget.viewModel.uri, null); - } - } + // const notebookWidgets = this.notebookEditorService.retrieveAllExistingWidgets(); + // for (const borrowWidget of notebookWidgets) { + // const widget = borrowWidget.value; + // if (!widget || !widget.viewModel) { + // continue; + // } + // const askMax = isNumber(query.maxResults) ? query.maxResults + 1 : Number.MAX_SAFE_INTEGER; + // let matches = await widget + // .find(query.contentPattern.pattern, { + // regex: query.contentPattern.isRegExp, + // wholeWord: query.contentPattern.isWordMatch, + // caseSensitive: query.contentPattern.isCaseSensitive, + // includeMarkupInput: false, + // includeMarkupPreview: true, + // includeCodeInput: true, + // includeOutput: true, + // }, CancellationToken.None); + + + // if (matches.length) { + // if (askMax && matches.length >= askMax) { + // limitHit = true; + // matches = matches.slice(0, askMax - 1); + // } + + // const fileMatch = new FileMatch(widget.viewModel.uri); + // localResults.set(widget.viewModel.uri, fileMatch); + + // fileMatch.results = notebookEditorMatchesToTextSearchResults(matches, query.previewOptions); + // } else { + // localResults.set(widget.viewModel.uri, null); + // } + // } } return { From 188ee99c6bb057c9a98a1c93c2b5916955b75f2d Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 17 Jan 2023 14:49:52 -0800 Subject: [PATCH 10/45] working replace --- .../browser/view/renderers/webviewPreloads.ts | 54 +++++++++++++++++-- .../contrib/search/browser/replaceService.ts | 35 ++++++++++-- .../browser/searchActionsRemoveReplace.ts | 4 +- .../contrib/search/browser/searchModel.ts | 40 +++++++++----- .../search/browser/searchNotebookHelpers.ts | 12 +++-- .../contrib/search/browser/searchView.ts | 2 +- 6 files changed, 116 insertions(+), 31 deletions(-) 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 8ac1c97a597d8..7be700c47f248 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1002,18 +1002,24 @@ async function webviewPreloads(ctx: PreloadContext) { function extractSelectionLine(selection: Selection): ISearchPreviewInfo { const range = selection.getRangeAt(0); - const oldRange = document.createRange(); - oldRange.setStart(range.startContainer, range.startOffset); - oldRange.setEnd(range.endContainer, range.endOffset); + const oldRange = range.cloneRange(); + const captureLength = selection.toString().length; + // selection.collapseToStart(); + // oldRange.setStart(range.startContainer, range.startOffset); + // oldRange.setEnd(range.endContainer, range.endOffset); selection.modify('move', 'backward', 'lineboundary'); selection.modify('extend', 'forward', 'lineboundary'); const newRange = selection.getRangeAt(0); const line = selection.toString(); + const rangeStart = getStartOffset(newRange, oldRange); + // const rangeStart = oldRange.startOffset - newRange.startOffset; + // const rangeEnd = oldRange.endOffset - newRange.startOffset; + // console.log(getStartOffset(newRange, oldRange)); const lineRange = { - start: oldRange.startOffset - newRange.startOffset, - end: oldRange.endOffset - newRange.startOffset, + start: rangeStart, + end: rangeStart + captureLength, }; // re-add the old range so that the selection is restored @@ -1023,6 +1029,44 @@ async function webviewPreloads(ctx: PreloadContext) { return { line, range: lineRange }; } + function getStartOffset(lineRange: Range, originalRange: Range) { + const firstCommonAncestor = findFirstCommonAncestor(lineRange.startContainer, originalRange.startContainer); + + const selectionOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, lineRange.startContainer) + lineRange.startOffset; + const textOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, originalRange.startContainer) + originalRange.startOffset; + return textOffset - selectionOffset; + } + + // modified from https://stackoverflow.com/a/68583466/16253823 + function findFirstCommonAncestor(nodeA: Node, nodeB: Node) { + const range = new Range(); + range.setStart(nodeA, 0); + range.setEnd(nodeB, 0); + return range.commonAncestorContainer; + } + + // modified from https://stackoverflow.com/a/48812529/16253823 + function getSelectionOffsetRelativeTo(parentElement: Node, currentNode: Node | null): number { + if (!currentNode) { + return 0; + } + let offset = 0; + + if (currentNode === parentElement || !parentElement.contains(currentNode)) { + return offset; + } + + let prevSibling = currentNode.previousSibling; + + while (prevSibling) { + const nodeContent = prevSibling.nodeValue || ''; + offset += nodeContent.length; + prevSibling = prevSibling.previousSibling; + } + + return offset + getSelectionOffsetRelativeTo(parentElement, currentNode.parentNode); + } + const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { let find = true; const matches: IFindMatch[] = []; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 9d7dced2e8b2f..6c8137e21fb64 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -11,7 +11,7 @@ import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService } from 'vs/workbench/contrib/search/browser/searchModel'; +import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService, NotebookMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -27,6 +27,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { dirname } from 'vs/base/common/resources'; import { Promises } from 'vs/base/common/async'; import { SaveSourceRegistry } from 'vs/workbench/common/editor'; +import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const REPLACE_PREVIEW = 'replacePreview'; @@ -109,7 +110,23 @@ export class ReplaceService implements IReplaceService { const edits = this.createEdits(arg, resource); await this.bulkEditorService.apply(edits, { progress }); - return Promises.settled(edits.map(async e => this.textFileService.files.get(e.resource)?.save({ source: ReplaceService.REPLACE_SAVE_SOURCE }))); + const rawTextPromises = edits.map(async e => { + if (e.resource.scheme === network.Schemas.vscodeNotebookCell) { + const notebookResource = CellUri.parse(e.resource)?.notebook; + if (notebookResource) { + return this.editorService.save([...this.editorService.findEditors(notebookResource)]); + } else { + return Promise.resolve(); + } + } else { + return this.textFileService.files.get(e.resource)?.save({ source: ReplaceService.REPLACE_SAVE_SOURCE }); + } + }); + + + // const notebookPromises = edits.notebookEdits.map(async e => this.editorService.save([...this.editorService.findEditors(e.resource)])); + + return Promises.settled(rawTextPromises); } async openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { @@ -177,8 +194,13 @@ export class ReplaceService implements IReplaceService { const edits: ResourceTextEdit[] = []; if (arg instanceof Match) { - const match = arg; - edits.push(this.createEdit(match, match.replaceString, resource)); + if (arg instanceof NotebookMatch) { + const match = arg; + edits.push(this.createEdit(match, match.replaceString, match.cell.uri)); + } else { + const match = arg; + edits.push(this.createEdit(match, match.replaceString, resource)); + } } if (arg instanceof FileMatch) { @@ -189,7 +211,9 @@ export class ReplaceService implements IReplaceService { arg.forEach(element => { const fileMatch = element; if (fileMatch.count() > 0) { - edits.push(...fileMatch.matches().map(match => this.createEdit(match, match.replaceString, resource))); + edits.push(...fileMatch.matches().map( + match => this.createEdit(match, match.replaceString, (match instanceof NotebookMatch) ? match.cell.uri : resource) + )); } }); } @@ -199,6 +223,7 @@ export class ReplaceService implements IReplaceService { private createEdit(match: Match, text: string, resource: URI | null = null): ResourceTextEdit { const fileMatch: FileMatch = match.parent(); + return new ResourceTextEdit( resource ?? fileMatch.resource, { range: match.range(), text }, undefined, undefined diff --git a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts index e318b33266a64..067dce68f4bd3 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts @@ -13,7 +13,7 @@ import { searchRemoveIcon, searchReplaceIcon } from 'vs/workbench/contrib/search import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; -import { arrayContainsElementOrParent, FileMatch, FolderMatch, Match, RenderableMatch, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { arrayContainsElementOrParent, FileMatch, FolderMatch, Match, NotebookMatch, RenderableMatch, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ISearchConfiguration, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -310,7 +310,7 @@ function performReplace(accessor: ServicesAccessor, if (nextFocusElement instanceof Match) { const useReplacePreview = configurationService.getValue().search.useReplacePreview; - if (!useReplacePreview || hasToOpenFile(accessor, nextFocusElement)) { + if (!useReplacePreview || hasToOpenFile(accessor, nextFocusElement) || nextFocusElement instanceof NotebookMatch) { viewlet?.open(nextFocusElement, true); } else { accessor.get(IReplaceService).openReplacePreview(nextFocusElement, true); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 27e56b0768096..383204afb6cd2 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -36,6 +36,7 @@ import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecoratio // import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { notebookEditorMatchesToTextSearchResults, NotebookMatchInfo, NotebookTextSearchMatch } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; // import { addContextToNotebookEditorMatches, notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; @@ -263,6 +264,7 @@ export class FileMatch extends Disposable implements IFileMatch { private _name: Lazy; private _updateScheduler: RunOnceScheduler; + private _notebookUpdateScheduler: RunOnceScheduler; private _modelDecorations: string[] = []; private _currentMatchCellDecorations: string[] = []; private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; @@ -289,6 +291,7 @@ export class FileMatch extends Disposable implements IFileMatch { this._matches = new Map(); this._removedMatches = new Set(); this._updateScheduler = new RunOnceScheduler(this.updateMatchesForModel.bind(this), 250); + this._notebookUpdateScheduler = new RunOnceScheduler(this.updateMatchesForEditorWidget.bind(this), 250); this._name = new Lazy(() => labelService.getUriBasenameLabel(this.resource)); this.createMatches(); } @@ -329,9 +332,14 @@ export class FileMatch extends Disposable implements IFileMatch { bindEditorWidget(widget: NotebookEditorWidget) { this._notebookEditorWidget = widget; - this._editorWidgetListener = this._notebookEditorWidget.viewModel?.onDidChangeViewCells(() => { - this._updateScheduler.schedule(); + + this._editorWidgetListener = this._notebookEditorWidget.textModel?.onDidChangeContent((e) => { + if (!e.rawEvents.some(event => event.kind === NotebookCellsChangeType.ChangeCellContent || event.kind === NotebookCellsChangeType.ModelChange)) { + return; + } + this._notebookUpdateScheduler.schedule(); }) ?? null; + this.updateHighlights(); console.log(`added widget ${this._notebookEditorWidget.textModel?.uri}`); } @@ -341,7 +349,7 @@ export class FileMatch extends Disposable implements IFileMatch { if (this._notebookEditorWidget) { this._updateScheduler.cancel(); this._model = null; - this._editorWidgetListener!.dispose(); + this._editorWidgetListener?.dispose(); } this._notebookEditorWidget = null; console.log(`removed widget ${widget.textModel?.uri}`); @@ -394,11 +402,11 @@ export class FileMatch extends Disposable implements IFileMatch { wholeWord: this._query.isWordMatch, caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, - includeMarkupInput: false, - includeMarkupPreview: true, + includeMarkupInput: true, + includeMarkupPreview: false, includeCodeInput: true, - includeOutput: true, - }, CancellationToken.None); + includeOutput: false, + }, CancellationToken.None, true); this.updateNotebookMatches(allMatches, true); } @@ -419,6 +427,7 @@ export class FileMatch extends Disposable implements IFileMatch { const wordSeparators = this._query.isWordMatch && this._query.wordSeparators ? this._query.wordSeparators : null; const matches = this._model.findMatches(this._query.pattern, range, !!this._query.isRegExp, !!this._query.isCaseSensitive, wordSeparators, false, this._maxResults ?? Number.MAX_SAFE_INTEGER); this.updateMatches(matches, modelChange, this._model); + this.updateMatchesForEditorWidget(); } private updateNotebookMatches(matches: CellFindMatchWithIndex[], modelChange: boolean): void { @@ -598,7 +607,10 @@ export class FileMatch extends Disposable implements IFileMatch { public async showMatch(match: NotebookMatch) { const offset = await this.highlightCurrentFindMatchDecoration(match); - await this.revealCellRange(match, offset); + // if (this._notebookEditorWidget) { + // this._notebookEditorWidget.focusNotebookCell(match.cell, 'container', {}); + // } + this.revealCellRange(match, offset); } private async highlightCurrentFindMatchDecoration(match: NotebookMatch): Promise { @@ -612,7 +624,7 @@ export class FileMatch extends Disposable implements IFileMatch { // only webviewmatch: // - if (!match.webviewIndex) { + if (match.webviewIndex === undefined) { this.clearCurrentFindMatchDecoration(); // match is an editor FindMatch, we update find match decoration in the editor // we will highlight the match in the webview @@ -688,7 +700,7 @@ export class FileMatch extends Disposable implements IFileMatch { if (!this._notebookEditorWidget) { return; } - if (!match.webviewIndex) { + if (match.webviewIndex) { // reveal output range this._notebookEditorWidget.focusElement(match.cell); const index = this._notebookEditorWidget.getCellIndex(match.cell); @@ -697,7 +709,7 @@ export class FileMatch extends Disposable implements IFileMatch { this._notebookEditorWidget.revealCellOffsetInCenterAsync(match.cell, outputOffset ?? 0); } } else { - this._notebookEditorWidget.focusElement(match.cell); + this._notebookEditorWidget.focusNotebookCell(match.cell, 'editor'); this._notebookEditorWidget.setCellEditorSelection(match.cell, match.range()); this._notebookEditorWidget.revealRangeInCenterIfOutsideViewportAsync(match.cell, match.range()); } @@ -1757,10 +1769,10 @@ export class SearchModel extends Disposable { regex: query.contentPattern.isRegExp, wholeWord: query.contentPattern.isWordMatch, caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: false, - includeMarkupPreview: true, + includeMarkupInput: true, + includeMarkupPreview: false, includeCodeInput: true, - includeOutput: true, + includeOutput: false, }, CancellationToken.None); diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index dad92876e1d5a..9a52633ecd746 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -69,8 +69,10 @@ export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFi const cellIndex = cellFindMatch.index; cellFindMatch.contentMatches.forEach((match, index) => { if (match.range.startLineNumber !== previousEndLine) { - currentMatches = []; - groupedMatches.push({ matches: currentMatches, notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: index, cell: cellFindMatch.cell } }); + if (currentMatches.length > 0) { + groupedMatches.push({ matches: [...currentMatches], notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: index, cell: cellFindMatch.cell } }); + currentMatches = []; + } startIndexOfCurrentMatches = cellIndex + 1; } @@ -78,8 +80,10 @@ export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFi previousEndLine = match.range.endLineNumber; }); - currentMatches = []; - groupedMatches.push({ matches: currentMatches, notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: cellFindMatch.contentMatches.length - 1, cell: cellFindMatch.cell } }); + if (currentMatches.length > 0) { + groupedMatches.push({ matches: [...currentMatches], notebookMatchInfo: { cellIndex, matchStartIndex: startIndexOfCurrentMatches, matchEndIndex: cellFindMatch.contentMatches.length - 1, cell: cellFindMatch.cell } }); + currentMatches = []; + } cellFindMatch.webviewMatches.forEach((match, index) => { groupedMatches.push({ matches: match, notebookMatchInfo: { cellIndex, matchStartIndex: index, matchEndIndex: index, cell: cellFindMatch.cell, webviewMatchInfo: { index: match.index } } }); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 03aaae8497ca9..056c6160b76a6 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1760,7 +1760,7 @@ export class SearchView extends ViewPane { private onFocus(lineMatch: Match, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; - return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString) ? + return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(lineMatch instanceof NotebookMatch)) ? this.replaceService.openReplacePreview(lineMatch, preserveFocus, sideBySide, pinned) : this.open(lineMatch, preserveFocus, sideBySide, pinned); } From 51c667e923594cf3dab58dd174bb29349511f61c Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 11:47:10 -0800 Subject: [PATCH 11/45] add propert highlighting and open notebook editor when replace active --- .../contrib/search/browser/searchModel.ts | 65 +++++++++++++++---- .../contrib/search/browser/searchView.ts | 22 +++++-- 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 383204afb6cd2..71abd5d923d78 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -268,6 +268,8 @@ export class FileMatch extends Disposable implements IFileMatch { private _modelDecorations: string[] = []; private _currentMatchCellDecorations: string[] = []; private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; + private _allMatchesDecorations: ICellModelDecorations[] = []; + private _allMatchesCellDecorations: string[] = []; private _context: Map = new Map(); public get context(): Map { @@ -339,16 +341,22 @@ export class FileMatch extends Disposable implements IFileMatch { } this._notebookUpdateScheduler.schedule(); }) ?? null; + this._notebookUpdateScheduler.schedule(); - this.updateHighlights(); console.log(`added widget ${this._notebookEditorWidget.textModel?.uri}`); } unbindEditorWidget(widget: NotebookEditorWidget) { - this.updateMatchesForModel(); + this.updateMatchesForEditorWidget(); if (this._notebookEditorWidget) { - this._updateScheduler.cancel(); - this._model = null; + this._notebookUpdateScheduler.cancel(); + this._notebookEditorWidget.changeModelDecorations((accessor) => { + this._currentMatchDecorations = { + kind: 'input', + decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []) + }; + }); + this._notebookEditorWidget = null; this._editorWidgetListener?.dispose(); } this._notebookEditorWidget = null; @@ -443,13 +451,8 @@ export class FileMatch extends Disposable implements IFileMatch { }); }); - // this.addContext( - // addContextToEditorMatches(textSearchResults, model, this.parent().parent().query!) - // .filter((result => !resultIsMatch(result)) as ((a: any) => a is ITextSearchContext)) - // .map(context => ({ ...context, lineNumber: context.lineNumber + 1 }))); - + this.setAllFindMatchesDecorations(matches); this._onChange.fire({ forceUpdateModel: modelChange }); - // this.updateHighlights(); } private updateMatches(matches: FindMatch[], modelChange: boolean, model: ITextModel): void { @@ -607,9 +610,6 @@ export class FileMatch extends Disposable implements IFileMatch { public async showMatch(match: NotebookMatch) { const offset = await this.highlightCurrentFindMatchDecoration(match); - // if (this._notebookEditorWidget) { - // this._notebookEditorWidget.focusNotebookCell(match.cell, 'container', {}); - // } this.revealCellRange(match, offset); } @@ -679,6 +679,45 @@ export class FileMatch extends Disposable implements IFileMatch { } } + private setAllFindMatchesDecorations(cellFindMatches: CellFindMatchWithIndex[]) { + if (!this._notebookEditorWidget) { + return; + } + this._notebookEditorWidget.changeModelDecorations((accessor) => { + + const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; + + const deltaDecorations: ICellModelDeltaDecorations[] = cellFindMatches.map(cellFindMatch => { + // Find matches + const newFindMatchesDecorations: IModelDeltaDecoration[] = new Array(cellFindMatch.length); + for (let i = 0; i < cellFindMatch.contentMatches.length; i++) { + newFindMatchesDecorations[i] = { + range: cellFindMatch.contentMatches[i].range, + options: findMatchesOptions + }; + } + + return { ownerId: cellFindMatch.cell.handle, decorations: newFindMatchesDecorations }; + }); + + this._allMatchesDecorations = accessor.deltaDecorations(this._allMatchesDecorations, deltaDecorations); + }); + + this._allMatchesCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._allMatchesCellDecorations, cellFindMatches.map(cellFindMatch => { + return { + ownerId: cellFindMatch.cell.handle, + handle: cellFindMatch.cell.handle, + options: { + overviewRuler: { + color: overviewRulerFindMatchForeground, + modelRanges: cellFindMatch.contentMatches.map(match => match.range), + includeOutput: cellFindMatch.webviewMatches.length > 0 + } + } + }; + })); + } + private clearCurrentFindMatchDecoration() { if (!this._notebookEditorWidget) { return; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 056c6160b76a6..ef02b15371bf4 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -81,6 +81,7 @@ import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/se import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, SearchCompletionExitCode, SearchSortOrder, TextSearchCompleteMessageType, ViewMode } from 'vs/workbench/services/search/common/search'; import { TextSearchCompleteMessage } from 'vs/workbench/services/search/common/searchExtTypes'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; const $ = dom.$; @@ -186,6 +187,7 @@ export class SearchView extends ViewPane { @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, + @INotebookService private readonly notebookService: INotebookService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -1758,17 +1760,24 @@ export class SearchView extends ViewPane { this.currentSelectedFileMatch = undefined; } + private shouldOpenInNotebookEditor(uri: URI): boolean { + return this.notebookService.getContributedNotebookTypes(uri).length > 0; + } + private onFocus(lineMatch: Match, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; - return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(lineMatch instanceof NotebookMatch)) ? + + const resource = lineMatch instanceof Match ? lineMatch.parent().resource : (lineMatch).resource; + return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(this.shouldOpenInNotebookEditor(resource))) ? this.replaceService.openReplacePreview(lineMatch, preserveFocus, sideBySide, pinned) : - this.open(lineMatch, preserveFocus, sideBySide, pinned); + this.open(lineMatch, preserveFocus, sideBySide, pinned, resource); } - async open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { + async open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean, resourceInput?: URI): Promise { const selection = this.getSelectionFrom(element); - const resource = element instanceof Match ? element.parent().resource : (element).resource; - + const oldParentMatches = element instanceof Match ? element.parent().matches() : []; + const resource = resourceInput ?? (element instanceof Match ? element.parent().resource : (element).resource); + this.shouldOpenInNotebookEditor(resource); let editor: IEditorPane | undefined; try { editor = await this.editorService.openEditor({ @@ -1798,13 +1807,12 @@ export class SearchView extends ViewPane { if (editor instanceof NotebookEditor) { const controller = editor.getControl()?.getContribution(NotebookFindContrib.id); if (element instanceof Match) { - const matchIndex = element.parent().matches().findIndex(e => e.id() === element.id()); if (element instanceof NotebookMatch) { // no op for now! element.parent().showMatch(element); } else { - + const matchIndex = oldParentMatches.findIndex(e => e.id() === element.id()); controller?.show(this.searchWidget.searchInput.getValue(), { matchIndex, focus: false }); } } From da22b6ac4727a7f335d8e7b562884d0a56c2a12c Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 12:19:43 -0800 Subject: [PATCH 12/45] cleanup and compile error fixing --- .../contrib/search/browser/replaceService.ts | 3 -- .../contrib/search/browser/searchModel.ts | 11 +++-- .../search/browser/searchNotebookHelpers.ts | 17 ------- .../services/search/browser/searchService.ts | 3 -- .../services/search/common/searchService.ts | 44 +------------------ 5 files changed, 9 insertions(+), 69 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index 6c8137e21fb64..ede2f234da58c 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -123,9 +123,6 @@ export class ReplaceService implements IReplaceService { } }); - - // const notebookPromises = edits.notebookEdits.map(async e => this.editorService.save([...this.editorService.findEditors(e.resource)])); - return Promises.settled(rawTextPromises); } diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 71c18fc2129f2..a5fdb5d03a880 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -33,7 +33,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { minimapFindMatch, overviewRulerFindMatchForeground, overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration, NotebookOverviewRulerLane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; // import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; @@ -665,7 +665,8 @@ export class FileMatch extends Disposable implements IFileMatch { overviewRuler: { color: overviewRulerSelectionHighlightForeground, modelRanges: [match.range()], - includeOutput: false + includeOutput: false, + position: NotebookOverviewRulerLane.Center } } } as INotebookDeltaDecoration]); @@ -683,7 +684,8 @@ export class FileMatch extends Disposable implements IFileMatch { overviewRuler: { color: overviewRulerSelectionHighlightForeground, modelRanges: [], - includeOutput: true + includeOutput: true, + position: NotebookOverviewRulerLane.Center } } } as INotebookDeltaDecoration]); @@ -724,7 +726,8 @@ export class FileMatch extends Disposable implements IFileMatch { overviewRuler: { color: overviewRulerFindMatchForeground, modelRanges: cellFindMatch.contentMatches.map(match => match.range), - includeOutput: cellFindMatch.webviewMatches.length > 0 + includeOutput: cellFindMatch.webviewMatches.length > 0, + position: NotebookOverviewRulerLane.Center } } }; diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index 9a52633ecd746..f02e122c820d4 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -94,20 +94,3 @@ export function notebookEditorMatchesToTextSearchResults(cellFindMatches: CellFi return notebookEditorMatchToTextSearchResult(sameLineMatches, previewOptions); }).filter((elem): elem is NotebookTextSearchMatch => !!elem); } - -// export function addContextToNotebookEditorMatches(cellFindMatches: CellFindMatchWithIndex[], query: ITextQuery): ITextSearchResult[] { -// const results: ITextSearchResult[] = []; - -// // we need a cell and all of its corresponding textmatches -// // ^ we need this split by webview and content matches - -// // -// cellFindMatches.forEach(findMatch => { -// findMatch.contentMatches.forEach(contentMatch => { -// const result = addContextToEditorMatches() -// }) -// }) - - -// return results; -// } diff --git a/src/vs/workbench/services/search/browser/searchService.ts b/src/vs/workbench/services/search/browser/searchService.ts index 0e90f9c4e529f..f1fc4a45c5b0d 100644 --- a/src/vs/workbench/services/search/browser/searchService.ts +++ b/src/vs/workbench/services/search/browser/searchService.ts @@ -27,8 +27,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; -// import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; - export class RemoteSearchService extends SearchService { constructor( @IModelService modelService: IModelService, @@ -39,7 +37,6 @@ export class RemoteSearchService extends SearchService { @IFileService fileService: IFileService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IUriIdentityService uriIdentityService: IUriIdentityService, - // @INotebookEditorService notebookEditorService: INotebookEditorService ) { super(modelService, editorService, telemetryService, logService, extensionService, fileService, uriIdentityService); const searchProvider = this.instantiationService.createInstance(LocalFileSearchWorkerClient); diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 74441e3cf08b4..63b1c192a8668 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -19,10 +19,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; - -// import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; -// import { notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; - import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, isFileMatch, isProgressMessage, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/workbench/services/search/common/search'; @@ -48,7 +44,6 @@ export class SearchService extends Disposable implements ISearchService { @IExtensionService private readonly extensionService: IExtensionService, @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, - // @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, ) { super(); } @@ -80,7 +75,7 @@ export class SearchService extends Disposable implements ISearchService { async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { // Get local results from dirty/untitled - const localResults = await this.getLocalResults(query); + const localResults = this.getLocalResults(query); if (onProgress) { arrays.coalesce([...localResults.results.values()]).forEach(onProgress); @@ -409,7 +404,7 @@ export class SearchService extends Disposable implements ISearchService { } } - private async getLocalResults(query: ITextQuery): Promise<{ results: ResourceMap; limitHit: boolean }> { + private getLocalResults(query: ITextQuery): { results: ResourceMap; limitHit: boolean } { const localResults = new ResourceMap(uri => this.uriIdentityService.extUri.getComparisonKey(uri)); let limitHit = false; @@ -478,41 +473,6 @@ export class SearchService extends Disposable implements ISearchService { localResults.set(originalResource, null); } }); - - - // const notebookWidgets = this.notebookEditorService.retrieveAllExistingWidgets(); - // for (const borrowWidget of notebookWidgets) { - // const widget = borrowWidget.value; - // if (!widget || !widget.viewModel) { - // continue; - // } - // const askMax = isNumber(query.maxResults) ? query.maxResults + 1 : Number.MAX_SAFE_INTEGER; - // let matches = await widget - // .find(query.contentPattern.pattern, { - // regex: query.contentPattern.isRegExp, - // wholeWord: query.contentPattern.isWordMatch, - // caseSensitive: query.contentPattern.isCaseSensitive, - // includeMarkupInput: false, - // includeMarkupPreview: true, - // includeCodeInput: true, - // includeOutput: true, - // }, CancellationToken.None); - - - // if (matches.length) { - // if (askMax && matches.length >= askMax) { - // limitHit = true; - // matches = matches.slice(0, askMax - 1); - // } - - // const fileMatch = new FileMatch(widget.viewModel.uri); - // localResults.set(widget.viewModel.uri, fileMatch); - - // fileMatch.results = notebookEditorMatchesToTextSearchResults(matches, query.previewOptions); - // } else { - // localResults.set(widget.viewModel.uri, null); - // } - // } } return { From 779e7210b8b93638e6e7a326a2f07f02652e056b Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 15:19:38 -0800 Subject: [PATCH 13/45] fix function for notebook opening in search --- src/vs/workbench/contrib/search/browser/searchView.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index d6474021c5ebb..5aab5ab9fbedd 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -21,6 +21,7 @@ import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import * as network from 'vs/base/common/network'; import 'vs/css!./media/searchview'; import { getCodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -1769,14 +1770,15 @@ export class SearchView extends ViewPane { } private shouldOpenInNotebookEditor(uri: URI): boolean { - return this.notebookService.getContributedNotebookTypes(uri).length > 0; + // + return uri.scheme !== network.Schemas.untitled && this.notebookService.getContributedNotebookTypes(uri).length > 0; } private onFocus(lineMatch: Match, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; const resource = lineMatch instanceof Match ? lineMatch.parent().resource : (lineMatch).resource; - return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(this.shouldOpenInNotebookEditor(resource))) ? + return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(lineMatch instanceof NotebookMatch) && !(this.shouldOpenInNotebookEditor(resource))) ? this.replaceService.openReplacePreview(lineMatch, preserveFocus, sideBySide, pinned) : this.open(lineMatch, preserveFocus, sideBySide, pinned, resource); } From b8c51333f3de9ca938db78a4c2cb2bbfc3d46e4d Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 15:23:45 -0800 Subject: [PATCH 14/45] clarity on shouldOpenInNotebookEditor --- src/vs/workbench/contrib/search/browser/searchView.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 5aab5ab9fbedd..32446c432b7a9 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1769,16 +1769,17 @@ export class SearchView extends ViewPane { this.currentSelectedFileMatch = undefined; } - private shouldOpenInNotebookEditor(uri: URI): boolean { - // - return uri.scheme !== network.Schemas.untitled && this.notebookService.getContributedNotebookTypes(uri).length > 0; + private shouldOpenInNotebookEditor(match: Match, uri: URI): boolean { + // Untitled files will return a false positive for getContributedNotebookTypes. + // Since untitled files are already open, then untitled notebooks should return NotebookMatch results. + return match instanceof NotebookMatch || (uri.scheme !== network.Schemas.untitled && this.notebookService.getContributedNotebookTypes(uri).length > 0); } private onFocus(lineMatch: Match, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise { const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; const resource = lineMatch instanceof Match ? lineMatch.parent().resource : (lineMatch).resource; - return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(lineMatch instanceof NotebookMatch) && !(this.shouldOpenInNotebookEditor(resource))) ? + return (useReplacePreview && this.viewModel.isReplaceActive() && !!this.viewModel.replaceString && !(this.shouldOpenInNotebookEditor(lineMatch, resource))) ? this.replaceService.openReplacePreview(lineMatch, preserveFocus, sideBySide, pinned) : this.open(lineMatch, preserveFocus, sideBySide, pinned, resource); } @@ -1787,7 +1788,6 @@ export class SearchView extends ViewPane { const selection = this.getSelectionFrom(element); const oldParentMatches = element instanceof Match ? element.parent().matches() : []; const resource = resourceInput ?? (element instanceof Match ? element.parent().resource : (element).resource); - this.shouldOpenInNotebookEditor(resource); let editor: IEditorPane | undefined; try { editor = await this.editorService.openEditor({ From 15ea8bca9054491a1fc6c83392c6af8b62d883ea Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 16:30:32 -0800 Subject: [PATCH 15/45] added experimental flag --- .../contrib/search/browser/search.contribution.ts | 5 +++++ .../workbench/contrib/search/browser/searchModel.ts | 13 ++++++++++--- src/vs/workbench/services/search/common/search.ts | 3 +++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 3cfb478d33cc3..652eb8009207c 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -350,6 +350,11 @@ configurationRegistry.registerConfiguration({ ], 'description': nls.localize('search.defaultViewMode', "Controls the default search result view mode.") }, + 'search.experimental.notebookSearch': { + type: 'boolean', + description: nls.localize('search.experimental.notebookSearch', "Controls whether to use the experimental notebook search in the global search."), + default: false + }, } }); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index a5fdb5d03a880..63a39ab753668 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -34,13 +34,11 @@ import { minimapFindMatch, overviewRulerFindMatchForeground, overviewRulerSelect import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration, NotebookOverviewRulerLane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -// import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { notebookEditorMatchesToTextSearchResults, NotebookMatchInfo, NotebookTextSearchMatch } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; -// import { addContextToNotebookEditorMatches, notebookEditorMatchesToTextSearchResults } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, QueryType, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -1420,6 +1418,7 @@ export class SearchResult extends Disposable { @IModelService private readonly modelService: IModelService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); @@ -1531,6 +1530,12 @@ export class SearchResult extends Disposable { } private onDidAddNotebookEditorWidget(widget: NotebookEditorWidget): void { + const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; + + if (!experimentalNotebooksEnabled) { + return; + } + widget.onWillChangeModel( (model) => { if (model) { @@ -1895,7 +1900,9 @@ export class SearchModel extends Disposable { onProgress?.(p); }; - const notebookResult = await this.notebookSearch(query, onProgressCall); + const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; + + const notebookResult = experimentalNotebooksEnabled ? await this.notebookSearch(query, onProgressCall) : { messages: [], results: [] }; const currentResult = await this.searchService.textSearch( searchQuery, this.currentCancelTokenSource.token, onProgressCall, diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 27f3b8a1a75ee..d3520c993907d 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -400,6 +400,9 @@ export interface ISearchConfigurationProperties { badges: boolean; }; defaultViewMode: ViewMode; + experimental: { + notebookSearch: boolean; + }; } export interface ISearchConfiguration extends IFilesConfiguration { From 310c13a80ebd44f732c8f74cc980238b60567326 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 19 Jan 2023 17:05:44 -0800 Subject: [PATCH 16/45] extra docs for notebook extractSelectionLine --- .../browser/view/renderers/webviewPreloads.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) 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 ddbb00f2f7172..6a7a7d2014b28 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -950,23 +950,27 @@ async function webviewPreloads(ctx: PreloadContext) { } function extractSelectionLine(selection: Selection): ISearchPreviewInfo { - const range = selection.getRangeAt(0); + + // we need to keep a reference to the old selection range to re-apply later const oldRange = range.cloneRange(); const captureLength = selection.toString().length; - // selection.collapseToStart(); - // oldRange.setStart(range.startContainer, range.startOffset); - // oldRange.setEnd(range.endContainer, range.endOffset); + // use selection API to modify selection to get entire line (the first line if multi-select) + + // collapse selection to start so that the cursor position is at beginning of match + selection.collapseToStart(); + + // extend selection in both directions to select the line selection.modify('move', 'backward', 'lineboundary'); selection.modify('extend', 'forward', 'lineboundary'); - const newRange = selection.getRangeAt(0); const line = selection.toString(); - const rangeStart = getStartOffset(newRange, oldRange); - // const rangeStart = oldRange.startOffset - newRange.startOffset; - // const rangeEnd = oldRange.endOffset - newRange.startOffset; - // console.log(getStartOffset(newRange, oldRange)); + + // using the original range and the new range, we can find the offset of the match from the line start. + const rangeStart = getStartOffset(selection.getRangeAt(0), oldRange); + + // line range for match const lineRange = { start: rangeStart, end: rangeStart + captureLength, @@ -980,6 +984,8 @@ async function webviewPreloads(ctx: PreloadContext) { } function getStartOffset(lineRange: Range, originalRange: Range) { + // sometimes, the old and new range are in different DOM elements (ie: when the match is inside of ) + // so we need to find the first common ancestor DOM element and find the positions of the old and new range relative to that. const firstCommonAncestor = findFirstCommonAncestor(lineRange.startContainer, originalRange.startContainer); const selectionOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, lineRange.startContainer) + lineRange.startOffset; @@ -1006,8 +1012,9 @@ async function webviewPreloads(ctx: PreloadContext) { return offset; } - let prevSibling = currentNode.previousSibling; + // count the number of chars before the current dom elem and the start of the dom + let prevSibling = currentNode.previousSibling; while (prevSibling) { const nodeContent = prevSibling.nodeValue || ''; offset += nodeContent.length; From a7827473c9d9fb5c64d01cc0a07412e493c52c27 Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 20 Jan 2023 13:07:40 -0800 Subject: [PATCH 17/45] fix experimental flag bug --- src/vs/workbench/contrib/search/browser/searchModel.ts | 4 +++- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 63a39ab753668..a183c84e3c56b 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -298,6 +298,7 @@ export class FileMatch extends Disposable implements IFileMatch { @IReplaceService private readonly replaceService: IReplaceService, @ILabelService readonly labelService: ILabelService, @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); this._resource = this.rawMatch.resource; @@ -315,7 +316,8 @@ export class FileMatch extends Disposable implements IFileMatch { private async createMatches(): Promise { const model = this.modelService.getModel(this._resource); - const notebookEditorWidgetBorrow = this.notebookEditorService.retrieveExistingWidgetFromURI(this._resource); + const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; + const notebookEditorWidgetBorrow = experimentalNotebooksEnabled ? this.notebookEditorService.retrieveExistingWidgetFromURI(this._resource) : undefined; if (notebookEditorWidgetBorrow?.value) { this.bindEditorWidget(notebookEditorWidgetBorrow.value); await this.updateMatchesForEditorWidget(); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 32446c432b7a9..b1b0665e2a405 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1772,6 +1772,8 @@ export class SearchView extends ViewPane { private shouldOpenInNotebookEditor(match: Match, uri: URI): boolean { // Untitled files will return a false positive for getContributedNotebookTypes. // Since untitled files are already open, then untitled notebooks should return NotebookMatch results. + + // notebookMatch are only created when search.experimental.notebookSearch is enabled, so this should never return true if experimental flag is disabled. return match instanceof NotebookMatch || (uri.scheme !== network.Schemas.untitled && this.notebookService.getContributedNotebookTypes(uri).length > 0); } @@ -1818,8 +1820,6 @@ export class SearchView extends ViewPane { const controller = editor.getControl()?.getContribution(NotebookFindContrib.id); if (element instanceof Match) { if (element instanceof NotebookMatch) { - - // no op for now! element.parent().showMatch(element); } else { const matchIndex = oldParentMatches.findIndex(e => e.id() === element.id()); From eff7d265127a6620881f0a432182adf4992eaaad Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 20 Jan 2023 16:53:44 -0800 Subject: [PATCH 18/45] incorporated shared findMatchDecorationModel --- .../contrib/find/findMatchDecorationModel.ts | 131 +++++++----- .../browser/contrib/find/findModel.ts | 11 +- .../contrib/search/browser/searchModel.ts | 190 +++--------------- .../contrib/search/browser/searchView.ts | 12 +- 4 files changed, 125 insertions(+), 219 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts index 2f407b50de5d3..4669eddb4e4bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { FindMatch, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { FindDecorations } from 'vs/editor/contrib/find/browser/findDecorations'; +import { Range } from 'vs/editor/common/core/range'; import { overviewRulerSelectionHighlightForeground, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; -import { CellFindMatchWithIndex, CellWebviewFindMatch, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, INotebookDeltaDecoration, INotebookEditor, NotebookOverviewRulerLane, } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, INotebookDeltaDecoration, INotebookEditor, NotebookOverviewRulerLane, } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; export class FindMatchDecorationModel extends Disposable { private _allMatchesDecorations: ICellModelDecorations[] = []; @@ -16,7 +17,7 @@ export class FindMatchDecorationModel extends Disposable { private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; constructor( - private readonly _notebookEditor: INotebookEditor + private _notebookEditor: INotebookEditor | undefined, ) { super(); } @@ -25,68 +26,91 @@ export class FindMatchDecorationModel extends Disposable { return this._currentMatchDecorations; } - public async highlightCurrentFindMatchDecoration(cell: ICellViewModel, match: FindMatch | CellWebviewFindMatch): Promise { + set notebookEditor(notebookEditor: INotebookEditor | undefined) { + if (this._notebookEditor) { + // clear any previous decorations if applicable + this.clearDecorations(); + } + this._notebookEditor = notebookEditor; + } - if (match instanceof FindMatch) { - this.clearCurrentFindMatchDecoration(); + public clearDecorations() { + this.clearCurrentFindMatchDecoration(); + this.setAllFindMatchesDecorations([]); + } - // match is an editor FindMatch, we update find match decoration in the editor - // we will highlight the match in the webview - this._notebookEditor.changeModelDecorations(accessor => { - const findMatchesOptions: ModelDecorationOptions = FindDecorations._CURRENT_FIND_MATCH_DECORATION; - - const decorations: IModelDeltaDecoration[] = [ - { range: match.range, options: findMatchesOptions } - ]; - const deltaDecoration: ICellModelDeltaDecorations = { - ownerId: cell.handle, - decorations: decorations - }; - - this._currentMatchDecorations = { - kind: 'input', - decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], [deltaDecoration]) - }; - }); - - this._currentMatchCellDecorations = this._notebookEditor.deltaCellDecorations(this._currentMatchCellDecorations, [{ - ownerId: cell.handle, - handle: cell.handle, - options: { - overviewRuler: { - color: overviewRulerSelectionHighlightForeground, - modelRanges: [match.range], - includeOutput: false, - position: NotebookOverviewRulerLane.Center - } - } - } as INotebookDeltaDecoration]); + public async highlightCurrentFindMatchDecorationInCell(cell: ICellViewModel, cellRange: Range): Promise { + if (!this._notebookEditor) { return null; - } else { - this.clearCurrentFindMatchDecoration(); + } + + this.clearCurrentFindMatchDecoration(); - const offset = await this._notebookEditor.highlightFind(cell, match.index); - this._currentMatchDecorations = { kind: 'output', index: match.index }; + // match is an editor FindMatch, we update find match decoration in the editor + // we will highlight the match in the webview + this._notebookEditor.changeModelDecorations(accessor => { + const findMatchesOptions: ModelDecorationOptions = FindDecorations._CURRENT_FIND_MATCH_DECORATION; - this._currentMatchCellDecorations = this._notebookEditor.deltaCellDecorations(this._currentMatchCellDecorations, [{ + const decorations: IModelDeltaDecoration[] = [ + { range: cellRange, options: findMatchesOptions } + ]; + const deltaDecoration: ICellModelDeltaDecorations = { ownerId: cell.handle, - handle: cell.handle, - options: { - overviewRuler: { - color: overviewRulerSelectionHighlightForeground, - modelRanges: [], - includeOutput: true, - position: NotebookOverviewRulerLane.Center - } + decorations: decorations + }; + + this._currentMatchDecorations = { + kind: 'input', + decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], [deltaDecoration]) + }; + }); + + this._currentMatchCellDecorations = this._notebookEditor.deltaCellDecorations(this._currentMatchCellDecorations, [{ + ownerId: cell.handle, + handle: cell.handle, + options: { + overviewRuler: { + color: overviewRulerSelectionHighlightForeground, + modelRanges: [cellRange], + includeOutput: false, + position: NotebookOverviewRulerLane.Center } - } as INotebookDeltaDecoration]); + } + } as INotebookDeltaDecoration]); - return offset; + return null; + } + + public async highlightCurrentFindMatchDecorationInWebview(cell: ICellViewModel, index: number): Promise { + if (!this._notebookEditor) { + return null; } + this.clearCurrentFindMatchDecoration(); + + const offset = await this._notebookEditor.highlightFind(index); + this._currentMatchDecorations = { kind: 'output', index: index }; + + this._currentMatchCellDecorations = this._notebookEditor.deltaCellDecorations(this._currentMatchCellDecorations, [{ + ownerId: cell.handle, + handle: cell.handle, + options: { + overviewRuler: { + color: overviewRulerSelectionHighlightForeground, + modelRanges: [], + includeOutput: true, + position: NotebookOverviewRulerLane.Center + } + } + } as INotebookDeltaDecoration]); + + return offset; } public clearCurrentFindMatchDecoration() { + if (!this._notebookEditor) { + return; + } if (this._currentMatchDecorations?.kind === 'input') { this._notebookEditor.changeModelDecorations(accessor => { accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []); @@ -101,6 +125,9 @@ export class FindMatchDecorationModel extends Disposable { public setAllFindMatchesDecorations(cellFindMatches: CellFindMatchWithIndex[]) { + if (!this._notebookEditor) { + return; + } this._notebookEditor.changeModelDecorations((accessor) => { const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; 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 6848616e61e28..7341092794ab5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -450,11 +450,12 @@ export class FindModel extends Disposable { private async highlightCurrentFindMatchDecoration(cellIndex: number, matchIndex: number): Promise { const cell = this._findMatches[cellIndex].cell; const match = this._findMatches[cellIndex].getMatch(matchIndex); - return this._findMatchDecorationModel.highlightCurrentFindMatchDecoration(cell, - (matchIndex < this._findMatches[cellIndex].contentMatches.length) ? - (match as FindMatch) : - (match as CellWebviewFindMatch) - ); + + if (matchIndex < this._findMatches[cellIndex].contentMatches.length) { + return this._findMatchDecorationModel.highlightCurrentFindMatchDecorationInCell(cell, (match as FindMatch).range); + } else { + return this._findMatchDecorationModel.highlightCurrentFindMatchDecorationInWebview(cell, (match as CellWebviewFindMatch).index); + } } clear() { diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index a183c84e3c56b..2b8dbd174c494 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -22,7 +22,6 @@ import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IModelService } from 'vs/editor/common/services/model'; -import { FindDecorations } from 'vs/editor/contrib/find/browser/findDecorations'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -30,10 +29,11 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { minimapFindMatch, overviewRulerFindMatchForeground, overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/common/colorRegistry'; +import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration, NotebookOverviewRulerLane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { FindMatchDecorationModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel'; +import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -277,10 +277,7 @@ export class FileMatch extends Disposable implements IFileMatch { private _updateScheduler: RunOnceScheduler; private _notebookUpdateScheduler: RunOnceScheduler; private _modelDecorations: string[] = []; - private _currentMatchCellDecorations: string[] = []; - private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; - private _allMatchesDecorations: ICellModelDecorations[] = []; - private _allMatchesCellDecorations: string[] = []; + private _findMatchDecorationModel: FindMatchDecorationModel; private _context: Map = new Map(); public get context(): Map { @@ -307,6 +304,7 @@ export class FileMatch extends Disposable implements IFileMatch { this._updateScheduler = new RunOnceScheduler(this.updateMatchesForModel.bind(this), 250); this._notebookUpdateScheduler = new RunOnceScheduler(this.updateMatchesForEditorWidget.bind(this), 250); this._name = new Lazy(() => labelService.getUriBasenameLabel(this.resource)); + this._findMatchDecorationModel = new FindMatchDecorationModel(undefined); this.createMatches(); } @@ -345,9 +343,26 @@ export class FileMatch extends Disposable implements IFileMatch { this.updateHighlights(); } + private onModelWillDispose(): void { + // Update matches because model might have some dirty changes + this.updateMatchesForModel(); + this.unbindModel(); + } + + private unbindModel(): void { + if (this._model) { + this._updateScheduler.cancel(); + this._model.changeDecorations((accessor) => { + this._modelDecorations = accessor.deltaDecorations(this._modelDecorations, []); + }); + this._model = null; + this._modelListener!.dispose(); + } + } + bindEditorWidget(widget: NotebookEditorWidget) { this._notebookEditorWidget = widget; - + this._findMatchDecorationModel.notebookEditor = widget; this._editorWidgetListener = this._notebookEditorWidget.textModel?.onDidChangeContent((e) => { if (!e.rawEvents.some(event => event.kind === NotebookCellsChangeType.ChangeCellContent || event.kind === NotebookCellsChangeType.ModelChange)) { return; @@ -355,42 +370,16 @@ export class FileMatch extends Disposable implements IFileMatch { this._notebookUpdateScheduler.schedule(); }) ?? null; this._notebookUpdateScheduler.schedule(); - - console.log(`added widget ${this._notebookEditorWidget.textModel?.uri}`); } - unbindEditorWidget(widget: NotebookEditorWidget) { + unbindEditorWidget() { this.updateMatchesForEditorWidget(); if (this._notebookEditorWidget) { this._notebookUpdateScheduler.cancel(); - this._notebookEditorWidget.changeModelDecorations((accessor) => { - this._currentMatchDecorations = { - kind: 'input', - decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []) - }; - }); - this._notebookEditorWidget = null; + this._findMatchDecorationModel.notebookEditor = undefined; this._editorWidgetListener?.dispose(); } this._notebookEditorWidget = null; - console.log(`removed widget ${widget.textModel?.uri}`); - } - - private onModelWillDispose(): void { - // Update matches because model might have some dirty changes - this.updateMatchesForModel(); - this.unbindModel(); - } - - private unbindModel(): void { - if (this._model) { - this._updateScheduler.cancel(); - this._model.changeDecorations((accessor) => { - this._modelDecorations = accessor.deltaDecorations(this._modelDecorations, []); - }); - this._model = null; - this._modelListener!.dispose(); - } } private updateMatchesForModel(): void { @@ -464,7 +453,7 @@ export class FileMatch extends Disposable implements IFileMatch { }); }); - this.setAllFindMatchesDecorations(matches); + this._findMatchDecorationModel.setAllFindMatchesDecorations(matches); this._onChange.fire({ forceUpdateModel: modelChange }); } @@ -617,6 +606,8 @@ export class FileMatch extends Disposable implements IFileMatch { override dispose(): void { this.setSelectedMatch(null); this.unbindModel(); + this.unbindEditorWidget(); + this._findMatchDecorationModel.dispose(); this._onDispose.fire(); super.dispose(); } @@ -627,140 +618,21 @@ export class FileMatch extends Disposable implements IFileMatch { } private async highlightCurrentFindMatchDecoration(match: NotebookMatch): Promise { - if (!this._notebookEditorWidget) { - return Promise.resolve(null); - } - // needs - // notebook cell - // only cellmatch: - // match range - // only webviewmatch: - // - if (match.webviewIndex === undefined) { - this.clearCurrentFindMatchDecoration(); - // match is an editor FindMatch, we update find match decoration in the editor - // we will highlight the match in the webview - this._notebookEditorWidget.changeModelDecorations(accessor => { - const findMatchesOptions: ModelDecorationOptions = FindDecorations._CURRENT_FIND_MATCH_DECORATION; - - const decorations: IModelDeltaDecoration[] = [ - { range: match.range(), options: findMatchesOptions } - ]; - const deltaDecoration: ICellModelDeltaDecorations = { - ownerId: match.cell.handle, - decorations: decorations - }; - - this._currentMatchDecorations = { - kind: 'input', - decorations: accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], [deltaDecoration]) - }; - }); - - this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, [{ - ownerId: match.cell.handle, - handle: match.cell.handle, - options: { - overviewRuler: { - color: overviewRulerSelectionHighlightForeground, - modelRanges: [match.range()], - includeOutput: false, - position: NotebookOverviewRulerLane.Center - } - } - } as INotebookDeltaDecoration]); - - return null; + return this._findMatchDecorationModel.highlightCurrentFindMatchDecorationInCell(match.cell, match.range()); } else { - this.clearCurrentFindMatchDecoration(); - const offset = await this._notebookEditorWidget.highlightFind(match.webviewIndex); - this._currentMatchDecorations = { kind: 'output', index: match.webviewIndex }; - - this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, [{ - ownerId: match.cell.handle, - handle: match.cell.handle, - options: { - overviewRuler: { - color: overviewRulerSelectionHighlightForeground, - modelRanges: [], - includeOutput: true, - position: NotebookOverviewRulerLane.Center - } - } - } as INotebookDeltaDecoration]); - - return offset; + return this._findMatchDecorationModel.highlightCurrentFindMatchDecorationInWebview(match.cell, match.webviewIndex); } } - private setAllFindMatchesDecorations(cellFindMatches: CellFindMatchWithIndex[]) { - if (!this._notebookEditorWidget) { - return; - } - this._notebookEditorWidget.changeModelDecorations((accessor) => { - - const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; - - const deltaDecorations: ICellModelDeltaDecorations[] = cellFindMatches.map(cellFindMatch => { - // Find matches - const newFindMatchesDecorations: IModelDeltaDecoration[] = new Array(cellFindMatch.length); - for (let i = 0; i < cellFindMatch.contentMatches.length; i++) { - newFindMatchesDecorations[i] = { - range: cellFindMatch.contentMatches[i].range, - options: findMatchesOptions - }; - } - - return { ownerId: cellFindMatch.cell.handle, decorations: newFindMatchesDecorations }; - }); - - this._allMatchesDecorations = accessor.deltaDecorations(this._allMatchesDecorations, deltaDecorations); - }); - - this._allMatchesCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._allMatchesCellDecorations, cellFindMatches.map(cellFindMatch => { - return { - ownerId: cellFindMatch.cell.handle, - handle: cellFindMatch.cell.handle, - options: { - overviewRuler: { - color: overviewRulerFindMatchForeground, - modelRanges: cellFindMatch.contentMatches.map(match => match.range), - includeOutput: cellFindMatch.webviewMatches.length > 0, - position: NotebookOverviewRulerLane.Center - } - } - }; - })); - } - - private clearCurrentFindMatchDecoration() { - if (!this._notebookEditorWidget) { - return; - } - - if (this._currentMatchDecorations?.kind === 'input') { - this._notebookEditorWidget.changeModelDecorations(accessor => { - accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []); - this._currentMatchDecorations = null; - }); - } else if (this._currentMatchDecorations?.kind === 'output') { - this._notebookEditorWidget.unHighlightFind(this._currentMatchDecorations.index); - } - - this._currentMatchCellDecorations = this._notebookEditorWidget.deltaCellDecorations(this._currentMatchCellDecorations, []); - } - private revealCellRange(match: NotebookMatch, outputOffset: number | null) { if (!this._notebookEditorWidget) { return; } if (match.webviewIndex) { - // reveal output range this._notebookEditorWidget.focusElement(match.cell); const index = this._notebookEditorWidget.getCellIndex(match.cell); if (index !== undefined) { - // const range: ICellRange = { start: index, end: index + 1 }; this._notebookEditorWidget.revealCellOffsetInCenterAsync(match.cell, outputOffset ?? 0); } } else { @@ -884,7 +756,7 @@ export class FolderMatch extends Disposable { const fileMatch = this._fileMatches.get(resource); if (fileMatch) { - fileMatch.unbindEditorWidget(editor); + fileMatch.unbindEditorWidget(); } else { const folderMatches = this.folderMatchesIterator(); for (const elem of folderMatches) { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index b1b0665e2a405..b9abfca7fe63a 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -61,7 +61,6 @@ import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/vie import { IEditorPane } from 'vs/workbench/common/editor'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { NotebookFindContrib } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget'; import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; import { ExcludePatternInputWidget, IncludePatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; import { appendKeyBindingLabel } from 'vs/workbench/contrib/search/browser/searchActionsBase'; @@ -1817,13 +1816,20 @@ export class SearchView extends ViewPane { } if (editor instanceof NotebookEditor) { - const controller = editor.getControl()?.getContribution(NotebookFindContrib.id); if (element instanceof Match) { if (element instanceof NotebookMatch) { element.parent().showMatch(element); } else { + // problems with this: + // 1: relies on the notebook having already been binded to the fileMatch (race condition) + // 2: doesn't wait for output to load before revealing cell, so when everything is loaded, there might be an offset const matchIndex = oldParentMatches.findIndex(e => e.id() === element.id()); - controller?.show(this.searchWidget.searchInput.getValue(), { matchIndex, focus: false }); + const sortedMatches = element.parent().matches().sort(searchMatchComparer); + const match = matchIndex >= sortedMatches.length ? sortedMatches[sortedMatches.length - 1] : sortedMatches[matchIndex]; + + if (match instanceof NotebookMatch) { + element.parent().showMatch(match); + } } } From b1e69a1283c15de699ba0158bbfb80a454e6889e Mon Sep 17 00:00:00 2001 From: andreamah Date: Mon, 23 Jan 2023 12:09:15 -0800 Subject: [PATCH 19/45] jump to nth result on raw text results to notebook results --- .../contrib/search/browser/searchModel.ts | 30 ++++++++++++------- .../contrib/search/browser/searchView.ts | 26 ++++++++++------ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 2b8dbd174c494..6b67c369419d5 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -317,8 +317,7 @@ export class FileMatch extends Disposable implements IFileMatch { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; const notebookEditorWidgetBorrow = experimentalNotebooksEnabled ? this.notebookEditorService.retrieveExistingWidgetFromURI(this._resource) : undefined; if (notebookEditorWidgetBorrow?.value) { - this.bindEditorWidget(notebookEditorWidgetBorrow.value); - await this.updateMatchesForEditorWidget(); + await this.bindEditorWidget(notebookEditorWidgetBorrow.value); } else if (model) { this.bindModel(model); this.updateMatchesForModel(); @@ -360,7 +359,12 @@ export class FileMatch extends Disposable implements IFileMatch { } } - bindEditorWidget(widget: NotebookEditorWidget) { + async bindEditorWidget(widget: NotebookEditorWidget) { + + if (this._notebookEditorWidget === widget) { + return; + } + this._notebookEditorWidget = widget; this._findMatchDecorationModel.notebookEditor = widget; this._editorWidgetListener = this._notebookEditorWidget.textModel?.onDidChangeContent((e) => { @@ -369,10 +373,14 @@ export class FileMatch extends Disposable implements IFileMatch { } this._notebookUpdateScheduler.schedule(); }) ?? null; - this._notebookUpdateScheduler.schedule(); + await this.updateMatchesForEditorWidget(); } - unbindEditorWidget() { + unbindEditorWidget(widget?: NotebookEditorWidget) { + if (widget && this._notebookEditorWidget !== widget) { + return; + } + this.updateMatchesForEditorWidget(); if (this._notebookEditorWidget) { this._notebookUpdateScheduler.cancel(); @@ -739,15 +747,15 @@ export class FolderMatch extends Disposable { } } - bindEditorWidget(editor: NotebookEditorWidget, resource: URI) { + async bindEditorWidget(editor: NotebookEditorWidget, resource: URI) { const fileMatch = this._fileMatches.get(resource); if (fileMatch) { - fileMatch.bindEditorWidget(editor); + await fileMatch.bindEditorWidget(editor); } else { const folderMatches = this.folderMatchesIterator(); for (const elem of folderMatches) { - elem.bindEditorWidget(editor, resource); + await elem.bindEditorWidget(editor, resource); } } } @@ -756,7 +764,7 @@ export class FolderMatch extends Disposable { const fileMatch = this._fileMatches.get(resource); if (fileMatch) { - fileMatch.unbindEditorWidget(); + fileMatch.unbindEditorWidget(editor); } else { const folderMatches = this.folderMatchesIterator(); for (const elem of folderMatches) { @@ -1433,9 +1441,9 @@ export class SearchResult extends Disposable { folderMatch?.bindModel(model); } - private onNotebookEditorWidgetAdded(editor: NotebookEditorWidget, resource: URI): void { + private async onNotebookEditorWidgetAdded(editor: NotebookEditorWidget, resource: URI): Promise { const folderMatch = this._folderMatchesMap.findSubstr(resource); - folderMatch?.bindEditorWidget(editor, resource); + await folderMatch?.bindEditorWidget(editor, resource); } private onNotebookEditorWidgetRemoved(editor: NotebookEditorWidget, resource: URI): void { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index b9abfca7fe63a..13634c62ba79e 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1820,16 +1820,24 @@ export class SearchView extends ViewPane { if (element instanceof NotebookMatch) { element.parent().showMatch(element); } else { - // problems with this: - // 1: relies on the notebook having already been binded to the fileMatch (race condition) - // 2: doesn't wait for output to load before revealing cell, so when everything is loaded, there might be an offset - const matchIndex = oldParentMatches.findIndex(e => e.id() === element.id()); - const sortedMatches = element.parent().matches().sort(searchMatchComparer); - const match = matchIndex >= sortedMatches.length ? sortedMatches[sortedMatches.length - 1] : sortedMatches[matchIndex]; - - if (match instanceof NotebookMatch) { - element.parent().showMatch(match); + const editorWidget = editor.getControl(); + if (editorWidget) { + // Ensure that the editor widget is binded. If if is, then this should return immediately. + // Otherwise, it will bind the widget. + await element.parent().bindEditorWidget(editorWidget); + + const matchIndex = oldParentMatches.findIndex(e => e.id() === element.id()); + const matches = element.parent().matches(); + const match = matchIndex >= matches.length ? matches[matches.length - 1] : matches[matchIndex]; + + if (match instanceof NotebookMatch) { + element.parent().showMatch(match); + } + this.tree.setSelection([match], getSelectionKeyboardEvent()); + // todo: bug sometimes where results disappear + // todo: focus goes automatically to notebook when using arrow keys } + } } From 7b973328aafd460df29114d351181683cfa8c339 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 7 Feb 2023 14:53:34 -0800 Subject: [PATCH 20/45] fix bug with focus shift --- src/vs/workbench/contrib/search/browser/searchModel.ts | 5 ++--- src/vs/workbench/contrib/search/browser/searchView.ts | 7 ++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 09370ea8789c0..df86d3cfebb4f 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -33,7 +33,7 @@ import { minimapFindMatch, overviewRulerFindMatchForeground } from 'vs/platform/ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { FindMatchDecorationModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel'; -import { CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -638,13 +638,12 @@ export class FileMatch extends Disposable implements IFileMatch { return; } if (match.webviewIndex) { - this._notebookEditorWidget.focusElement(match.cell); const index = this._notebookEditorWidget.getCellIndex(match.cell); if (index !== undefined) { this._notebookEditorWidget.revealCellOffsetInCenterAsync(match.cell, outputOffset ?? 0); } } else { - this._notebookEditorWidget.focusNotebookCell(match.cell, 'editor'); + match.cell.updateEditState(CellEditState.Editing, 'focusNotebookCell'); this._notebookEditorWidget.setCellEditorSelection(match.cell, match.range()); this._notebookEditorWidget.revealRangeInCenterIfOutsideViewportAsync(match.cell, match.range()); } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 13634c62ba79e..65770caea73b3 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1833,9 +1833,10 @@ export class SearchView extends ViewPane { if (match instanceof NotebookMatch) { element.parent().showMatch(match); } - this.tree.setSelection([match], getSelectionKeyboardEvent()); - // todo: bug sometimes where results disappear - // todo: focus goes automatically to notebook when using arrow keys + + if (!this.tree.getFocus().includes(match) || !this.tree.getSelection().includes(match)) { + this.tree.setSelection([match], getSelectionKeyboardEvent()); + } } } From 349b100fb66797133a04b2c2edef36c044b33b8d Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 7 Feb 2023 15:08:18 -0800 Subject: [PATCH 21/45] cleanup --- .../notebook/browser/services/notebookEditorService.ts | 1 + src/vs/workbench/contrib/search/browser/replaceService.ts | 1 - src/vs/workbench/contrib/search/browser/searchModel.ts | 6 +----- src/vs/workbench/contrib/search/browser/searchView.ts | 1 - 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts index 97bd92d99856b..92c3b01e76d4f 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts @@ -22,6 +22,7 @@ export interface INotebookEditorService { _serviceBrand: undefined; retrieveWidget(accessor: ServicesAccessor, group: IEditorGroup, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, dimension?: Dimension): IBorrowValue; + retrieveExistingWidgetFromURI(resource: URI): IBorrowValue | undefined; retrieveAllExistingWidgets(): IBorrowValue[]; onDidAddNotebookEditor: Event; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index ede2f234da58c..242dcff1b5ac3 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -220,7 +220,6 @@ export class ReplaceService implements IReplaceService { private createEdit(match: Match, text: string, resource: URI | null = null): ResourceTextEdit { const fileMatch: FileMatch = match.parent(); - return new ResourceTextEdit( resource ?? fileMatch.resource, { range: match.range(), text }, undefined, undefined diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index df86d3cfebb4f..3a938912a8d05 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -515,9 +515,7 @@ export class FileMatch extends Disposable implements IFileMatch { } matches(): Match[] { - const vals = this._matches.values(); - const arr = Array.from(vals); - return arr; + return Array.from(this._matches.values()); } remove(matches: Match | Match[]): void { @@ -1769,7 +1767,6 @@ export class SearchModel extends Disposable { }; } - private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, onProgress?: (result: ISearchProgressItem) => void) { const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); const onProgressCall = (p: ISearchProgressItem) => { @@ -1788,7 +1785,6 @@ export class SearchModel extends Disposable { ); tokenSource.dispose(); return { ...currentResult, ...notebookResult }; - } async search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 65770caea73b3..a31d8c5fbefe1 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -553,7 +553,6 @@ export class SearchView extends ViewPane { } refreshTree(event?: IChangeEvent): void { - // animation frame and debounce const collapseResults = this.searchConfig.collapseResults; if (!event || event.added || event.removed) { // Refresh whole tree From d11d1692abf90959280a34d787847be5215517da Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Feb 2023 12:48:41 -0800 Subject: [PATCH 22/45] fix tests and cleanup --- .../browser/view/renderers/webviewPreloads.ts | 1 - .../contrib/search/browser/searchModel.ts | 2 +- .../search/test/browser/searchActions.test.ts | 14 +++++++++- .../search/test/browser/searchModel.test.ts | 26 +++++++++++++------ .../search/test/browser/searchResult.test.ts | 14 +++++++++- .../search/test/browser/searchViewlet.test.ts | 17 +++++++++++- 6 files changed, 61 insertions(+), 13 deletions(-) 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 6a7a7d2014b28..965e10973258e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -923,7 +923,6 @@ async function webviewPreloads(ctx: PreloadContext) { if (match) { let offset = 0; try { - console.log(match.id); const outputOffset = document.getElementById(match.id)!.getBoundingClientRect().top; const rangeOffset = match.originalRange.getBoundingClientRect().top; offset = rangeOffset - outputOffset; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 3a938912a8d05..c3d355ef8af49 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1767,7 +1767,7 @@ export class SearchModel extends Disposable { }; } - private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, onProgress?: (result: ISearchProgressItem) => void) { + private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); const onProgressCall = (p: ISearchProgressItem) => { progressEmitter.fire(); diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index ca2031f95908d..f27540fe7c831 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -21,6 +21,10 @@ import { MockObjectTree } from 'vs/workbench/contrib/search/test/browser/mockSea import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { ILabelService } from 'vs/platform/label/common/label'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TestEditorGroupsService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('Search Actions', () => { @@ -30,6 +34,7 @@ suite('Search Actions', () => { setup(() => { instantiationService = new TestInstantiationService(); instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); instantiationService.stub(IKeybindingService, {}); instantiationService.stub(ILabelService, { getUriBasenameLabel: (uri: URI) => '' }); instantiationService.stub(IKeybindingService, 'resolveKeybinding', (keybinding: Keybinding) => USLayoutResolvedKeybinding.resolveKeybinding(keybinding, OS)); @@ -169,8 +174,15 @@ suite('Search Actions', () => { } function stubModelService(instantiationService: TestInstantiationService): IModelService { - instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IThemeService, new TestThemeService()); + const config = new TestConfigurationService(); + config.setUserConfiguration('search', { searchOnType: true, experimental: { notebookSearch: false } }); + instantiationService.stub(IConfigurationService, config); return instantiationService.createInstance(ModelService); } + + function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { + instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); + return instantiationService.createInstance(NotebookEditorWidgetService); + } }); diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 697e1d4a1eb56..2cb9123f044a6 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -25,6 +25,10 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { isWindows } from 'vs/base/common/platform'; import { ILabelService } from 'vs/platform/label/common/label'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TestEditorGroupsService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; const nullEvent = new class { id: number = -1; @@ -75,14 +79,14 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(ILabelService, { getUriBasenameLabel: (uri: URI) => '' }); instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); instantiationService.stub(ISearchService, {}); instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stub(ILogService, new NullLogService()); - - const config = new TestConfigurationService(); - config.setUserConfiguration('search', { searchOnType: true }); - instantiationService.stub(IConfigurationService, config); + // const config = new TestConfigurationService(); + // config.setUserConfiguration('search', { searchOnType: true }); + // instantiationService.stub(IConfigurationService, config); }); teardown(() => { @@ -171,9 +175,8 @@ suite('SearchModel', () => { await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); assert.ok(target.calledThrice); - const data = target.args[2]; - data[1].duration = -1; - assert.deepStrictEqual(['searchResultsFirstRender', { duration: -1 }], data); + assert.ok(target.calledWith('searchResultsFirstRender')); + assert.ok(target.calledWith('searchResultsFinished')); }); test('Search Model: Search reports timed telemetry on search when progress is not called', () => { @@ -355,9 +358,16 @@ suite('SearchModel', () => { } function stubModelService(instantiationService: TestInstantiationService): IModelService { - instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IThemeService, new TestThemeService()); + const config = new TestConfigurationService(); + config.setUserConfiguration('search', { searchOnType: true, experimental: { notebookSearch: false } }); + instantiationService.stub(IConfigurationService, config); return instantiationService.createInstance(ModelService); } + function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { + instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); + return instantiationService.createInstance(NotebookEditorWidgetService); + } + }); diff --git a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index 54679cf406870..4b81f2a8651aa 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -25,6 +25,10 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; import { MockLabelService } from 'vs/workbench/services/label/test/common/mockLabelService'; import { isWindows } from 'vs/base/common/platform'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TestEditorGroupsService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; const lineOneRange = new OneLineRange(1, 0, 1); @@ -36,6 +40,7 @@ suite('SearchResult', () => { instantiationService = new TestInstantiationService(); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); + instantiationService.stub(INotebookEditorService, stubNotebookEditorService(instantiationService)); instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stubPromise(IReplaceService, {}); instantiationService.stub(IReplaceService, 'replace', () => Promise.resolve(null)); @@ -516,11 +521,18 @@ suite('SearchResult', () => { } function stubModelService(instantiationService: TestInstantiationService): IModelService { - instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IThemeService, new TestThemeService()); + const config = new TestConfigurationService(); + config.setUserConfiguration('search', { searchOnType: true, experimental: { notebookSearch: false } }); + instantiationService.stub(IConfigurationService, config); return instantiationService.createInstance(ModelService); } + function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { + instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); + return instantiationService.createInstance(NotebookEditorWidgetService); + } + function getPopulatedSearchResult() { const testObject = aSearchResult(); diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index f9471b65e6d71..1da5e284d825e 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -25,6 +25,10 @@ import { FileMatch, FolderMatch, Match, searchComparer, searchMatchComparer, Sea import { MockLabelService } from 'vs/workbench/services/label/test/common/mockLabelService'; import { IFileMatch, ITextSearchMatch, OneLineRange, QueryType, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { TestEditorGroupsService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; @@ -33,6 +37,8 @@ suite('Search - Viewlet', () => { instantiation = new TestInstantiationService(); instantiation.stub(ILanguageConfigurationService, TestLanguageConfigurationService); instantiation.stub(IModelService, stubModelService(instantiation)); + instantiation.stub(INotebookEditorService, stubNotebookEditorService(instantiation)); + instantiation.set(IWorkspaceContextService, new TestContextService(TestWorkspace)); instantiation.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiation.stub(ILabelService, new MockLabelService()); @@ -194,11 +200,20 @@ suite('Search - Viewlet', () => { } function stubModelService(instantiationService: TestInstantiationService): IModelService { - instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IThemeService, new TestThemeService()); + + const config = new TestConfigurationService(); + config.setUserConfiguration('search', { searchOnType: true, experimental: { notebookSearch: false } }); + instantiationService.stub(IConfigurationService, config); + return instantiationService.createInstance(ModelService); } + function stubNotebookEditorService(instantiationService: TestInstantiationService): INotebookEditorService { + instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService()); + return instantiationService.createInstance(NotebookEditorWidgetService); + } + function createFileUriFromPathFromRoot(path?: string): URI { const rootName = getRootName(); if (path) { From 4cd6107cf1509a2728ad25b559e29c500756bc97 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Feb 2023 13:39:12 -0800 Subject: [PATCH 23/45] extra cleanup for match decorations --- .../contrib/find/findMatchDecorationModel.ts | 25 +++---------------- .../notebook/browser/notebookEditorWidget.ts | 4 --- .../contrib/search/browser/searchModel.ts | 17 +++++++------ 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts index 4669eddb4e4bf..02d327305322a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts @@ -17,7 +17,7 @@ export class FindMatchDecorationModel extends Disposable { private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; constructor( - private _notebookEditor: INotebookEditor | undefined, + private _notebookEditor: INotebookEditor, ) { super(); } @@ -26,14 +26,6 @@ export class FindMatchDecorationModel extends Disposable { return this._currentMatchDecorations; } - set notebookEditor(notebookEditor: INotebookEditor | undefined) { - if (this._notebookEditor) { - // clear any previous decorations if applicable - this.clearDecorations(); - } - this._notebookEditor = notebookEditor; - } - public clearDecorations() { this.clearCurrentFindMatchDecoration(); this.setAllFindMatchesDecorations([]); @@ -41,9 +33,6 @@ export class FindMatchDecorationModel extends Disposable { public async highlightCurrentFindMatchDecorationInCell(cell: ICellViewModel, cellRange: Range): Promise { - if (!this._notebookEditor) { - return null; - } this.clearCurrentFindMatchDecoration(); @@ -83,9 +72,7 @@ export class FindMatchDecorationModel extends Disposable { } public async highlightCurrentFindMatchDecorationInWebview(cell: ICellViewModel, index: number): Promise { - if (!this._notebookEditor) { - return null; - } + this.clearCurrentFindMatchDecoration(); const offset = await this._notebookEditor.highlightFind(index); @@ -108,9 +95,7 @@ export class FindMatchDecorationModel extends Disposable { } public clearCurrentFindMatchDecoration() { - if (!this._notebookEditor) { - return; - } + if (this._currentMatchDecorations?.kind === 'input') { this._notebookEditor.changeModelDecorations(accessor => { accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []); @@ -125,9 +110,7 @@ export class FindMatchDecorationModel extends Disposable { public setAllFindMatchesDecorations(cellFindMatches: CellFindMatchWithIndex[]) { - if (!this._notebookEditor) { - return; - } + this._notebookEditor.changeModelDecorations((accessor) => { const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8e582f89aece6..01272dd17cf06 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -158,8 +158,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD private readonly onDidRenderOutput = this._onDidRenderOutput.event; private readonly _onDidResizeOutputEmitter = this._register(new Emitter()); readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event; - private readonly _onDidDispose = this._register(new Emitter()); - readonly onDidDispose = this._onDidDispose.event; //#endregion private _overlayContainer!: HTMLElement; @@ -2938,13 +2936,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._overlayContainer.remove(); this.viewModel?.dispose(); - this.viewModel = undefined; this._renderedEditors.clear(); this._baseCellEditorOptions.forEach(v => v.dispose()); this._baseCellEditorOptions.clear(); - this._onDidDispose.fire(); this._notebookOverviewRulerContainer.remove(); super.dispose(); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index c3d355ef8af49..b4b0edf552a72 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -277,7 +277,7 @@ export class FileMatch extends Disposable implements IFileMatch { private _updateScheduler: RunOnceScheduler; private _notebookUpdateScheduler: RunOnceScheduler; private _modelDecorations: string[] = []; - private _findMatchDecorationModel: FindMatchDecorationModel; + private _findMatchDecorationModel: FindMatchDecorationModel | undefined; private _context: Map = new Map(); public get context(): Map { @@ -304,7 +304,6 @@ export class FileMatch extends Disposable implements IFileMatch { this._updateScheduler = new RunOnceScheduler(this.updateMatchesForModel.bind(this), 250); this._notebookUpdateScheduler = new RunOnceScheduler(this.updateMatchesForEditorWidget.bind(this), 250); this._name = new Lazy(() => labelService.getUriBasenameLabel(this.resource)); - this._findMatchDecorationModel = new FindMatchDecorationModel(undefined); this.createMatches(); } @@ -366,7 +365,9 @@ export class FileMatch extends Disposable implements IFileMatch { } this._notebookEditorWidget = widget; - this._findMatchDecorationModel.notebookEditor = widget; + + this._findMatchDecorationModel?.dispose(); + this._findMatchDecorationModel = new FindMatchDecorationModel(widget); this._editorWidgetListener = this._notebookEditorWidget.textModel?.onDidChangeContent((e) => { if (!e.rawEvents.some(event => event.kind === NotebookCellsChangeType.ChangeCellContent || event.kind === NotebookCellsChangeType.ModelChange)) { return; @@ -384,7 +385,7 @@ export class FileMatch extends Disposable implements IFileMatch { this.updateMatchesForEditorWidget(); if (this._notebookEditorWidget) { this._notebookUpdateScheduler.cancel(); - this._findMatchDecorationModel.notebookEditor = undefined; + this._findMatchDecorationModel?.dispose(); this._editorWidgetListener?.dispose(); } this._notebookEditorWidget = null; @@ -460,8 +461,7 @@ export class FileMatch extends Disposable implements IFileMatch { } }); }); - - this._findMatchDecorationModel.setAllFindMatchesDecorations(matches); + this._findMatchDecorationModel?.setAllFindMatchesDecorations(matches); this._onChange.fire({ forceUpdateModel: modelChange }); } @@ -613,7 +613,7 @@ export class FileMatch extends Disposable implements IFileMatch { this.setSelectedMatch(null); this.unbindModel(); this.unbindEditorWidget(); - this._findMatchDecorationModel.dispose(); + this._findMatchDecorationModel?.dispose(); this._onDispose.fire(); super.dispose(); } @@ -624,6 +624,9 @@ export class FileMatch extends Disposable implements IFileMatch { } private async highlightCurrentFindMatchDecoration(match: NotebookMatch): Promise { + if (!this._findMatchDecorationModel) { + return null; + } if (match.webviewIndex === undefined) { return this._findMatchDecorationModel.highlightCurrentFindMatchDecorationInCell(match.cell, match.range()); } else { From 540974591a6de657aa34092a3d97c91d9f0806a8 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Feb 2023 13:56:58 -0800 Subject: [PATCH 24/45] cleanup diffs --- .../notebook/browser/contrib/find/findMatchDecorationModel.ts | 2 -- src/vs/workbench/contrib/search/browser/searchModel.ts | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts index 02d327305322a..3d54e5e4a1387 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts @@ -95,7 +95,6 @@ export class FindMatchDecorationModel extends Disposable { } public clearCurrentFindMatchDecoration() { - if (this._currentMatchDecorations?.kind === 'input') { this._notebookEditor.changeModelDecorations(accessor => { accessor.deltaDecorations(this._currentMatchDecorations?.kind === 'input' ? this._currentMatchDecorations.decorations : [], []); @@ -110,7 +109,6 @@ export class FindMatchDecorationModel extends Disposable { public setAllFindMatchesDecorations(cellFindMatches: CellFindMatchWithIndex[]) { - this._notebookEditor.changeModelDecorations((accessor) => { const findMatchesOptions: ModelDecorationOptions = FindDecorations._FIND_MATCH_DECORATION; diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index b4b0edf552a72..201174d7afbcb 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -46,7 +46,6 @@ import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/ export class Match { private static readonly MAX_PREVIEW_CHARS = 250; - // need to make sure that IDs are different for same-line-different-cell stuff protected _id: string; protected _range: Range; private _oneLinePreviewText: string; @@ -407,8 +406,6 @@ export class FileMatch extends Disposable implements IFileMatch { } private async updateMatchesForEditorWidget(): Promise { - // this is called from a timeout and might fire - // after the model has been disposed if (!this._notebookEditorWidget) { return; } @@ -466,7 +463,6 @@ export class FileMatch extends Disposable implements IFileMatch { } private updateMatches(matches: FindMatch[], modelChange: boolean, model: ITextModel): void { - const textSearchResults = editorMatchesToTextSearchResults(matches, model, this._previewOptions); textSearchResults.forEach(textSearchResult => { textSearchResultToMatches(textSearchResult, this).forEach(match => { From a368db12b586ce842c219447f471914ef4962266 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Feb 2023 15:15:47 -0800 Subject: [PATCH 25/45] remove comments and add readonly field --- .../notebook/browser/contrib/find/findMatchDecorationModel.ts | 2 +- .../workbench/contrib/search/test/browser/searchModel.test.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts index 3d54e5e4a1387..e5ac8c7adcb5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts @@ -17,7 +17,7 @@ export class FindMatchDecorationModel extends Disposable { private _currentMatchDecorations: { kind: 'input'; decorations: ICellModelDecorations[] } | { kind: 'output'; index: number } | null = null; constructor( - private _notebookEditor: INotebookEditor, + private readonly _notebookEditor: INotebookEditor ) { super(); } diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 2cb9123f044a6..ca89c3854ae3e 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -84,9 +84,6 @@ suite('SearchModel', () => { instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stub(ILogService, new NullLogService()); - // const config = new TestConfigurationService(); - // config.setUserConfiguration('search', { searchOnType: true }); - // instantiationService.stub(IConfigurationService, config); }); teardown(() => { From 917ac78cde6e980badaea09577fdf0405eae4387 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 9 Feb 2023 10:06:31 -0800 Subject: [PATCH 26/45] remove webview extract match for now --- .../notebook/browser/notebookBrowser.ts | 2 +- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 97 +------------------ .../search/browser/searchNotebookHelpers.ts | 9 +- 4 files changed, 13 insertions(+), 97 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 36d82c1104316..369cd5c09a5cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -740,7 +740,7 @@ export interface ISearchPreviewInfo { export interface CellWebviewFindMatch { readonly index: number; - readonly searchPreviewInfo: ISearchPreviewInfo; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export type CellContentFindMatch = FindMatch; 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 9f01237fa5378..3e46646d23e05 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -424,7 +424,7 @@ export interface IFindMatch { readonly cellId: string; readonly id: string; readonly index: number; - readonly searchPreviewInfo: ISearchPreviewInfo; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export interface IDidFindMessage extends BaseToWebviewMessage { 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 965e10973258e..46a103a4875cb 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -812,18 +812,9 @@ async function webviewPreloads(ctx: PreloadContext) { container: Node; originalRange: Range; isShadow: boolean; - searchPreviewInfo: ISearchPreviewInfo; highlightResult?: IHighlightResult; } - interface ISearchPreviewInfo { - line: string; - range: { - start: number; - end: number; - }; - } - interface IHighlighter { highlightCurrentMatch(index: number): void; unHighlightCurrentMatch(index: number): void; @@ -948,81 +939,6 @@ async function webviewPreloads(ctx: PreloadContext) { } } - function extractSelectionLine(selection: Selection): ISearchPreviewInfo { - const range = selection.getRangeAt(0); - - // we need to keep a reference to the old selection range to re-apply later - const oldRange = range.cloneRange(); - const captureLength = selection.toString().length; - - // use selection API to modify selection to get entire line (the first line if multi-select) - - // collapse selection to start so that the cursor position is at beginning of match - selection.collapseToStart(); - - // extend selection in both directions to select the line - selection.modify('move', 'backward', 'lineboundary'); - selection.modify('extend', 'forward', 'lineboundary'); - - const line = selection.toString(); - - // using the original range and the new range, we can find the offset of the match from the line start. - const rangeStart = getStartOffset(selection.getRangeAt(0), oldRange); - - // line range for match - const lineRange = { - start: rangeStart, - end: rangeStart + captureLength, - }; - - // re-add the old range so that the selection is restored - selection.removeAllRanges(); - selection.addRange(oldRange); - - return { line, range: lineRange }; - } - - function getStartOffset(lineRange: Range, originalRange: Range) { - // sometimes, the old and new range are in different DOM elements (ie: when the match is inside of ) - // so we need to find the first common ancestor DOM element and find the positions of the old and new range relative to that. - const firstCommonAncestor = findFirstCommonAncestor(lineRange.startContainer, originalRange.startContainer); - - const selectionOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, lineRange.startContainer) + lineRange.startOffset; - const textOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, originalRange.startContainer) + originalRange.startOffset; - return textOffset - selectionOffset; - } - - // modified from https://stackoverflow.com/a/68583466/16253823 - function findFirstCommonAncestor(nodeA: Node, nodeB: Node) { - const range = new Range(); - range.setStart(nodeA, 0); - range.setEnd(nodeB, 0); - return range.commonAncestorContainer; - } - - // modified from https://stackoverflow.com/a/48812529/16253823 - function getSelectionOffsetRelativeTo(parentElement: Node, currentNode: Node | null): number { - if (!currentNode) { - return 0; - } - let offset = 0; - - if (currentNode === parentElement || !parentElement.contains(currentNode)) { - return offset; - } - - - // count the number of chars before the current dom elem and the start of the dom - let prevSibling = currentNode.previousSibling; - while (prevSibling) { - const nodeContent = prevSibling.nodeValue || ''; - offset += nodeContent.length; - prevSibling = prevSibling.previousSibling; - } - - return offset + getSelectionOffsetRelativeTo(parentElement, currentNode.parentNode); - } - const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { let find = true; const matches: IFindMatch[] = []; @@ -1069,7 +985,6 @@ async function webviewPreloads(ctx: PreloadContext) { container: preview, isShadow: true, originalRange: shadowSelection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(shadowSelection), }); } } @@ -1089,8 +1004,7 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: outputNode, isShadow: true, - originalRange: shadowSelection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(shadowSelection) + originalRange: shadowSelection.getRangeAt(0) }); } } @@ -1108,8 +1022,7 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: lastEl.cellId, container: lastEl.container, isShadow: false, - originalRange: selection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(selection), + originalRange: selection.getRangeAt(0) }); } else { @@ -1129,8 +1042,7 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: node, isShadow: false, - originalRange: selection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(selection), + originalRange: selection.getRangeAt(0) }); } break; @@ -1166,8 +1078,7 @@ async function webviewPreloads(ctx: PreloadContext) { type: match.type, id: match.id, cellId: match.cellId, - index, - searchPreviewInfo: match.searchPreviewInfo, + index })) }); }; diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index f02e122c820d4..84c48e8a41279 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -50,9 +50,14 @@ function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTex } } else { + // TODO: this is a placeholder for webview matches + const searchPreviewInfo = matches.searchPreviewInfo ?? { + line: '', range: { start: 0, end: 0 } + }; + return new NotebookTextSearchMatch( - matches.searchPreviewInfo.line, - new Range(0, matches.searchPreviewInfo.range.start, 0, matches.searchPreviewInfo.range.end), + searchPreviewInfo.line, + new Range(0, searchPreviewInfo.range.start, 0, searchPreviewInfo.range.end), cellInfo.notebookMatchInfo, previewOptions); } From aa79ffcefb6b9e93a0508aad1295a750297b2a72 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 9 Feb 2023 10:10:33 -0800 Subject: [PATCH 27/45] remove comma --- .../contrib/notebook/browser/view/renderers/webviewPreloads.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 46a103a4875cb..485b6f262cc52 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -984,7 +984,7 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: preview.id, container: preview, isShadow: true, - originalRange: shadowSelection.getRangeAt(0), + originalRange: shadowSelection.getRangeAt(0) }); } } From 41b21f3446bff80ba253d167c42785c51b70a7ca Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 9 Feb 2023 10:14:39 -0800 Subject: [PATCH 28/45] Revert "remove comma" This reverts commit aa79ffcefb6b9e93a0508aad1295a750297b2a72. --- .../contrib/notebook/browser/view/renderers/webviewPreloads.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 485b6f262cc52..46a103a4875cb 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -984,7 +984,7 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: preview.id, container: preview, isShadow: true, - originalRange: shadowSelection.getRangeAt(0) + originalRange: shadowSelection.getRangeAt(0), }); } } From 716a8d0b422e6e6da0a39d9e0b1c79d14c80509b Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 9 Feb 2023 10:15:07 -0800 Subject: [PATCH 29/45] Revert "remove webview extract match for now" This reverts commit 917ac78cde6e980badaea09577fdf0405eae4387. --- .../notebook/browser/notebookBrowser.ts | 2 +- .../browser/view/renderers/webviewMessages.ts | 2 +- .../browser/view/renderers/webviewPreloads.ts | 97 ++++++++++++++++++- .../search/browser/searchNotebookHelpers.ts | 9 +- 4 files changed, 97 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 369cd5c09a5cf..36d82c1104316 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -740,7 +740,7 @@ export interface ISearchPreviewInfo { export interface CellWebviewFindMatch { readonly index: number; - readonly searchPreviewInfo?: ISearchPreviewInfo; + readonly searchPreviewInfo: ISearchPreviewInfo; } export type CellContentFindMatch = FindMatch; 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 3e46646d23e05..9f01237fa5378 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -424,7 +424,7 @@ export interface IFindMatch { readonly cellId: string; readonly id: string; readonly index: number; - readonly searchPreviewInfo?: ISearchPreviewInfo; + readonly searchPreviewInfo: ISearchPreviewInfo; } export interface IDidFindMessage extends BaseToWebviewMessage { 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 46a103a4875cb..965e10973258e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -812,9 +812,18 @@ async function webviewPreloads(ctx: PreloadContext) { container: Node; originalRange: Range; isShadow: boolean; + searchPreviewInfo: ISearchPreviewInfo; highlightResult?: IHighlightResult; } + interface ISearchPreviewInfo { + line: string; + range: { + start: number; + end: number; + }; + } + interface IHighlighter { highlightCurrentMatch(index: number): void; unHighlightCurrentMatch(index: number): void; @@ -939,6 +948,81 @@ async function webviewPreloads(ctx: PreloadContext) { } } + function extractSelectionLine(selection: Selection): ISearchPreviewInfo { + const range = selection.getRangeAt(0); + + // we need to keep a reference to the old selection range to re-apply later + const oldRange = range.cloneRange(); + const captureLength = selection.toString().length; + + // use selection API to modify selection to get entire line (the first line if multi-select) + + // collapse selection to start so that the cursor position is at beginning of match + selection.collapseToStart(); + + // extend selection in both directions to select the line + selection.modify('move', 'backward', 'lineboundary'); + selection.modify('extend', 'forward', 'lineboundary'); + + const line = selection.toString(); + + // using the original range and the new range, we can find the offset of the match from the line start. + const rangeStart = getStartOffset(selection.getRangeAt(0), oldRange); + + // line range for match + const lineRange = { + start: rangeStart, + end: rangeStart + captureLength, + }; + + // re-add the old range so that the selection is restored + selection.removeAllRanges(); + selection.addRange(oldRange); + + return { line, range: lineRange }; + } + + function getStartOffset(lineRange: Range, originalRange: Range) { + // sometimes, the old and new range are in different DOM elements (ie: when the match is inside of ) + // so we need to find the first common ancestor DOM element and find the positions of the old and new range relative to that. + const firstCommonAncestor = findFirstCommonAncestor(lineRange.startContainer, originalRange.startContainer); + + const selectionOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, lineRange.startContainer) + lineRange.startOffset; + const textOffset = getSelectionOffsetRelativeTo(firstCommonAncestor, originalRange.startContainer) + originalRange.startOffset; + return textOffset - selectionOffset; + } + + // modified from https://stackoverflow.com/a/68583466/16253823 + function findFirstCommonAncestor(nodeA: Node, nodeB: Node) { + const range = new Range(); + range.setStart(nodeA, 0); + range.setEnd(nodeB, 0); + return range.commonAncestorContainer; + } + + // modified from https://stackoverflow.com/a/48812529/16253823 + function getSelectionOffsetRelativeTo(parentElement: Node, currentNode: Node | null): number { + if (!currentNode) { + return 0; + } + let offset = 0; + + if (currentNode === parentElement || !parentElement.contains(currentNode)) { + return offset; + } + + + // count the number of chars before the current dom elem and the start of the dom + let prevSibling = currentNode.previousSibling; + while (prevSibling) { + const nodeContent = prevSibling.nodeValue || ''; + offset += nodeContent.length; + prevSibling = prevSibling.previousSibling; + } + + return offset + getSelectionOffsetRelativeTo(parentElement, currentNode.parentNode); + } + const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { let find = true; const matches: IFindMatch[] = []; @@ -985,6 +1069,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: preview, isShadow: true, originalRange: shadowSelection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(shadowSelection), }); } } @@ -1004,7 +1089,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: outputNode, isShadow: true, - originalRange: shadowSelection.getRangeAt(0) + originalRange: shadowSelection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(shadowSelection) }); } } @@ -1022,7 +1108,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: lastEl.cellId, container: lastEl.container, isShadow: false, - originalRange: selection.getRangeAt(0) + originalRange: selection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(selection), }); } else { @@ -1042,7 +1129,8 @@ async function webviewPreloads(ctx: PreloadContext) { cellId: cellId, container: node, isShadow: false, - originalRange: selection.getRangeAt(0) + originalRange: selection.getRangeAt(0), + searchPreviewInfo: extractSelectionLine(selection), }); } break; @@ -1078,7 +1166,8 @@ async function webviewPreloads(ctx: PreloadContext) { type: match.type, id: match.id, cellId: match.cellId, - index + index, + searchPreviewInfo: match.searchPreviewInfo, })) }); }; diff --git a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts index 84c48e8a41279..f02e122c820d4 100644 --- a/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/searchNotebookHelpers.ts @@ -50,14 +50,9 @@ function notebookEditorMatchToTextSearchResult(cellInfo: CellFindMatchInfoForTex } } else { - // TODO: this is a placeholder for webview matches - const searchPreviewInfo = matches.searchPreviewInfo ?? { - line: '', range: { start: 0, end: 0 } - }; - return new NotebookTextSearchMatch( - searchPreviewInfo.line, - new Range(0, searchPreviewInfo.range.start, 0, searchPreviewInfo.range.end), + matches.searchPreviewInfo.line, + new Range(0, matches.searchPreviewInfo.range.start, 0, matches.searchPreviewInfo.range.end), cellInfo.notebookMatchInfo, previewOptions); } From 4c23aac5e632662253cb229809672ace65d35336 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 14 Feb 2023 15:26:00 -0800 Subject: [PATCH 30/45] some support for output search --- .../find/notebookFindReplaceWidget.css | 6 +++-- .../contrib/find/notebookFindReplaceWidget.ts | 2 +- .../contrib/search/browser/replaceService.ts | 15 +++++++----- .../contrib/search/browser/searchModel.ts | 16 ++++++------- .../contrib/search/browser/searchView.ts | 9 ++++++- .../contrib/search/browser/searchWidget.ts | 24 ++++++++++++++++--- .../services/search/common/search.ts | 3 +++ 7 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css index 083fb5197c204..a6d4275d2eecf 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css @@ -11,7 +11,7 @@ right: 18px; width: var(--notebook-find-width); max-width: calc(100% - 28px - 28px - 8px); - padding:0 var(--notebook-find-horizontal-padding); + padding: 0 var(--notebook-find-horizontal-padding); transition: top 200ms linear; visibility: hidden; background-color: var(--vscode-editorWidget-background) !important; @@ -139,7 +139,8 @@ background-color: inherit !important; } -.monaco-workbench .simple-fr-find-part-wrapper .find-filter-button { +.monaco-workbench .simple-fr-find-part-wrapper .find-filter-button, +.monaco-workbench .search-container .find-filter-button { color: inherit; margin-left: 2px; float: left; @@ -167,6 +168,7 @@ .monaco-workbench .simple-fr-replace-part .monaco-inputbox > .ibwrapper > .input { height: 24px; } + .monaco-workbench .simple-fr-find-part-wrapper .monaco-sash { left: 0 !important; background-color: var(--vscode-editorWidget-resizeBorder, var(--vscode-editorWidget-border)); 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 7e1f8d698a452..62a2cddbea421 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -150,7 +150,7 @@ class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { } } -class NotebookFindInput extends FindInput { +export class NotebookFindInput extends FindInput { private _filterButtonContainer: HTMLElement; private _actionbar: ActionBar | null = null; private _filterChecked: boolean = false; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index c991021c2b9bc..c27c86870d526 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as network from 'vs/base/common/network'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IReference } from 'vs/base/common/lifecycle'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IModelService } from 'vs/editor/common/services/model'; @@ -27,7 +27,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { dirname } from 'vs/base/common/resources'; import { Promises } from 'vs/base/common/async'; import { SaveSourceRegistry } from 'vs/workbench/common/editor'; -import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService'; const REPLACE_PREVIEW = 'replacePreview'; @@ -116,10 +116,13 @@ export class ReplaceService implements IReplaceService { if (e.resource.scheme === network.Schemas.vscodeNotebookCell) { const notebookResource = CellUri.parse(e.resource)?.notebook; if (notebookResource) { - // todo: find whether there is a common API for saving notebooks and text files - const ref = await this.notebookEditorModelResolverService.resolve(notebookResource); - await ref.object.save({ source: ReplaceService.REPLACE_SAVE_SOURCE }); - ref.dispose(); + let ref: IReference | undefined; + try { + ref = await this.notebookEditorModelResolverService.resolve(notebookResource); + await ref.object.save({ source: ReplaceService.REPLACE_SAVE_SOURCE }); + } finally { + ref?.dispose(); + } } return; } else { diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 1d11c9e7c26e8..306713c440527 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -418,10 +418,10 @@ export class FileMatch extends Disposable implements IFileMatch { wholeWord: this._query.isWordMatch, caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, - includeMarkupInput: true, - includeMarkupPreview: false, - includeCodeInput: true, - includeOutput: false, + includeMarkupInput: this._query.isNotebookMarkdownInput, + includeMarkupPreview: !this._query.isNotebookMarkdownInput, + includeCodeInput: this._query.isNotebookCellInput, + includeOutput: this._query.isNotebookCellOutput, }, CancellationToken.None, true); this.updateNotebookMatches(allMatches, true); @@ -1738,10 +1738,10 @@ export class SearchModel extends Disposable { regex: query.contentPattern.isRegExp, wholeWord: query.contentPattern.isWordMatch, caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: true, - includeMarkupPreview: false, - includeCodeInput: true, - includeOutput: false, + includeMarkupInput: query.contentPattern.isNotebookMarkdownInput, + includeMarkupPreview: !query.contentPattern.isNotebookMarkdownInput, + includeCodeInput: query.contentPattern.isNotebookCellInput, + includeOutput: query.contentPattern.isNotebookCellOutput, }, token); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index eee8b95ed8906..3972a149a2b3b 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1380,6 +1380,10 @@ export class SearchView extends ViewPane { } const isRegex = this.searchWidget.searchInput.getRegex(); + const isNotebookMarkdownInput = this.searchWidget.getFilters().markupInput; + const isNotebookCellInput = this.searchWidget.getFilters().codeInput; + const isNotebookCellOutput = this.searchWidget.getFilters().codeOutput; + const isWholeWords = this.searchWidget.searchInput.getWholeWords(); const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); const contentPattern = this.searchWidget.searchInput.getValue(); @@ -1398,7 +1402,10 @@ export class SearchView extends ViewPane { pattern: contentPattern, isRegExp: isRegex, isCaseSensitive: isCaseSensitive, - isWordMatch: isWholeWords + isWordMatch: isWholeWords, + isNotebookMarkdownInput, + isNotebookCellInput, + isNotebookCellOutput }; const excludePattern = this.inputPatternExcludes.getValue(); diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 6732fde2c4a11..1c6565a540a64 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -20,7 +20,7 @@ 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'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; @@ -36,6 +36,9 @@ import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, sea import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { NotebookFindInput } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; +import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -153,6 +156,7 @@ export class SearchWidget extends Widget { private showContextToggle!: Toggle; public contextLinesInput!: InputBox; + private _filters: NotebookFindFilters; constructor( container: HTMLElement, options: ISearchWidgetOptions, @@ -161,12 +165,15 @@ export class SearchWidget extends Widget { @IKeybindingService private readonly keybindingService: IKeybindingService, @IClipboardService private readonly clipboardServce: IClipboardService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.contextKeyService); this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.contextKeyService); this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService); + this._filters = new NotebookFindFilters(true, false, true, true); this._replaceHistoryDelayer = new Delayer(500); @@ -181,6 +188,10 @@ export class SearchWidget extends Widget { this.updateAccessibilitySupport(); } + getFilters() { + return this._filters; + } + focus(select: boolean = true, focusReplace: boolean = false, suppressGlobalSearchBuffer = false): void { this.ignoreGlobalFindBufferOnNextFocus = suppressGlobalSearchBuffer; @@ -326,7 +337,14 @@ export class SearchWidget extends Widget { }; const searchInputContainer = dom.append(parent, dom.$('.search-container.input-box')); - this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); + + const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; + if (experimentalNotebooksEnabled) { + const scopedContextKeyService = this._register(this.contextKeyService.createScoped(searchInputContainer)); + this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, inputOptions,)); + } else { + this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); + } this.searchInput.onKeyDown((keyboardEvent: IKeyboardEvent) => this.onSearchInputKeyDown(keyboardEvent)); this.searchInput.setValue(options.value || ''); this.searchInput.setRegex(!!options.isRegex); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index ad3553fba7cb8..168ea1beea3db 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -145,6 +145,9 @@ export interface IPatternInfo { isMultiline?: boolean; isUnicode?: boolean; isCaseSensitive?: boolean; + isNotebookMarkdownInput?: boolean; + isNotebookCellInput?: boolean; + isNotebookCellOutput?: boolean; } export interface IExtendedExtensionSearchOptions { From 244e4a134286ca6c56ae2869bfbd2967bbe20995 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 15 Feb 2023 10:09:10 -0800 Subject: [PATCH 31/45] work on repalce visibility --- .../browser/searchActionsRemoveReplace.ts | 18 +++++++++--------- .../search/browser/searchResultsView.ts | 4 ++-- .../contrib/search/browser/searchView.ts | 6 ++++++ .../contrib/search/common/constants.ts | 1 + 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts index f3b966b22c2fe..e4544e10082c1 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts @@ -153,20 +153,20 @@ registerAction2(class ReplaceAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, }, icon: searchReplaceIcon, menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), group: 'inline', order: 1 } @@ -192,7 +192,7 @@ registerAction2(class ReplaceAllAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -200,13 +200,13 @@ registerAction2(class ReplaceAllAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), group: 'inline', order: 1 } @@ -231,7 +231,7 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -239,13 +239,13 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), group: 'inline', order: 1 } diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 11b30513aad88..86890c0c983a4 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -18,7 +18,7 @@ import { ISearchConfigurationProperties } from 'vs/workbench/services/search/com import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; -import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, Match, RenderableMatch, SearchModel, FolderMatch, FolderMatchNoRoot, FolderMatchWorkspaceRoot, NotebookMatch } from 'vs/workbench/contrib/search/browser/searchModel'; import { isEqual } from 'vs/base/common/resources'; import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; @@ -316,7 +316,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender renderElement(node: ITreeNode, index: number, templateData: IMatchTemplate): void { const match = node.element; const preview = match.preview(); - const replace = this.searchModel.isReplaceActive() && !!this.searchModel.replaceString; + const replace = this.searchModel.isReplaceActive() && !!this.searchModel.replaceString && !(match instanceof NotebookMatch && match.isWebviewMatch()); templateData.before.textContent = preview.before; templateData.match.textContent = preview.inside; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 3972a149a2b3b..5d78e07a9ef49 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -115,6 +115,7 @@ export class SearchView extends ViewPane { private folderMatchFocused: IContextKey; private folderMatchWithResourceFocused: IContextKey; private matchFocused: IContextKey; + private editableItemFocused: IContextKey; private hasSearchResultsKey: IContextKey; private lastFocusState: 'input' | 'tree' = 'input'; @@ -203,6 +204,7 @@ export class SearchView extends ViewPane { this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService); this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService); this.folderMatchWithResourceFocused = Constants.ResourceFolderFocusKey.bindTo(this.contextKeyService); + this.editableItemFocused = Constants.EditableItemFocused.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService); @@ -820,6 +822,9 @@ export class SearchView extends ViewPane { this.fileMatchOrMatchFocused.set(!!focus); this.fileMatchFocused.set(focus instanceof FileMatch); this.folderMatchFocused.set(focus instanceof FolderMatch); + + const editable = (!(focus instanceof NotebookMatch)) || !focus.isWebviewMatch(); + this.editableItemFocused.set(editable); this.matchFocused.set(focus instanceof Match); this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch); this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource); @@ -837,6 +842,7 @@ export class SearchView extends ViewPane { this.fileMatchOrFolderMatchFocus.reset(); this.fileMatchOrFolderMatchWithResourceFocus.reset(); this.folderMatchWithResourceFocused.reset(); + this.editableItemFocused.reset(); })); } diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index f0700ce6a992f..6b1ce727d8508 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -64,6 +64,7 @@ export const FileMatchOrFolderMatchWithResourceFocusKey = new RawContextKey('fileMatchFocus', false); export const FolderFocusKey = new RawContextKey('folderMatchFocus', false); export const ResourceFolderFocusKey = new RawContextKey('folderMatchWithResourceFocus', false); +export const EditableItemFocused = new RawContextKey('editableItemFocused', true); export const MatchFocusKey = new RawContextKey('matchFocus', false); export const ViewHasSearchPatternKey = new RawContextKey('viewHasSearchPattern', false); export const ViewHasReplacePatternKey = new RawContextKey('viewHasReplacePattern', false); From 655c4bb6c75ac5fdcbb106734959da2361042fc8 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 15 Feb 2023 16:57:03 -0800 Subject: [PATCH 32/45] basic output working --- .../notebook/browser/notebookBrowser.ts | 2 +- .../notebook/browser/notebookEditorWidget.ts | 4 +-- .../view/renderers/backLayerWebView.ts | 2 +- .../browser/view/renderers/webviewMessages.ts | 4 +-- .../browser/view/renderers/webviewPreloads.ts | 12 +++---- .../contrib/search/browser/replaceService.ts | 12 ++++--- .../browser/searchActionsRemoveReplace.ts | 18 +++++----- .../contrib/search/browser/searchModel.ts | 16 ++++++--- .../search/browser/searchResultsView.ts | 14 ++++++-- .../contrib/search/browser/searchView.ts | 35 +++++++++---------- .../contrib/search/common/constants.ts | 2 +- 11 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 36d82c1104316..369cd5c09a5cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -740,7 +740,7 @@ export interface ISearchPreviewInfo { export interface CellWebviewFindMatch { readonly index: number; - readonly searchPreviewInfo: ISearchPreviewInfo; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export type CellContentFindMatch = FindMatch; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 64685b8ff14e4..c06ede393d9e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2395,7 +2395,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return Promise.all(requests); } - async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false): Promise { + async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false): Promise { if (!this._notebookViewModel) { return []; } @@ -2425,7 +2425,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD if (this._webview) { // request all outputs to be rendered await this._warmupAll(!!options.includeOutput); - const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput }); + const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo }); // attach webview matches to model find matches webviewMatches.forEach(match => { if (!options.includeMarkupPreview && match.type === 'preview') { 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 6b9a4b324d9f7..bb8fa02d550ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1438,7 +1438,7 @@ export class BackLayerWebView extends Themable { }); } - async find(query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }): Promise { + async find(query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean }): Promise { if (query === '') { return []; } 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 9f01237fa5378..a6601083f2c49 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -393,7 +393,7 @@ export interface ITokenizedStylesChangedMessage { export interface IFindMessage { readonly type: 'find'; readonly query: string; - readonly options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }; + readonly options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean }; } @@ -424,7 +424,7 @@ export interface IFindMatch { readonly cellId: string; readonly id: string; readonly index: number; - readonly searchPreviewInfo: ISearchPreviewInfo; + readonly searchPreviewInfo?: ISearchPreviewInfo; } export interface IDidFindMessage extends BaseToWebviewMessage { 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 965e10973258e..7270525db8b4a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -812,7 +812,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: Node; originalRange: Range; isShadow: boolean; - searchPreviewInfo: ISearchPreviewInfo; + searchPreviewInfo?: ISearchPreviewInfo; highlightResult?: IHighlightResult; } @@ -1023,7 +1023,7 @@ async function webviewPreloads(ctx: PreloadContext) { return offset + getSelectionOffsetRelativeTo(parentElement, currentNode.parentNode); } - const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean }) => { + const find = (query: string, options: { wholeWord?: boolean; caseSensitive?: boolean; includeMarkup: boolean; includeOutput: boolean; shouldGetSearchPreviewInfo: boolean }) => { let find = true; const matches: IFindMatch[] = []; @@ -1069,7 +1069,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: preview, isShadow: true, originalRange: shadowSelection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(shadowSelection), + searchPreviewInfo: options.shouldGetSearchPreviewInfo ? extractSelectionLine(shadowSelection) : undefined, }); } } @@ -1090,7 +1090,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: outputNode, isShadow: true, originalRange: shadowSelection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(shadowSelection) + searchPreviewInfo: options.shouldGetSearchPreviewInfo ? extractSelectionLine(shadowSelection) : undefined, }); } } @@ -1109,7 +1109,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: lastEl.container, isShadow: false, originalRange: selection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(selection), + searchPreviewInfo: options.shouldGetSearchPreviewInfo ? extractSelectionLine(selection) : undefined, }); } else { @@ -1130,7 +1130,7 @@ async function webviewPreloads(ctx: PreloadContext) { container: node, isShadow: false, originalRange: selection.getRangeAt(0), - searchPreviewInfo: extractSelectionLine(selection), + searchPreviewInfo: options.shouldGetSearchPreviewInfo ? extractSelectionLine(selection) : undefined, }); } break; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index c27c86870d526..1d1e46802eda3 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -199,8 +199,11 @@ export class ReplaceService implements IReplaceService { if (arg instanceof Match) { if (arg instanceof NotebookMatch) { - const match = arg; - edits.push(this.createEdit(match, match.replaceString, match.cell.uri)); + if (!arg.isWebviewMatch()) { + // only apply edits if it's not a webview match, since webview matches are read-only + const match = arg; + edits.push(this.createEdit(match, match.replaceString, match.cell.uri)); + } } else { const match = arg; edits.push(this.createEdit(match, match.replaceString, resource)); @@ -215,13 +218,12 @@ export class ReplaceService implements IReplaceService { arg.forEach(element => { const fileMatch = element; if (fileMatch.count() > 0) { - edits.push(...fileMatch.matches().map( - match => this.createEdit(match, match.replaceString, (match instanceof NotebookMatch) ? match.cell.uri : resource) + edits.push(...fileMatch.matches().flatMap( + match => this.createEdits(match, resource) )); } }); } - return edits; } diff --git a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts index e4544e10082c1..058f27a37612a 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts @@ -153,20 +153,20 @@ registerAction2(class ReplaceAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, }, icon: searchReplaceIcon, menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), group: 'inline', order: 1 } @@ -192,7 +192,7 @@ registerAction2(class ReplaceAllAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -200,13 +200,13 @@ registerAction2(class ReplaceAllAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), group: 'inline', order: 1 } @@ -231,7 +231,7 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -239,13 +239,13 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.EditableItemFocused), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), group: 'inline', order: 1 } diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 306713c440527..b762d1d09c497 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -310,6 +310,9 @@ export class FileMatch extends Disposable implements IFileMatch { return this._closestRoot; } + hasWebviewMatches(): boolean { + return this.matches().some(m => m instanceof NotebookMatch && m.isWebviewMatch()); + } private async createMatches(): Promise { const model = this.modelService.getModel(this._resource); const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; @@ -422,7 +425,7 @@ export class FileMatch extends Disposable implements IFileMatch { includeMarkupPreview: !this._query.isNotebookMarkdownInput, includeCodeInput: this._query.isNotebookCellInput, includeOutput: this._query.isNotebookCellOutput, - }, CancellationToken.None, true); + }, CancellationToken.None, false, true); this.updateNotebookMatches(allMatches, true); } @@ -795,7 +798,7 @@ export class FolderMatch extends Disposable { replace(match: FileMatch): Promise { return this.replaceService.replace([match]).then(() => { - this.doRemoveFile([match]); + this.doRemoveFile([match], true, true, true); }); } @@ -947,7 +950,7 @@ export class FolderMatch extends Disposable { const allMatches = getFileMatches(matches); await this.replaceService.replace(allMatches); - this.doRemoveFile(allMatches, true, true); + this.doRemoveFile(allMatches, true, true, true); } public onFileChange(fileMatch: FileMatch, removed = false): void { @@ -978,11 +981,14 @@ export class FolderMatch extends Disposable { this._onChange.fire(event); } - private doRemoveFile(fileMatches: FileMatch[], dispose: boolean = true, trigger: boolean = true): void { + private doRemoveFile(fileMatches: FileMatch[], dispose: boolean = true, trigger: boolean = true, keepReadonly = false): void { const removed = []; for (const match of fileMatches as FileMatch[]) { if (this._fileMatches.get(match.resource)) { + if (keepReadonly && match.hasWebviewMatches()) { + continue; + } this._fileMatches.delete(match.resource); if (dispose) { match.dispose(); @@ -1742,7 +1748,7 @@ export class SearchModel extends Disposable { includeMarkupPreview: !query.contentPattern.isNotebookMarkdownInput, includeCodeInput: query.contentPattern.isNotebookCellInput, includeOutput: query.contentPattern.isNotebookCellOutput, - }, token); + }, token, false, true); if (matches.length) { diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 86890c0c983a4..b282c1736d3e0 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -28,7 +28,7 @@ import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/br import { ISearchActionContext } from 'vs/workbench/contrib/search/browser/searchActionsRemoveReplace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { FileFocusKey, FolderFocusKey, MatchFocusKey } from 'vs/workbench/contrib/search/common/constants'; +import { IsEditableItem, FileFocusKey, FolderFocusKey, MatchFocusKey } from 'vs/workbench/contrib/search/common/constants'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; interface IFolderMatchTemplate { @@ -56,6 +56,7 @@ interface IMatchTemplate { lineNumber: HTMLElement; actions: MenuWorkbenchToolBar; disposables: DisposableStore; + contextKeyService: IContextKeyService; } export class SearchDelegate implements IListVirtualDelegate { @@ -289,8 +290,12 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender const disposables = new DisposableStore(); - const contextKeyService = this.contextKeyService.createOverlay([[MatchFocusKey.key, true], [FileFocusKey.key, false], [FolderFocusKey.key, false]]); - const instantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyService])); + const contextKeyServiceMain = disposables.add(this.contextKeyService.createScoped(container)); + MatchFocusKey.bindTo(contextKeyServiceMain).set(true); + FileFocusKey.bindTo(contextKeyServiceMain).set(false); + FolderFocusKey.bindTo(contextKeyServiceMain).set(false); + // const contextKeyServiceOverlay = contextKeyServiceMain.createOverlay([[MatchFocusKey.key, true], [FileFocusKey.key, false], [FolderFocusKey.key, false]]); + const instantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain])); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { shouldForwardArgs: true @@ -310,6 +315,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender lineNumber, actions, disposables, + contextKeyService: contextKeyServiceMain }; } @@ -325,6 +331,8 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender templateData.after.textContent = preview.after; templateData.parent.title = (preview.before + (replace ? match.replaceString : preview.inside) + preview.after).trim().substr(0, 999); + IsEditableItem.bindTo(templateData.contextKeyService).set(!(match instanceof NotebookMatch && match.isWebviewMatch())); + const numLines = match.range().endLineNumber - match.range().startLineNumber; const extraLinesStr = numLines > 0 ? `+${numLines}` : ''; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 5d78e07a9ef49..0ead34790810a 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -115,7 +115,7 @@ export class SearchView extends ViewPane { private folderMatchFocused: IContextKey; private folderMatchWithResourceFocused: IContextKey; private matchFocused: IContextKey; - private editableItemFocused: IContextKey; + private isEditableItem: IContextKey; private hasSearchResultsKey: IContextKey; private lastFocusState: 'input' | 'tree' = 'input'; @@ -204,7 +204,7 @@ export class SearchView extends ViewPane { this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService); this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService); this.folderMatchWithResourceFocused = Constants.ResourceFolderFocusKey.bindTo(this.contextKeyService); - this.editableItemFocused = Constants.EditableItemFocused.bindTo(this.contextKeyService); + this.isEditableItem = Constants.IsEditableItem.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService); @@ -481,6 +481,7 @@ export class SearchView extends ViewPane { this._register(this.searchWidget.onSearchSubmit(options => this.triggerQueryChange(options))); this._register(this.searchWidget.onSearchCancel(({ focus }) => this.cancelSearch(focus))); this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.triggerQueryChange())); + this._register(this.searchWidget.getFilters().onDidChange(() => this.triggerQueryChange())); const updateHasPatternKey = () => this.hasSearchPatternKey.set(this.searchWidget.searchInput.getValue().length > 0); updateHasPatternKey(); @@ -816,21 +817,19 @@ export class SearchView extends ViewPane { })); this._register(Event.any(this.tree.onDidFocus, this.tree.onDidChangeFocus)(() => { - if (this.tree.isDOMFocused()) { - const focus = this.tree.getFocus()[0]; - this.firstMatchFocused.set(this.tree.navigate().first() === focus); - this.fileMatchOrMatchFocused.set(!!focus); - this.fileMatchFocused.set(focus instanceof FileMatch); - this.folderMatchFocused.set(focus instanceof FolderMatch); - - const editable = (!(focus instanceof NotebookMatch)) || !focus.isWebviewMatch(); - this.editableItemFocused.set(editable); - this.matchFocused.set(focus instanceof Match); - this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch); - this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource); - this.folderMatchWithResourceFocused.set(focus instanceof FolderMatchWithResource); - this.lastFocusState = 'tree'; - } + const focus = this.tree.getFocus()[0]; + this.firstMatchFocused.set(this.tree.navigate().first() === focus); + this.fileMatchOrMatchFocused.set(!!focus); + this.fileMatchFocused.set(focus instanceof FileMatch); + this.folderMatchFocused.set(focus instanceof FolderMatch); + + const editable = (!(focus instanceof NotebookMatch)) || !focus.isWebviewMatch(); + this.isEditableItem.set(editable); + this.matchFocused.set(focus instanceof Match); + this.fileMatchOrFolderMatchFocus.set(focus instanceof FileMatch || focus instanceof FolderMatch); + this.fileMatchOrFolderMatchWithResourceFocus.set(focus instanceof FileMatch || focus instanceof FolderMatchWithResource); + this.folderMatchWithResourceFocused.set(focus instanceof FolderMatchWithResource); + this.lastFocusState = 'tree'; })); this._register(this.tree.onDidBlur(() => { @@ -842,7 +841,7 @@ export class SearchView extends ViewPane { this.fileMatchOrFolderMatchFocus.reset(); this.fileMatchOrFolderMatchWithResourceFocus.reset(); this.folderMatchWithResourceFocused.reset(); - this.editableItemFocused.reset(); + this.isEditableItem.reset(); })); } diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index 6b1ce727d8508..dd0f4c1236c9d 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -64,7 +64,7 @@ export const FileMatchOrFolderMatchWithResourceFocusKey = new RawContextKey('fileMatchFocus', false); export const FolderFocusKey = new RawContextKey('folderMatchFocus', false); export const ResourceFolderFocusKey = new RawContextKey('folderMatchWithResourceFocus', false); -export const EditableItemFocused = new RawContextKey('editableItemFocused', true); +export const IsEditableItem = new RawContextKey('isEditableItem', true); export const MatchFocusKey = new RawContextKey('matchFocus', false); export const ViewHasSearchPatternKey = new RawContextKey('viewHasSearchPattern', false); export const ViewHasReplacePatternKey = new RawContextKey('viewHasReplacePattern', false); From 821e0fcb330476e652c114b7cd97bc05057cf414 Mon Sep 17 00:00:00 2001 From: andreamah Date: Tue, 7 Mar 2023 10:31:15 -0800 Subject: [PATCH 33/45] add options to notebook find widget to show/hide filter --- src/vs/base/browser/ui/findinput/findInput.ts | 2 ++ .../contrib/find/notebookFindReplaceWidget.ts | 5 +++++ .../contrib/search/browser/searchModel.ts | 2 +- .../contrib/search/browser/searchWidget.ts | 21 ++++++++++++++++++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index d663c086cffe4..ff9f05395e2ef 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -36,6 +36,8 @@ export interface IFindInputOptions { readonly showHistoryHint?: () => boolean; readonly toggleStyles: IToggleStyles; readonly inputBoxStyles: IInputBoxStyles; + + readonly filterStartVisibliity?: boolean; } const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input"); 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 62a2cddbea421..bc1731f66a0af 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -185,9 +185,14 @@ export class NotebookFindInput extends FindInput { } })); + this.filterVisible = options.filterStartVisibliity ?? true; this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this.getFilterWidth(); } + set filterVisible(show: boolean) { + this._filterButtonContainer.style.display = show ? '' : 'none'; + } + private getFilterWidth() { return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; } diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index b762d1d09c497..1c9c71017c55c 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1306,10 +1306,10 @@ export class SearchResult extends Disposable { this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); this._register(this.modelService.onModelAdded(model => this.onModelAdded(model))); - this._register(this.notebookEditorService.onDidAddNotebookEditorWidget(widget => this.onDidAddNotebookEditorWidget(widget))); const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { + // this._register(this.notebookEditorService.onDidAddNotebookEditorWidget(widget => this.onDidAddNotebookEditorWidget(widget))); this._register(this.notebookEditorService.onDidAddNotebookEditor(widget => { if (widget instanceof NotebookEditorWidget) { this.onDidAddNotebookEditorWidget(widget); diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 1c6565a540a64..aa24323f7907d 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -39,6 +39,9 @@ import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/br import { NotebookFindInput } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; +import { GroupModelChangeKind } from 'vs/workbench/common/editor'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -157,6 +160,7 @@ export class SearchWidget extends Widget { public contextLinesInput!: InputBox; private _filters: NotebookFindFilters; + constructor( container: HTMLElement, options: ISearchWidgetOptions, @@ -168,6 +172,7 @@ export class SearchWidget extends Widget { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly editorService: IEditorService, ) { super(); this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.contextKeyService); @@ -175,6 +180,14 @@ export class SearchWidget extends Widget { this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService); this._filters = new NotebookFindFilters(true, false, true, true); + this.editorService.onDidEditorsChange((e) => { + if (this.searchInput instanceof NotebookFindInput && + e.event.editor instanceof NotebookEditorInput && + (e.event.kind === GroupModelChangeKind.EDITOR_OPEN || e.event.kind === GroupModelChangeKind.EDITOR_CLOSE)) { + this.searchInput.filterVisible = this._hasNotebookOpen(); + } + }); + this._replaceHistoryDelayer = new Delayer(500); this.render(container, options); @@ -184,10 +197,16 @@ export class SearchWidget extends Widget { this.updateAccessibilitySupport(); } }); + this.accessibilityService.onDidChangeScreenReaderOptimized(() => this.updateAccessibilitySupport()); this.updateAccessibilitySupport(); } + private _hasNotebookOpen(): boolean { + const editors = this.editorService.editors; + return editors.some(editor => editor instanceof NotebookEditorInput); + } + getFilters() { return this._filters; } @@ -341,7 +360,7 @@ export class SearchWidget extends Widget { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { const scopedContextKeyService = this._register(this.contextKeyService.createScoped(searchInputContainer)); - this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, inputOptions,)); + this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, { ...inputOptions, filterStartVisibliity: this._hasNotebookOpen() },)); } else { this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); } From 8ff2b29b6e6985c8ca89feca6bed4d285477a2d9 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 09:47:32 -0800 Subject: [PATCH 34/45] cleanup --- .../browser/services/notebookEditorService.ts | 1 - .../services/notebookEditorServiceImpl.ts | 4 ---- .../search/browser/search.contribution.ts | 2 +- .../browser/searchActionsRemoveReplace.ts | 18 +++++++++--------- .../contrib/search/browser/searchModel.ts | 17 ++++++++--------- .../search/browser/searchResultsView.ts | 4 ++-- .../contrib/search/browser/searchView.ts | 11 +++++++---- .../contrib/search/common/constants.ts | 2 +- .../workbench/services/search/common/search.ts | 4 ++++ 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts index 92c3b01e76d4f..c9f22f1f832de 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts @@ -27,7 +27,6 @@ export interface INotebookEditorService { retrieveAllExistingWidgets(): IBorrowValue[]; onDidAddNotebookEditor: Event; onDidRemoveNotebookEditor: Event; - onDidAddNotebookEditorWidget: Event; addNotebookEditor(editor: INotebookEditor): void; removeNotebookEditor(editor: INotebookEditor): void; getNotebookEditor(editorId: string): INotebookEditor | undefined; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index a1fbf03208a9c..e61cfbe818125 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -30,9 +30,6 @@ export class NotebookEditorWidgetService implements INotebookEditorService { readonly onDidAddNotebookEditor = this._onNotebookEditorAdd.event; readonly onDidRemoveNotebookEditor = this._onNotebookEditorsRemove.event; - private readonly _onDidAddNotebookEditorWidget = new Emitter(); - readonly onDidAddNotebookEditorWidget = this._onDidAddNotebookEditorWidget.event; - private readonly _borrowableEditors = new Map>(); constructor( @@ -161,7 +158,6 @@ export class NotebookEditorWidgetService implements INotebookEditorService { // NEW widget const instantiationService = accessor.get(IInstantiationService); const widget = instantiationService.createInstance(NotebookEditorWidget, creationOptions ?? getDefaultNotebookCreationOptions(), initialDimension); - this._onDidAddNotebookEditorWidget.fire(widget); const token = this._tokenPool++; value = { widget, token }; diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 8f0b863a10ab6..68ef0aaa62551 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -352,7 +352,7 @@ configurationRegistry.registerConfiguration({ }, 'search.experimental.notebookSearch': { type: 'boolean', - description: nls.localize('search.experimental.notebookSearch', "Controls whether to use the experimental notebook search in the global search. Please refresh your search for changes to this setting to take effect."), + description: nls.localize('search.experimental.notebookSearch', "Controls whether to use the experimental notebook search in the global search. Please refresh VS Code instance for changes to this setting to take effect."), default: false }, } diff --git a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts index 5639eb5624565..c9e36f7d55e11 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsRemoveReplace.ts @@ -156,20 +156,20 @@ registerAction2(class ReplaceAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItemKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, }, icon: searchReplaceIcon, menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItemKey), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.MatchFocusKey, Constants.IsEditableItemKey), group: 'inline', order: 1 } @@ -195,7 +195,7 @@ registerAction2(class ReplaceAllAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItemKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -203,13 +203,13 @@ registerAction2(class ReplaceAllAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItemKey), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FileFocusKey, Constants.IsEditableItemKey), group: 'inline', order: 1 } @@ -234,7 +234,7 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { category, keybinding: { weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItemKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.Digit1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], }, @@ -242,13 +242,13 @@ registerAction2(class ReplaceAllInFolderAction extends Action2 { menu: [ { id: MenuId.SearchContext, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItemKey), group: 'search', order: 1 }, { id: MenuId.SearchActionMenu, - when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItem), + when: ContextKeyExpr.and(Constants.ReplaceActiveKey, Constants.FolderFocusKey, Constants.IsEditableItemKey), group: 'inline', order: 1 } diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 1c9c71017c55c..c4a467b5b1029 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -421,10 +421,10 @@ export class FileMatch extends Disposable implements IFileMatch { wholeWord: this._query.isWordMatch, caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, - includeMarkupInput: this._query.isNotebookMarkdownInput, - includeMarkupPreview: !this._query.isNotebookMarkdownInput, - includeCodeInput: this._query.isNotebookCellInput, - includeOutput: this._query.isNotebookCellOutput, + includeMarkupInput: this._query.notebookInfo?.isNotebookMarkdownInput, + includeMarkupPreview: !this._query.notebookInfo?.isNotebookMarkdownInput, + includeCodeInput: this._query.notebookInfo?.isNotebookCellInput, + includeOutput: this._query.notebookInfo?.isNotebookCellOutput, }, CancellationToken.None, false, true); this.updateNotebookMatches(allMatches, true); @@ -1309,7 +1309,6 @@ export class SearchResult extends Disposable { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { - // this._register(this.notebookEditorService.onDidAddNotebookEditorWidget(widget => this.onDidAddNotebookEditorWidget(widget))); this._register(this.notebookEditorService.onDidAddNotebookEditor(widget => { if (widget instanceof NotebookEditorWidget) { this.onDidAddNotebookEditorWidget(widget); @@ -1744,10 +1743,10 @@ export class SearchModel extends Disposable { regex: query.contentPattern.isRegExp, wholeWord: query.contentPattern.isWordMatch, caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: query.contentPattern.isNotebookMarkdownInput, - includeMarkupPreview: !query.contentPattern.isNotebookMarkdownInput, - includeCodeInput: query.contentPattern.isNotebookCellInput, - includeOutput: query.contentPattern.isNotebookCellOutput, + includeMarkupInput: query.contentPattern.notebookInfo?.isNotebookMarkdownInput, + includeMarkupPreview: !query.contentPattern.notebookInfo?.isNotebookMarkdownInput, + includeCodeInput: query.contentPattern.notebookInfo?.isNotebookCellInput, + includeOutput: query.contentPattern.notebookInfo?.isNotebookCellOutput, }, token, false, true); diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index b282c1736d3e0..9585e8c009bbf 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -28,7 +28,7 @@ import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/br import { ISearchActionContext } from 'vs/workbench/contrib/search/browser/searchActionsRemoveReplace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IsEditableItem, FileFocusKey, FolderFocusKey, MatchFocusKey } from 'vs/workbench/contrib/search/common/constants'; +import { IsEditableItemKey, FileFocusKey, FolderFocusKey, MatchFocusKey } from 'vs/workbench/contrib/search/common/constants'; import { defaultCountBadgeStyles } from 'vs/platform/theme/browser/defaultStyles'; interface IFolderMatchTemplate { @@ -331,7 +331,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender templateData.after.textContent = preview.after; templateData.parent.title = (preview.before + (replace ? match.replaceString : preview.inside) + preview.after).trim().substr(0, 999); - IsEditableItem.bindTo(templateData.contextKeyService).set(!(match instanceof NotebookMatch && match.isWebviewMatch())); + IsEditableItemKey.bindTo(templateData.contextKeyService).set(!(match instanceof NotebookMatch && match.isWebviewMatch())); const numLines = match.range().endLineNumber - match.range().startLineNumber; const extraLinesStr = numLines > 0 ? `+${numLines}` : ''; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index ba3ed5a8979ca..441b178ea9f7e 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -203,7 +203,7 @@ export class SearchView extends ViewPane { this.fileMatchFocused = Constants.FileFocusKey.bindTo(this.contextKeyService); this.folderMatchFocused = Constants.FolderFocusKey.bindTo(this.contextKeyService); this.folderMatchWithResourceFocused = Constants.ResourceFolderFocusKey.bindTo(this.contextKeyService); - this.isEditableItem = Constants.IsEditableItem.bindTo(this.contextKeyService); + this.isEditableItem = Constants.IsEditableItemKey.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.searchStateKey = SearchStateKey.bindTo(this.contextKeyService); @@ -822,6 +822,7 @@ export class SearchView extends ViewPane { this.fileMatchFocused.set(focus instanceof FileMatch); this.folderMatchFocused.set(focus instanceof FolderMatch); + // we don't need to check experimental flag here because NotebookMatches only exist when the flag is enabled const editable = (!(focus instanceof NotebookMatch)) || !focus.isWebviewMatch(); this.isEditableItem.set(editable); this.matchFocused.set(focus instanceof Match); @@ -1407,9 +1408,11 @@ export class SearchView extends ViewPane { isRegExp: isRegex, isCaseSensitive: isCaseSensitive, isWordMatch: isWholeWords, - isNotebookMarkdownInput, - isNotebookCellInput, - isNotebookCellOutput + notebookInfo: { + isNotebookMarkdownInput, + isNotebookCellInput, + isNotebookCellOutput + } }; const excludePattern = this.inputPatternExcludes.getValue(); diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index dd0f4c1236c9d..2b81f549838b3 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -64,7 +64,7 @@ export const FileMatchOrFolderMatchWithResourceFocusKey = new RawContextKey('fileMatchFocus', false); export const FolderFocusKey = new RawContextKey('folderMatchFocus', false); export const ResourceFolderFocusKey = new RawContextKey('folderMatchWithResourceFocus', false); -export const IsEditableItem = new RawContextKey('isEditableItem', true); +export const IsEditableItemKey = new RawContextKey('isEditableItem', true); export const MatchFocusKey = new RawContextKey('matchFocus', false); export const ViewHasSearchPatternKey = new RawContextKey('viewHasSearchPattern', false); export const ViewHasReplacePatternKey = new RawContextKey('viewHasReplacePattern', false); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 168ea1beea3db..54e5ded64ddde 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -145,6 +145,10 @@ export interface IPatternInfo { isMultiline?: boolean; isUnicode?: boolean; isCaseSensitive?: boolean; + notebookInfo?: INotebookPatternInfo; +} + +export interface INotebookPatternInfo { isNotebookMarkdownInput?: boolean; isNotebookCellInput?: boolean; isNotebookCellOutput?: boolean; From ee05e1f2b8fd98154f16753c6db42106ac9d0c41 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 12:48:10 -0800 Subject: [PATCH 35/45] fix rendered markdown not resolving and first rendered markdown opening in edit more --- src/vs/workbench/contrib/search/browser/searchModel.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index c4a467b5b1029..289c1011b9511 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -363,6 +363,8 @@ export class FileMatch extends Disposable implements IFileMatch { async bindNotebookEditorWidget(widget: NotebookEditorWidget) { if (this._notebookEditorWidget === widget) { + // ensure that the matches are up to date, but everything else should be configured already. + await this.updateMatchesForEditorWidget(); return; } @@ -637,7 +639,7 @@ export class FileMatch extends Disposable implements IFileMatch { if (!this._notebookEditorWidget) { return; } - if (match.webviewIndex) { + if (match.webviewIndex !== undefined) { const index = this._notebookEditorWidget.getCellIndex(match.cell); if (index !== undefined) { this._notebookEditorWidget.revealCellOffsetInCenterAsync(match.cell, outputOffset ?? 0); From 9696d2c15fedaca00ae74a29febdfd011ddf5835 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 12:54:56 -0800 Subject: [PATCH 36/45] remove commented out code --- src/vs/workbench/contrib/search/browser/searchResultsView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchResultsView.ts b/src/vs/workbench/contrib/search/browser/searchResultsView.ts index 9585e8c009bbf..910ed817280af 100644 --- a/src/vs/workbench/contrib/search/browser/searchResultsView.ts +++ b/src/vs/workbench/contrib/search/browser/searchResultsView.ts @@ -294,7 +294,7 @@ export class MatchRenderer extends Disposable implements ICompressibleTreeRender MatchFocusKey.bindTo(contextKeyServiceMain).set(true); FileFocusKey.bindTo(contextKeyServiceMain).set(false); FolderFocusKey.bindTo(contextKeyServiceMain).set(false); - // const contextKeyServiceOverlay = contextKeyServiceMain.createOverlay([[MatchFocusKey.key, true], [FileFocusKey.key, false], [FolderFocusKey.key, false]]); + const instantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, contextKeyServiceMain])); const actions = disposables.add(instantiationService.createInstance(MenuWorkbenchToolBar, actionBarContainer, MenuId.SearchActionMenu, { menuOptions: { From 8059cef9e9257b743f9c209003a2ad7543e089a7 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 13:18:49 -0800 Subject: [PATCH 37/45] remove filterStartVisibliity from findInput options since it's only for notebooks --- src/vs/base/browser/ui/findinput/findInput.ts | 2 -- .../browser/contrib/find/notebookFindReplaceWidget.ts | 5 +++-- src/vs/workbench/contrib/search/browser/searchWidget.ts | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index ff9f05395e2ef..d663c086cffe4 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -36,8 +36,6 @@ export interface IFindInputOptions { readonly showHistoryHint?: () => boolean; readonly toggleStyles: IToggleStyles; readonly inputBoxStyles: IInputBoxStyles; - - readonly filterStartVisibliity?: boolean; } const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input"); 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 bc1731f66a0af..fd0779dfded7d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -164,7 +164,8 @@ export class NotebookFindInput extends FindInput { readonly instantiationService: IInstantiationService, parent: HTMLElement | null, contextViewProvider: IContextViewProvider, - options: IFindInputOptions + options: IFindInputOptions, + filterStartVisibliity = true // TODO: create notebook find options to define this ) { super(parent, contextViewProvider, options); @@ -185,7 +186,7 @@ export class NotebookFindInput extends FindInput { } })); - this.filterVisible = options.filterStartVisibliity ?? true; + this.filterVisible = filterStartVisibliity ?? true; // TODO: if filterStartVisibliity is false, don't initialize the filtering functionality yet this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this.getFilterWidth(); } diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index aa24323f7907d..4f3d12f2b2994 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -360,7 +360,7 @@ export class SearchWidget extends Widget { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { const scopedContextKeyService = this._register(this.contextKeyService.createScoped(searchInputContainer)); - this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, { ...inputOptions, filterStartVisibliity: this._hasNotebookOpen() },)); + this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, inputOptions, this._hasNotebookOpen())); } else { this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); } From 652cc51286d51e103dc1ff868cb0e99cad4805e9 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 13:23:07 -0800 Subject: [PATCH 38/45] add todo --- src/vs/workbench/contrib/search/browser/searchView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 441b178ea9f7e..1d2cf3fd0212e 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1966,6 +1966,7 @@ export class SearchView extends ViewPane { } public override saveState(): void { + // TODO: save the state of notebook filter fields const patternExcludes = this.inputPatternExcludes?.getValue().trim() ?? ''; const patternIncludes = this.inputPatternIncludes?.getValue().trim() ?? ''; const onlyOpenEditors = this.inputPatternIncludes?.onlySearchInOpenEditors() ?? false; From 4aae12592ef7b1f900585d061ed25948789e7560 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 8 Mar 2023 13:34:05 -0800 Subject: [PATCH 39/45] add another todo --- src/vs/workbench/contrib/search/browser/searchModel.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 289c1011b9511..57ebd0eed246e 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -364,6 +364,7 @@ export class FileMatch extends Disposable implements IFileMatch { if (this._notebookEditorWidget === widget) { // ensure that the matches are up to date, but everything else should be configured already. + // TODO: try to reduce calls that occur when the notebook hasn't loaded yet await this.updateMatchesForEditorWidget(); return; } From a4d525c96a235a461e5def52837c4e8c6ae631f9 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 9 Mar 2023 16:25:36 -0800 Subject: [PATCH 40/45] refresh -> reload --- src/vs/workbench/contrib/search/browser/search.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 68ef0aaa62551..535ebac877ec2 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -352,7 +352,7 @@ configurationRegistry.registerConfiguration({ }, 'search.experimental.notebookSearch': { type: 'boolean', - description: nls.localize('search.experimental.notebookSearch', "Controls whether to use the experimental notebook search in the global search. Please refresh VS Code instance for changes to this setting to take effect."), + description: nls.localize('search.experimental.notebookSearch', "Controls whether to use the experimental notebook search in the global search. Please reload your VS Code instance for changes to this setting to take effect."), default: false }, } From 9d652d757bcc9a85f3ace2fcc28d8b26ca25d1bc Mon Sep 17 00:00:00 2001 From: andreamah Date: Fri, 10 Mar 2023 17:27:09 -0800 Subject: [PATCH 41/45] isolate notebook find filtering into its own class --- .../contrib/find/notebookFindReplaceWidget.ts | 83 +++++++++++++------ .../contrib/search/browser/searchWidget.ts | 31 ++++++- 2 files changed, 85 insertions(+), 29 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 fd0779dfded7d..2c2d74e7c28d0 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -38,6 +38,7 @@ 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 { Disposable } from 'vs/base/common/lifecycle'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -150,32 +151,27 @@ class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { } } -export class NotebookFindInput extends FindInput { +export class NotebookFindInputFilter extends Disposable { private _filterButtonContainer: HTMLElement; - private _actionbar: ActionBar | null = null; private _filterChecked: boolean = false; private _filtersAction: IAction; private _toggleStyles: IToggleStyles; + private _actionbar: ActionBar | null = null; constructor( readonly filters: NotebookFindFilters, - contextKeyService: IContextKeyService, readonly contextMenuService: IContextMenuService, readonly instantiationService: IInstantiationService, - parent: HTMLElement | null, - contextViewProvider: IContextViewProvider, options: IFindInputOptions, - filterStartVisibliity = true // TODO: create notebook find options to define this + filterStartVisiblitity = true // TODO: create notebook find options to define this ) { - super(parent, contextViewProvider, options); + super(); this._toggleStyles = options.toggleStyles; - this._register(registerAndCreateHistoryNavigationContext(contextKeyService, this.inputBox)); this._filtersAction = new Action('notebookFindFilterAction', NOTEBOOK_FIND_FILTERS, 'notebook-filters ' + ThemeIcon.asClassName(filterIcon)); this._filtersAction.checked = false; this._filterButtonContainer = dom.$('.find-filter-button'); - this.controls.appendChild(this._filterButtonContainer); this.createFilters(this._filterButtonContainer); this._register(this.filters.onDidChange(() => { @@ -185,16 +181,35 @@ export class NotebookFindInput extends FindInput { this._filtersAction.checked = false; } })); + this.visible = filterStartVisiblitity ?? true; - this.filterVisible = filterStartVisibliity ?? true; // TODO: if filterStartVisibliity is false, don't initialize the filtering functionality yet - this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this.getFilterWidth(); } - set filterVisible(show: boolean) { + set visible(show: boolean) { this._filterButtonContainer.style.display = show ? '' : 'none'; } - private getFilterWidth() { + get container() { + return this._filterButtonContainer; + } + + get filterChecked() { + return this._filterChecked; + } + + set filterChecked(checked: boolean) { + this._filterChecked = checked; + } + + applyStyles(): void { + const toggleStyles = this._toggleStyles; + + this._filterButtonContainer.style.borderColor = (this._filterChecked && toggleStyles.inputActiveOptionBorder) || ''; + this._filterButtonContainer.style.color = (this._filterChecked && toggleStyles.inputActiveOptionForeground) || 'inherit'; + this._filterButtonContainer.style.backgroundColor = (this._filterChecked && toggleStyles.inputActiveOptionBackground) || ''; + } + + get width() { return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; } @@ -209,20 +224,46 @@ export class NotebookFindInput extends FindInput { })); this._actionbar.push(this._filtersAction, { icon: true, label: false }); } +} + +export class NotebookFindInput extends FindInput { + private _findFilter: NotebookFindInputFilter; + + constructor( + readonly filters: NotebookFindFilters, + contextKeyService: IContextKeyService, + readonly contextMenuService: IContextMenuService, + readonly instantiationService: IInstantiationService, + parent: HTMLElement | null, + contextViewProvider: IContextViewProvider, + options: IFindInputOptions, + ) { + super(parent, contextViewProvider, options); + + this._register(registerAndCreateHistoryNavigationContext(contextKeyService, this.inputBox)); + this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options, true)); + + this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; + this.controls.appendChild(this._findFilter.container); + } override setEnabled(enabled: boolean) { super.setEnabled(enabled); - if (enabled && !this._filterChecked) { + if (enabled && !this._findFilter.filterChecked) { this.regex?.enable(); } else { this.regex?.disable(); } } + set filterVisible(show: boolean) { + this._findFilter.visible = show; + } + updateFilterState(changed: boolean) { - this._filterChecked = changed; + this._findFilter.filterChecked = changed; if (this.regex) { - if (this._filterChecked) { + if (this._findFilter.filterChecked) { this.regex.disable(); this.regex.domNode.tabIndex = -1; this.regex.domNode.classList.toggle('disabled', true); @@ -232,15 +273,7 @@ export class NotebookFindInput extends FindInput { this.regex.domNode.classList.toggle('disabled', false); } } - this.applyStyles(); - } - - private applyStyles(): void { - const toggleStyles = this._toggleStyles; - - this._filterButtonContainer.style.borderColor = (this._filterChecked && toggleStyles.inputActiveOptionBorder) || ''; - this._filterButtonContainer.style.color = (this._filterChecked && toggleStyles.inputActiveOptionForeground) || 'inherit'; - this._filterButtonContainer.style.backgroundColor = (this._filterChecked && toggleStyles.inputActiveOptionBackground) || ''; + this._findFilter.applyStyles(); } getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 4f3d12f2b2994..456743ada831e 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -36,12 +36,13 @@ import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, sea import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { NotebookFindInput } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; +import { NotebookFindInputFilter } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { GroupModelChangeKind } from 'vs/workbench/common/editor'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -99,6 +100,29 @@ function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: stri } } +class SearchFindInput extends ContextScopedFindInput { + private _findFilter: NotebookFindInputFilter; + constructor( + container: HTMLElement | null, + contextViewProvider: IContextViewProvider, + options: IFindInputOptions, + contextKeyService: IContextKeyService, + readonly contextMenuService: IContextMenuService, + readonly instantiationService: IInstantiationService, + readonly filters: NotebookFindFilters, + filterStartVisiblitity: boolean + ) { + super(container, contextViewProvider, options, contextKeyService); + this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options, filterStartVisiblitity)); + this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; + this.controls.appendChild(this._findFilter.container); + } + + set filterVisible(show: boolean) { + this._findFilter.visible = show; + } +} + export class SearchWidget extends Widget { private static readonly INPUT_MAX_HEIGHT = 134; @@ -181,7 +205,7 @@ export class SearchWidget extends Widget { this._filters = new NotebookFindFilters(true, false, true, true); this.editorService.onDidEditorsChange((e) => { - if (this.searchInput instanceof NotebookFindInput && + if (this.searchInput instanceof SearchFindInput && e.event.editor instanceof NotebookEditorInput && (e.event.kind === GroupModelChangeKind.EDITOR_OPEN || e.event.kind === GroupModelChangeKind.EDITOR_CLOSE)) { this.searchInput.filterVisible = this._hasNotebookOpen(); @@ -359,8 +383,7 @@ export class SearchWidget extends Widget { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { - const scopedContextKeyService = this._register(this.contextKeyService.createScoped(searchInputContainer)); - this.searchInput = this._register(new NotebookFindInput(this._filters, scopedContextKeyService, this.contextMenuService, this.instantiationService, searchInputContainer, this.contextViewService, inputOptions, this._hasNotebookOpen())); + this.searchInput = this._register(new SearchFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService, this.contextMenuService, this.instantiationService, this._filters, this._hasNotebookOpen())); } else { this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); } From 5a7209d711f3da81860280b95b0cfc10e6b7088d Mon Sep 17 00:00:00 2001 From: andreamah Date: Mon, 13 Mar 2023 14:19:42 -0700 Subject: [PATCH 42/45] notebook find filter isolation cleanup --- .../contrib/find/notebookFindReplaceWidget.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 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 2c2d74e7c28d0..ae2a2c9dfe9cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -153,10 +153,10 @@ class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { export class NotebookFindInputFilter extends Disposable { private _filterButtonContainer: HTMLElement; + private _actionbar: ActionBar | null = null; private _filterChecked: boolean = false; private _filtersAction: IAction; private _toggleStyles: IToggleStyles; - private _actionbar: ActionBar | null = null; constructor( readonly filters: NotebookFindFilters, @@ -201,6 +201,10 @@ export class NotebookFindInputFilter extends Disposable { this._filterChecked = checked; } + get width() { + return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; + } + applyStyles(): void { const toggleStyles = this._toggleStyles; @@ -209,10 +213,6 @@ export class NotebookFindInputFilter extends Disposable { this._filterButtonContainer.style.backgroundColor = (this._filterChecked && toggleStyles.inputActiveOptionBackground) || ''; } - get width() { - return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; - } - private createFilters(container: HTMLElement): void { this._actionbar = this._register(new ActionBar(container, { actionViewItemProvider: action => { @@ -256,10 +256,6 @@ export class NotebookFindInput extends FindInput { } } - set filterVisible(show: boolean) { - this._findFilter.visible = show; - } - updateFilterState(changed: boolean) { this._findFilter.filterChecked = changed; if (this.regex) { From 630a1ee0956ee56450625687634805bb14898777 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 15 Mar 2023 16:02:55 -0700 Subject: [PATCH 43/45] refactoring SearchFindInput --- .../find/notebookFindReplaceWidget.css | 3 +- .../contrib/find/notebookFindReplaceWidget.ts | 38 +++------ .../search/browser/media/searchview.css | 19 +++++ .../contrib/search/browser/searchFindInput.ts | 77 +++++++++++++++++++ .../contrib/search/browser/searchModel.ts | 16 ++-- .../contrib/search/browser/searchView.ts | 34 ++++++-- .../contrib/search/browser/searchWidget.ts | 55 ++++++------- .../services/search/common/search.ts | 6 +- 8 files changed, 171 insertions(+), 77 deletions(-) create mode 100644 src/vs/workbench/contrib/search/browser/searchFindInput.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css index a6d4275d2eecf..336e84d21decb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css @@ -139,8 +139,7 @@ background-color: inherit !important; } -.monaco-workbench .simple-fr-find-part-wrapper .find-filter-button, -.monaco-workbench .search-container .find-filter-button { +.monaco-workbench .simple-fr-find-part-wrapper .find-filter-button { color: inherit; margin-left: 2px; float: left; 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 ae2a2c9dfe9cc..4d5bf873c52f6 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -154,7 +154,6 @@ class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { export class NotebookFindInputFilter extends Disposable { private _filterButtonContainer: HTMLElement; private _actionbar: ActionBar | null = null; - private _filterChecked: boolean = false; private _filtersAction: IAction; private _toggleStyles: IToggleStyles; @@ -163,13 +162,13 @@ export class NotebookFindInputFilter extends Disposable { readonly contextMenuService: IContextMenuService, readonly instantiationService: IInstantiationService, options: IFindInputOptions, - filterStartVisiblitity = true // TODO: create notebook find options to define this + tooltip: string = NOTEBOOK_FIND_FILTERS, ) { super(); this._toggleStyles = options.toggleStyles; - this._filtersAction = new Action('notebookFindFilterAction', NOTEBOOK_FIND_FILTERS, 'notebook-filters ' + ThemeIcon.asClassName(filterIcon)); + this._filtersAction = new Action('notebookFindFilterAction', tooltip, 'notebook-filters ' + ThemeIcon.asClassName(filterIcon)); this._filtersAction.checked = false; this._filterButtonContainer = dom.$('.find-filter-button'); this.createFilters(this._filterButtonContainer); @@ -181,36 +180,22 @@ export class NotebookFindInputFilter extends Disposable { this._filtersAction.checked = false; } })); - this.visible = filterStartVisiblitity ?? true; - - } - - set visible(show: boolean) { - this._filterButtonContainer.style.display = show ? '' : 'none'; } get container() { return this._filterButtonContainer; } - get filterChecked() { - return this._filterChecked; - } - - set filterChecked(checked: boolean) { - this._filterChecked = checked; - } - get width() { return 2 /*margin left*/ + 2 /*border*/ + 2 /*padding*/ + 16 /* icon width */; } - applyStyles(): void { + applyStyles(filterChecked: boolean): void { const toggleStyles = this._toggleStyles; - this._filterButtonContainer.style.borderColor = (this._filterChecked && toggleStyles.inputActiveOptionBorder) || ''; - this._filterButtonContainer.style.color = (this._filterChecked && toggleStyles.inputActiveOptionForeground) || 'inherit'; - this._filterButtonContainer.style.backgroundColor = (this._filterChecked && toggleStyles.inputActiveOptionBackground) || ''; + this._filterButtonContainer.style.borderColor = (filterChecked && toggleStyles.inputActiveOptionBorder) || ''; + this._filterButtonContainer.style.color = (filterChecked && toggleStyles.inputActiveOptionForeground) || 'inherit'; + this._filterButtonContainer.style.backgroundColor = (filterChecked && toggleStyles.inputActiveOptionBackground) || ''; } private createFilters(container: HTMLElement): void { @@ -228,6 +213,7 @@ export class NotebookFindInputFilter extends Disposable { export class NotebookFindInput extends FindInput { private _findFilter: NotebookFindInputFilter; + private _filterChecked: boolean = false; constructor( readonly filters: NotebookFindFilters, @@ -241,7 +227,7 @@ export class NotebookFindInput extends FindInput { super(parent, contextViewProvider, options); this._register(registerAndCreateHistoryNavigationContext(contextKeyService, this.inputBox)); - this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options, true)); + this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options)); this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; this.controls.appendChild(this._findFilter.container); @@ -249,7 +235,7 @@ export class NotebookFindInput extends FindInput { override setEnabled(enabled: boolean) { super.setEnabled(enabled); - if (enabled && !this._findFilter.filterChecked) { + if (enabled && !this._filterChecked) { this.regex?.enable(); } else { this.regex?.disable(); @@ -257,9 +243,9 @@ export class NotebookFindInput extends FindInput { } updateFilterState(changed: boolean) { - this._findFilter.filterChecked = changed; + this._filterChecked = changed; if (this.regex) { - if (this._findFilter.filterChecked) { + if (this._filterChecked) { this.regex.disable(); this.regex.domNode.tabIndex = -1; this.regex.domNode.classList.toggle('disabled', true); @@ -269,7 +255,7 @@ export class NotebookFindInput extends FindInput { this.regex.domNode.classList.toggle('disabled', false); } } - this._findFilter.applyStyles(); + this._findFilter.applyStyles(this._filterChecked); } getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index 2b87730ba8c1a..2c434917669a0 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -376,3 +376,22 @@ .monaco-workbench .search-view .monaco-list.element-focused .monaco-list-row.focused.selected:not(.highlighted) .action-label:focus { outline-color: var(--vscode-list-activeSelectionForeground) } + +.monaco-workbench .search-container .monaco-custom-toggle.disabled { + opacity: 0.3; + cursor: default; + user-select: none; + -webkit-user-select: none; + pointer-events: none; + background-color: inherit !important; +} + +.monaco-workbench .search-container .find-filter-button { + color: inherit; + margin-left: 2px; + float: left; + cursor: pointer; + box-sizing: border-box; + user-select: none; + -webkit-user-select: none; +} diff --git a/src/vs/workbench/contrib/search/browser/searchFindInput.ts b/src/vs/workbench/contrib/search/browser/searchFindInput.ts new file mode 100644 index 0000000000000..564663a1ed73d --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchFindInput.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ContextScopedFindInput } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; +import { NotebookFindInputFilter } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; +import * as nls from 'vs/nls'; + +export class SearchFindInput extends ContextScopedFindInput { + private _findFilter: NotebookFindInputFilter; + private _filterChecked: boolean = false; + private _visible: boolean = false; + + constructor( + container: HTMLElement | null, + contextViewProvider: IContextViewProvider, + options: IFindInputOptions, + contextKeyService: IContextKeyService, + readonly contextMenuService: IContextMenuService, + readonly instantiationService: IInstantiationService, + readonly filters: NotebookFindFilters, + filterStartVisiblitity: boolean + ) { + super(container, contextViewProvider, options, contextKeyService); + this._findFilter = this._register( + new NotebookFindInputFilter( + filters, + contextMenuService, + instantiationService, + options, + nls.localize('searchFindInputNotebookFilter.label', "Notebook Find Filters") + )); + this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; + this.controls.appendChild(this._findFilter.container); + this._findFilter.container.classList.add('monaco-custom-toggle'); + + this.filterVisible = filterStartVisiblitity; + } + + set filterVisible(show: boolean) { + this._findFilter.container.style.display = show ? '' : 'none'; + this._visible = show; + this.updateStyles(); + } + + override setEnabled(enabled: boolean) { + super.setEnabled(enabled); + if (enabled && (!this._filterChecked || !this._visible)) { + this.regex?.enable(); + } else { + this.regex?.disable(); + } + } + + updateStyles() { + this._filterChecked = this.filters.markupPreview || this.filters.codeOutput; + if (this.regex) { + if (this._filterChecked && this._visible) { + this.regex.disable(); + this.regex.domNode.tabIndex = -1; + this.regex.domNode.classList.toggle('disabled', true); + } else { + this.regex.enable(); + this.regex.domNode.tabIndex = 0; + this.regex.domNode.classList.toggle('disabled', false); + } + } + this._findFilter.applyStyles(this._filterChecked); + } +} diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 57ebd0eed246e..c5ed43d8fb682 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -424,10 +424,10 @@ export class FileMatch extends Disposable implements IFileMatch { wholeWord: this._query.isWordMatch, caseSensitive: this._query.isCaseSensitive, wordSeparators: wordSeparators ?? undefined, - includeMarkupInput: this._query.notebookInfo?.isNotebookMarkdownInput, - includeMarkupPreview: !this._query.notebookInfo?.isNotebookMarkdownInput, - includeCodeInput: this._query.notebookInfo?.isNotebookCellInput, - includeOutput: this._query.notebookInfo?.isNotebookCellOutput, + includeMarkupInput: this._query.notebookInfo?.isInNotebookMarkdownInput, + includeMarkupPreview: !this._query.notebookInfo?.isInNotebookMarkdownInput, + includeCodeInput: this._query.notebookInfo?.isInNotebookCellInput, + includeOutput: this._query.notebookInfo?.isInNotebookCellOutput, }, CancellationToken.None, false, true); this.updateNotebookMatches(allMatches, true); @@ -1746,10 +1746,10 @@ export class SearchModel extends Disposable { regex: query.contentPattern.isRegExp, wholeWord: query.contentPattern.isWordMatch, caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: query.contentPattern.notebookInfo?.isNotebookMarkdownInput, - includeMarkupPreview: !query.contentPattern.notebookInfo?.isNotebookMarkdownInput, - includeCodeInput: query.contentPattern.notebookInfo?.isNotebookCellInput, - includeOutput: query.contentPattern.notebookInfo?.isNotebookCellOutput, + includeMarkupInput: query.contentPattern.notebookInfo?.isInNotebookMarkdownInput, + includeMarkupPreview: !query.contentPattern.notebookInfo?.isInNotebookMarkdownInput, + includeCodeInput: query.contentPattern.notebookInfo?.isInNotebookCellInput, + includeOutput: query.contentPattern.notebookInfo?.isInNotebookCellOutput, }, token, false, true); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 1d2cf3fd0212e..ee9f46ad96b3c 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -460,6 +460,11 @@ export class SearchView extends ViewPane { const showReplace = typeof this.viewletState['view.showReplace'] === 'boolean' ? this.viewletState['view.showReplace'] : true; const preserveCase = this.viewletState['query.preserveCase'] === true; + const isInNotebookMarkdownInput = this.viewletState['query.isInNotebookMarkdownInput']; + const isInNotebookCellInput = this.viewletState['query.isInNotebookCellInput']; + const isInNotebookCellOutput = this.viewletState['query.isInNotebookCellOutput']; + + this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, container, { value: contentPattern, replaceValue: replaceText, @@ -470,7 +475,12 @@ export class SearchView extends ViewPane { replaceHistory: replaceHistory, preserveCase: preserveCase, inputBoxStyles: defaultInputBoxStyles, - toggleStyles: defaultToggleStyles + toggleStyles: defaultToggleStyles, + notebookOptions: { + isInNotebookMarkdownInput, + isInNotebookCellInput, + isInNotebookCellOutput, + } })); if (showReplace) { @@ -1385,9 +1395,9 @@ export class SearchView extends ViewPane { } const isRegex = this.searchWidget.searchInput.getRegex(); - const isNotebookMarkdownInput = this.searchWidget.getFilters().markupInput; - const isNotebookCellInput = this.searchWidget.getFilters().codeInput; - const isNotebookCellOutput = this.searchWidget.getFilters().codeOutput; + const isInNotebookMarkdownInput = this.searchWidget.getFilters().markupInput; + const isInNotebookCellInput = this.searchWidget.getFilters().codeInput; + const isInNotebookCellOutput = this.searchWidget.getFilters().codeOutput; const isWholeWords = this.searchWidget.searchInput.getWholeWords(); const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); @@ -1409,9 +1419,9 @@ export class SearchView extends ViewPane { isCaseSensitive: isCaseSensitive, isWordMatch: isWholeWords, notebookInfo: { - isNotebookMarkdownInput, - isNotebookCellInput, - isNotebookCellOutput + isInNotebookMarkdownInput, + isInNotebookCellInput, + isInNotebookCellOutput } }; @@ -1966,7 +1976,7 @@ export class SearchView extends ViewPane { } public override saveState(): void { - // TODO: save the state of notebook filter fields + const patternExcludes = this.inputPatternExcludes?.getValue().trim() ?? ''; const patternIncludes = this.inputPatternIncludes?.getValue().trim() ?? ''; const onlyOpenEditors = this.inputPatternIncludes?.onlySearchInOpenEditors() ?? false; @@ -1979,10 +1989,18 @@ export class SearchView extends ViewPane { const isCaseSensitive = this.searchWidget.searchInput.getCaseSensitive(); const contentPattern = this.searchWidget.searchInput.getValue(); + const isInNotebookCellInput = this.searchWidget.getFilters().codeInput; + const isInNotebookCellOutput = this.searchWidget.getFilters().codeOutput; + const isInNotebookMarkdownInput = this.searchWidget.getFilters().markupInput; + this.viewletState['query.contentPattern'] = contentPattern; this.viewletState['query.regex'] = isRegex; this.viewletState['query.wholeWords'] = isWholeWords; this.viewletState['query.caseSensitive'] = isCaseSensitive; + + this.viewletState['query.isInNotebookMarkdownInput'] = isInNotebookMarkdownInput; + this.viewletState['query.isInNotebookCellInput'] = isInNotebookCellInput; + this.viewletState['query.isInNotebookCellOutput'] = isInNotebookCellOutput; } this.viewletState['query.folderExclusions'] = patternExcludes; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 456743ada831e..833f081f721a9 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -36,13 +36,12 @@ import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, sea import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants'; import { showHistoryKeybindingHint } from 'vs/platform/history/browser/historyWidgetKeybindingHint'; import { defaultInputBoxStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { NotebookFindInputFilter } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { GroupModelChangeKind } from 'vs/workbench/common/editor'; -import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { SearchFindInput } from 'vs/workbench/contrib/search/browser/searchFindInput'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -60,6 +59,11 @@ export interface ISearchWidgetOptions { showContextToggle?: boolean; inputBoxStyles: IInputBoxStyles; toggleStyles: IToggleStyles; + notebookOptions?: { + isInNotebookMarkdownInput: boolean; + isInNotebookCellInput: boolean; + isInNotebookCellOutput: boolean; + }; } class ReplaceAllAction extends Action { @@ -100,28 +104,6 @@ function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: stri } } -class SearchFindInput extends ContextScopedFindInput { - private _findFilter: NotebookFindInputFilter; - constructor( - container: HTMLElement | null, - contextViewProvider: IContextViewProvider, - options: IFindInputOptions, - contextKeyService: IContextKeyService, - readonly contextMenuService: IContextMenuService, - readonly instantiationService: IInstantiationService, - readonly filters: NotebookFindFilters, - filterStartVisiblitity: boolean - ) { - super(container, contextViewProvider, options, contextKeyService); - this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options, filterStartVisiblitity)); - this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; - this.controls.appendChild(this._findFilter.container); - } - - set filterVisible(show: boolean) { - this._findFilter.visible = show; - } -} export class SearchWidget extends Widget { private static readonly INPUT_MAX_HEIGHT = 134; @@ -183,7 +165,7 @@ export class SearchWidget extends Widget { private showContextToggle!: Toggle; public contextLinesInput!: InputBox; - private _filters: NotebookFindFilters; + private _notebookFilters: NotebookFindFilters; constructor( container: HTMLElement, @@ -202,15 +184,28 @@ export class SearchWidget extends Widget { this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.contextKeyService); this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.contextKeyService); this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService); - this._filters = new NotebookFindFilters(true, false, true, true); - this.editorService.onDidEditorsChange((e) => { + this._notebookFilters = this._register( + new NotebookFindFilters( + options.notebookOptions?.isInNotebookMarkdownInput ?? true, + !options.notebookOptions?.isInNotebookMarkdownInput ?? false, + options.notebookOptions?.isInNotebookCellInput ?? true, + options.notebookOptions?.isInNotebookCellOutput ?? false + )); + + this._register( + this._notebookFilters.onDidChange(() => { + if (this.searchInput instanceof SearchFindInput) { + this.searchInput.updateStyles(); + } + })); + this._register(this.editorService.onDidEditorsChange((e) => { if (this.searchInput instanceof SearchFindInput && e.event.editor instanceof NotebookEditorInput && (e.event.kind === GroupModelChangeKind.EDITOR_OPEN || e.event.kind === GroupModelChangeKind.EDITOR_CLOSE)) { this.searchInput.filterVisible = this._hasNotebookOpen(); } - }); + })); this._replaceHistoryDelayer = new Delayer(500); @@ -232,7 +227,7 @@ export class SearchWidget extends Widget { } getFilters() { - return this._filters; + return this._notebookFilters; } focus(select: boolean = true, focusReplace: boolean = false, suppressGlobalSearchBuffer = false): void { @@ -383,7 +378,7 @@ export class SearchWidget extends Widget { const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental.notebookSearch; if (experimentalNotebooksEnabled) { - this.searchInput = this._register(new SearchFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService, this.contextMenuService, this.instantiationService, this._filters, this._hasNotebookOpen())); + this.searchInput = this._register(new SearchFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService, this.contextMenuService, this.instantiationService, this._notebookFilters, this._hasNotebookOpen())); } else { this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService)); } diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 54e5ded64ddde..2a33627a41e2e 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -149,9 +149,9 @@ export interface IPatternInfo { } export interface INotebookPatternInfo { - isNotebookMarkdownInput?: boolean; - isNotebookCellInput?: boolean; - isNotebookCellOutput?: boolean; + isInNotebookMarkdownInput?: boolean; + isInNotebookCellInput?: boolean; + isInNotebookCellOutput?: boolean; } export interface IExtendedExtensionSearchOptions { From e6e734bf300049c0551850c8a978a42b7897da27 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 15 Mar 2023 16:06:56 -0700 Subject: [PATCH 44/45] remove changes to notebookFindReplaceWidget.css --- .../browser/contrib/find/notebookFindReplaceWidget.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css index 336e84d21decb..083fb5197c204 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.css @@ -11,7 +11,7 @@ right: 18px; width: var(--notebook-find-width); max-width: calc(100% - 28px - 28px - 8px); - padding: 0 var(--notebook-find-horizontal-padding); + padding:0 var(--notebook-find-horizontal-padding); transition: top 200ms linear; visibility: hidden; background-color: var(--vscode-editorWidget-background) !important; @@ -167,7 +167,6 @@ .monaco-workbench .simple-fr-replace-part .monaco-inputbox > .ibwrapper > .input { height: 24px; } - .monaco-workbench .simple-fr-find-part-wrapper .monaco-sash { left: 0 !important; background-color: var(--vscode-editorWidget-resizeBorder, var(--vscode-editorWidget-border)); From 5192011aa7ec422b01bdd038f564a097a67b7491 Mon Sep 17 00:00:00 2001 From: andreamah Date: Wed, 15 Mar 2023 16:18:23 -0700 Subject: [PATCH 45/45] NotebookFindInputFilter -> NotebookFindInputFilterButton --- .../browser/contrib/find/notebookFindReplaceWidget.ts | 6 +++--- src/vs/workbench/contrib/search/browser/searchFindInput.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 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 b30ec2a323c49..bf30ba0fe1962 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -151,7 +151,7 @@ class NotebookFindFilterActionViewItem extends DropdownMenuActionViewItem { } } -export class NotebookFindInputFilter extends Disposable { +export class NotebookFindInputFilterButton extends Disposable { private _filterButtonContainer: HTMLElement; private _actionbar: ActionBar | null = null; private _filtersAction: IAction; @@ -212,7 +212,7 @@ export class NotebookFindInputFilter extends Disposable { } export class NotebookFindInput extends FindInput { - private _findFilter: NotebookFindInputFilter; + private _findFilter: NotebookFindInputFilterButton; private _filterChecked: boolean = false; constructor( @@ -227,7 +227,7 @@ export class NotebookFindInput extends FindInput { super(parent, contextViewProvider, options); this._register(registerAndCreateHistoryNavigationContext(contextKeyService, this.inputBox)); - this._findFilter = this._register(new NotebookFindInputFilter(filters, contextMenuService, instantiationService, options)); + this._findFilter = this._register(new NotebookFindInputFilterButton(filters, contextMenuService, instantiationService, options)); this.inputBox.paddingRight = (this.caseSensitive?.width() ?? 0) + (this.wholeWords?.width() ?? 0) + (this.regex?.width() ?? 0) + this._findFilter.width; this.controls.appendChild(this._findFilter.container); diff --git a/src/vs/workbench/contrib/search/browser/searchFindInput.ts b/src/vs/workbench/contrib/search/browser/searchFindInput.ts index 564663a1ed73d..250018083bf72 100644 --- a/src/vs/workbench/contrib/search/browser/searchFindInput.ts +++ b/src/vs/workbench/contrib/search/browser/searchFindInput.ts @@ -10,11 +10,11 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ContextScopedFindInput } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; -import { NotebookFindInputFilter } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; +import { NotebookFindInputFilterButton } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import * as nls from 'vs/nls'; export class SearchFindInput extends ContextScopedFindInput { - private _findFilter: NotebookFindInputFilter; + private _findFilter: NotebookFindInputFilterButton; private _filterChecked: boolean = false; private _visible: boolean = false; @@ -30,7 +30,7 @@ export class SearchFindInput extends ContextScopedFindInput { ) { super(container, contextViewProvider, options, contextKeyService); this._findFilter = this._register( - new NotebookFindInputFilter( + new NotebookFindInputFilterButton( filters, contextMenuService, instantiationService,