Skip to content

Commit f625d37

Browse files
committed
Improves changes hover accuracy & diff rendering
Adds hovers.changesDiff setting for line vs hunk diff
1 parent dc40811 commit f625d37

15 files changed

+179
-135
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ GitLens is highly customizable and provides many configuration settings to allow
695695
| `gitlens.hovers.annotations.enabled` | Specifies whether to provide any hovers when showing blame annotations |
696696
| `gitlens.hovers.annotations.over` | Specifies when to trigger hovers when showing blame annotations<br /><br />`annotation` - only shown when hovering over the line annotation<br />`line` - shown when hovering anywhere over the line |
697697
| `gitlens.hovers.avatars` | Specifies whether to show avatar images in hovers |
698+
| `gitlens.hovers.changesDiff` | Specifies whether to show just the changes to the line or the full set of related changes in the _changes (diff)_ hover<br /><br />`line` - Shows only the changes to the line<br /><br />`hunk` - Shows the full set of related changes |
698699
| `gitlens.hovers.currentLine.changes` | Specifies whether to provide a _changes (diff)_ hover for the current line |
699700
| `gitlens.hovers.currentLine.details` | Specifies whether to provide a _commit details_ hover for the current line |
700701
| `gitlens.hovers.currentLine.enabled` | Specifies whether to provide any hovers for the current line |

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,20 @@
563563
"markdownDescription": "Specifies whether to show avatar images in hovers",
564564
"scope": "window"
565565
},
566+
"gitlens.hovers.changesDiff": {
567+
"type": "string",
568+
"default": "line",
569+
"enum": [
570+
"line",
571+
"hunk"
572+
],
573+
"enumDescriptions": [
574+
"Shows only the changes to the line",
575+
"Shows the full set of related changes"
576+
],
577+
"markdownDescription": "Specifies whether to show just the changes to the line or the full set of related changes in the _changes (diff)_ hover",
578+
"scope": "window"
579+
},
566580
"gitlens.hovers.detailsMarkdownFormat": {
567581
"type": "string",
568582
"default": "[${avatar} &nbsp;__${author}__](mailto:${email}), ${ago} &nbsp; _(${date})_ \n\n${message}\n\n${commands}",

src/annotations/annotations.ts

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import { Container } from '../container';
1414
import {
1515
CommitFormatOptions,
1616
CommitFormatter,
17+
GitBlameCommit,
1718
GitCommit,
18-
GitDiffChunkLine,
19+
GitDiffHunkLine,
1920
GitRemote,
2021
GitService,
2122
GitUri
@@ -108,11 +109,11 @@ export class Annotations {
108109
static getHoverDiffMessage(
109110
commit: GitCommit,
110111
uri: GitUri,
111-
chunkLine: GitDiffChunkLine | undefined
112+
hunkLine: GitDiffHunkLine | undefined
112113
): MarkdownString | undefined {
113-
if (chunkLine === undefined || commit.previousSha === undefined) return undefined;
114+
if (hunkLine === undefined || commit.previousSha === undefined) return undefined;
114115

115-
const codeDiff = this.getCodeDiff(chunkLine);
116+
const diff = this.getDiffFromHunkLine(hunkLine);
116117

117118
let message: string;
118119
if (commit.isUncommitted) {
@@ -121,12 +122,12 @@ export class Annotations {
121122
GlyphChars.Dash
122123
} &nbsp; [\`${commit.previousShortSha}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(
123124
commit.previousSha!
124-
)} "Show Commit Details") ${GlyphChars.ArrowLeftRightLong} _${uri.shortSha}_\n${codeDiff}`;
125+
)} "Show Commit Details") ${GlyphChars.ArrowLeftRightLong} _${uri.shortSha}_\n${diff}`;
125126
}
126127
else {
127128
message = `[\`Changes\`](${DiffWithCommand.getMarkdownCommandArgs(commit)} "Open Changes") &nbsp; ${
128129
GlyphChars.Dash
129-
} &nbsp; _uncommitted changes_\n${codeDiff}`;
130+
} &nbsp; _uncommitted changes_\n${diff}`;
130131
}
131132
}
132133
else {
@@ -136,31 +137,44 @@ export class Annotations {
136137
commit.previousSha!
137138
)} "Show Commit Details") ${GlyphChars.ArrowLeftRightLong} [\`${
138139
commit.shortSha
139-
}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(
140-
commit.sha
141-
)} "Show Commit Details")\n${codeDiff}`;
140+
}\`](${ShowQuickCommitDetailsCommand.getMarkdownCommandArgs(commit.sha)} "Show Commit Details")\n${diff}`;
142141
}
143142

144143
const markdown = new MarkdownString(message);
145144
markdown.isTrusted = true;
146145
return markdown;
147146
}
148147

