Skip to content

Commit fb20d45

Browse files
authored
Fix inflated match count on collapsed folders in Explorer Find (#234830)
* fixes #234129 * correctly invoce * 💄 * fix tests
1 parent c0699e2 commit fb20d45

File tree

3 files changed

+28
-12
lines changed

3 files changed

+28
-12
lines changed

src/vs/workbench/contrib/files/browser/views/explorerView.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ export class ExplorerView extends ViewPane implements IExplorerView {
435435
const explorerLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility });
436436
this._register(explorerLabels);
437437

438-
this.findProvider = this.instantiationService.createInstance(ExplorerFindProvider, () => this.tree);
438+
this.findProvider = this.instantiationService.createInstance(ExplorerFindProvider, this.filter, () => this.tree);
439439

440440
const updateWidth = (stat: ExplorerItem) => this.tree.updateWidth(stat);
441441
this.renderer = this.instantiationService.createInstance(FilesRenderer, container, explorerLabels, this.findProvider.highlightTree, updateWidth);

src/vs/workbench/contrib/files/browser/views/explorerViewer.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ export class ExplorerFindProvider implements IAsyncFindProvider<ExplorerItem> {
291291
}
292292

293293
constructor(
294+
private readonly filesFilter: FilesFilter,
294295
private readonly treeProvider: () => WorkbenchCompressibleAsyncDataTree<ExplorerItem | ExplorerItem[], ExplorerItem, FuzzyScore>,
295296
@ISearchService private readonly searchService: ISearchService,
296297
@IFileService private readonly fileService: IFileService,
@@ -621,7 +622,10 @@ export class ExplorerFindProvider implements IAsyncFindProvider<ExplorerItem> {
621622
const fileResultResources = fileResults.results.map(result => result.resource);
622623
const directoryResources = getMatchingDirectoriesFromFiles(folderResults.results.map(result => result.resource), root, segmentMatchPattern);
623624

624-
return { explorerRoot: root, files: fileResultResources, directories: directoryResources, hitMaxResults: !!fileResults.limitHit || !!folderResults.limitHit };
625+
const filteredFileResources = fileResultResources.filter(resource => !this.filesFilter.isIgnored(resource, root.resource, false));
626+
const filteredDirectoryResources = directoryResources.filter(resource => !this.filesFilter.isIgnored(resource, root.resource, true));
627+
628+
return { explorerRoot: root, files: filteredFileResources, directories: filteredDirectoryResources, hitMaxResults: !!fileResults.limitHit || !!folderResults.limitHit };
625629
}
626630
}
627631

