Skip to content

Commit 5d415b8

Browse files
committed
Adds issue-linking for commit messages in hovers
1 parent adda808 commit 5d415b8

File tree

12 files changed

+114
-51
lines changed

12 files changed

+114
-51
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88
### Added
9+
- Adds issue linking for commit messages in hovers
10+
11+
![Issue linking](./images/ss-cl-issue-linking.png)
12+
913
- Adds multi-cursor support to current line annotations — closes [#291](https://github.com/eamodio/vscode-gitlens/issues/291)
1014
- Adds support to toggle annotations for each file individually or for all files at once — closes [#289](https://github.com/eamodio/vscode-gitlens/issues/289)
1115
- Adds new controls the interactive settings editor (*Open Settings* from the Command Palette) to configure this new behavior

images/ss-cl-issue-linking.png

10.3 KB
Loading

src/annotations/annotations.ts

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DiffWithCommand, OpenCommitInRemoteCommand, OpenFileRevisionCommand, Sh
44
import { FileAnnotationType } from './../configuration';
55
import { GlyphChars } from '../constants';
66
import { Container } from '../container';
7-
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
7+
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitRemote, GitService, GitUri, ICommitFormatOptions } from '../gitService';
88

99
interface IHeatmapConfig {
1010
enabled: boolean;
@@ -62,7 +62,7 @@ export class Annotations {
6262
return commandBar;
6363
}
6464

65-
static getHoverMessage(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): MarkdownString {
65+
static getHoverMessage(commit: GitCommit, dateFormat: string | null, remotes: GitRemote[], annotationType?: FileAnnotationType, line: number = 0): MarkdownString {
6666
if (dateFormat === null) {
6767
dateFormat = 'MMMM Do, YYYY h:mma';
6868
}
@@ -71,10 +71,18 @@ export class Annotations {
7171
let commandBar = '';
7272
let showCommitDetailsCommand = '';
7373
if (!commit.isUncommitted) {
74-
commandBar = `\n\n${this.getHoverCommandBar(commit, hasRemote, annotationType, line)}`;
74+
commandBar = `\n\n${this.getHoverCommandBar(commit, remotes.length !== 0, annotationType, line)}`;
7575
showCommitDetailsCommand = `[\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)} "Show Commit Details")`;
7676

77-
message = commit.message
77+
message = commit.message;
78+
for (const r of remotes) {
79+
if (r.provider === undefined) continue;
80+
81+
message = r.provider.enrichMessage(message);
82+
break;
83+
}
84+
85+
message
7886
// Escape markdown
7987
.replace(escapeMarkdownRegEx, '\\$&')
8088
// Escape markdown header (since the above regex won't match it)
@@ -135,12 +143,12 @@ export class Annotations {
135143
} as DecorationOptions;
136144
}
137145

138-
static detailsHover(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): DecorationOptions {
139-
const message = this.getHoverMessage(commit, dateFormat, hasRemote, annotationType);
140-
return {
141-
hoverMessage: message
142-
} as DecorationOptions;
143-
}
146+
// static detailsHover(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): DecorationOptions {
147+
// const message = this.getHoverMessage(commit, dateFormat, hasRemote, annotationType);
148+
// return {
149+
// hoverMessage: message
150+
// } as DecorationOptions;
151+
// }
144152

145153
static gutter(commit: GitCommit, format: string, dateFormatOrFormatOptions: string | null | ICommitFormatOptions, renderOptions: IRenderOptions): DecorationOptions {
146154
const decoration = {
@@ -227,28 +235,28 @@ export class Annotations {
227235
} as IRenderOptions;
228236
}
229237

230-
static hover(commit: GitCommit, renderOptions: IRenderOptions, now: number): DecorationOptions {
231-
const decoration = {
232-
renderOptions: { before: { ...renderOptions } }
233-
} as DecorationOptions;
238+
// static hover(commit: GitCommit, renderOptions: IRenderOptions, now: number): DecorationOptions {
239+
// const decoration = {
240+
// renderOptions: { before: { ...renderOptions } }
241+
// } as DecorationOptions;
234242

235-
this.applyHeatmap(decoration, commit.date, now);
243+
// this.applyHeatmap(decoration, commit.date, now);
236244

237-
return decoration;
238-
}
245+
// return decoration;
246+
// }
239247

240-
static hoverRenderOptions(heatmap: IHeatmapConfig): IRenderOptions {
241-
if (!heatmap.enabled) return { before: undefined };
248+
// static hoverRenderOptions(heatmap: IHeatmapConfig): IRenderOptions {
249+
// if (!heatmap.enabled) return { before: undefined };
242250

243-
return {
244-
borderStyle: 'solid',
245-
borderWidth: '0 0 0 2px',
246-
contentText: GlyphChars.ZeroWidthSpace,
247-
height: '100%',
248-
margin: '0 26px 0 0',
249-
textDecoration: 'none'
250-
} as IRenderOptions;
251-
}
251+
// return {
252+
// borderStyle: 'solid',
253+
// borderWidth: '0 0 0 2px',
254+
// contentText: GlyphChars.ZeroWidthSpace,
255+
// height: '100%',
256+
// margin: '0 26px 0 0',
257+
// textDecoration: 'none'
258+
// } as IRenderOptions;
259+
// }
252260

253261
static trailing(commit: GitCommit, format: string, dateFormat: string | null): DecorationOptions {
254262
const message = CommitFormatter.fromTemplate(format, commit, {
@@ -269,20 +277,20 @@ export class Annotations {
269277
} as DecorationOptions;
270278
}
271279

272-
static withRange(decoration: DecorationOptions, start?: number, end?: number): DecorationOptions {
273-
let range = decoration.range;
274-
if (start !== undefined) {
275-
range = range.with({
276-
start: range.start.with({ character: start })
277-
});
278-
}
279-
280-
if (end !== undefined) {
281-
range = range.with({
282-
end: range.end.with({ character: end })
283-
});
284-
}
285-
286-
return { ...decoration, range: range };
287-
}
280+
// static withRange(decoration: DecorationOptions, start?: number, end?: number): DecorationOptions {
281+
// let range = decoration.range;
282+
// if (start !== undefined) {
283+
// range = range.with({
284+
// start: range.start.with({ character: start })
285+
// });
286+
// }
287+
288+
// if (end !== undefined) {
289+
// range = range.with({
290+
// end: range.end.with({ character: end })
291+
// });
292+
// }
293+
294+
// return { ...decoration, range: range };
295+
// }
288296
}

src/annotations/blameAnnotationProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
121121
}
122122
}
123123

124-
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.hasRemote(commit.repoPath), this.annotationType, this.editor.selection.active.line);
124+
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.getRemotes(commit.repoPath), this.annotationType, this.editor.selection.active.line);
125125
return new Hover(message, document.validateRange(new Range(position.line, 0, position.line, RangeEndOfLineIndex)));
126126
}
127127

src/annotations/recentChangesAnnotationProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
5656
if (cfg.hovers.enabled && cfg.hovers.annotations.enabled) {
5757
if (cfg.hovers.annotations.details) {
5858
this.decorations.push({
59-
hoverMessage: Annotations.getHoverMessage(commit, dateFormat, await Container.git.hasRemote(commit.repoPath), this.annotationType, this.editor.selection.active.line),
59+
hoverMessage: Annotations.getHoverMessage(commit, dateFormat, await Container.git.getRemotes(commit.repoPath), this.annotationType, this.editor.selection.active.line),
6060
range: range
6161
} as DecorationOptions);
6262
}

src/currentLineController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export class CurrentLineController extends Disposable {
263263
const trackedDocument = await Container.tracker.get(document);
264264
if (trackedDocument === undefined) return undefined;
265265

266-
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, trackedDocument.hasRemotes, fileAnnotations, position.line);
266+
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.getRemotes(commit.repoPath), fileAnnotations, position.line);
267267
return new Hover(message, range);
268268
}
269269

src/git/remotes/bitbucket-server.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { Range } from 'vscode';
33
import { RemoteProvider } from './provider';
44

5+
const issueEnricherRegEx = /(^|\s)(issue #([0-9]+))\b/gi;
6+
const prEnricherRegEx = /(^|\s)(pull request #([0-9]+))\b/gi;
7+
58
export class BitbucketServerService extends RemoteProvider {
69

710
constructor(
@@ -14,13 +17,21 @@ export class BitbucketServerService extends RemoteProvider {
1417
super(domain, path, protocol, name, custom);
1518
}
1619

20+
protected get baseUrl() {
21+
const [project, repo] = this.splitPath();
22+
return `https://${this.domain}/projects/${project}/repos/${repo}`;
23+
}
24+
1725
get name() {
1826
return this.formatName('Bitbucket Server');
1927
}
2028

21-
protected get baseUrl() {
22-
const [project, repo] = this.splitPath();
23-
return `https://${this.domain}/projects/${project}/repos/${repo}`;
29+
enrichMessage(message: string): string {
30+
return message
31+
// Matches issue #123
32+
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
33+
// Matches pull request #123
34+
.replace(prEnricherRegEx, `$1[$2](${this.baseUrl}/pull-requests/$3 "Open PR $2")`);
2435
}
2536

2637
protected getUrlForBranches(): string {

src/git/remotes/bitbucket.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { Range } from 'vscode';
33
import { RemoteProvider } from './provider';
44

5+
const issueEnricherRegEx = /(^|\s)(issue #([0-9]+))\b/gi;
6+
const prEnricherRegEx = /(^|\s)(pull request #([0-9]+))\b/gi;
7+
58
export class BitbucketService extends RemoteProvider {
69

710
constructor(
@@ -18,6 +21,14 @@ export class BitbucketService extends RemoteProvider {
1821
return this.formatName('Bitbucket');
1922
}
2023

24+
enrichMessage(message: string): string {
25+
return message
26+
// Matches issue #123
27+
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
28+
// Matches pull request #123
29+
.replace(prEnricherRegEx, `$1[$2](${this.baseUrl}/pull-requests/$3 "Open PR $2")`);
30+
}
31+
2132
protected getUrlForBranches(): string {
2233
return `${this.baseUrl}/branches`;
2334
}

src/git/remotes/github.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import { Range } from 'vscode';
33
import { RemoteProvider } from './provider';
44

5+
const issueEnricherRegEx = /(^|\s)((?:#|gh-)([0-9]+))\b/gi;
6+
const issueEnricher3rdParyRegEx = /\b((\w+-?\w+(?!-)\/\w+-?\w+(?!-))#([0-9]+))\b/g;
7+
58
export class GitHubService extends RemoteProvider {
69

710
constructor(
@@ -18,6 +21,14 @@ export class GitHubService extends RemoteProvider {
1821
return this.formatName('GitHub');
1922
}
2023

24+
enrichMessage(message: string): string {
25+
return message
26+
// Matches #123 or gh-123 or GH-123
27+
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
28+
// Matches eamodio/vscode-gitlens#123
29+
.replace(issueEnricher3rdParyRegEx, `[$1](${this.protocol}://${this.domain}/$2/issues/$3 "Open Issue #$3 from $2")`);
30+
}
31+
2132
protected getUrlForBranches(): string {
2233
return `${this.baseUrl}/branches`;
2334
}

src/git/remotes/gitlab.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import { Range } from 'vscode';
33
import { RemoteProvider } from './provider';
44

5+
const issueEnricherRegEx = /(^|\s)(#([0-9]+))\b/gi;
6+
57
export class GitLabService extends RemoteProvider {
68

79
constructor(
@@ -18,6 +20,11 @@ export class GitLabService extends RemoteProvider {
1820
return this.formatName('GitLab');
1921
}
2022

23+
enrichMessage(message: string): string {
24+
// Matches #123
25+
return message.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`);
26+
}
27+
2128
protected getUrlForBranches(): string {
2229
return `${this.baseUrl}/branches`;
2330
}

0 commit comments

Comments
 (0)