ソースを参照

Improves avatar handling & querying

main
Eric Amodio 4年前
コミット
2603c3a382
12個のファイルの変更219行の追加165行の削除
  1. +1
    -1
      src/annotations/gutterBlameAnnotationProvider.ts
  2. +22
    -70
      src/avatars.ts
  3. +113
    -64
      src/git/formatters/commitFormatter.ts
  4. +44
    -0
      src/git/formatters/formatter.ts
  5. +2
    -10
      src/git/models/commit.ts
  6. +2
    -10
      src/git/models/contributor.ts
  7. +1
    -1
      src/hovers/hovers.ts
  8. +29
    -2
      src/system/string.ts
  9. +1
    -1
      src/views/nodes/commitFileNode.ts
  10. +1
    -1
      src/views/nodes/commitNode.ts
  11. +1
    -3
      src/views/nodes/contributorNode.ts
  12. +2
    -2
      src/webviews/rebaseEditor.ts

+ 1
- 1
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);

+ 22
- 70
src/avatars.ts ファイルの表示

@ -21,7 +21,7 @@ interface Avatar {
type SerializedAvatar = Avatar<string>;
let avatarCache: Map<string, Avatar> | undefined;
const avatarQueue = new Map<string, Promise<Uri | undefined> | null>();
const avatarQueue = new Map<string, Promise<Uri>>();
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<Uri>;
export function getAvatarUri(
email: string | undefined,
repoPathOrCommit: string | GitRevisionReference | undefined,
wait: boolean,
options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number },
): Uri | Promise<Uri>;
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<Uri> {
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<Uri | undefined>,
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) {

+ 113
- 64
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() class="o">: string {
return this._padOrTruncate(this._dateAgo, this._options.tokenOptions.ago);
}
get agoOrDate() {
get agoOrDate() class="o">: 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() class="o">: 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() class="o">: 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() class="o">: 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() class="o">: 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<string> {
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() class="o">: string {
return this._padOrTruncate(
GitLogCommit.is(this._item) ? this._item.getFormattedDiffStatus() : emptyStr,
this._options.tokenOptions.changes,
);
}
get changesShort() {
get changesShort() class="o">: 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() class="o">: 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 += `&nbsp; **[\`${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() class="o">: string {
return this._padOrTruncate(this._committerDateAgo, this._options.tokenOptions.committerAgo);
}
get committerAgoOrDate() {
get committerAgoOrDate() class="o">: 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() class="o">: 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() class="o">: string {
return this._padOrTruncate(this._committerDate, this._options.tokenOptions.committerDate);
}
get date() {
get date() class="o">: string {
return this._padOrTruncate(this._date, this._options.tokenOptions.date);
}
get email() {
get email() class="o">: 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() class="o">: string {
return this._padOrTruncate(this._item.shortSha ?? emptyStr, this._options.tokenOptions.id);
}
get message() {
get message() class="o">: 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() class="o">: 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() class="o">: string {
return this._padOrTruncate(this._pullRequestDateAgo, this._options.tokenOptions.pullRequestAgo);
}
get pullRequestAgoOrDate() {
get pullRequestAgoOrDate() class="o">: string {
return this._padOrTruncate(this._pullRequestDateOrAgo, this._options.tokenOptions.pullRequestAgoOrDate);
}
get pullRequestDate() {
get pullRequestDate() class="o">: string {
return this._padOrTruncate(this._pullRequestDate, this._options.tokenOptions.pullRequestDate);
}
get pullRequestState() {
get pullRequestState() class="o">: 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() class="o">: string {
return this._padOrTruncate(this._item.shortSha ?? emptyStr, this._options.tokenOptions.sha);
}
get tips() {
get tips() class="o">: 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<number, string>();
}
}
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<string>;
static fromTemplateAsync(template: string, commit: GitCommit, options?: CommitFormatOptions): Promise<string>;
static fromTemplateAsync(
template: string,
commit: GitCommit,
dateFormatOrOptions?: string | null | CommitFormatOptions,
): Promise<string>;
static fromTemplateAsync(
template: string,
commit: GitCommit,
dateFormatOrOptions?: string | null | CommitFormatOptions,
): Promise<string> {
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<CommitFormatOptions['tokenOptions']>)[]): boolean {

+ 44
- 0
src/git/formatters/formatter.ts ファイルの表示

@ -136,6 +136,50 @@ export abstract class Formatter
return Strings.interpolate(template, this._formatter);
}
protected static fromTemplateCoreAsync<
TFormatter extends Formatter<Item, Options>,
Item,
Options extends FormatOptions
>(
formatter: TFormatter | Constructor<TFormatter>,
template: string,
item: Item,
dateFormatOrOptions?: string | null | Options,
): Promise<string> {
// 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<TOptions extends FormatOptions>(
template: string,
...tokens: (keyof NonNullable<TOptions['tokenOptions']>)[]

+ 2
- 10
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<Uri>;
getAvatarUri(
wait: boolean,
options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number },
): Uri | Promise<Uri> {
return getAvatarUri(this.email, this, wait, options);
getAvatarUri(options?: { fallback?: GravatarDefaultStyle; size?: number }): Uri | Promise<Uri> {
return getAvatarUri(this.email, this, options);
}
@memoize()

+ 2
- 10
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<Uri>;
getAvatarUri(
wait: boolean,
options?: { fallback?: GravatarDefaultStyle; listener?: () => void; size?: number },
): Uri | Promise<Uri> {
return getAvatarUri(this.email, undefined /*this.repoPath*/, wait, options);
getAvatarUri(options?: { fallback?: GravatarDefaultStyle; size?: number }): Uri | Promise<Uri> {
return getAvatarUri(this.email, undefined /*this.repoPath*/, options);
}
toCoauthor(): string {

+ 1
- 1
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,

+ 29
- 2
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<string, Function>();
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<string, typeof AsyncFunction>();
export async function interpolateAsync(template: string, context: object | undefined): Promise<string> {
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<string> {
let i = 0;
while (i < s.length) {

+ 1
- 1
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 });
}
}

+ 1
- 1
src/views/nodes/commitNode.ts ファイルの表示

@ -121,7 +121,7 @@ export class CommitNode extends ViewRefNode
item.iconPath = this.unpublished
? new ThemeIcon('arrow-up')
: !(this.view instanceof StashesView) && this.view.config.avatars
? await this.commit.getAvatarUri(true, { fallback: Container.config.defaultGravatarsStyle })
? await this.commit.getAvatarUri({ fallback: Container.config.defaultGravatarsStyle })
: new ThemeIcon('git-commit');
item.tooltip = this.tooltip;

+ 1
- 3
src/views/nodes/contributorNode.ts ファイルの表示

@ -80,9 +80,7 @@ export class ContributorNode extends ViewNode
}\n${Strings.pluralize('commit', this.contributor.count)}`;
if (this.view.config.avatars) {
item.iconPath = await this.contributor.getAvatarUri(true, {
fallback: Container.config.defaultGravatarsStyle,
});
item.iconPath = await this.contributor.getAvatarUri({ fallback: Container.config.defaultGravatarsStyle });
}
return item;

+ 2
- 2
src/webviews/rebaseEditor.ts ファイルの表示

@ -156,7 +156,7 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
authors.set(commit.author, {
author: commit.author,
avatarUrl: (
await commit.getAvatarUri(true, { fallback: Container.config.defaultGravatarsStyle })
await commit.getAvatarUri({ fallback: Container.config.defaultGravatarsStyle })
).toString(true),
email: commit.email,
});
@ -183,7 +183,7 @@ export class RebaseEditorProvider implements CustomTextEditorProvider, Disposabl
authors.set(commit.author, {
author: commit.author,
avatarUrl: (
await commit.getAvatarUri(true, { fallback: Container.config.defaultGravatarsStyle })
await commit.getAvatarUri({ fallback: Container.config.defaultGravatarsStyle })
).toString(true),
email: commit.email,
});

読み込み中…
キャンセル
保存