diff --git a/src/annotations/autolinks.ts b/src/annotations/autolinks.ts index ac245e711d814..5717ec460c344 100644 --- a/src/annotations/autolinks.ts +++ b/src/annotations/autolinks.ts @@ -4,8 +4,12 @@ import { GlyphChars } from '../constants'; import { Container } from '../container'; import { GitRemote, IssueOrPullRequest } from '../git/models'; import { Logger } from '../logger'; -import { Dates, debug, Encoding, Iterables, Strings } from '../system'; +import { fromNow } from '../system/date'; +import { debug } from '../system/decorators/log'; +import { encodeUrl } from '../system/encoding'; +import { every, join, map } from '../system/iterable'; import { PromiseCancelledError, raceAll } from '../system/promise'; +import { escapeMarkdown, escapeRegex, getSuperscript } from '../system/string'; const numRegex = //g; @@ -70,9 +74,7 @@ export class Autolinks implements Disposable { if (ref.messageRegex === undefined) { ref.messageRegex = new RegExp( - `(?<=^|\\s|\\(|\\\\\\[)(${Strings.escapeRegex(ref.prefix)}([${ - ref.alphanumeric ? '\\w' : '0-9' - }]+))\\b`, + `(?<=^|\\s|\\(|\\\\\\[)(${escapeRegex(ref.prefix)}([${ref.alphanumeric ? '\\w' : '0-9'}]+))\\b`, ref.ignoreCase ? 'gi' : 'g', ); } @@ -90,7 +92,7 @@ export class Autolinks implements Disposable { if (ids.size === 0) return undefined; const issuesOrPullRequests = await raceAll(ids.values(), id => provider.getIssueOrPullRequest(id), timeout); - if (issuesOrPullRequests.size === 0 || Iterables.every(issuesOrPullRequests.values(), pr => pr === undefined)) { + if (issuesOrPullRequests.size === 0 || every(issuesOrPullRequests.values(), pr => pr === undefined)) { return undefined; } @@ -146,7 +148,7 @@ export class Autolinks implements Disposable { try { if (ref.messageMarkdownRegex === undefined) { ref.messageMarkdownRegex = new RegExp( - `(?<=^|\\s|\\(|\\\\\\[)(${Strings.escapeRegex(Strings.escapeMarkdown(ref.prefix))}([${ + `(?<=^|\\s|\\(|\\\\\\[)(${escapeRegex(escapeMarkdown(ref.prefix))}([${ ref.alphanumeric ? '\\w' : '0-9' }]+))\\b`, ref.ignoreCase ? 'gi' : 'g', @@ -154,7 +156,7 @@ export class Autolinks implements Disposable { } if (issuesOrPullRequests == null || issuesOrPullRequests.size === 0) { - const replacement = `[$1](${Encoding.encodeUrl(ref.url.replace(numRegex, '$2'))}${ + const replacement = `[$1](${encodeUrl(ref.url.replace(numRegex, '$2'))}${ ref.title ? ` "${ref.title.replace(numRegex, '$2')}"` : '' })`; ref.linkify = (text: string, markdown: boolean) => @@ -171,7 +173,7 @@ export class Autolinks implements Disposable { return text.replace(ref.messageMarkdownRegex!, (_substring, linkText, num) => { const issue = issuesOrPullRequests?.get(num); - const issueUrl = Encoding.encodeUrl(ref.url.replace(numRegex, num)); + const issueUrl = encodeUrl(ref.url.replace(numRegex, num)); let title = ''; if (ref.title) { @@ -191,15 +193,15 @@ export class Autolinks implements Disposable { issue, )} [**${issueTitle}**](${issueUrl}${title}")\\\n${GlyphChars.Space.repeat( 5, - )}${linkText} ${issue.closed ? 'closed' : 'opened'} ${Dates.getFormatter( + )}${linkText} ${issue.closed ? 'closed' : 'opened'} ${fromNow( issue.closedDate ?? issue.date, - ).fromNow()}`, + )}`, ); } title += `\n${GlyphChars.Dash.repeat(2)}\n${issueTitle}\n${ issue.closed ? 'Closed' : 'Opened' - }, ${Dates.getFormatter(issue.closedDate ?? issue.date).fromNow()}`; + }, ${fromNow(issue.closedDate ?? issue.date)}`; } } title += '"'; @@ -223,17 +225,17 @@ export class Autolinks implements Disposable { `${linkText}: ${ issue instanceof PromiseCancelledError ? 'Details timed out' - : `${issue.title} ${GlyphChars.Dot} ${ - issue.closed ? 'Closed' : 'Opened' - }, ${Dates.getFormatter(issue.closedDate ?? issue.date).fromNow()}` + : `${issue.title} ${GlyphChars.Dot} ${issue.closed ? 'Closed' : 'Opened'}, ${fromNow( + issue.closedDate ?? issue.date, + )}` }`, ); - return `${linkText}${Strings.getSuperscript(index)}`; + return `${linkText}${getSuperscript(index)}`; }); return includeFootnotes && footnotes != null && footnotes.size !== 0 - ? `${text}\n${GlyphChars.Dash.repeat(2)}\n${Iterables.join( - Iterables.map(footnotes, ([i, footnote]) => `${Strings.getSuperscript(i)} ${footnote}`), + ? `${text}\n${GlyphChars.Dash.repeat(2)}\n${join( + map(footnotes, ([i, footnote]) => `${getSuperscript(i)} ${footnote}`), '\n', )}` : text; diff --git a/src/commands/git/fetch.ts b/src/commands/git/fetch.ts index 396a37297017f..08ca2e9582a5c 100644 --- a/src/commands/git/fetch.ts +++ b/src/commands/git/fetch.ts @@ -2,7 +2,9 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranchReference, GitReference, Repository } from '../../git/models'; import { FlagsQuickPickItem } from '../../quickpicks'; -import { Arrays, Dates, Strings } from '../../system'; +import { isStringArray } from '../../system/array'; +import { fromNow } from '../../system/date'; +import { pad } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -86,12 +88,7 @@ export class FetchGitCommand extends QuickCommand { while (this.canStepsContinue(state)) { context.title = this.title; - if ( - state.counter < 1 || - state.repos == null || - state.repos.length === 0 || - Arrays.isStringArray(state.repos) - ) { + if (state.counter < 1 || state.repos == null || state.repos.length === 0 || isStringArray(state.repos)) { skippedStepOne = false; if (context.repos.length === 1) { skippedStepOne = true; @@ -137,9 +134,7 @@ export class FetchGitCommand extends QuickCommand { if (state.repos.length === 1) { const lastFetched = await state.repos[0].getLastFetched(); if (lastFetched !== 0) { - lastFetchedOn = `${Strings.pad(GlyphChars.Dot, 2, 2)}Last fetched ${Dates.getFormatter( - new Date(lastFetched), - ).fromNow()}`; + lastFetchedOn = `${pad(GlyphChars.Dot, 2, 2)}Last fetched ${fromNow(new Date(lastFetched))}`; } } diff --git a/src/commands/git/pull.ts b/src/commands/git/pull.ts index 909a4e03dcca6..3be5441e2feca 100644 --- a/src/commands/git/pull.ts +++ b/src/commands/git/pull.ts @@ -2,7 +2,9 @@ import { GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranch, GitBranchReference, GitReference, Repository } from '../../git/models'; import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; -import { Arrays, Dates, Strings } from '../../system'; +import { isStringArray } from '../../system/array'; +import { fromNow } from '../../system/date'; +import { pad, pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -92,12 +94,7 @@ export class PullGitCommand extends QuickCommand { while (this.canStepsContinue(state)) { context.title = this.title; - if ( - state.counter < 1 || - state.repos == null || - state.repos.length === 0 || - Arrays.isStringArray(state.repos) - ) { + if (state.counter < 1 || state.repos == null || state.repos.length === 0 || isStringArray(state.repos)) { skippedStepOne = false; if (context.repos.length === 1) { skippedStepOne = true; @@ -182,10 +179,9 @@ export class PullGitCommand extends QuickCommand { label: this.title, detail: `Will pull${ branch.state.behind - ? ` ${Strings.pluralize( - 'commit', - branch.state.behind, - )} into ${GitReference.toString(branch)}` + ? ` ${pluralize('commit', branch.state.behind)} into ${GitReference.toString( + branch, + )}` : ` into ${GitReference.toString(branch)}` }`, }), @@ -198,14 +194,12 @@ export class PullGitCommand extends QuickCommand { let lastFetchedOn = ''; if (lastFetched !== 0) { - lastFetchedOn = `${Strings.pad(GlyphChars.Dot, 2, 2)}Last fetched ${Dates.getFormatter( - new Date(lastFetched), - ).fromNow()}`; + lastFetchedOn = `${pad(GlyphChars.Dot, 2, 2)}Last fetched ${fromNow(new Date(lastFetched))}`; } const pullDetails = status?.state.behind != null - ? ` ${Strings.pluralize('commit', status.state.behind)} into $(repo) ${repo.formattedName}` + ? ` ${pluralize('commit', status.state.behind)} into $(repo) ${repo.formattedName}` : ` into $(repo) ${repo.formattedName}`; step = this.createConfirmStep( @@ -227,7 +221,7 @@ export class PullGitCommand extends QuickCommand { onDidClickButton: async (quickpick, button) => { if (button !== QuickCommandButtons.Fetch || quickpick.busy) return false; - quickpick.title = `Confirm ${context.title}${Strings.pad(GlyphChars.Dot, 2, 2)}Fetching${ + quickpick.title = `Confirm ${context.title}${pad(GlyphChars.Dot, 2, 2)}Fetching${ GlyphChars.Ellipsis }`; diff --git a/src/commands/git/push.ts b/src/commands/git/push.ts index 910f948767bbc..4818575f3fe97 100644 --- a/src/commands/git/push.ts +++ b/src/commands/git/push.ts @@ -3,7 +3,9 @@ import { BuiltInGitConfiguration, GlyphChars } from '../../constants'; import { Container } from '../../container'; import { GitBranch, GitBranchReference, GitReference, Repository } from '../../git/models'; import { Directive, DirectiveQuickPickItem, FlagsQuickPickItem } from '../../quickpicks'; -import { Arrays, Dates, Strings } from '../../system'; +import { isStringArray } from '../../system/array'; +import { fromNow } from '../../system/date'; +import { pad, pluralize } from '../../system/string'; import { ViewsWithRepositoryFolders } from '../../views/viewBase'; import { appendReposToTitle, @@ -98,12 +100,7 @@ export class PushGitCommand extends QuickCommand { while (this.canStepsContinue(state)) { context.title = this.title; - if ( - state.counter < 1 || - state.repos == null || - state.repos.length === 0 || - Arrays.isStringArray(state.repos) - ) { + if (state.counter < 1 || state.repos == null || state.repos.length === 0 || isStringArray(state.repos)) { skippedStepOne = false; if (context.repos.length === 1) { skippedStepOne = true; @@ -233,15 +230,10 @@ export class PushGitCommand extends QuickCommand { label: `Force ${this.title}${useForceWithLease ? ' (with lease)' : ''}`, description: `--force${useForceWithLease ? '-with-lease' : ''}`, detail: `Will force push${useForceWithLease ? ' (with lease)' : ''} ${ - branch?.state.ahead - ? ` ${Strings.pluralize('commit', branch.state.ahead)}` - : '' + branch?.state.ahead ? ` ${pluralize('commit', branch.state.ahead)}` : '' }${branch.getRemoteName() ? ` to ${branch.getRemoteName()}` : ''}${ branch != null && branch.state.behind > 0 - ? `, overwriting ${Strings.pluralize( - 'commit', - branch.state.behind, - )}${ + ? `, overwriting ${pluralize('commit', branch.state.behind)}${ branch?.getRemoteName() ? ` on ${branch.getRemoteName()}` : '' @@ -255,17 +247,14 @@ export class PushGitCommand extends QuickCommand { label: `Cancel ${this.title}`, detail: `Cannot push; ${GitReference.toString( branch, - )} is behind ${branch.getRemoteName()} by ${Strings.pluralize( - 'commit', - branch.state.behind, - )}`, + )} is behind ${branch.getRemoteName()} by ${pluralize('commit', branch.state.behind)}`, }), ); } else if (branch != null && branch?.state.ahead > 0) { step = this.createConfirmStep(appendReposToTitle(`Confirm ${context.title}`, state, context), [ FlagsQuickPickItem.create(state.flags, [branch.getRemoteName()!], { label: this.title, - detail: `Will push ${Strings.pluralize( + detail: `Will push ${pluralize( 'commit', branch.state.ahead, )} from ${GitReference.toString(branch)} to ${branch.getRemoteName()}`, @@ -343,9 +332,7 @@ export class PushGitCommand extends QuickCommand { const lastFetched = await repo.getLastFetched(); if (lastFetched !== 0) { - lastFetchedOn = `${Strings.pad(GlyphChars.Dot, 2, 2)}Last fetched ${Dates.getFormatter( - new Date(lastFetched), - ).fromNow()}`; + lastFetchedOn = `${pad(GlyphChars.Dot, 2, 2)}Last fetched ${fromNow(new Date(lastFetched))}`; } let pushDetails; @@ -358,9 +345,9 @@ export class PushGitCommand extends QuickCommand { : '' }${status?.upstream ? ` to ${GitBranch.getRemote(status.upstream)}` : ''}`; } else { - pushDetails = `${ - status?.state.ahead ? ` ${Strings.pluralize('commit', status.state.ahead)}` : '' - }${status?.upstream ? ` to ${GitBranch.getRemote(status.upstream)}` : ''}`; + pushDetails = `${status?.state.ahead ? ` ${pluralize('commit', status.state.ahead)}` : ''}${ + status?.upstream ? ` to ${GitBranch.getRemote(status.upstream)}` : '' + }`; } step = this.createConfirmStep( @@ -379,7 +366,7 @@ export class PushGitCommand extends QuickCommand { description: `--force${useForceWithLease ? '-with-lease' : ''}`, detail: `Will force push${useForceWithLease ? ' (with lease)' : ''} ${pushDetails}${ status != null && status.state.behind > 0 - ? `, overwriting ${Strings.pluralize('commit', status.state.behind)}${ + ? `, overwriting ${pluralize('commit', status.state.behind)}${ status?.upstream ? ` on ${GitBranch.getRemote(status.upstream)}` : '' }` : '' @@ -391,7 +378,7 @@ export class PushGitCommand extends QuickCommand { label: `Cancel ${this.title}`, detail: `Cannot push; ${GitReference.toString(branch)} is behind${ status?.upstream ? ` ${GitBranch.getRemote(status.upstream)}` : '' - } by ${Strings.pluralize('commit', status.state.behind)}`, + } by ${pluralize('commit', status.state.behind)}`, }) : undefined, ); @@ -400,7 +387,7 @@ export class PushGitCommand extends QuickCommand { step.onDidClickButton = async (quickpick, button) => { if (button !== QuickCommandButtons.Fetch || quickpick.busy) return false; - quickpick.title = `Confirm ${context.title}${Strings.pad(GlyphChars.Dot, 2, 2)}Fetching${ + quickpick.title = `Confirm ${context.title}${pad(GlyphChars.Dot, 2, 2)}Fetching${ GlyphChars.Ellipsis }`; diff --git a/src/git/models/branch.ts b/src/git/models/branch.ts index be814cd58a7c8..864ba91244393 100644 --- a/src/git/models/branch.ts +++ b/src/git/models/branch.ts @@ -1,7 +1,10 @@ import { BranchSorting, configuration, DateStyle } from '../../configuration'; import { Starred, WorkspaceState } from '../../constants'; import { Container } from '../../container'; -import { Dates, debug, memoize, Strings } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; +import { debug } from '../../system/decorators/log'; +import { memoize } from '../../system/decorators/memoize'; +import { sortCompare } from '../../system/string'; import { GitBranchReference, GitReference, GitRevision } from '../models'; import { PullRequest, PullRequestState } from './pullRequest'; import { GitRemote } from './remote'; @@ -78,7 +81,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) || - Strings.sortCompare(a.name, b.name), + sortCompare(a.name, b.name), ); case BranchSorting.NameDesc: return branches.sort( @@ -92,7 +95,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) || - Strings.sortCompare(b.name, a.name), + sortCompare(b.name, a.name), ); case BranchSorting.DateDesc: default: @@ -152,22 +155,17 @@ export class GitBranch implements GitBranchReference { return this.detached ? this.sha! : this.name; } - @memoize() - private get dateFormatter(): Dates.DateFormatter | undefined { - return this.date == null ? undefined : Dates.getFormatter(this.date); - } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null): string { if (format == null) { format = 'MMMM Do, YYYY h:mma'; } - return this.dateFormatter?.format(format) ?? ''; + return this.date != null ? formatDate(this.date, format) : ''; } formatDateFromNow(): string { - return this.dateFormatter?.fromNow() ?? ''; + return this.date != null ? fromNow(this.date) : ''; } @debug() diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index 1d1b9981b229e..1b26d3b01807c 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -2,7 +2,8 @@ import { Uri } from 'vscode'; import { getAvatarUri } from '../../avatars'; import { configuration, DateSource, DateStyle, GravatarDefaultStyle } from '../../configuration'; import { Container } from '../../container'; -import { Dates, memoize } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; +import { memoize } from '../../system/decorators/memoize'; import { CommitFormatter } from '../formatters'; import { GitUri } from '../gitUri'; import { GitReference, GitRevision, GitRevisionReference, PullRequest } from '../models'; @@ -173,59 +174,34 @@ export abstract class GitCommit implements GitRevisionReference { return Container.instance.git.getWorkingUri(this.repoPath, this.uri); } - @memoize() - private get authorDateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.authorDate); - } - - @memoize() - private get committerDateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.committerDate); - } - - private get dateFormatter(): Dates.DateFormatter { - return CommitDateFormatting.dateSource === DateSource.Committed - ? this.committerDateFormatter - : this.authorDateFormatter; - } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatAuthorDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.authorDateFormatter.format(format); + return formatDate(this.authorDate, format ?? 'MMMM Do, YYYY h:mma'); } formatAuthorDateFromNow(short?: boolean) { - return this.authorDateFormatter.fromNow(short); + return fromNow(this.authorDate, short); } @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatCommitterDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.committerDateFormatter.format(format); + return formatDate(this.committerDate, format ?? 'MMMM Do, YYYY h:mma'); } formatCommitterDateFromNow(short?: boolean) { - return this.committerDateFormatter.fromNow(short); + return fromNow(this.committerDate, short); } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.dateFormatter.format(format); + return CommitDateFormatting.dateSource === DateSource.Committed + ? this.formatCommitterDate(format) + : this.formatAuthorDate(format); } formatDateFromNow(short?: boolean) { - return this.dateFormatter.fromNow(short); + return CommitDateFormatting.dateSource === DateSource.Committed + ? this.formatCommitterDateFromNow(short) + : this.formatAuthorDateFromNow(short); } getFormattedPath(options: { relativeTo?: string; suffix?: string; truncateTo?: number } = {}): string { diff --git a/src/git/models/contributor.ts b/src/git/models/contributor.ts index 3995fbcc3eb28..c5253408b46e0 100644 --- a/src/git/models/contributor.ts +++ b/src/git/models/contributor.ts @@ -1,7 +1,9 @@ import { Uri } from 'vscode'; import { getAvatarUri } from '../../avatars'; import { configuration, ContributorSorting, GravatarDefaultStyle } from '../../configuration'; -import { Dates, memoize, Strings } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; +import { memoize } from '../../system/decorators/memoize'; +import { sortCompare } from '../../system/string'; export interface ContributorSortOptions { current?: true; @@ -40,11 +42,11 @@ export class GitContributor { ); case ContributorSorting.NameAsc: return contributors.sort( - (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || Strings.sortCompare(a.name, b.name), + (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || sortCompare(a.name, b.name), ); case ContributorSorting.NameDesc: return contributors.sort( - (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || Strings.sortCompare(b.name, a.name), + (a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || sortCompare(b.name, a.name), ); case ContributorSorting.CountDesc: default: @@ -71,22 +73,13 @@ export class GitContributor { public readonly current: boolean = false, ) {} - @memoize() - private get dateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.date); - } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.dateFormatter.format(format); + return formatDate(this.date, format ?? 'MMMM Do, YYYY h:mma'); } formatDateFromNow(short?: boolean) { - return this.dateFormatter.fromNow(short); + return fromNow(this.date, short); } getAvatarUri(options?: { defaultStyle?: GravatarDefaultStyle; size?: number }): Uri | Promise { diff --git a/src/git/models/pullRequest.ts b/src/git/models/pullRequest.ts index 924843419fa9e..aa4a70b1bac2a 100644 --- a/src/git/models/pullRequest.ts +++ b/src/git/models/pullRequest.ts @@ -1,7 +1,8 @@ import { ColorThemeKind, ThemeColor, ThemeIcon, window } from 'vscode'; import { configuration, DateStyle } from '../../configuration'; import { Colors } from '../../constants'; -import { Dates, memoize } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; +import { memoize } from '../../system/decorators/memoize'; import { RemoteProviderReference } from './remoteProvider'; export const PullRequestDateFormatting = { @@ -79,75 +80,43 @@ export class PullRequest { : this.formatDateFromNow(); } - @memoize() - private get dateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.mergedDate ?? this.closedDate ?? this.date); - } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.dateFormatter.format(format); + return formatDate(this.mergedDate ?? this.closedDate ?? this.date, format ?? 'MMMM Do, YYYY h:mma'); } formatDateFromNow() { - return this.dateFormatter.fromNow(); - } - - @memoize() - private get closedDateFormatter(): Dates.DateFormatter | undefined { - return this.closedDate === undefined ? undefined : Dates.getFormatter(this.closedDate); + return fromNow(this.mergedDate ?? this.closedDate ?? this.date); } @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatClosedDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.closedDateFormatter?.format(format) ?? ''; + if (this.closedDate == null) return ''; + return formatDate(this.closedDate, format ?? 'MMMM Do, YYYY h:mma'); } formatClosedDateFromNow() { - return this.closedDateFormatter?.fromNow() ?? ''; - } - - @memoize() - private get mergedDateFormatter(): Dates.DateFormatter | undefined { - return this.mergedDate === undefined ? undefined : Dates.getFormatter(this.mergedDate); + if (this.closedDate == null) return ''; + return fromNow(this.closedDate); } @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatMergedDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.mergedDateFormatter?.format(format) ?? ''; + if (this.mergedDate == null) return ''; + return formatDate(this.mergedDate, format ?? 'MMMM Do, YYYY h:mma') ?? ''; } formatMergedDateFromNow() { - return this.mergedDateFormatter?.fromNow() ?? ''; - } - - @memoize() - private get updatedDateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.date); + if (this.mergedDate == null) return ''; + return fromNow(this.mergedDate); } @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatUpdatedDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.updatedDateFormatter.format(format); + return formatDate(this.date, format ?? 'MMMM Do, YYYY h:mma') ?? ''; } formatUpdatedDateFromNow() { - return this.updatedDateFormatter.fromNow(); + return fromNow(this.date); } } diff --git a/src/git/models/reflog.ts b/src/git/models/reflog.ts index ac530da6848ed..26d2423238f9d 100644 --- a/src/git/models/reflog.ts +++ b/src/git/models/reflog.ts @@ -1,5 +1,6 @@ import { DateStyle } from '../../config'; import { Dates, memoize } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; import { CommitDateFormatting, GitRevision } from '../models'; export interface GitReflog { @@ -28,15 +29,11 @@ export class GitReflogRecord { @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.dateFormatter.format(format); + return formatDate(this.date, format ?? 'MMMM Do, YYYY h:mma'); } formatDateFromNow() { - return this.dateFormatter.fromNow(); + return fromNow(this.date); } get formattedDate(): string { @@ -86,9 +83,4 @@ export class GitReflogRecord { this._selector = selector; } } - - @memoize() - private get dateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.date); - } } diff --git a/src/git/models/repository.ts b/src/git/models/repository.ts index 2ce341e4b464e..fc1092b30325e 100644 --- a/src/git/models/repository.ts +++ b/src/git/models/repository.ts @@ -20,7 +20,7 @@ import { Logger } from '../../logger'; import { Messages } from '../../messages'; import { asRepoComparisonKey } from '../../repositories'; import { filterMap, groupByMap } from '../../system/array'; -import { getFormatter } from '../../system/date'; +import { formatDate, fromNow } from '../../system/date'; import { gate } from '../../system/decorators/gate'; import { debug, log, logName } from '../../system/decorators/log'; import { debounce } from '../../system/function'; @@ -139,13 +139,13 @@ export interface RepositoryFileSystemChangeEvent { @logName((r, name) => `${name}(${r.id})`) export class Repository implements Disposable { static formatLastFetched(lastFetched: number, short: boolean = true): string { - const formatter = getFormatter(new Date(lastFetched)); + const date = new Date(lastFetched); if (Date.now() - lastFetched < millisecondsPerDay) { - return formatter.fromNow(); + return fromNow(date); } if (short) { - return formatter.format(Container.instance.config.defaultDateShortFormat ?? 'MMM D, YYYY'); + return formatDate(date, Container.instance.config.defaultDateShortFormat ?? 'MMM D, YYYY'); } let format = @@ -154,7 +154,7 @@ export class Repository implements Disposable { if (!/[hHm]/.test(format)) { format += ` [at] ${Container.instance.config.defaultTimeFormat ?? 'h:mma'}`; } - return formatter.format(format); + return formatDate(date, format); } static getLastFetchedUpdateInterval(lastFetched: number): number { diff --git a/src/git/models/tag.ts b/src/git/models/tag.ts index c010dffaca328..2a1866892d04e 100644 --- a/src/git/models/tag.ts +++ b/src/git/models/tag.ts @@ -1,5 +1,7 @@ import { configuration, DateStyle, TagSorting } from '../../configuration'; -import { Dates, memoize, Strings } from '../../system'; +import { formatDate, fromNow } from '../../system/date'; +import { memoize } from '../../system/decorators/memoize'; +import { sortCompare } from '../../system/string'; import { GitReference, GitTagReference } from '../models'; export const TagDateFormatting = { @@ -33,9 +35,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) => Strings.sortCompare(a.name, b.name)); + return tags.sort((a, b) => sortCompare(a.name, b.name)); case TagSorting.NameDesc: - return tags.sort((a, b) => Strings.sortCompare(b.name, a.name)); + return tags.sort((a, b) => sortCompare(b.name, a.name)); case TagSorting.DateDesc: default: return tags.sort((a, b) => b.date.getTime() - a.date.getTime()); @@ -63,46 +65,22 @@ export class GitTag implements GitTagReference { return this.name; } - @memoize() - private get commitDateFormatter(): Dates.DateFormatter | undefined { - return this.commitDate == null ? undefined : Dates.getFormatter(this.commitDate); - } - - @memoize() - private get dateFormatter(): Dates.DateFormatter { - return Dates.getFormatter(this.date); - } - @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatCommitDate(format?: string | null) { - const formatter = this.commitDateFormatter; - if (formatter == null) return ''; - - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return formatter.format(format); + return this.commitDate != null ? formatDate(this.commitDate, format ?? 'MMMM Do, YYYY h:mma') : ''; } formatCommitDateFromNow() { - const formatter = this.commitDateFormatter; - if (formatter == null) return ''; - - return formatter.fromNow(); + return this.commitDate != null ? fromNow(this.commitDate) : ''; } @memoize(format => (format == null ? 'MMMM Do, YYYY h:mma' : format)) formatDate(format?: string | null) { - if (format == null) { - format = 'MMMM Do, YYYY h:mma'; - } - - return this.dateFormatter.format(format); + return formatDate(this.date, format ?? 'MMMM Do, YYYY h:mma'); } formatDateFromNow() { - return this.dateFormatter.fromNow(); + return fromNow(this.date); } @memoize() diff --git a/src/quickpicks/gitQuickPickItems.ts b/src/quickpicks/gitQuickPickItems.ts index 9857b59026cda..282627ea9d9cc 100644 --- a/src/quickpicks/gitQuickPickItems.ts +++ b/src/quickpicks/gitQuickPickItems.ts @@ -14,7 +14,8 @@ import { GitTag, Repository, } from '../git/models'; -import { Dates, Strings } from '../system'; +import { fromNow } from '../system/date'; +import { pad } from '../system/string'; import { CommandQuickPickItem, QuickPickItemOfT } from './quickPicksItems'; export class GitCommandQuickPickItem extends CommandQuickPickItem<[GitCommandsCommandArgs]> { @@ -105,13 +106,13 @@ export namespace BranchQuickPickItem { if (options.ref) { if (branch.sha) { description = description - ? `${description}${Strings.pad('$(git-commit)', 2, 2)}${GitRevision.shorten(branch.sha)}` - : `${Strings.pad('$(git-commit)', 0, 2)}${GitRevision.shorten(branch.sha)}`; + ? `${description}${pad('$(git-commit)', 2, 2)}${GitRevision.shorten(branch.sha)}` + : `${pad('$(git-commit)', 0, 2)}${GitRevision.shorten(branch.sha)}`; } if (branch.date !== undefined) { description = description - ? `${description}${Strings.pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}` + ? `${description}${pad(GlyphChars.Dot, 2, 2)}${branch.formattedDate}` : branch.formattedDate; } } @@ -119,7 +120,7 @@ export namespace BranchQuickPickItem { const checked = options.checked || (options.checked == null && options.current === 'checkmark' && branch.current); const item: BranchQuickPickItem = { - label: `${Strings.pad('$(git-branch)', 0, 2)}${branch.starred ? '$(star-full) ' : ''}${branch.name}${ + label: `${pad('$(git-branch)', 0, 2)}${branch.starred ? '$(star-full) ' : ''}${branch.name}${ checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' }`, description: description, @@ -154,12 +155,10 @@ export namespace CommitQuickPickItem { if (options.compact) { const item: CommitQuickPickItem = { - label: `${options.icon ? Strings.pad('$(archive)', 0, 2) : ''}${number}${commit.getShortMessage()}`, - description: `${commit.formattedDate}${Strings.pad( - GlyphChars.Dot, - 2, - 2, - )}${commit.getFormattedDiffStatus({ compact: true })}`, + label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.getShortMessage()}`, + description: `${commit.formattedDate}${pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ + compact: true, + })}`, alwaysShow: options.alwaysShow, buttons: options.buttons, picked: picked, @@ -170,9 +169,9 @@ export namespace CommitQuickPickItem { } const item: CommitQuickPickItem = { - label: `${options.icon ? Strings.pad('$(archive)', 0, 2) : ''}${number}${commit.getShortMessage()}`, + label: `${options.icon ? pad('$(archive)', 0, 2) : ''}${number}${commit.getShortMessage()}`, description: '', - detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${Strings.pad( + detail: `${GlyphChars.Space.repeat(2)}${commit.formattedDate}${pad( GlyphChars.Dot, 2, 2, @@ -188,10 +187,10 @@ export namespace CommitQuickPickItem { if (options.compact) { const item: CommitQuickPickItem = { - label: `${options.icon ? Strings.pad('$(git-commit)', 0, 2) : ''}${commit.getShortMessage()}`, - description: `${commit.author}, ${commit.formattedDate}${Strings.pad('$(git-commit)', 2, 2)}${ + label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.getShortMessage()}`, + description: `${commit.author}, ${commit.formattedDate}${pad('$(git-commit)', 2, 2)}${ commit.shortSha - }${Strings.pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ compact: true })}`, + }${pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ compact: true })}`, alwaysShow: options.alwaysShow, buttons: options.buttons, picked: picked, @@ -201,13 +200,13 @@ export namespace CommitQuickPickItem { } const item: CommitQuickPickItem = { - label: `${options.icon ? Strings.pad('$(git-commit)', 0, 2) : ''}${commit.getShortMessage()}`, + label: `${options.icon ? pad('$(git-commit)', 0, 2) : ''}${commit.getShortMessage()}`, description: '', - detail: `${GlyphChars.Space.repeat(2)}${commit.author}, ${commit.formattedDate}${Strings.pad( + detail: `${GlyphChars.Space.repeat(2)}${commit.author}, ${commit.formattedDate}${pad( '$(git-commit)', 2, 2, - )}${commit.shortSha}${Strings.pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ + )}${commit.shortSha}${pad(GlyphChars.Dot, 2, 2)}${commit.getFormattedDiffStatus({ compact: true, })}`, alwaysShow: options.alwaysShow, @@ -254,7 +253,7 @@ export namespace RefQuickPickItem { ): RefQuickPickItem { if (ref === '') { return { - label: `${options.icon ? Strings.pad('$(file-directory)', 0, 2) : ''}Working Tree`, + label: `${options.icon ? pad('$(file-directory)', 0, 2) : ''}Working Tree`, description: '', alwaysShow: options.alwaysShow, buttons: options.buttons, @@ -268,7 +267,7 @@ export namespace RefQuickPickItem { if (ref === 'HEAD') { return { - label: `${options.icon ? Strings.pad('$(git-branch)', 0, 2) : ''}HEAD`, + label: `${options.icon ? pad('$(git-branch)', 0, 2) : ''}HEAD`, description: '', alwaysShow: options.alwaysShow, buttons: options.buttons, @@ -349,7 +348,7 @@ export namespace RepositoryQuickPickItem { if (repoStatus.files.length !== 0) { workingStatus = repoStatus.getFormattedDiffStatus({ compact: true, - prefix: Strings.pad(GlyphChars.Dot, 2, 2), + prefix: pad(GlyphChars.Dot, 2, 2), }); } @@ -366,10 +365,8 @@ export namespace RepositoryQuickPickItem { if (options.fetched) { const lastFetched = await repository.getLastFetched(); if (lastFetched !== 0) { - const fetched = `Last fetched ${Dates.getFormatter(new Date(lastFetched)).fromNow()}`; - description = `${ - description ? `${description}${Strings.pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched - }`; + const fetched = `Last fetched ${fromNow(new Date(lastFetched))}`; + description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${fetched}` : fetched}`; } } @@ -412,22 +409,20 @@ export namespace TagQuickPickItem { } if (options.ref) { - description = `${description}${Strings.pad('$(git-commit)', description ? 2 : 0, 2)}${GitRevision.shorten( + description = `${description}${pad('$(git-commit)', description ? 2 : 0, 2)}${GitRevision.shorten( tag.sha, )}`; - description = `${description ? `${description}${Strings.pad(GlyphChars.Dot, 2, 2)}` : ''}${ - tag.formattedDate - }`; + description = `${description ? `${description}${pad(GlyphChars.Dot, 2, 2)}` : ''}${tag.formattedDate}`; } if (options.message) { const message = emojify(tag.message); - description = description ? `${description}${Strings.pad(GlyphChars.Dot, 2, 2)}${message}` : message; + description = description ? `${description}${pad(GlyphChars.Dot, 2, 2)}${message}` : message; } const item: TagQuickPickItem = { - label: `${Strings.pad('$(tag)', 0, 2)}${tag.name}${ + label: `${pad('$(tag)', 0, 2)}${tag.name}${ options.checked ? `${GlyphChars.Space.repeat(2)}$(check)${GlyphChars.Space}` : '' }`, description: description, diff --git a/src/system/date.ts b/src/system/date.ts index f52825f6ac587..62ff5ed84529c 100644 --- a/src/system/date.ts +++ b/src/system/date.ts @@ -25,114 +25,111 @@ export interface DateFormatter { format(format: DateTimeFormat | string | null | undefined): string; } -export function getFormatter(date: Date): DateFormatter { - return { - fromNow: function (short?: boolean) { - const elapsed = date.getTime() - new Date().getTime(); - - for (const [unit, threshold, shortUnit] of relativeUnitThresholds) { - const elapsedABS = Math.abs(elapsed); - if (elapsedABS >= threshold || threshold === 1000 /* second */) { - if (short) { - if (locale == null) { - if (defaultShortRelativeTimeFormat != null) { - locale = defaultShortRelativeTimeFormat.resolvedOptions().locale; - } else if (defaultRelativeTimeFormat != null) { - locale = defaultRelativeTimeFormat.resolvedOptions().locale; - } else { - defaultShortRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { - localeMatcher: 'best fit', - numeric: 'always', - style: 'narrow', - }); - locale = defaultShortRelativeTimeFormat.resolvedOptions().locale; - } - } - - if (locale === 'en' || locale?.startsWith('en-')) { - const value = Math.round(elapsedABS / threshold); - return `${value}${shortUnit}`; - } - - if (defaultShortRelativeTimeFormat == null) { - defaultShortRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { - localeMatcher: 'best fit', - numeric: 'always', - style: 'narrow', - }); - } - - return defaultShortRelativeTimeFormat.format(Math.round(elapsed / threshold), unit); - } - - if (defaultRelativeTimeFormat == null) { - defaultRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { +export function fromNow(date: Date, short?: boolean): string { + const elapsed = date.getTime() - new Date().getTime(); + + for (const [unit, threshold, shortUnit] of relativeUnitThresholds) { + const elapsedABS = Math.abs(elapsed); + if (elapsedABS >= threshold || threshold === 1000 /* second */) { + if (short) { + if (locale == null) { + if (defaultShortRelativeTimeFormat != null) { + locale = defaultShortRelativeTimeFormat.resolvedOptions().locale; + } else if (defaultRelativeTimeFormat != null) { + locale = defaultRelativeTimeFormat.resolvedOptions().locale; + } else { + defaultShortRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { localeMatcher: 'best fit', - numeric: 'auto', - style: 'long', + numeric: 'always', + style: 'narrow', }); + locale = defaultShortRelativeTimeFormat.resolvedOptions().locale; } - return defaultRelativeTimeFormat.format(Math.round(elapsed / threshold), unit); } - } - return ''; - }, - format: function (format: 'full' | 'long' | 'medium' | 'short' | string | null | undefined) { - format = format ?? undefined; - - let formatter = dateTimeFormatCache.get(format); - if (formatter == null) { - const options = getDateTimeFormatOptionsFromFormatString(format); - formatter = new Intl.DateTimeFormat(undefined, options); - dateTimeFormatCache.set(format, formatter); + if (locale === 'en' || locale?.startsWith('en-')) { + const value = Math.round(elapsedABS / threshold); + return `${value}${shortUnit}`; + } + + if (defaultShortRelativeTimeFormat == null) { + defaultShortRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { + localeMatcher: 'best fit', + numeric: 'always', + style: 'narrow', + }); + } + + return defaultShortRelativeTimeFormat.format(Math.round(elapsed / threshold), unit); } - if (format == null || dateTimeFormatRegex.test(format)) { - return formatter.format(date); + if (defaultRelativeTimeFormat == null) { + defaultRelativeTimeFormat = new Intl.RelativeTimeFormat(undefined, { + localeMatcher: 'best fit', + numeric: 'auto', + style: 'long', + }); } + return defaultRelativeTimeFormat.format(Math.round(elapsed / threshold), unit); + } + } - const parts = formatter.formatToParts(date); - return format.replace( - customDateTimeFormatParserRegex, - ( - _match, - literal, - _year, - _month, - _day, - _weekday, - _hour, - _minute, - _second, - _fractionalSecond, - _dayPeriod, - _timeZoneName, - _offset, - _s, - groups, - ) => { - if (literal != null) return (literal as string).substring(1, literal.length - 1); - - for (const key in groups) { - const value = groups[key]; - if (value == null) continue; - - const part = parts.find(p => p.type === key); - - if (value === 'Do' && part?.type === 'day') { - return formatWithOrdinal(Number(part.value)); - } else if (value === 'a' && part?.type === 'dayPeriod') { - return part.value.toLocaleLowerCase(); - } - return part?.value ?? ''; - } + return ''; +} + +export function formatDate(date: Date, format: 'full' | 'long' | 'medium' | 'short' | string | null | undefined) { + format = format ?? undefined; - return ''; - }, - ); + let formatter = dateTimeFormatCache.get(format); + if (formatter == null) { + const options = getDateTimeFormatOptionsFromFormatString(format); + formatter = new Intl.DateTimeFormat(undefined, options); + dateTimeFormatCache.set(format, formatter); + } + + if (format == null || dateTimeFormatRegex.test(format)) { + return formatter.format(date); + } + + const parts = formatter.formatToParts(date); + return format.replace( + customDateTimeFormatParserRegex, + ( + _match, + literal, + _year, + _month, + _day, + _weekday, + _hour, + _minute, + _second, + _fractionalSecond, + _dayPeriod, + _timeZoneName, + _offset, + _s, + groups, + ) => { + if (literal != null) return (literal as string).substring(1, literal.length - 1); + + for (const key in groups) { + const value = groups[key]; + if (value == null) continue; + + const part = parts.find(p => p.type === key); + + if (value === 'Do' && part?.type === 'day') { + return formatWithOrdinal(Number(part.value)); + } else if (value === 'a' && part?.type === 'dayPeriod') { + return part.value.toLocaleLowerCase(); + } + return part?.value ?? ''; + } + + return ''; }, - }; + ); } function getDateTimeFormatOptionsFromFormatString( diff --git a/src/views/nodes/autolinkedItemNode.ts b/src/views/nodes/autolinkedItemNode.ts index c1fda370f92a1..f0ad5d42340e0 100644 --- a/src/views/nodes/autolinkedItemNode.ts +++ b/src/views/nodes/autolinkedItemNode.ts @@ -1,7 +1,7 @@ import { MarkdownString, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { GitUri } from '../../git/gitUri'; import { GitFile, IssueOrPullRequest, IssueOrPullRequestType } from '../../git/models'; -import { Dates } from '../../system'; +import { fromNow } from '../../system/date'; import { ViewsWithCommits } from '../viewBase'; import { ContextValues, ViewNode } from './viewNode'; @@ -37,10 +37,10 @@ export class AutolinkedItemNode extends ViewNode { } getTreeItem(): TreeItem { - const formatter = Dates.getFormatter(this.issue.closedDate ?? this.issue.date); + const relativeTime = fromNow(this.issue.closedDate ?? this.issue.date); const item = new TreeItem(`${this.issue.id}: ${this.issue.title}`, TreeItemCollapsibleState.None); - item.description = formatter.fromNow(); + item.description = relativeTime; item.iconPath = IssueOrPullRequest.getThemeIcon(this.issue); item.contextValue = this.issue.type === IssueOrPullRequestType.PullRequest @@ -55,7 +55,7 @@ export class AutolinkedItemNode extends ViewNode { this.issue.url }${linkTitle}) \\\n[#${this.issue.id}](${this.issue.url}${linkTitle}) was ${ this.issue.closed ? 'closed' : 'opened' - } ${formatter.fromNow()}`, + } ${relativeTime}`, true, ); tooltip.supportHtml = true; diff --git a/src/views/nodes/branchTrackingStatusNode.ts b/src/views/nodes/branchTrackingStatusNode.ts index 234a03801bb0a..bdfcb118b60eb 100644 --- a/src/views/nodes/branchTrackingStatusNode.ts +++ b/src/views/nodes/branchTrackingStatusNode.ts @@ -2,7 +2,11 @@ import { MarkdownString, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleSta import { Colors } from '../../constants'; import { GitUri } from '../../git/gitUri'; import { GitBranch, GitLog, GitRemote, GitRevision, GitTrackingState } from '../../git/models'; -import { Dates, debug, gate, Iterables, Strings } from '../../system'; +import { fromNow } from '../../system/date'; +import { gate } from '../../system/decorators/gate'; +import { debug } from '../../system/decorators/log'; +import { first, map } from '../../system/iterable'; +import { pluralize } from '../../system/string'; import { ViewsWithCommits } from '../viewBase'; import { BranchNode } from './branchNode'; import { BranchTrackingStatusFilesNode } from './branchTrackingStatusFilesNode'; @@ -82,7 +86,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme ref: commit.sha, }); if (previousLog != null) { - commits[commits.length - 1] = Iterables.first(previousLog.commits.values()); + commits[commits.length - 1] = first(previousLog.commits.values()); } } } else { @@ -113,10 +117,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme } else { children.push( ...insertDateMarkers( - Iterables.map( - commits, - c => new CommitNode(this.view, this, c, this.upstreamType === 'ahead', this.branch), - ), + map(commits, c => new CommitNode(this.view, this, c, this.upstreamType === 'ahead', this.branch)), this, 1, ), @@ -166,12 +167,10 @@ export class BranchTrackingStatusNode extends ViewNode impleme label = `Changes to push to ${remote?.name ?? GitBranch.getRemote(this.status.upstream!)}${ remote?.provider?.name ? ` on ${remote?.provider.name}` : '' }`; - description = Strings.pluralize('commit', this.status.state.ahead); - tooltip = `Branch $(git-branch) ${this.branch.name} is ${Strings.pluralize( - 'commit', - this.status.state.ahead, - { infix: '$(arrow-up) ' }, - )} ahead of $(git-branch) ${this.status.upstream}${ + description = pluralize('commit', this.status.state.ahead); + tooltip = `Branch $(git-branch) ${this.branch.name} is ${pluralize('commit', this.status.state.ahead, { + infix: '$(arrow-up) ', + })} ahead of $(git-branch) ${this.status.upstream}${ remote?.provider?.name ? ` on ${remote.provider.name}` : '' }`; @@ -189,12 +188,10 @@ export class BranchTrackingStatusNode extends ViewNode impleme label = `Changes to pull from ${remote?.name ?? GitBranch.getRemote(this.status.upstream!)}${ remote?.provider?.name ? ` on ${remote.provider.name}` : '' }`; - description = Strings.pluralize('commit', this.status.state.behind); - tooltip = `Branch $(git-branch) ${this.branch.name} is ${Strings.pluralize( - 'commit', - this.status.state.behind, - { infix: '$(arrow-down) ' }, - )} behind $(git-branch) ${this.status.upstream}${ + description = pluralize('commit', this.status.state.behind); + tooltip = `Branch $(git-branch) ${this.branch.name} is ${pluralize('commit', this.status.state.behind, { + infix: '$(arrow-down) ', + })} behind $(git-branch) ${this.status.upstream}${ remote?.provider?.name ? ` on ${remote.provider.name}` : '' }`; @@ -212,7 +209,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme label = `Up to date with ${remote?.name ?? GitBranch.getRemote(this.status.upstream!)}${ remote?.provider?.name ? ` on ${remote.provider.name}` : '' }`; - description = lastFetched ? `Last fetched ${Dates.getFormatter(new Date(lastFetched)).fromNow()}` : ''; + description = lastFetched ? `Last fetched ${fromNow(new Date(lastFetched))}` : ''; tooltip = `Branch $(git-branch) ${this.branch.name} is up to date with $(git-branch) ${ this.status.upstream }${remote?.provider?.name ? ` on ${remote.provider.name}` : ''}`; @@ -251,7 +248,7 @@ export class BranchTrackingStatusNode extends ViewNode impleme item.contextValue = contextValue; item.description = description; if (lastFetched) { - tooltip += `\n\nLast fetched ${Dates.getFormatter(new Date(lastFetched)).fromNow()}`; + tooltip += `\n\nLast fetched ${fromNow(new Date(lastFetched))}`; } item.iconPath = icon; diff --git a/src/webviews/apps/shared/appWithConfigBase.ts b/src/webviews/apps/shared/appWithConfigBase.ts index 35f52223b8deb..e5b73a20210ee 100644 --- a/src/webviews/apps/shared/appWithConfigBase.ts +++ b/src/webviews/apps/shared/appWithConfigBase.ts @@ -8,13 +8,13 @@ import { PreviewConfigurationCommandType, UpdateConfigurationCommandType, } from '../../protocol'; -import { getFormatter } from '../shared/date'; +import { formatDate } from '../shared/date'; import { App } from './appBase'; import { DOM } from './dom'; const offset = (new Date().getTimezoneOffset() / 60) * 100; -const dateFormatter = getFormatter( - new Date(`Wed Jul 25 2018 19:18:00 GMT${offset >= 0 ? '-' : '+'}${String(Math.abs(offset)).padStart(4, '0')}`), +const date = new Date( + `Wed Jul 25 2018 19:18:00 GMT${offset >= 0 ? '-' : '+'}${String(Math.abs(offset)).padStart(4, '0')}`, ); let ipcSequence = 0; @@ -419,7 +419,7 @@ export abstract class AppWithConfig extends A value = el.dataset.settingPreviewDefault; } - el.innerText = value == null ? '' : dateFormatter.format(value); + el.innerText = value == null ? '' : formatDate(date, value); break; } case 'commit': {