mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
* Polish #154913. Set current find match based on notebook selection. * update current match when query is seeded from selection. * update test
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async';
|
||||
import { INotebookEditor, CellFindMatch, CellEditState, CellFindMatchWithIndex, OutputFindMatch, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditor, CellFindMatch, CellEditState, CellFindMatchWithIndex, OutputFindMatch, ICellModelDecorations, ICellModelDeltaDecorations, INotebookDeltaDecoration, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FindDecorations } from 'vs/editor/contrib/find/browser/findDecorations';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
@@ -88,6 +88,34 @@ export class FindModel extends Disposable {
|
||||
};
|
||||
}
|
||||
|
||||
refreshCurrentMatch(focus: { cell: ICellViewModel; range: Range }) {
|
||||
const findMatchIndex = this.findMatches.findIndex(match => match.cell === focus.cell);
|
||||
|
||||
if (findMatchIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const findMatch = this.findMatches[findMatchIndex];
|
||||
const index = findMatch.matches.slice(0, findMatch.modelMatchCount).findIndex(match => (match as FindMatch).range.intersectRanges(focus.range) !== null);
|
||||
|
||||
if (index === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchesBefore = findMatchIndex === 0 ? 0 : (this._findMatchesStarts?.getPrefixSum(findMatchIndex - 1) ?? 0);
|
||||
this._currentMatch = matchesBefore + index;
|
||||
|
||||
this.highlightCurrentFindMatchDecoration(findMatchIndex, index).then(offset => {
|
||||
this.revealCellRange(findMatchIndex, index, offset);
|
||||
|
||||
this._state.changeMatchInfo(
|
||||
this._currentMatch,
|
||||
this._findMatches.reduce((p, c) => p + c.matches.length, 0),
|
||||
undefined
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
find(option: { previous: boolean } | { index: number }) {
|
||||
if (!this.findMatches.length) {
|
||||
return;
|
||||
@@ -189,20 +217,28 @@ export class FindModel extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const findFirstMatchAfterCellIndex = (cellIndex: number) => {
|
||||
const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.index), index => index >= cellIndex);
|
||||
this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection));
|
||||
};
|
||||
|
||||
if (this._currentMatch === -1) {
|
||||
// no active current match
|
||||
this.set(findMatches, false);
|
||||
return;
|
||||
if (this._notebookEditor.getLength() === 0) {
|
||||
this.set(findMatches, false);
|
||||
return;
|
||||
} else {
|
||||
const focus = this._notebookEditor.getFocus().start;
|
||||
findFirstMatchAfterCellIndex(focus);
|
||||
this.set(findMatches, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const oldCurrIndex = this._findMatchesStarts!.getIndexOf(this._currentMatch);
|
||||
const oldCurrCell = this._findMatches[oldCurrIndex.index].cell;
|
||||
const oldCurrMatchCellIndex = this._notebookEditor.getCellIndex(oldCurrCell);
|
||||
|
||||
const findFirstMatchAfterCellIndex = (cellIndex: number) => {
|
||||
const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.index), index => index >= cellIndex);
|
||||
this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection));
|
||||
};
|
||||
|
||||
if (oldCurrMatchCellIndex < 0) {
|
||||
// the cell containing the active match is deleted
|
||||
|
||||
@@ -18,7 +18,7 @@ import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { NotebookFindWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget';
|
||||
import { IShowNotebookFindWidgetOptions, NotebookFindWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget';
|
||||
import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
@@ -92,21 +92,27 @@ function notebookContainsTextModel(uri: URI, textModel: ITextModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getSearchString(editor: ICodeEditor, opts: IFindStartOptions) {
|
||||
function getSearchStringOptions(editor: ICodeEditor, opts: IFindStartOptions) {
|
||||
// Get the search string result, following the same logic in _start function in 'vs/editor/contrib/find/browser/findController'
|
||||
let searchString = '';
|
||||
if (opts.seedSearchStringFromSelection === 'single') {
|
||||
const selectionSearchString = getSelectionSearchString(editor, opts.seedSearchStringFromSelection, opts.seedSearchStringFromNonEmptySelection);
|
||||
if (selectionSearchString) {
|
||||
searchString = selectionSearchString;
|
||||
return {
|
||||
searchString: selectionSearchString,
|
||||
selection: editor.getSelection()
|
||||
};
|
||||
}
|
||||
} else if (opts.seedSearchStringFromSelection === 'multiple' && !opts.updateSearchScope) {
|
||||
const selectionSearchString = getSelectionSearchString(editor, opts.seedSearchStringFromSelection);
|
||||
if (selectionSearchString) {
|
||||
searchString = selectionSearchString;
|
||||
return {
|
||||
searchString: selectionSearchString,
|
||||
selection: editor.getSelection()
|
||||
};
|
||||
}
|
||||
}
|
||||
return searchString;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +124,10 @@ StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!codeEditor.hasModel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!editor.hasEditorFocus() && !editor.hasWebviewFocus()) {
|
||||
const codeEditorService = accessor.get(ICodeEditorService);
|
||||
// check if the active pane contains the active text editor
|
||||
@@ -131,7 +141,7 @@ StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor:
|
||||
|
||||
const controller = editor.getContribution<NotebookFindWidget>(NotebookFindWidget.id);
|
||||
|
||||
const searchString = getSearchString(codeEditor, {
|
||||
const searchStringOptions = getSearchStringOptions(codeEditor, {
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
|
||||
seedSearchStringFromNonEmptySelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection === 'selection',
|
||||
@@ -142,7 +152,19 @@ StartFindAction.addImplementation(100, (accessor: ServicesAccessor, codeEditor:
|
||||
loop: codeEditor.getOption(EditorOption.find).loop
|
||||
});
|
||||
|
||||
controller.show(searchString);
|
||||
let options: IShowNotebookFindWidgetOptions | undefined = undefined;
|
||||
const uri = codeEditor.getModel().uri;
|
||||
const data = CellUri.parse(uri);
|
||||
if (searchStringOptions?.selection && data) {
|
||||
const cell = editor.getCellByHandle(data.handle);
|
||||
if (cell) {
|
||||
options = {
|
||||
searchStringSeededFrom: { cell, range: searchStringOptions.selection },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
controller.show(searchStringOptions?.searchString, options);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -154,9 +176,13 @@ StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, codeE
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!codeEditor.hasModel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const controller = editor.getContribution<NotebookFindWidget>(NotebookFindWidget.id);
|
||||
|
||||
const searchString = getSearchString(codeEditor, {
|
||||
const searchStringOptions = getSearchStringOptions(codeEditor, {
|
||||
forceRevealReplace: false,
|
||||
seedSearchStringFromSelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection !== 'never' ? 'single' : 'none',
|
||||
seedSearchStringFromNonEmptySelection: codeEditor.getOption(EditorOption.find).seedSearchStringFromSelection === 'selection',
|
||||
@@ -168,7 +194,7 @@ StartFindReplaceAction.addImplementation(100, (accessor: ServicesAccessor, codeE
|
||||
});
|
||||
|
||||
if (controller) {
|
||||
controller.replace(searchString);
|
||||
controller.replace(searchStringOptions?.searchString);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters';
|
||||
import { FindModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel';
|
||||
import { SimpleFindReplaceWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget';
|
||||
import { CellEditState, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellEditState, ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
|
||||
const FIND_HIDE_TRANSITION = 'find-hide-transition';
|
||||
@@ -37,6 +37,7 @@ export interface IShowNotebookFindWidgetOptions {
|
||||
matchCase?: boolean;
|
||||
matchIndex?: number;
|
||||
focus?: boolean;
|
||||
searchStringSeededFrom?: { cell: ICellViewModel; range: Range };
|
||||
}
|
||||
|
||||
export class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEditorContribution {
|
||||
@@ -214,6 +215,7 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote
|
||||
protected onFindInputFocusTrackerBlur(): void { }
|
||||
|
||||
override async show(initialInput?: string, options?: IShowNotebookFindWidgetOptions): Promise<void> {
|
||||
const searchStringUpdate = this._state.searchString !== initialInput;
|
||||
super.show(initialInput, options);
|
||||
this._state.change({ searchString: initialInput ?? '', isRevealed: true }, false);
|
||||
|
||||
@@ -226,6 +228,10 @@ export class NotebookFindWidget extends SimpleFindReplaceWidget implements INote
|
||||
this._findInput.select();
|
||||
}
|
||||
|
||||
if (!searchStringUpdate && options?.searchStringSeededFrom) {
|
||||
this._findModel.refreshCurrentMatch(options.searchStringSeededFrom);
|
||||
}
|
||||
|
||||
if (this._showTimeout === null) {
|
||||
if (this._hideTimeout !== null) {
|
||||
window.clearTimeout(this._hideTimeout);
|
||||
|
||||
@@ -67,13 +67,13 @@ suite('Notebook Find', () => {
|
||||
state.change({ searchString: '1' }, true);
|
||||
await found;
|
||||
assert.strictEqual(model.findMatches.length, 2);
|
||||
assert.strictEqual(model.currentMatch, -1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
|
||||
assert.strictEqual(editor.textModel.length, 3);
|
||||
|
||||
@@ -88,7 +88,7 @@ suite('Notebook Find', () => {
|
||||
await found2;
|
||||
assert.strictEqual(editor.textModel.length, 4);
|
||||
assert.strictEqual(model.findMatches.length, 3);
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,13 +114,13 @@ suite('Notebook Find', () => {
|
||||
await found;
|
||||
// find matches is not necessarily find results
|
||||
assert.strictEqual(model.findMatches.length, 4);
|
||||
assert.strictEqual(model.currentMatch, -1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 2);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 3);
|
||||
|
||||
const found2 = new Promise<boolean>(resolve => state.onFindReplaceStateChange(e => {
|
||||
if (e.matchesCount) { resolve(true); }
|
||||
@@ -131,15 +131,15 @@ suite('Notebook Find', () => {
|
||||
await found2;
|
||||
assert.strictEqual(model.findMatches.length, 3);
|
||||
|
||||
assert.strictEqual(model.currentMatch, 2);
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: true });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 2);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 3);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,7 +165,7 @@ suite('Notebook Find', () => {
|
||||
await found;
|
||||
// find matches is not necessarily find results
|
||||
assert.strictEqual(model.findMatches.length, 4);
|
||||
assert.strictEqual(model.currentMatch, -1);
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: true });
|
||||
assert.strictEqual(model.currentMatch, 4);
|
||||
|
||||
@@ -207,11 +207,11 @@ suite('Notebook Find', () => {
|
||||
await found;
|
||||
// find matches is not necessarily find results
|
||||
assert.strictEqual(model.findMatches.length, 4);
|
||||
assert.strictEqual(model.currentMatch, -1);
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
model.find({ previous: false });
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 2);
|
||||
assert.strictEqual(model.currentMatch, 3);
|
||||
const found2 = new Promise<boolean>(resolve => state.onFindReplaceStateChange(e => {
|
||||
if (e.matchesCount) { resolve(true); }
|
||||
}));
|
||||
@@ -243,13 +243,13 @@ suite('Notebook Find', () => {
|
||||
state.change({ searchString: '1' }, true);
|
||||
await found;
|
||||
assert.strictEqual(model.findMatches.length, 2);
|
||||
assert.strictEqual(model.currentMatch, -1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 0);
|
||||
model.find({ previous: false });
|
||||
assert.strictEqual(model.currentMatch, 1);
|
||||
|
||||
assert.strictEqual(editor.textModel.length, 3);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user