Skip to content

Commit cc11bbd

Browse files
nzaytseveamodio
authored andcommitted
Adds monospace formatting in commit messages
1 parent f47c257 commit cc11bbd

File tree

7 files changed

+91
-3
lines changed

7 files changed

+91
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
99
### Added
1010

1111
- Adds [Cursor](https://cursor.so) support — closes [#3222](https://github.com/gitkraken/vscode-gitlens/issues/3222)
12+
- Adds monospace formatting in commit messages — closes [#2350](https://github.com/gitkraken/vscode-gitlens/issues/2350)
1213

1314
### Changed
1415

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17951,6 +17951,7 @@
1795117951
"@swc/core": "1.7.28",
1795217952
"@twbs/fantasticon": "3.0.0",
1795317953
"@types/eslint__js": "8.42.3",
17954+
"@types/markdown-it": "^14.1.2",
1795417955
"@types/mocha": "10.0.8",
1795517956
"@types/node": "18.15.0",
1795617957
"@types/react": "17.0.82",

src/git/formatters/commitFormatter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ export class CommitFormatter extends Formatter<GitCommit, CommitFormatOptions> {
692692
message = encodeHtmlWeak(message);
693693
}
694694
if (outputFormat === 'markdown') {
695-
message = escapeMarkdown(message, { quoted: true });
695+
message = escapeMarkdown(message, { quoted: true, inlineBackticks: true });
696696
}
697697

698698
if (this._options.messageAutolinks) {

src/system/markdown.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* escapes markdown code blocks
3+
*/
4+
function escapeTripleBackticks(s: string) {
5+
const tripleBackticks = '```';
6+
const escapedTripleBackticks = '\\`\\`\\`';
7+
let str = '';
8+
let allowed = true;
9+
let quotesOpened = false;
10+
let buffer = '';
11+
12+
for (let i = 0; i < s.length; i += 1) {
13+
const char = s[i];
14+
const chain = s.substring(i, i + 3);
15+
if (char === '\n' && quotesOpened) {
16+
allowed = false;
17+
}
18+
if (chain === tripleBackticks) {
19+
if (quotesOpened) {
20+
quotesOpened = false;
21+
if (allowed) {
22+
str += `${tripleBackticks}${buffer}${tripleBackticks}`;
23+
} else {
24+
str += `${escapedTripleBackticks}${buffer}${escapedTripleBackticks}`;
25+
allowed = true;
26+
}
27+
buffer = '';
28+
} else {
29+
quotesOpened = true;
30+
}
31+
// skip chain
32+
i += 2;
33+
continue;
34+
}
35+
if (quotesOpened) {
36+
buffer += char;
37+
} else {
38+
str += char;
39+
}
40+
}
41+
return str;
42+
}
43+
44+
export { escapeTripleBackticks };

src/system/string.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
} from '@gk-nzaytsev/fast-string-truncated-width';
77
import getTruncatedStringWidth from '@gk-nzaytsev/fast-string-truncated-width';
88
import { CharCode } from '../constants';
9+
import { escapeTripleBackticks } from './markdown';
910

1011
export { fromBase64, base64 } from '@env/base64';
1112

@@ -130,22 +131,28 @@ export function encodeHtmlWeak(s: string | undefined): string | undefined {
130131
});
131132
}
132133

133-
const escapeMarkdownRegex = /[\\`*_{}[\]()#+\-.!]/g;
134+
const escapeMarkdownRegex = /[\\*_{}[\]()#+\-.!]/g;
134135
const unescapeMarkdownRegex = /\\([\\`*_{}[\]()#+\-.!])/g;
135136

136137
const escapeMarkdownHeaderRegex = /^===/gm;
137138
const unescapeMarkdownHeaderRegex = /^\u200b===/gm;
138139

139140
// const sampleMarkdown = '## message `not code` *not important* _no underline_ \n> don\'t quote me \n- don\'t list me \n+ don\'t list me \n1. don\'t list me \nnot h1 \n=== \nnot h2 \n---\n***\n---\n___';
140141
const markdownQuotedRegex = /\r?\n/g;
142+
const markdownBacktickRegex = /`/g;
141143

142-
export function escapeMarkdown(s: string, options: { quoted?: boolean } = {}): string {
144+
export function escapeMarkdown(s: string, options: { quoted?: boolean; inlineBackticks?: boolean } = {}): string {
143145
s = s
144146
// Escape markdown
145147
.replace(escapeMarkdownRegex, '\\$&')
146148
// Escape markdown header (since the above regex won't match it)
147149
.replace(escapeMarkdownHeaderRegex, '\u200b===');
148150

151+
if (options.inlineBackticks) {
152+
s = escapeTripleBackticks(s);
153+
} else {
154+
s = s.replace(markdownBacktickRegex, '\\$&');
155+
}
149156
if (!options.quoted) return s;
150157

151158
// Keep under the same block-quote but with line breaks

src/webviews/apps/plus/graph/GraphWrapper.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import type { SearchNavigationEventDetail } from '../../shared/components/search
8383
import type { DateTimeFormat } from '../../shared/date';
8484
import { formatDate, fromNow } from '../../shared/date';
8585
import { GlGraphHover } from './hover/graphHover.react';
86+
import { Markdown } from './markdown/markdown';
8687
import type { GraphMinimapDaySelectedEventDetail } from './minimap/minimap';
8788
import { GlGraphMinimapContainer } from './minimap/minimap-container.react';
8889
import { GlGraphSideBar } from './sidebar/sidebar.react';
@@ -1604,6 +1605,8 @@ export function GraphWrapper({
16041605
avatarUrlByEmail={avatars}
16051606
columnsSettings={columns}
16061607
contexts={context}
1608+
// @ts-expect-error returnType of formatCommitMessage callback expects to be string, but it works fine with react element
1609+
formatCommitMessage={e => <Markdown>{e}</Markdown>}
16071610
cssVariables={styleProps?.cssVariables}
16081611
dimMergeCommits={graphConfig?.dimMergeCommits}
16091612
downstreamsByUpstream={downstreams}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import markdownit from 'markdown-it';
2+
import React, { useEffect, useState } from 'react';
3+
4+
const mdRules = [
5+
'text',
6+
'linkify',
7+
'newline',
8+
'escape',
9+
'backticks',
10+
'strikethrough',
11+
'emphasis',
12+
'link',
13+
'image',
14+
'autolink',
15+
'html_inline',
16+
'entity',
17+
];
18+
19+
const md = markdownit();
20+
md.disable(mdRules);
21+
md.enable(['backticks']);
22+
23+
export const Markdown = ({ children }: { children: string }) => {
24+
const [span, setSpan] = useState<HTMLSpanElement | null>(null);
25+
useEffect(() => {
26+
if (!span) {
27+
return;
28+
}
29+
span.setHTMLUnsafe(md.renderInline(children));
30+
}, [children, span]);
31+
return <span ref={setSpan}></span>;
32+
};

0 commit comments

Comments
 (0)