diff --git a/src/annotations/gutterBlameAnnotationProvider.ts b/src/annotations/gutterBlameAnnotationProvider.ts index 208302a..a6e2856 100644 --- a/src/annotations/gutterBlameAnnotationProvider.ts +++ b/src/annotations/gutterBlameAnnotationProvider.ts @@ -215,7 +215,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { height: '16px', width: '16px', textDecoration: `none;position:absolute;top:1px;left:5px;background:url(${( - await commit.getAvatarUri(true, { fallback: gravatarDefault }) + await commit.getAvatarUri({ fallback: gravatarDefault }) ).toString()});background-size:16px 16px`, }; map.set(commit.email!, avatarDecoration); diff --git a/src/avatars.ts b/src/avatars.ts index 02aa31b..cfe1276 100644 --- a/src/avatars.ts +++ b/src/avatars.ts @@ -21,7 +21,7 @@ interface Avatar { type SerializedAvatar = Avatar; let avatarCache: Map | undefined; -const avatarQueue = new Map | null>(); +const avatarQueue = new Map>(); const missingGravatarHash = '00000000000000000000000000000000'; @@ -47,7 +47,7 @@ onDidFetchAvatar( }, ]), ); - }, 5000), + }, 1000), ); export function clearAvatarCache() { @@ -72,39 +72,10 @@ function ensureAvatarCache(cache: Map | undefined): asserts cach } } -function getAvatarUriFromGitHubNoReplyAddress(email: string | undefined, size: number = 16): Uri | undefined { - if (email == null || email.length === 0) return undefined; - - const match = gitHubNoReplyAddressRegex.exec(email); - if (match == null) return undefined; - - const [, userId, userName] = match; - return Uri.parse(`https://avatars.githubusercontent.com/${userId ? `u/${userId}` : userName}?size=${size}`); -} - -export function getAvatarUri( - email: string | undefined, - repoPathOrCommit: string | GitRevisionReference | undefined, - wait: false, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, -): Uri; export function getAvatarUri( email: string | undefined, repoPathOrCommit: string | GitRevisionReference | undefined, - wait: true, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, -): Promise; -export function getAvatarUri( - email: string | undefined, - repoPathOrCommit: string | GitRevisionReference | undefined, - wait: boolean, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, -): Uri | Promise; -export function getAvatarUri( - email: string | undefined, - repoPathOrCommit: string | GitRevisionReference | undefined, - wait: boolean, - { fallback, listener, size = 16 }: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number } = {}, + { fallback, size = 16 }: { fallback?: GravatarDefaultStyle; size?: number } = {}, ): Uri | Promise { ensureAvatarCache(avatarCache); @@ -131,32 +102,37 @@ export function getAvatarUri( let avatar = avatarCache.get(key); if (avatar == null) { avatar = { - fallback: - getAvatarUriFromGitHubNoReplyAddress(email, size) ?? - Uri.parse(`https://www.gravatar.com/avatar/${hash}.jpg?s=${size}&d=${fallback}`), + uri: getAvatarUriFromGitHubNoReplyAddress(email, size), + fallback: Uri.parse(`https://www.gravatar.com/avatar/${hash}.jpg?s=${size}&d=${fallback}`), timestamp: Date.now(), }; avatarCache.set(key, avatar); } + if (avatar.uri != null) return avatar.uri; + let query = avatarQueue.get(key); if (query == null && avatar.uri === undefined && repoPathOrCommit != null) { - query = getRemoteProviderAvatarUri(key, email, repoPathOrCommit, avatar.fallback, { size: size }); + query = getAvatarUriFromRemoteProvider(key, email, repoPathOrCommit, avatar.fallback, { size: size }).then( + uri => uri ?? avatar!.uri ?? avatar!.fallback, + ); avatarQueue.set(key, query); - - void signalOnAvatarQueryComplete(email, query, listener, true); - } else if (query != null) { - void signalOnAvatarQueryComplete(email, query, listener, false); } - if (wait && query != null) { - return query.then(value => value ?? avatar!.uri ?? avatar!.fallback); - } + if (query != null) return query; return avatar.uri ?? avatar.fallback; } -async function getRemoteProviderAvatarUri( +function getAvatarUriFromGitHubNoReplyAddress(email: string, size: number = 16): Uri | undefined { + const match = gitHubNoReplyAddressRegex.exec(email); + if (match == null) return undefined; + + const [, userId, userName] = match; + return Uri.parse(`https://avatars.githubusercontent.com/${userId ? `u/${userId}` : userName}?size=${size}`); +} + +async function getAvatarUriFromRemoteProvider( key: string, email: string, repoPathOrCommit: string | GitRevisionReference, @@ -191,6 +167,8 @@ async function getRemoteProviderAvatarUri( }); } + _onDidFetchAvatar.fire({ email: email }); + return uri; } catch { avatarCache.set(key, { uri: null, fallback: fallback, timestamp: Date.now() }); @@ -201,32 +179,6 @@ async function getRemoteProviderAvatarUri( } } -async function signalOnAvatarQueryComplete( - email: string, - query: Promise, - listener: (() => void) | undefined, - fire: boolean, -) { - if (listener == null) { - if (fire) { - _onDidFetchAvatar.fire({ email: email }); - } - - return; - } - - const disposable = onDidFetchAvatar(listener); - try { - await query; - - if (fire) { - _onDidFetchAvatar.fire({ email: email }); - } - } finally { - disposable.dispose(); - } -} - export function getPresenceDataUri(status: ContactPresenceStatus) { let dataUri = presenceCache.get(status); if (dataUri == null) { diff --git a/src/git/formatters/commitFormatter.ts b/src/git/formatters/commitFormatter.ts index 5d1d279..4836d5e 100644 --- a/src/git/formatters/commitFormatter.ts +++ b/src/git/formatters/commitFormatter.ts @@ -54,8 +54,10 @@ export interface CommitFormatOptions extends FormatOptions { authorAgoOrDateShort?: Strings.TokenOptions; authorDate?: Strings.TokenOptions; authorNotYou?: Strings.TokenOptions; + avatar?: Strings.TokenOptions; changes?: Strings.TokenOptions; changesShort?: Strings.TokenOptions; + commands?: Strings.TokenOptions; committerAgo?: Strings.TokenOptions; committerAgoOrDate?: Strings.TokenOptions; committerAgoOrDateShort?: Strings.TokenOptions; @@ -70,6 +72,7 @@ export interface CommitFormatOptions extends FormatOptions { pullRequestAgoOrDate?: Strings.TokenOptions; pullRequestDate?: Strings.TokenOptions; pullRequestState?: Strings.TokenOptions; + sha?: Strings.TokenOptions; tips?: Strings.TokenOptions; }; } @@ -130,11 +133,11 @@ export class CommitFormatter extends Formatter { return dateStyle === DateStyle.Absolute ? this._pullRequestDate : this._pullRequestDateAgo; } - get ago() { + get ago(): string { return this._padOrTruncate(this._dateAgo, this._options.tokenOptions.ago); } - get agoOrDate() { + get agoOrDate(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._date : this._dateAgo, @@ -142,7 +145,7 @@ export class CommitFormatter extends Formatter { ); } - get agoOrDateShort() { + get agoOrDateShort(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._date : this._dateAgoShort, @@ -150,7 +153,7 @@ export class CommitFormatter extends Formatter { ); } - get author() { + get author(): string { const author = this._padOrTruncate(this._item.author, this._options.tokenOptions.author); if (!this._options.markdown) { return author; @@ -159,22 +162,11 @@ export class CommitFormatter extends Formatter { return `[${author}](mailto:${this._item.email} "Email ${this._item.author} (${this._item.email})")`; } - get authorNotYou() { - if (this._item.author === 'You') return emptyStr; - - const author = this._padOrTruncate(this._item.author, this._options.tokenOptions.authorNotYou); - if (!this._options.markdown) { - return author; - } - - return `[${author}](mailto:${this._item.email} "Email ${this._item.author} (${this._item.email})")`; - } - - get authorAgo() { + get authorAgo(): string { return this._padOrTruncate(this._authorDateAgo, this._options.tokenOptions.authorAgo); } - get authorAgoOrDate() { + get authorAgoOrDate(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._authorDate : this._authorDateAgo, @@ -182,7 +174,7 @@ export class CommitFormatter extends Formatter { ); } - get authorAgoOrDateShort() { + get authorAgoOrDateShort(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._authorDate : this._authorDateAgoShort, @@ -190,13 +182,24 @@ export class CommitFormatter extends Formatter { ); } - get authorDate() { + get authorDate(): string { return this._padOrTruncate(this._authorDate, this._options.tokenOptions.authorDate); } - get avatar() { + get authorNotYou(): string { + if (this._item.author === 'You') return emptyStr; + + const author = this._padOrTruncate(this._item.author, this._options.tokenOptions.authorNotYou); + if (!this._options.markdown) { + return author; + } + + return `[${author}](mailto:${this._item.email} "Email ${this._item.author} (${this._item.email})")`; + } + + get avatar(): string | Promise { if (!this._options.markdown || !Container.config.hovers.avatars) { - return emptyStr; + return this._padOrTruncate(emptyStr, this._options.tokenOptions.avatar); } const presence = this._options.presence; @@ -205,31 +208,42 @@ export class CommitFormatter extends Formatter { presence.status === 'dnd' ? 'in ' : emptyStr }${presence.statusText.toLocaleLowerCase()}`; - return `${this._getAvatarMarkdown(title)}${this._getPresenceMarkdown(presence, title)}`; + const avatarMarkdownPromise = this._getAvatarMarkdown(title); + return avatarMarkdownPromise.then(md => + this._padOrTruncate( + `${md}${this._getPresenceMarkdown(presence, title)}`, + this._options.tokenOptions.avatar, + ), + ); } return this._getAvatarMarkdown(this._item.author); } - private _getAvatarMarkdown(title: string) { + private async _getAvatarMarkdown(title: string) { const size = Container.config.hovers.avatarSize; - return `![${title}](${this._item - .getAvatarUri(false, { fallback: Container.config.defaultGravatarsStyle, size: size }) - .toString(true)}|width=${size},height=${size} "${title}")`; + const avatarPromise = this._item.getAvatarUri({ + fallback: Container.config.defaultGravatarsStyle, + size: size, + }); + return this._padOrTruncate( + `![${title}](${(await avatarPromise).toString(true)}|width=${size},height=${size} "${title}")`, + this._options.tokenOptions.avatar, + ); } private _getPresenceMarkdown(presence: ContactPresence, title: string) { return `![${title}](${getPresenceDataUri(presence.status)} "${title}")`; } - get changes() { + get changes(): string { return this._padOrTruncate( GitLogCommit.is(this._item) ? this._item.getFormattedDiffStatus() : emptyStr, this._options.tokenOptions.changes, ); } - get changesShort() { + get changesShort(): string { return this._padOrTruncate( GitLogCommit.is(this._item) ? this._item.getFormattedDiffStatus({ compact: true, separator: emptyStr }) @@ -238,8 +252,8 @@ export class CommitFormatter extends Formatter { ); } - get commands() { - if (!this._options.markdown) return emptyStr; + get commands(): string { + if (!this._options.markdown) return this._padOrTruncate(emptyStr, this._options.tokenOptions.commands); let commands; if (this._item.isUncommitted) { @@ -251,7 +265,7 @@ export class CommitFormatter extends Formatter { ? diffUris.current.sha : GitRevision.uncommitted, )!, - this._options.tokenOptions.id, + this._options.tokenOptions.commands, )}\``; commands += `  **[\`${GlyphChars.MuchLessThan}\`](${DiffWithCommand.getMarkdownCommandArgs({ @@ -271,7 +285,7 @@ export class CommitFormatter extends Formatter { GitRevision.shorten( this._item.isUncommittedStaged ? GitRevision.uncommittedStaged : GitRevision.uncommitted, )!, - this._options.tokenOptions.id, + this._options.tokenOptions.commands, )}\``; } @@ -340,14 +354,14 @@ export class CommitFormatter extends Formatter { revisionUri: GitUri.toRevisionUri(this._item.toGitUri()).toString(true), })} "Show More Actions")`; - return commands; + return this._padOrTruncate(commands, this._options.tokenOptions.commands); } - get committerAgo() { + get committerAgo(): string { return this._padOrTruncate(this._committerDateAgo, this._options.tokenOptions.committerAgo); } - get committerAgoOrDate() { + get committerAgoOrDate(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._committerDate : this._committerDateAgo, @@ -355,7 +369,7 @@ export class CommitFormatter extends Formatter { ); } - get committerAgoOrDateShort() { + get committerAgoOrDateShort(): string { const dateStyle = this._options.dateStyle != null ? this._options.dateStyle : Container.config.defaultDateStyle; return this._padOrTruncate( dateStyle === DateStyle.Absolute ? this._committerDate : this._committerDateAgoShort, @@ -363,35 +377,38 @@ export class CommitFormatter extends Formatter { ); } - get committerDate() { + get committerDate(): string { return this._padOrTruncate(this._committerDate, this._options.tokenOptions.committerDate); } - get date() { + get date(): string { return this._padOrTruncate(this._date, this._options.tokenOptions.date); } - get email() { + get email(): string { return this._padOrTruncate(this._item.email ?? emptyStr, this._options.tokenOptions.email); } - get footnotes() { - if (this._options.footnotes == null || this._options.footnotes.size === 0) return ''; - + get footnotes(): string { return this._padOrTruncate( - Iterables.join( - Iterables.map(this._options.footnotes, ([i, footnote]) => `${Strings.getSuperscript(i)} ${footnote}`), - '\n', - ), + this._options.footnotes == null || this._options.footnotes.size === 0 + ? emptyStr + : Iterables.join( + Iterables.map( + this._options.footnotes, + ([i, footnote]) => `${Strings.getSuperscript(i)} ${footnote}`, + ), + '\n', + ), this._options.tokenOptions.footnotes, ); } - get id() { + get id(): string { return this._padOrTruncate(this._item.shortSha ?? emptyStr, this._options.tokenOptions.id); } - get message() { + get message(): string { if (this._item.isUncommitted) { const staged = this._item.isUncommittedStaged || @@ -431,9 +448,9 @@ export class CommitFormatter extends Formatter { return this._options.markdown ? `\n> ${message}` : message; } - get pullRequest() { + get pullRequest(): string { const { pullRequestOrRemote: pr } = this._options; - if (pr == null) return emptyStr; + if (pr == null) return this._padOrTruncate(emptyStr, this._options.tokenOptions.pullRequest); let text; if (PullRequest.is(pr)) { @@ -463,34 +480,33 @@ export class CommitFormatter extends Formatter { return this._padOrTruncate(text, this._options.tokenOptions.pullRequest); } - get pullRequestAgo() { + get pullRequestAgo(): string { return this._padOrTruncate(this._pullRequestDateAgo, this._options.tokenOptions.pullRequestAgo); } - get pullRequestAgoOrDate() { + get pullRequestAgoOrDate(): string { return this._padOrTruncate(this._pullRequestDateOrAgo, this._options.tokenOptions.pullRequestAgoOrDate); } - get pullRequestDate() { + get pullRequestDate(): string { return this._padOrTruncate(this._pullRequestDate, this._options.tokenOptions.pullRequestDate); } - get pullRequestState() { + get pullRequestState(): string { const { pullRequestOrRemote: pr } = this._options; - if (pr == null || !PullRequest.is(pr)) return emptyStr; - - return this._padOrTruncate(pr.state ?? emptyStr, this._options.tokenOptions.pullRequestState); + return this._padOrTruncate( + pr == null || !PullRequest.is(pr) ? emptyStr : pr.state ?? emptyStr, + this._options.tokenOptions.pullRequestState, + ); } - get sha() { - return this.id; + get sha(): string { + return this._padOrTruncate(this._item.shortSha ?? emptyStr, this._options.tokenOptions.sha); } - get tips() { + get tips(): string { const branchAndTagTips = this._options.getBranchAndTagTips?.(this._item.sha); - if (branchAndTagTips == null) return emptyStr; - - return this._padOrTruncate(branchAndTagTips, this._options.tokenOptions.tips); + return this._padOrTruncate(branchAndTagTips ?? emptyStr, this._options.tokenOptions.tips); } static fromTemplate(template: string, commit: GitCommit, dateFormat: string | null): string; @@ -505,6 +521,39 @@ export class CommitFormatter extends Formatter { commit: GitCommit, dateFormatOrOptions?: string | null | CommitFormatOptions, ): string { + if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') { + dateFormatOrOptions = { + dateFormat: dateFormatOrOptions, + }; + } + + if (CommitFormatter.has(template, 'footnotes')) { + if (dateFormatOrOptions.footnotes == null) { + dateFormatOrOptions.footnotes = new Map(); + } + } + + if (CommitFormatter.has(template, 'avatar') && dateFormatOrOptions?.markdown) { + // eslint-disable-next-line no-debugger + debugger; + throw new Error("Invalid template token 'avatar' used in non-async call"); + } + + return super.fromTemplateCore(this, template, commit, dateFormatOrOptions); + } + + static fromTemplateAsync(template: string, commit: GitCommit, dateFormat: string | null): Promise; + static fromTemplateAsync(template: string, commit: GitCommit, options?: CommitFormatOptions): Promise; + static fromTemplateAsync( + template: string, + commit: GitCommit, + dateFormatOrOptions?: string | null | CommitFormatOptions, + ): Promise; + static fromTemplateAsync( + template: string, + commit: GitCommit, + dateFormatOrOptions?: string | null | CommitFormatOptions, + ): Promise { if (CommitFormatter.has(template, 'footnotes')) { if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') { dateFormatOrOptions = { @@ -517,7 +566,7 @@ export class CommitFormatter extends Formatter { } } - return super.fromTemplateCore(this, template, commit, dateFormatOrOptions); + return super.fromTemplateCoreAsync(this, template, commit, dateFormatOrOptions); } static has(template: string, ...tokens: (keyof NonNullable)[]): boolean { diff --git a/src/git/formatters/formatter.ts b/src/git/formatters/formatter.ts index 74c066f..0b6c7f9 100644 --- a/src/git/formatters/formatter.ts +++ b/src/git/formatters/formatter.ts @@ -136,6 +136,50 @@ export abstract class Formatter, + Item, + Options extends FormatOptions + >( + formatter: TFormatter | Constructor, + template: string, + item: Item, + dateFormatOrOptions?: string | null | Options, + ): Promise { + // Preserve spaces + template = template.replace(spaceReplacementRegex, '\u00a0'); + if (formatter instanceof Formatter) return Strings.interpolateAsync(template, formatter); + + let options: Options | undefined = undefined; + if (dateFormatOrOptions == null || typeof dateFormatOrOptions === 'string') { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + options = { + dateFormat: dateFormatOrOptions, + } as Options; + } else { + options = dateFormatOrOptions; + } + + if (options.tokenOptions == null) { + const tokenOptions = Strings.getTokensFromTemplate(template).reduce<{ + [token: string]: Strings.TokenOptions | undefined; + }>((map, token) => { + map[token.key] = token.options; + return map; + }, Object.create(null)); + + options.tokenOptions = tokenOptions; + } + + if (this._formatter === undefined) { + this._formatter = new formatter(item, options); + } else { + this._formatter.reset(item, options); + } + + return Strings.interpolateAsync(template, this._formatter); + } + static has( template: string, ...tokens: (keyof NonNullable)[] diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index dbd8700..6878f17 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -225,16 +225,8 @@ export abstract class GitCommit implements GitRevisionReference { return GitUri.getFormattedPath(this.fileName, options); } - getAvatarUri(wait: false, options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }): Uri; - getAvatarUri( - wait: true, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, - ): Promise; - getAvatarUri( - wait: boolean, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, - ): Uri | Promise { - return getAvatarUri(this.email, this, wait, options); + getAvatarUri(options?: { fallback?: GravatarDefaultStyle; size?: number }): Uri | Promise { + return getAvatarUri(this.email, this, options); } @memoize() diff --git a/src/git/models/contributor.ts b/src/git/models/contributor.ts index bbee8e6..f908505 100644 --- a/src/git/models/contributor.ts +++ b/src/git/models/contributor.ts @@ -20,16 +20,8 @@ export class GitContributor { public readonly current: boolean = false, ) {} - getAvatarUri(wait: false, options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }): Uri; - getAvatarUri( - wait: true, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, - ): Promise; - getAvatarUri( - wait: boolean, - options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number }, - ): Uri | Promise { - return getAvatarUri(this.email, undefined /*this.repoPath*/, wait, options); + getAvatarUri(options?: { fallback?: GravatarDefaultStyle; size?: number }): Uri | Promise { + return getAvatarUri(this.email, undefined /*this.repoPath*/, options); } toCoauthor(): string { diff --git a/src/hovers/hovers.ts b/src/hovers/hovers.ts index ed196de..3e78b7a 100644 --- a/src/hovers/hovers.ts +++ b/src/hovers/hovers.ts @@ -196,7 +196,7 @@ export namespace Hovers { Container.vsls.maybeGetPresence(commit.email).catch(() => undefined), ]); - const details = CommitFormatter.fromTemplate(Container.config.hovers.detailsMarkdownFormat, commit, { + const details = await CommitFormatter.fromTemplateAsync(Container.config.hovers.detailsMarkdownFormat, commit, { autolinkedIssuesOrPullRequests: autolinkedIssuesOrPullRequests, dateFormat: dateFormat, line: editorLine, diff --git a/src/system/string.ts b/src/system/string.ts index 4b678ad..9fed688 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -77,8 +77,6 @@ const pathNormalizeRegex = /\\/g; const pathStripTrailingSlashRegex = /\/$/g; const tokenRegex = /\$\{(".*?"|\W*)?([^|]*?)(?:\|(\d+)(-|\?)?)?(".*?"|\W*)?\}/g; const tokenSanitizeRegex = /\$\{(?:".*?"|\W*)?(\w*?)(?:".*?"|[\W\d]*)\}/g; -// eslint-disable-next-line no-template-curly-in-string -const tokenSanitizeReplacement = "$${this.$1 ?? ''}"; export interface TokenOptions { collapseWhitespace: boolean; @@ -115,6 +113,8 @@ export function getTokensFromTemplate(template: string) { return tokens; } +// eslint-disable-next-line no-template-curly-in-string +const tokenSanitizeReplacement = "$${this.$1 ?? ''}"; const interpolationMap = new Map(); export function interpolate(template: string, context: object | undefined): string { @@ -131,6 +131,33 @@ export function interpolate(template: string, context: object | undefined): stri return fn.call(context); } +// eslint-disable-next-line prefer-arrow-callback +const AsyncFunction = Object.getPrototypeOf(async function () { + /* noop */ +}).constructor; + +const tokenSanitizeReplacementAsync = + // eslint-disable-next-line no-template-curly-in-string + "$${$1=this.$1,($1 != null && typeof $1.then === 'function' ? await $1 : $1) ?? ''}"; + +const interpolationAsyncMap = new Map(); + +export async function interpolateAsync(template: string, context: object | undefined): Promise { + if (template == null || template.length === 0) return template; + if (context == null) return template.replace(tokenSanitizeRegex, emptyStr); + + let fn = interpolationAsyncMap.get(template); + if (fn == null) { + // // eslint-disable-next-line @typescript-eslint/no-implied-eval + const body = `return \`${template.replace(tokenSanitizeRegex, tokenSanitizeReplacementAsync)}\`;`; + fn = new AsyncFunction(body); + interpolationAsyncMap.set(template, fn); + } + + const value = await fn.call(context); + return value; +} + export function* lines(s: string): IterableIterator { let i = 0; while (i < s.length) { diff --git a/src/views/nodes/commitFileNode.ts b/src/views/nodes/commitFileNode.ts index 0c42316..f882b35 100644 --- a/src/views/nodes/commitFileNode.ts +++ b/src/views/nodes/commitFileNode.ts @@ -90,7 +90,7 @@ export class CommitFileNode extends ViewRefFileNode { if (!this.commit.isUncommitted && !(this.view instanceof StashesView) && this.view.config.avatars) { item.iconPath = this._options.unpublished ? new ThemeIcon('arrow-up') - : await this.commit.getAvatarUri(true, { fallback: Container.config.defaultGravatarsStyle }); + : await this.commit.getAvatarUri({ fallback: Container.config.defaultGravatarsStyle }); } } diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts index 616eec5..5611753 100644 --- a/src/views/nodes/commitNode.ts +++ b/src/views/nodes/commitNode.ts @@ -121,7 +121,7 @@ export class CommitNode extends ViewRefNode