From 98031210defbc0cf4ae6cc63c5ce29398556087e Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 19 Dec 2021 01:00:33 -0500 Subject: [PATCH] Consolidates case insensative comparisons --- src/api/actionRunners.ts | 7 ++----- src/git/models/branch.ts | 6 +++--- src/git/models/contributor.ts | 10 +++------- src/git/models/remote.ts | 3 ++- src/git/models/tag.ts | 10 +++------- src/git/providers/localGitProvider.ts | 2 +- src/system/string.ts | 23 +++++++++++++++++------ src/system/version.ts | 5 ++--- src/views/nodes/branchTrackingStatusFilesNode.ts | 6 +----- src/views/nodes/commitNode.ts | 4 +--- src/views/nodes/folderNode.ts | 4 ++-- src/views/nodes/mergeStatusNode.ts | 4 +--- src/views/nodes/rebaseStatusNode.ts | 8 ++------ src/views/nodes/resultsFilesNode.ts | 6 +----- src/views/nodes/stashNode.ts | 4 +--- src/views/nodes/statusFilesNode.ts | 6 +----- 16 files changed, 43 insertions(+), 65 deletions(-) diff --git a/src/api/actionRunners.ts b/src/api/actionRunners.ts index ce1e1f2..9c67ff4 100644 --- a/src/api/actionRunners.ts +++ b/src/api/actionRunners.ts @@ -5,6 +5,7 @@ import { Config, configuration } from '../configuration'; import { ContextKeys, setContext } from '../constants'; import { Container } from '../container'; import { getQuickPickIgnoreFocusOut } from '../quickpicks'; +import { Strings } from '../system'; import type { Action, ActionContext, ActionRunner } from './gitlens'; type Actions = ActionContext['type']; @@ -255,11 +256,7 @@ export class ActionRunners implements Disposable { if (runners.length > 1 || runners.every(r => r.type !== ActionRunnerType.BuiltIn)) { const items: (ActionRunnerQuickPickItem | NoActionRunnersQuickPickItem)[] = runners // .filter(r => r.when(context)) - .sort( - (a, b) => - a.order - b.order || - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }), - ) + .sort((a, b) => a.order - b.order || Strings.sortCompare(a.name, b.name)) .map(r => new ActionRunnerQuickPickItem(r, context)); if (items.length === 0) { diff --git a/src/git/models/branch.ts b/src/git/models/branch.ts index 8664fe2..887b16a 100644 --- a/src/git/models/branch.ts +++ b/src/git/models/branch.ts @@ -2,7 +2,7 @@ import { BranchSorting, configuration, DateStyle } from '../../configuration'; import { Starred, WorkspaceState } from '../../constants'; import { Container } from '../../container'; -import { Dates, debug, memoize } from '../../system'; +import { Dates, debug, memoize, Strings } from '../../system'; import { GitRemote, GitRevision } from '../git'; import { GitBranchReference, GitReference, PullRequest, PullRequestState } from './models'; import { GitStatus } from './status'; @@ -78,7 +78,7 @@ export class GitBranch implements GitBranchReference { (a.name === 'master' ? -1 : 1) - (b.name === 'master' ? -1 : 1) || (a.name === 'develop' ? -1 : 1) - (b.name === 'develop' ? -1 : 1) || (b.remote ? -1 : 1) - (a.remote ? -1 : 1) || - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }), + Strings.sortCompare(a.name, b.name), ); case BranchSorting.NameDesc: return branches.sort( @@ -92,7 +92,7 @@ export class GitBranch implements GitBranchReference { (a.name === 'master' ? -1 : 1) - (b.name === 'master' ? -1 : 1) || (a.name === 'develop' ? -1 : 1) - (b.name === 'develop' ? -1 : 1) || (b.remote ? -1 : 1) - (a.remote ? -1 : 1) || - b.name.localeCompare(a.name, undefined, { numeric: true, sensitivity: 'base' }), + Strings.sortCompare(b.name, a.name), ); case BranchSorting.DateDesc: default: diff --git a/src/git/models/contributor.ts b/src/git/models/contributor.ts index 7acff01..7a0c484 100644 --- a/src/git/models/contributor.ts +++ b/src/git/models/contributor.ts @@ -2,7 +2,7 @@ import { Uri } from 'vscode'; import { getAvatarUri } from '../../avatars'; import { configuration, ContributorSorting, GravatarDefaultStyle } from '../../configuration'; -import { Dates, memoize } from '../../system'; +import { Dates, memoize, Strings } from '../../system'; export interface ContributorSortOptions { current?: true; @@ -41,15 +41,11 @@ export class GitContributor { ); case ContributorSorting.NameAsc: return contributors.sort( - (a, b) => - (a.current ? -1 : 1) - (b.current ? -1 : 1) || - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }), + (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || Strings.sortCompare(a.name, b.name), ); case ContributorSorting.NameDesc: return contributors.sort( - (a, b) => - (a.current ? -1 : 1) - (b.current ? -1 : 1) || - b.name.localeCompare(a.name, undefined, { numeric: true, sensitivity: 'base' }), + (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || Strings.sortCompare(b.name, a.name), ); case ContributorSorting.CountDesc: default: diff --git a/src/git/models/remote.ts b/src/git/models/remote.ts index 79c7e35..e004da6 100644 --- a/src/git/models/remote.ts +++ b/src/git/models/remote.ts @@ -1,6 +1,7 @@ 'use strict'; import { WorkspaceState } from '../../constants'; import { Container } from '../../container'; +import { Strings } from '../../system'; import { RemoteProvider, RichRemoteProvider } from '../remotes/factory'; export const enum GitRemoteType { @@ -43,7 +44,7 @@ export class GitRemote (a.default ? -1 : 1) - (b.default ? -1 : 1) || (a.name === 'origin' ? -1 : 1) - (b.name === 'origin' ? -1 : 1) || - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }), + Strings.sortCompare(a.name, b.name), ); } diff --git a/src/git/models/tag.ts b/src/git/models/tag.ts index 024ed93..f33370c 100644 --- a/src/git/models/tag.ts +++ b/src/git/models/tag.ts @@ -1,6 +1,6 @@ 'use strict'; import { configuration, DateStyle, TagSorting } from '../../configuration'; -import { Dates, memoize } from '../../system'; +import { Dates, memoize, Strings } from '../../system'; import { GitReference, GitTagReference } from './models'; export const TagDateFormatting = { @@ -34,13 +34,9 @@ export class GitTag implements GitTagReference { case TagSorting.DateAsc: return tags.sort((a, b) => a.date.getTime() - b.date.getTime()); case TagSorting.NameAsc: - return tags.sort((a, b) => - a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }), - ); + return tags.sort((a, b) => Strings.sortCompare(a.name, b.name)); case TagSorting.NameDesc: - return tags.sort((a, b) => - b.name.localeCompare(a.name, undefined, { numeric: true, sensitivity: 'base' }), - ); + return tags.sort((a, b) => Strings.sortCompare(b.name, a.name)); case TagSorting.DateDesc: default: return tags.sort((a, b) => b.date.getTime() - a.date.getTime()); diff --git a/src/git/providers/localGitProvider.ts b/src/git/providers/localGitProvider.ts index 30b5fe9..929f052 100644 --- a/src/git/providers/localGitProvider.ts +++ b/src/git/providers/localGitProvider.ts @@ -3070,7 +3070,7 @@ export class LocalGitProvider implements GitProvider, Disposable { return; } - if (path.toLowerCase() === resolvedPath.toLowerCase()) { + if (Strings.equalsIgnoreCase(path, resolvedPath)) { Logger.debug(cc, `No symlink detected; repoPath=${repoPath}`); resolve(repoPath); return; diff --git a/src/system/string.ts b/src/system/string.ts index da51e0a..e37deca 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -22,6 +22,23 @@ export const enum CharCode { z = 122, } +const compareCollator = new Intl.Collator(undefined, { sensitivity: 'accent' }); +export function compareIgnoreCase(a: string, b: string): 0 | -1 | 1 { + const result = compareCollator.compare(a, b); + // Intl.Collator.compare isn't guaranteed to always return 1 or -1 on all platforms so normalize it + return result === 0 ? 0 : result > 0 ? 1 : -1; +} + +export function equalsIgnoreCase(a: string | null | undefined, b: string | null | undefined): boolean { + // Treat `null` & `undefined` as equivalent + if (a == null && b == null) return true; + if (a == null || b == null) return false; + return compareIgnoreCase(a, b) === 0; +} + +export const sortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); +export const sortCompare = sortCollator.compare; + export function compareSubstring( a: string, b: string, @@ -112,12 +129,6 @@ export function escapeMarkdown(s: string, options: { quoted?: boolean } = {}): s return s.replace(markdownQuotedRegex, '\t\n> '); } -export function equalsIgnoreCase(a: string | null | undefined, b: string | null | undefined): boolean { - if (a == null && b == null) return true; - if (a == null || b == null) return false; - return a.localeCompare(b, undefined, { sensitivity: 'accent' }) === 0; -} - export function escapeRegex(s: string) { return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); } diff --git a/src/system/version.ts b/src/system/version.ts index 0865ad1..c37956b 100644 --- a/src/system/version.ts +++ b/src/system/version.ts @@ -1,4 +1,5 @@ 'use strict'; +import { compareIgnoreCase } from './string'; declare type VersionComparisonResult = -1 | 0 | 1; @@ -29,9 +30,7 @@ export function compare(v1: string | Version, v2: string | Version): VersionComp if (v1.pre === undefined && v2.pre !== undefined) return 1; if (v1.pre !== undefined && v2.pre === undefined) return -1; - if (v1.pre !== undefined && v2.pre !== undefined) { - return v1.pre.localeCompare(v2.pre) as VersionComparisonResult; - } + if (v1.pre !== undefined && v2.pre !== undefined) return compareIgnoreCase(v1.pre, v2.pre); return 0; } diff --git a/src/views/nodes/branchTrackingStatusFilesNode.ts b/src/views/nodes/branchTrackingStatusFilesNode.ts index f105c65..46d79bf 100644 --- a/src/views/nodes/branchTrackingStatusFilesNode.ts +++ b/src/views/nodes/branchTrackingStatusFilesNode.ts @@ -91,11 +91,7 @@ export class BranchTrackingStatusFilesNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy, false); children = root.getChildren() as FileNode[]; } else { - children.sort( - (a, b) => - a.priority - b.priority || - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => a.priority - b.priority || Strings.sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts index 89c4395..e16b7a9 100644 --- a/src/views/nodes/commitNode.ts +++ b/src/views/nodes/commitNode.ts @@ -63,9 +63,7 @@ export class CommitNode extends ViewRefNode - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + (children as FileNode[]).sort((a, b) => Strings.sortCompare(a.label!, b.label!)); } if (!(this.view instanceof TagsView) && !(this.view instanceof FileHistoryView)) { diff --git a/src/views/nodes/folderNode.ts b/src/views/nodes/folderNode.ts index da73c2d..9c9638e 100644 --- a/src/views/nodes/folderNode.ts +++ b/src/views/nodes/folderNode.ts @@ -2,7 +2,7 @@ import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewFilesLayout, ViewsFilesConfig } from '../../configuration'; import { GitUri } from '../../git/gitUri'; -import { Arrays } from '../../system'; +import { Arrays, Strings } from '../../system'; import { FileHistoryView } from '../fileHistoryView'; import { StashesView } from '../stashesView'; import { ViewsWithCommits } from '../viewBase'; @@ -77,7 +77,7 @@ export class FolderNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort((a, b) => - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/rebaseStatusNode.ts b/src/views/nodes/rebaseStatusNode.ts index ba245f3..23dacf0 100644 --- a/src/views/nodes/rebaseStatusNode.ts +++ b/src/views/nodes/rebaseStatusNode.ts @@ -72,9 +72,7 @@ export class RebaseStatusNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort((a, b) => - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); } const commit = await this.view.container.git.getCommit( @@ -191,9 +189,7 @@ export class RebaseCommitNode extends ViewRefNode - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/resultsFilesNode.ts b/src/views/nodes/resultsFilesNode.ts index b0ce537..d5bc8cd 100644 --- a/src/views/nodes/resultsFilesNode.ts +++ b/src/views/nodes/resultsFilesNode.ts @@ -85,11 +85,7 @@ export class ResultsFilesNode extends ViewNode { const root = new FolderNode(this.view, this, this.repoPath, '', hierarchy); children = root.getChildren() as FileNode[]; } else { - children.sort( - (a, b) => - a.priority - b.priority || - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => a.priority - b.priority || Strings.sortCompare(a.label!, b.label!)); } return children; diff --git a/src/views/nodes/stashNode.ts b/src/views/nodes/stashNode.ts index abd83d5..33db651 100644 --- a/src/views/nodes/stashNode.ts +++ b/src/views/nodes/stashNode.ts @@ -49,9 +49,7 @@ export class StashNode extends ViewRefNode - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => Strings.sortCompare(a.label!, b.label!)); } return children; } diff --git a/src/views/nodes/statusFilesNode.ts b/src/views/nodes/statusFilesNode.ts index 16d3c90..01feee8 100644 --- a/src/views/nodes/statusFilesNode.ts +++ b/src/views/nodes/statusFilesNode.ts @@ -118,11 +118,7 @@ export class StatusFilesNode extends ViewNode { const root = new FolderNode(this.view, this, repoPath, '', hierarchy, true); children = root.getChildren() as FileNode[]; } else { - children.sort( - (a, b) => - a.priority - b.priority || - a.label!.localeCompare(b.label!, undefined, { numeric: true, sensitivity: 'base' }), - ); + children.sort((a, b) => a.priority - b.priority || Strings.sortCompare(a.label!, b.label!)); } return children;