Browse Source

Adds issue-linking for commit messages in hovers

main
Eric Amodio 7 years ago
parent
commit
5d415b86e8
12 changed files with 114 additions and 51 deletions
  1. +4
    -0
      CHANGELOG.md
  2. BIN
      images/ss-cl-issue-linking.png
  3. +52
    -44
      src/annotations/annotations.ts
  4. +1
    -1
      src/annotations/blameAnnotationProvider.ts
  5. +1
    -1
      src/annotations/recentChangesAnnotationProvider.ts
  6. +1
    -1
      src/currentLineController.ts
  7. +14
    -3
      src/git/remotes/bitbucket-server.ts
  8. +11
    -0
      src/git/remotes/bitbucket.ts
  9. +11
    -0
      src/git/remotes/github.ts
  10. +7
    -0
      src/git/remotes/gitlab.ts
  11. +5
    -1
      src/git/remotes/provider.ts
  12. +7
    -0
      src/git/remotes/visualStudio.ts

+ 4
- 0
CHANGELOG.md View File

@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
## [Unreleased]
### Added
- Adds issue linking for commit messages in hovers
![Issue linking](./images/ss-cl-issue-linking.png)
- Adds multi-cursor support to current line annotations — closes [#291](https://github.com/eamodio/vscode-gitlens/issues/291)
- 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)
- Adds new controls the interactive settings editor (*Open Settings* from the Command Palette) to configure this new behavior

BIN
images/ss-cl-issue-linking.png View File

Before After
Width: 477  |  Height: 104  |  Size: 10 KiB

+ 52
- 44
src/annotations/annotations.ts View File

