diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 6c79377f286f4361427adda04545a00f93a74dc4..570118ed8b792aeb7d4c021d3b56ad28b9af6c79 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -505,6 +505,7 @@ class ResourceLabelWidget extends IconLabel { italic: this.options?.italic, strikethrough: this.options?.strikethrough, matches: this.options?.matches, + descriptionMatches: this.options?.descriptionMatches, extraClasses: [], separator: this.options?.separator, domId: this.options?.domId diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index 21b166c0ac402d28555eac0503a2869bc2c2009d..92c78aa28a21b12ce9d30febc2bcbbe2f3d48352 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -40,7 +40,7 @@ import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/ import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; import { compareFileNames } from 'vs/base/common/comparers'; -import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; import { IViewDescriptor, IViewDescriptorService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { flatten, find } from 'vs/base/common/arrays'; @@ -77,6 +77,34 @@ import { ILabelService } from 'vs/platform/label/common/label'; type TreeElement = ISCMResourceGroup | IResourceNode | ISCMResource; +function splitMatches(uri: URI, filterData: FuzzyScore | undefined): [IMatch[] | undefined, IMatch[] | undefined] { + let matches: IMatch[] | undefined; + let descriptionMatches: IMatch[] | undefined; + + if (filterData) { + matches = []; + descriptionMatches = []; + + const fileName = basename(uri); + const allMatches = createMatches(filterData); + + for (const match of allMatches) { + if (match.end <= fileName.length) { + matches!.push(match); + } else { + descriptionMatches!.push( + { + start: match.start - fileName.length, + end: match.end - fileName.length + } + ); + } + } + } + + return [matches, descriptionMatches]; +} + interface ResourceGroupTemplate { readonly name: HTMLElement; readonly count: CountBadge; @@ -188,7 +216,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer e.name).join('/'); const fileKind = FileKind.FOLDER; + const [matches, descriptionMatches] = splitMatches(folder.uri, node.filterData); template.fileLabel.setResource({ resource: folder.uri, name: label }, { fileDecorations: { colors: false, badges: true }, fileKind, - matches: createMatches(node.filterData) + matches, + descriptionMatches }); template.actionBar.clear(); @@ -358,13 +390,20 @@ export class SCMTreeSorter implements ITreeSorter { export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyboardNavigationLabelProvider { + constructor(@ILabelService private readonly labelService: ILabelService) { } + getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } | undefined { if (ResourceTree.isResourceNode(element)) { return element.name; } else if (isSCMResourceGroup(element)) { return element.label; } else { - return basename(element.sourceUri); + // Since a match in the file name takes precedence over a match + // in the folder name we are returning the label as file/folder. + const fileName = basename(element.sourceUri); + const filePath = this.labelService.getUriLabel(dirname(element.sourceUri), { relative: true }); + + return filePath.length !== 0 ? `${fileName}${filePath}` : fileName; } } @@ -877,7 +916,7 @@ export class RepositoryPane extends ViewPane { const filter = new SCMTreeFilter(); const sorter = new SCMTreeSorter(() => this.viewModel); - const keyboardNavigationLabelProvider = new SCMTreeKeyboardNavigationLabelProvider(); + const keyboardNavigationLabelProvider = this.instantiationService.createInstance(SCMTreeKeyboardNavigationLabelProvider); const identityProvider = new SCMResourceIdentityProvider(); this.tree = this.instantiationService.createInstance(