From 947b988c69b1dd6fb37b39a2408c3a0c60e6aa11 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 17 Dec 2019 01:26:56 -0500 Subject: [PATCH] Adds autolink support in plain text (as footnotes) --- src/annotations/autolinks.ts | 85 ++++++++++++++++++++++++++--------- src/git/formatters/commitFormatter.ts | 9 ++-- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/annotations/autolinks.ts b/src/annotations/autolinks.ts index c41fe05..d2cb72f 100644 --- a/src/annotations/autolinks.ts +++ b/src/annotations/autolinks.ts @@ -9,14 +9,16 @@ import { GlyphChars } from '../constants'; const numRegex = //g; +const superscripts = ['\u00B9', '\u00B2', '\u00B3', '\u2074', '\u2075', '\u2076', '\u2077', '\u2078', '\u2079']; + export interface CacheableAutolinkReference extends AutolinkReference { - linkify?: ((text: string) => string) | null; + linkify?: ((text: string, markdown: boolean) => string) | null; messageMarkdownRegex?: RegExp; messageRegex?: RegExp; } export interface DynamicAutolinkReference { - linkify: (text: string) => string; + linkify: (text: string, markdown: boolean) => string; } function isDynamic(ref: AutolinkReference | DynamicAutolinkReference): ref is DynamicAutolinkReference { @@ -85,11 +87,16 @@ export class Autolinks implements Disposable { } @debug({ args: false }) - linkify(text: string, remotes?: GitRemote[], issues?: Map) { + linkify( + text: string, + markdown: boolean, + remotes?: GitRemote[], + issues?: Map + ) { for (const ref of this._references) { if (this.ensureAutolinkCached(ref, issues)) { if (ref.linkify != null) { - text = ref.linkify(text); + text = ref.linkify(text, markdown); } } } @@ -101,7 +108,7 @@ export class Autolinks implements Disposable { for (const ref of r.provider.autolinks) { if (this.ensureAutolinkCached(ref, issues)) { if (ref.linkify != null) { - text = ref.linkify(text); + text = ref.linkify(text, markdown); } } } @@ -126,32 +133,66 @@ export class Autolinks implements Disposable { } if (issues == null || issues.size === 0) { - const markdown = `[$1](${ref.url.replace(numRegex, '$2')}${ + const replacement = `[$1](${ref.url.replace(numRegex, '$2')}${ ref.title ? ` "${ref.title.replace(numRegex, '$2')}"` : '' })`; - ref.linkify = (text: string) => text.replace(ref.messageMarkdownRegex!, markdown); + ref.linkify = (text: string, markdown: boolean) => + !markdown ? text : text.replace(ref.messageMarkdownRegex!, replacement); return true; } - ref.linkify = (text: string) => - text.replace(ref.messageMarkdownRegex!, (substring, linkText, number) => { + ref.linkify = (text: string, markdown: boolean) => { + if (markdown) { + return text.replace(ref.messageMarkdownRegex!, (substring, linkText, number) => { + const issue = issues?.get(Number(number)); + + return `[${linkText}](${ref.url.replace(numRegex, number)}${ + ref.title + ? ` "${ref.title.replace(numRegex, number)}${ + issue instanceof Promises.CancellationError + ? `\n${GlyphChars.Dash.repeat(2)}\nDetails timed out` + : issue + ? `\n${GlyphChars.Dash.repeat(2)}\n${issue.title.replace( + /([")])/g, + '\\$1' + )}\n${issue.closed ? 'Closed' : 'Opened'}, ${Dates.getFormatter( + issue.closedDate ?? issue.date + ).fromNow()}` + : '' + }"` + : '' + })`; + }); + } + + let footnotes: string[] | undefined; + let superscript; + + text = text.replace(ref.messageRegex!, (substring, linkText, number) => { const issue = issues?.get(Number(number)); + if (issue == null) return linkText; - return `[${linkText}](${ref.url.replace(numRegex, number)}${ - ref.title - ? ` "${ref.title.replace(numRegex, number)}${ - issue instanceof Promises.CancellationError - ? `\n${GlyphChars.Dash.repeat(2)}\nDetails timed out` - : issue - ? `\n${GlyphChars.Dash.repeat(2)}\n${issue.title.replace(/([")])/g, '\\$1')}\n${ - issue.closed ? 'Closed' : 'Opened' - }, ${Dates.getFormatter(issue.closedDate ?? issue.date).fromNow()}` - : '' - }"` - : '' - })`; + if (footnotes === undefined) { + footnotes = []; + } + superscript = superscripts[footnotes.length]; + footnotes.push( + `${superscript} ${ + issue instanceof Promises.CancellationError + ? 'Details timed out' + : issue + ? `${issue.title} ${GlyphChars.Dot} ${ + issue.closed ? 'Closed' : 'Opened' + }, ${Dates.getFormatter(issue.closedDate ?? issue.date).fromNow()}` + : '' + }` + ); + return `${linkText}${superscript}`; }); + + return footnotes == null || footnotes.length === 0 ? text : `${text}\n\n${footnotes.join('\n')}`; + }; } catch (ex) { Logger.error( ex, diff --git a/src/git/formatters/commitFormatter.ts b/src/git/formatters/commitFormatter.ts index 4e10697..9637d6d 100644 --- a/src/git/formatters/commitFormatter.ts +++ b/src/git/formatters/commitFormatter.ts @@ -349,19 +349,16 @@ export class CommitFormatter extends Formatter { message = emojify(message); message = this._padOrTruncate(message, this._options.tokenOptions.message); - if (!this._options.markdown) { - return message; - } - if (Container.config.hovers.autolinks.enabled) { message = Container.autolinks.linkify( - Strings.escapeMarkdown(message, { quoted: true }), + this._options.markdown ? Strings.escapeMarkdown(message, { quoted: true }) : message, + this._options.markdown ?? false, this._options.remotes, this._options.autolinkedIssues ); } - return `\n> ${message}`; + return this._options.markdown ? `\n> ${message}` : message; } get pullRequest() {