@@ -1404,12 +1408,9 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
14041408
// Hide those that match Hidden Patterns
14051409
const cached = this.hiddenExpressionPerRoot.get(stat.root.resource.toString());
14061410
const globMatch = cached?.parsed(path.relative(stat.root.resource.path, stat.resource.path), stat.name, name => !!(stat.parent && stat.parent.getChild(name)));
1407-
// Small optimization to only traverse gitIgnore if the globMatch from fileExclude returned nothing
1408-
const ignoreFile = globMatch ? undefined : this.ignoreTreesPerRoot.get(stat.root.resource.toString())?.findSubstr(stat.resource);
1409-
const isIncludedInTraversal = ignoreFile?.isPathIncludedInTraversal(stat.resource.path, stat.isDirectory);
1410-
// Doing !undefined returns true and we want it to be false when undefined because that means it's not included in the ignore file
1411-
const isIgnoredByIgnoreFile = isIncludedInTraversal === undefined ? false : !isIncludedInTraversal;
1412-
if (isIgnoredByIgnoreFile || globMatch || stat.parent?.isExcluded) {
1411+
// Small optimization to only run isHiddenResource (traverse gitIgnore) if the globMatch from fileExclude returned nothing
1412+
const isHiddenResource = !!globMatch ? true : this.isIgnored(stat.resource, stat.root.resource, stat.isDirectory);
1413+
if (isHiddenResource || stat.parent?.isExcluded) {
14131414
stat.isExcluded = true;
14141415
const editors = this.editorService.visibleEditors;
14151416
const editor = editors.find(e => e.resource && this.uriIdentityService.extUri.isEqualOrParent(e.resource, stat.resource));
@@ -1424,6 +1425,14 @@ export class FilesFilter implements ITreeFilter<ExplorerItem, FuzzyScore> {
14241425
return true;
14251426
}
14261427

1428+
isIgnored(resource: URI, rootResource: URI, isDirectory: boolean): boolean {
1429+
const ignoreFile = this.ignoreTreesPerRoot.get(rootResource.toString())?.findSubstr(resource);
1430+
const isIncludedInTraversal = ignoreFile?.isPathIncludedInTraversal(resource.path, isDirectory);
1431+
1432+
// Doing !undefined returns true and we want it to be false when undefined because that means it's not included in the ignore file
1433+
return isIncludedInTraversal === undefined ? false : !isIncludedInTraversal;
1434+
}
1435+
14271436
dispose(): void {
14281437
dispose(this.toDispose);
14291438
}

src/vs/workbench/contrib/files/test/browser/explorerFindProvider.test.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import { ICompressedTreeNode } from '../../../../../base/browser/ui/tree/compres
99
import { ExplorerItem } from '../../common/explorerModel.js';
1010
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
1111
import { ITreeCompressionDelegate } from '../../../../../base/browser/ui/tree/asyncDataTree.js';
12-
import { ITreeNode, IAsyncDataSource } from '../../../../../base/browser/ui/tree/tree.js';
12+
import { ITreeNode, IAsyncDataSource, ITreeFilter, TreeFilterResult } from '../../../../../base/browser/ui/tree/tree.js';
1313
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
1414
import { TestFileService, workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';
1515
import { NullFilesConfigurationService } from '../../../../test/common/workbenchTestServices.js';
16-
import { ExplorerFindProvider } from '../../browser/views/explorerViewer.js';
16+
import { ExplorerFindProvider, FilesFilter } from '../../browser/views/explorerViewer.js';
1717
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
1818
import { IWorkbenchCompressibleAsyncDataTreeOptions, WorkbenchCompressibleAsyncDataTree } from '../../../../../platform/list/browser/listService.js';
1919
import { IListAccessibilityProvider } from '../../../../../base/browser/ui/list/listWidget.js';
@@ -120,7 +120,13 @@ class CompressionDelegate implements ITreeCompressionDelegate<ExplorerItem> {
120120
}
121121
}
122122

123-
suite('Files - ExplorerView', () => {
123+
class TestFilesFilter implements ITreeFilter<ExplorerItem> {
124+
filter(): TreeFilterResult<void> { return true; }
125+
isIgnored(): boolean { return false; }
126+
dispose() { }
127+
}
128+
129+
suite('Find Provider - ExplorerView', () => {
124130
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
125131

126132
const fileService = new TestFileService();
@@ -196,14 +202,15 @@ suite('Files - ExplorerView', () => {
196202
const compressionDelegate = new CompressionDelegate(dataSource);
197203
const keyboardNavigationLabelProvider = new KeyboardNavigationLabelProvider();
198204
const accessibilityProvider = new AccessibilityProvider();
205+
const filter = instantiationService.createInstance(TestFilesFilter) as unknown as FilesFilter;
199206

200207
const options: IWorkbenchCompressibleAsyncDataTreeOptions<ExplorerItem, FuzzyScore> = { identityProvider: new IdentityProvider(), keyboardNavigationLabelProvider, accessibilityProvider };
201208
const tree = disposables.add(instantiationService.createInstance(WorkbenchCompressibleAsyncDataTree<ExplorerItem | ExplorerItem[], ExplorerItem, FuzzyScore>, 'test', container, new VirtualDelegate(), compressionDelegate, [new Renderer()], dataSource, options));
202209
tree.layout(200);
203210

204211
await tree.setInput(root);
205212

206-
const findProvider = instantiationService.createInstance(ExplorerFindProvider, () => tree);
213+
const findProvider = instantiationService.createInstance(ExplorerFindProvider, filter, () => tree);
207214

208215
findProvider.startSession();
209216

0 commit comments

Comments
 (0)