@ -4,7 +4,7 @@ import { DiffWithCommand, OpenCommitInRemoteCommand, OpenFileRevisionCommand, Sh
import { FileAnnotationType } from './../configuration';
import { GlyphChars } from '../constants';
import { Container } from '../container';
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitService, GitUri, ICommitFormatOptions } from '../gitService';
import { CommitFormatter, GitCommit, GitDiffChunkLine, GitRemote, GitService, GitUri, ICommitFormatOptions } from '../gitService';
interface IHeatmapConfig {
enabled: boolean;
@ -62,7 +62,7 @@ export class Annotations {
return commandBar;
}
static getHoverMessage(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): MarkdownString {
static getHoverMessage(commit: GitCommit, dateFormat: string | null, remotes: GitRemote[], annotationType?: FileAnnotationType, line: number = 0): MarkdownString {
if (dateFormat === null) {
dateFormat = 'MMMM Do, YYYY h:mma';
}
@ -71,10 +71,18 @@ export class Annotations {
let commandBar = '';
let showCommitDetailsCommand = '';
if (!commit.isUncommitted) {
commandBar = `\n\n${this.getHoverCommandBar(commit, hasRemote, annotationType, line)}`;
commandBar = `\n\n${this.getHoverCommandBar(commit, remotes.length !== 0, annotationType, line)}`;
showCommitDetailsCommand = `[\`${commit.shortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)} "Show Commit Details")`;
message = commit.message
message = commit.message;
for (const r of remotes) {
if (r.provider === undefined) continue;
message = r.provider.enrichMessage(message);
break;
}
message
// Escape markdown
.replace(escapeMarkdownRegEx, '\$&')
// Escape markdown header (since the above regex won't match it)
@ -135,12 +143,12 @@ export class Annotations {
} as DecorationOptions;
}
static detailsHover(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): DecorationOptions {
const message = this.getHoverMessage(commit, dateFormat, hasRemote, annotationType);
return {
hoverMessage: message
} as DecorationOptions;
}
// static detailsHover(commit: GitCommit, dateFormat: string | null, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0): DecorationOptions {
// const message = this.getHoverMessage(commit, dateFormat, hasRemote, annotationType);
// return {
// hoverMessage: message
// } as DecorationOptions;
// }
static gutter(commit: GitCommit, format: string, dateFormatOrFormatOptions: string | null | ICommitFormatOptions, renderOptions: IRenderOptions): DecorationOptions {
const decoration = {
@ -227,28 +235,28 @@ export class Annotations {
} as IRenderOptions;
}
static hover(commit: GitCommit, renderOptions: IRenderOptions, now: number): DecorationOptions {
const decoration = {
renderOptions: { before: { ...renderOptions } }
} as DecorationOptions;
// static hover(commit: GitCommit, renderOptions: IRenderOptions, now: number): DecorationOptions {
// const decoration = {
// renderOptions: { before: { ...renderOptions } }
// } as DecorationOptions;
this.applyHeatmap(decoration, commit.date, now);
// this.applyHeatmap(decoration, commit.date, now);
return decoration;
}
// return decoration;
// }
static hoverRenderOptions(heatmap: IHeatmapConfig): IRenderOptions {
if (!heatmap.enabled) return { before: undefined };
// static hoverRenderOptions(heatmap: IHeatmapConfig): IRenderOptions {
// if (!heatmap.enabled) return { before: undefined };
return {
borderStyle: 'solid',
borderWidth: '0 0 0 2px',
contentText: GlyphChars.ZeroWidthSpace,
height: '100%',
margin: '0 26px 0 0',
textDecoration: 'none'
} as IRenderOptions;
}
// return {
// borderStyle: 'solid',
// borderWidth: '0 0 0 2px',
// contentText: GlyphChars.ZeroWidthSpace,
// height: '100%',
// margin: '0 26px 0 0',
// textDecoration: 'none'
// } as IRenderOptions;
// }
static trailing(commit: GitCommit, format: string, dateFormat: string | null): DecorationOptions {
const message = CommitFormatter.fromTemplate(format, commit, {
@ -269,20 +277,20 @@ export class Annotations {
} as DecorationOptions;
}
static withRange(decoration: DecorationOptions, start?: number, end?: number): DecorationOptions {
let range = decoration.range;
if (start !== undefined) {
range = range.with({
start: range.start.with({ character: start })
});
}
if (end !== undefined) {
range = range.with({
end: range.end.with({ character: end })
});
}
return { ...decoration, range: range };
}
// static withRange(decoration: DecorationOptions, start?: number, end?: number): DecorationOptions {
// let range = decoration.range;
// if (start !== undefined) {
// range = range.with({
// start: range.start.with({ character: start })
// });
// }
// if (end !== undefined) {
// range = range.with({
// end: range.end.with({ character: end })
// });
// }
// return { ...decoration, range: range };
// }
}

+ 1
- 1
src/annotations/blameAnnotationProvider.ts View File

@ -121,7 +121,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
}
}
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.hasRemote(commit.repoPath), this.annotationType, this.editor.selection.active.line);
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.getRemotes(commit.repoPath), this.annotationType, this.editor.selection.active.line);
return new Hover(message, document.validateRange(new Range(position.line, 0, position.line, RangeEndOfLineIndex)));
}

+ 1
- 1
src/annotations/recentChangesAnnotationProvider.ts View File

@ -56,7 +56,7 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
if (cfg.hovers.enabled && cfg.hovers.annotations.enabled) {
if (cfg.hovers.annotations.details) {
this.decorations.push({
hoverMessage: Annotations.getHoverMessage(commit, dateFormat, await Container.git.hasRemote(commit.repoPath), this.annotationType, this.editor.selection.active.line),
hoverMessage: Annotations.getHoverMessage(commit, dateFormat, await Container.git.getRemotes(commit.repoPath), this.annotationType, this.editor.selection.active.line),
range: range
} as DecorationOptions);
}

+ 1
- 1
src/currentLineController.ts View File

@ -263,7 +263,7 @@ export class CurrentLineController extends Disposable {
const trackedDocument = await Container.tracker.get(document);
if (trackedDocument === undefined) return undefined;
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, trackedDocument.hasRemotes, fileAnnotations, position.line);
const message = Annotations.getHoverMessage(logCommit || commit, Container.config.defaultDateFormat, await Container.git.getRemotes(commit.repoPath), fileAnnotations, position.line);
return new Hover(message, range);
}

+ 14
- 3
src/git/remotes/bitbucket-server.ts View File

@ -2,6 +2,9 @@
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
const issueEnricherRegEx = /(^|\s)(issue #([0-9]+))\b/gi;
const prEnricherRegEx = /(^|\s)(pull request #([0-9]+))\b/gi;
export class BitbucketServerService extends RemoteProvider {
constructor(
@ -14,13 +17,21 @@ export class BitbucketServerService extends RemoteProvider {
super(domain, path, protocol, name, custom);
}
protected get baseUrl() {
const [project, repo] = this.splitPath();
return `https://${this.domain}/projects/${project}/repos/${repo}`;
}
get name() {
return this.formatName('Bitbucket Server');
}
protected get baseUrl() {
const [project, repo] = this.splitPath();
return `https://${this.domain}/projects/${project}/repos/${repo}`;
enrichMessage(message: string): string {
return message
// Matches issue #123
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
// Matches pull request #123
.replace(prEnricherRegEx, `$1[$2](${this.baseUrl}/pull-requests/$3 "Open PR $2")`);
}
protected getUrlForBranches(): string {

+ 11
- 0
src/git/remotes/bitbucket.ts View File

@ -2,6 +2,9 @@
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
const issueEnricherRegEx = /(^|\s)(issue #([0-9]+))\b/gi;
const prEnricherRegEx = /(^|\s)(pull request #([0-9]+))\b/gi;
export class BitbucketService extends RemoteProvider {
constructor(
@ -18,6 +21,14 @@ export class BitbucketService extends RemoteProvider {
return this.formatName('Bitbucket');
}
enrichMessage(message: string): string {
return message
// Matches issue #123
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
// Matches pull request #123
.replace(prEnricherRegEx, `$1[$2](${this.baseUrl}/pull-requests/$3 "Open PR $2")`);
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}

+ 11
- 0
src/git/remotes/github.ts View File

@ -2,6 +2,9 @@
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
const issueEnricherRegEx = /(^|\s)((?:#|gh-)([0-9]+))\b/gi;
const issueEnricher3rdParyRegEx = /\b((\w+-?\w+(?!-)\/\w+-?\w+(?!-))#([0-9]+))\b/g;
export class GitHubService extends RemoteProvider {
constructor(
@ -18,6 +21,14 @@ export class GitHubService extends RemoteProvider {
return this.formatName('GitHub');
}
enrichMessage(message: string): string {
return message
// Matches #123 or gh-123 or GH-123
.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`)
// Matches eamodio/vscode-gitlens#123
.replace(issueEnricher3rdParyRegEx, `[$1](${this.protocol}://${this.domain}/$2/issues/$3 "Open Issue #$3 from $2")`);
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}

+ 7
- 0
src/git/remotes/gitlab.ts View File

@ -2,6 +2,8 @@
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
const issueEnricherRegEx = /(^|\s)(#([0-9]+))\b/gi;
export class GitLabService extends RemoteProvider {
constructor(
@ -18,6 +20,11 @@ export class GitLabService extends RemoteProvider {
return this.formatName('GitLab');
}
enrichMessage(message: string): string {
// Matches #123
return message.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/issues/$3 "Open Issue $2")`);
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}

+ 5
- 1
src/git/remotes/provider.ts View File

@ -39,7 +39,7 @@ export abstract class RemoteProvider {
constructor(
public readonly domain: string,
public readonly path: string,
private readonly protocol: string = 'https',
public readonly protocol: string = 'https',
name?: string,
public readonly custom: boolean = false
) {
@ -52,6 +52,10 @@ export abstract class RemoteProvider {
return `${this.protocol}://${this.domain}/${this.path}`;
}
enrichMessage(message: string): string {
return message;
}
protected formatName(name: string) {
if (this._name !== undefined) return this._name;
return `${name}${this.custom ? ` (${this.domain})` : ''}`;

+ 7
- 0
src/git/remotes/visualStudio.ts View File

@ -2,6 +2,8 @@
import { Range } from 'vscode';
import { RemoteProvider } from './provider';
const issueEnricherRegEx = /(^|\s)(#([0-9]+))\b/gi;
export class VisualStudioService extends RemoteProvider {
constructor(
@ -17,6 +19,11 @@ export class VisualStudioService extends RemoteProvider {
return 'Visual Studio Team Services';
}
enrichMessage(message: string): string {
// Matches #123
return message.replace(issueEnricherRegEx, `$1[$2](${this.baseUrl}/_workitems/edit/$3 "Open Work Item $2")`);
}
protected getUrlForBranches(): string {
return `${this.baseUrl}/branches`;
}

||||||
x
 
000:0
Loading…
Cancel
Save