Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions src/vs/editor/contrib/find/browser/findState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ import { Disposable } from '../../../../base/common/lifecycle.js';
import { Range } from '../../../common/core/range.js';
import { MATCHES_LIMIT } from './findModel.js';

function searchScopesEqual(a: Range[] | null, b: Range[] | null): boolean {
if (a === b) {
return true;
}
if (!a || !b || a.length !== b.length) {
return false;
}
const matched = new Array<boolean>(b.length).fill(false);
return a.every(rangeA => {
const index = b.findIndex((rangeB, i) => !matched[i] && Range.equalsRange(rangeA, rangeB));
if (index === -1) {
return false;
}
matched[index] = true;
return true;
});
}

export interface FindReplaceStateChangedEvent {
moveCursor: boolean;
updateHistory: boolean;
Expand Down Expand Up @@ -252,11 +270,7 @@ export class FindReplaceState<T extends { update: (value: T) => void } = { updat
this._preserveCase = newState.preserveCase;
}
if (typeof newState.searchScope !== 'undefined') {
if (!newState.searchScope?.every((newSearchScope) => {
return this._searchScope?.some(existingSearchScope => {
return !Range.equalsRange(existingSearchScope, newSearchScope);
});
})) {
if (!searchScopesEqual(newState.searchScope, this._searchScope)) {
this._searchScope = newState.searchScope;
changeEvent.searchScope = true;
somethingChanged = true;
Expand Down
39 changes: 39 additions & 0 deletions src/vs/editor/contrib/find/test/browser/findController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,45 @@ suite('FindController query options persistence', () => {
});
});

test('issue #237774: Update searchScope when the scope changes', async () => {
await withAsyncTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, async (editor) => {
const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController);
const findState = findController.getState();

findState.change({ searchScope: [new Selection(1, 1, 2, 1)] }, false);
assert.deepStrictEqual(findState.searchScope, [new Selection(1, 1, 2, 1)]);

// Move the single selection: the scope must follow the new selection
findState.change({ searchScope: [new Selection(2, 1, 3, 1)] }, false);
assert.deepStrictEqual(findState.searchScope, [new Selection(2, 1, 3, 1)]);

// Shrink from two selections to one: the scope must shrink too
findState.change({ searchScope: [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)] }, false);
assert.deepStrictEqual(findState.searchScope, [new Selection(1, 1, 2, 1), new Selection(2, 1, 2, 5)]);
findState.change({ searchScope: [new Selection(1, 1, 2, 1)] }, false);
assert.deepStrictEqual(findState.searchScope, [new Selection(1, 1, 2, 1)]);

// Duplicate ranges must not be treated as equal to distinct ranges
findState.change({ searchScope: [new Selection(1, 1, 2, 1), new Selection(1, 1, 2, 1)] }, false);
assert.deepStrictEqual(findState.searchScope, [new Selection(1, 1, 2, 1), new Selection(1, 1, 2, 1)]);

// Identical scope must not fire a change event
let changeEventFired = false;
const listener = findState.onFindReplaceStateChange(() => changeEventFired = true);
findState.change({ searchScope: [new Selection(1, 1, 2, 1), new Selection(1, 1, 2, 1)] }, false);
listener.dispose();
assert.strictEqual(changeEventFired, false);

// Clearing the scope must be honored
findState.change({ searchScope: null }, false);
assert.strictEqual(findState.searchScope, null);
});
});

test('issue #58604: Update searchScope if it is not empty', async () => {
await withAsyncTestCodeEditor([
'var x = (3 * 5)',
Expand Down