149-
private static getCodeDiff(chunkLine: GitDiffChunkLine): string {
150-
const previous = chunkLine.previous === undefined ? undefined : chunkLine.previous[0];
151-
return `\`\`\`
152-
- ${previous === undefined || previous.line === undefined ? '' : previous.line.trim()}
153-
+ ${chunkLine.line === undefined ? '' : chunkLine.line.trim()}
154-
\`\`\``;
148+
private static getDiffFromHunkLine(hunkLine: GitDiffHunkLine): string {
149+
if (Container.config.hovers.changesDiff === 'hunk') {
150+
return `\`\`\`diff\n${hunkLine.hunk.diff}\n\`\`\``;
151+
}
152+
153+
return `\`\`\`diff${hunkLine.previous === undefined ? '' : `\n-${hunkLine.previous.line}`}${
154+
hunkLine.current === undefined ? '' : `\n+${hunkLine.current.line}`
155+
}\n\`\`\``;
155156
}
156157

157-
static async changesHover(commit: GitCommit, line: number, uri: GitUri): Promise<Partial<DecorationOptions>> {
158-
const sha =
159-
!commit.isUncommitted || (uri.sha !== undefined && GitService.isStagedUncommitted(uri.sha))
160-
? commit.previousSha
161-
: undefined;
162-
const chunkLine = await Container.git.getDiffForLine(uri, line, sha);
163-
const message = this.getHoverDiffMessage(commit, uri, chunkLine);
158+
static async changesHover(
159+
commit: GitBlameCommit,
160+
editorLine: number,
161+
uri: GitUri
162+
): Promise<Partial<DecorationOptions>> {
163+
let ref;
164+
if (commit.isUncommitted) {
165+
if (uri.sha !== undefined && GitService.isStagedUncommitted(uri.sha)) {
166+
ref = uri.sha;
167+
}
168+
}
169+
else {
170+
ref = commit.sha;
171+
}
172+
173+
const line = editorLine + 1;
174+
const commitLine = commit.lines.find(l => l.line === line) || commit.lines[0];
175+
176+
const hunkLine = await Container.git.getDiffForLine(uri, commitLine.originalLine - 1, ref);
177+
const message = this.getHoverDiffMessage(commit, uri, hunkLine);
164178

165179
return {
166180
hoverMessage: message

src/annotations/blameAnnotationProvider.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
TextEditorDecorationType
1212
} from 'vscode';
1313
import { Container } from '../container';
14-
import { GitBlame, GitCommit, GitUri } from '../git/gitService';
14+
import { GitBlame, GitBlameCommit, GitCommit, GitUri } from '../git/gitService';
1515
import { Arrays, Iterables, log } from '../system';
1616
import { GitDocumentState, TrackedDocument } from '../trackers/gitDocumentTracker';
1717
import { AnnotationProviderBase } from './annotationProvider';
@@ -88,7 +88,8 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
8888

8989
const highlightDecorationRanges = Arrays.filterMap(blame.lines, l =>
9090
l.sha === sha
91-
? this.editor.document.validateRange(new Range(l.line, 0, l.line, Number.MAX_SAFE_INTEGER))
91+
? // editor lines are 0-based
92+
this.editor.document.validateRange(new Range(l.line - 1, 0, l.line - 1, Number.MAX_SAFE_INTEGER))
9293
: undefined
9394
);
9495

@@ -255,7 +256,7 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase
255256
);
256257
}
257258

258-
private async getCommitForHover(position: Position): Promise<GitCommit | undefined> {
259+
private async getCommitForHover(position: Position): Promise<GitBlameCommit | undefined> {
259260
if (Container.config.hovers.annotations.over !== 'line' && position.character !== 0) return undefined;
260261

261262
const blame = await this.getBlame();

src/annotations/gutterBlameAnnotationProvider.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
5858
}
5959

6060
for (const l of blame.lines) {
61-
const line = l.line;
61+
// editor lines are 0-based
62+
const editorLine = l.line - 1;
6263

6364
if (previousSha === l.sha) {
6465
if (gutter === undefined) continue;
@@ -84,7 +85,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
8485
compacted = true;
8586
}
8687

87-
gutter.range = new Range(line, 0, line, 0);
88+
gutter.range = new Range(editorLine, 0, editorLine, 0);
8889

8990
this.decorations.push(gutter);
9091

@@ -105,7 +106,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
105106
if (gutter !== undefined) {
106107
gutter = {
107108
...gutter,
108-
range: new Range(line, 0, line, 0)
109+
range: new Range(editorLine, 0, editorLine, 0)
109110
};
110111

111112
this.decorations.push(gutter);
@@ -123,7 +124,7 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase {
123124
Annotations.applyHeatmap(gutter, commit.date, computedHeatmap);
124125
}
125126

126-
gutter.range = new Range(line, 0, line, 0);
127+
gutter.range = new Range(editorLine, 0, editorLine, 0);
127128

128129
this.decorations.push(gutter);
129130

src/annotations/heatmapBlameAnnotationProvider.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase
3131
const computedHeatmap = this.getComputedHeatmap(blame);
3232

3333
for (const l of blame.lines) {
34-
const line = l.line;
34+
// editor lines are 0-based
35+
const editorLine = l.line - 1;
3536

3637
heatmap = decorationsMap[l.sha];
3738
if (heatmap !== undefined) {
3839
heatmap = {
3940
...heatmap,
40-
range: new Range(line, 0, line, 0)
41+
range: new Range(editorLine, 0, editorLine, 0)
4142
};
4243

4344
this.decorations.push(heatmap);
@@ -49,7 +50,7 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase
4950
if (commit === undefined) continue;
5051

5152
heatmap = Annotations.heatmap(commit, computedHeatmap, renderOptions) as DecorationOptions;
52-
heatmap.range = new Range(line, 0, line, 0);
53+
heatmap.range = new Range(editorLine, 0, editorLine, 0);
5354

5455
this.decorations.push(heatmap);
5556
decorationsMap[l.sha] = heatmap;

src/annotations/recentChangesAnnotationProvider.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
3232
const commit = await Container.git.getRecentLogCommitForFile(this._uri.repoPath, this._uri.fsPath);
3333
if (commit === undefined) return false;
3434

35-
const diff = await Container.git.getDiffForFile(this._uri, commit.previousSha);
35+
const diff = await Container.git.getDiffForFile(this._uri, commit.sha);
3636
if (diff === undefined) return false;
3737

3838
let start = process.hrtime();
@@ -42,14 +42,15 @@ export class RecentChangesAnnotationProvider extends AnnotationProviderBase {
4242

4343
this.decorations = [];
4444

45-
for (const chunk of diff.chunks) {
46-
let count = chunk.currentPosition.start - 2;
47-
for (const line of chunk.lines) {
48-
if (line.line === undefined) continue;
45+
for (const hunk of diff.hunks) {
46+
// Subtract 2 because editor lines are 0-based and we will be adding 1 in the first iteration of the loop
47+
let count = hunk.currentPosition.start - 2;
48+
for (const line of hunk.lines) {
49+
if (line.current === undefined) continue;
4950

5051
count++;
5152

52-
if (line.state === 'unchanged') continue;
53+
if (line.current.state === 'unchanged') continue;
5354

5455
const range = this.editor.document.validateRange(
5556
new Range(new Position(count, 0), new Position(count, Number.MAX_SAFE_INTEGER))

src/commands/diffLineWithWorking.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export class DiffLineWithWorkingCommand extends ActiveEditorCommand {
5959
previousSha: null,
6060
previousFileName: null
6161
});
62-
args.line = blame.line.line + 1;
62+
// editor lines are 0-based
63+
args.line = blame.line.line - 1;
6364
}
6465
}
6566
catch (ex) {

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export interface Config {
5151
over: 'line' | 'annotation';
5252
};
5353
avatars: boolean;
54+
changesDiff: 'line' | 'hunk';
5455
detailsMarkdownFormat: string;
5556
enabled: boolean;
5657
};

src/git/git.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ export class Git {
518518
ref2?: string,
519519
options: { encoding?: string; filter?: string } = {}
520520
): Promise<string> {
521-
const params = ['diff', '-M', '--no-ext-diff', '--minimal'];
521+
const params = ['diff', '-M', '--no-ext-diff', '-U0', '--minimal'];
522522
if (options.filter) {
523523
params.push(`--diff-filter=${options.filter}`);
524524
}
@@ -866,6 +866,10 @@ export class Git {
866866
}
867867
}
868868

869+
static show_diff(repoPath: string, fileName: string, ref: string) {
870+
return git<string>({ cwd: repoPath }, 'show', '--format=', '--minimal', '-U0', ref, '--', fileName);
871+
}
872+
869873
static show_status(repoPath: string, fileName: string, ref: string) {
870874
return git<string>({ cwd: repoPath }, 'show', '--name-status', '--format=', ref, '--', fileName);
871875
}

0 commit comments

Comments
 (0)