Skip to content

Commit 48a1ca7

Browse files
committed
Adds new recent changes annotations
1 parent 23c7171 commit 48a1ca7

19 files changed

+299
-136
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ GitLens provides an unobtrusive blame annotation at the end of the current line,
5353
- Adds a `Toggle Line Blame Annotations` command (`gitlens.toggleLineBlame`) to toggle the current line blame annotations on and off
5454
- Also adds a `Show Line Blame Annotations` command (`gitlens.showLineBlame`)
5555

56+
### Git Recent Changes Annotations
57+
58+
- Adds on-demand, [customizable](#file-recent-changes-annotation-settings) and [themeable](#theme-settings), **recent changes annotations** of the whole file
59+
- Highlights all of lines changed in the most recent commit
60+
- Also adds a `changes` (diff) hover annotation to the current line annotation which provides **instant** access to the line's previous version ([optional](#file-recent-changes-annotation-settings), on by default)
61+
62+
- Adds `Toggle Recent File Changes Annotations` command (`gitlens.toggleFileRecentChanges`) to toggle the recent changes annotations on and off
63+
5664
### Git Code Lens
5765

5866
- Adds **code lens** to the top of the file and on code blocks ([optional](#code-lens-settings), on by default)
@@ -244,6 +252,14 @@ GitLens is highly customizable and provides many configuration settings to allow
244252
|`gitlens.annotations.line.hover.details`|Specifies whether or not to provide a commit details hover annotation for the current line
245253
|`gitlens.annotations.line.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotation for the current line
246254

255+
### File Recent Changes Annotation Settings
256+
257+
|Name | Description
258+
|-----|------------
259+
|`gitlens.recentChanges.file.lineHighlight.locations`|Specifies where the highlights of the recently changed lines will be shown<br />`gutter` - adds a gutter glyph<br />`line` - adds a full-line highlight background color<br />`overviewRuler` - adds a decoration to the overviewRuler (scroll bar)
260+
|`gitlens.annotations.file.recentChanges.hover.changes`|Specifies whether or not to provide a changes (diff) hover annotations
261+
|`gitlens.annotations.file.recentChanges.hover.wholeLine`|Specifies whether or not to trigger hover annotations over the whole line
262+
247263
### Code Lens Settings
248264

249265
|Name | Description

package.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@
122122
"default": true,
123123
"description": "Specifies whether or not to trigger hover annotations over the whole line"
124124
},
125+
"gitlens.annotations.file.recentChanges.hover.changes": {
126+
"type": "boolean",
127+
"default": true,
128+
"description": "Specifies whether or not to provide a changes (diff) hover annotations"
129+
},
130+
"gitlens.annotations.file.recentChanges.hover.wholeLine": {
131+
"type": "boolean",
132+
"default": true,
133+
"description": "Specifies whether or not to trigger hover annotations over the whole line"
134+
},
125135
"gitlens.annotations.line.hover.details": {
126136
"type": "boolean",
127137
"default": true,
@@ -205,6 +215,26 @@
205215
],
206216
"description": "Specifies the type of blame annotations that will be shown for the current line\n `trailing` - adds an annotation to the end of the current line\n `hover` - shows annotations when hovering over the current line"
207217
},
218+
"gitlens.recentChanges.file.lineHighlight.locations": {
219+
"type": "array",
220+
"default": [
221+
"gutter",
222+
"line",
223+
"overviewRuler"
224+
],
225+
"items": {
226+
"type": "string",
227+
"enum": [
228+
"gutter",
229+
"line",
230+
"overviewRuler"
231+
]
232+
},
233+
"minItems": 1,
234+
"maxItems": 3,
235+
"uniqueItems": true,
236+
"description": "Specifies where the highlights of the recently changed lines will be shown\n `gutter` - adds a gutter glyph\n `line` - adds a full-line highlight background color\n `overviewRuler` - adds a decoration to the overviewRuler (scroll bar)"
237+
},
208238
"gitlens.codeLens.enabled": {
209239
"type": "boolean",
210240
"default": true,
@@ -757,6 +787,11 @@
757787
"dark": "images/git-icon-dark.svg",
758788
"light": "images/git-icon-light.svg"
759789
}
790+
},
791+
{
792+
"command": "gitlens.toggleFileRecentChanges",
793+
"title": "Toggle Recent File Changes Annotations",
794+
"category": "GitLens"
760795
},
761796
{
762797
"command": "gitlens.toggleLineBlame",
@@ -920,6 +955,10 @@
920955
{
921956
"command": "gitlens.toggleFileBlame",
922957
"when": "gitlens:isBlameable"
958+
},
959+
{
960+
"command": "gitlens.toggleFileRecentChanges",
961+
"when": "gitlens:isTracked"
923962
},
924963
{
925964
"command": "gitlens.toggleLineBlame",

src/annotations/annotationController.ts

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,29 @@ import { Functions, Objects } from '../system';
33
import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextDocumentChangeEvent, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode';
44
import { AnnotationProviderBase } from './annotationProvider';
55
import { TextDocumentComparer, TextEditorComparer } from '../comparers';
6-
import { BlameLineHighlightLocations, ExtensionKey, FileAnnotationType, IConfig, themeDefaults } from '../configuration';
6+
import { ExtensionKey, IConfig, LineHighlightLocations, themeDefaults } from '../configuration';
77
import { BlameabilityChangeEvent, GitContextTracker, GitService, GitUri } from '../gitService';
88
import { GutterBlameAnnotationProvider } from './gutterBlameAnnotationProvider';
99
import { HoverBlameAnnotationProvider } from './hoverBlameAnnotationProvider';
1010
import { Logger } from '../logger';
11+
import { RecentChangesAnnotationProvider } from './recentChangesAnnotationProvider';
1112
import { WhitespaceController } from './whitespaceController';
1213

14+
export type FileAnnotationType = 'gutter' | 'hover' | 'recentChanges';
15+
export const FileAnnotationType = {
16+
Gutter: 'gutter' as FileAnnotationType,
17+
Hover: 'hover' as FileAnnotationType,
18+
RecentChanges: 'recentChanges' as FileAnnotationType
19+
};
20+
1321
export const Decorations = {
14-
annotation: window.createTextEditorDecorationType({
22+
blameAnnotation: window.createTextEditorDecorationType({
1523
isWholeLine: true,
1624
textDecoration: 'none'
1725
} as DecorationRenderOptions),
18-
highlight: undefined as TextEditorDecorationType | undefined
26+
blameHighlight: undefined as TextEditorDecorationType | undefined,
27+
recentChangesAnnotation: undefined as TextEditorDecorationType | undefined,
28+
recentChangesHighlight: undefined as TextEditorDecorationType | undefined
1929
};
2030

2131
export class AnnotationController extends Disposable {
@@ -46,8 +56,8 @@ export class AnnotationController extends Disposable {
4656
dispose() {
4757
this._annotationProviders.forEach(async (p, i) => await this.clear(i));
4858

49-
Decorations.annotation && Decorations.annotation.dispose();
50-
Decorations.highlight && Decorations.highlight.dispose();
59+
Decorations.blameAnnotation && Decorations.blameAnnotation.dispose();
60+
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
5161

5262
this._annotationsDisposable && this._annotationsDisposable.dispose();
5363
this._whitespaceController && this._whitespaceController.dispose();
@@ -82,50 +92,83 @@ export class AnnotationController extends Disposable {
8292
}
8393

8494
const cfg = workspace.getConfiguration().get<IConfig>(ExtensionKey)!;
85-
const cfgHighlight = cfg.blame.file.lineHighlight;
95+
const cfgBlameHighlight = cfg.blame.file.lineHighlight;
96+
const cfgChangesHighlight = cfg.recentChanges.file.lineHighlight;
8697
const cfgTheme = cfg.theme.lineHighlight;
8798

88-
if (!Objects.areEquivalent(cfgHighlight, this._config && this._config.blame.file.lineHighlight) ||
99+
if (!Objects.areEquivalent(cfgBlameHighlight, this._config && this._config.blame.file.lineHighlight) ||
100+
!Objects.areEquivalent(cfgChangesHighlight, this._config && this._config.recentChanges.file.lineHighlight) ||
89101
!Objects.areEquivalent(cfgTheme, this._config && this._config.theme.lineHighlight)) {
90102
changed = true;
91103

92-
Decorations.highlight && Decorations.highlight.dispose();
104+
Decorations.blameHighlight && Decorations.blameHighlight.dispose();
93105

94-
if (cfgHighlight.enabled) {
95-
Decorations.highlight = window.createTextEditorDecorationType({
106+
if (cfgBlameHighlight.enabled) {
107+
Decorations.blameHighlight = window.createTextEditorDecorationType({
96108
gutterIconSize: 'contain',
97109
isWholeLine: true,
98110
overviewRulerLane: OverviewRulerLane.Right,
99111
dark: {
100-
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
112+
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
101113
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
102114
: undefined,
103-
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
115+
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
104116
? this.context.asAbsolutePath('images/blame-dark.svg')
105117
: undefined,
106-
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
118+
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
107119
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
108120
: undefined
109121
},
110122
light: {
111-
backgroundColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.Line)
123+
backgroundColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.Line)
112124
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
113125
: undefined,
114-
gutterIconPath: cfgHighlight.locations.includes(BlameLineHighlightLocations.Gutter)
126+
gutterIconPath: cfgBlameHighlight.locations.includes(LineHighlightLocations.Gutter)
115127
? this.context.asAbsolutePath('images/blame-light.svg')
116128
: undefined,
117-
overviewRulerColor: cfgHighlight.locations.includes(BlameLineHighlightLocations.OverviewRuler)
129+
overviewRulerColor: cfgBlameHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
118130
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
119131
: undefined
120132
}
121133
});
122134
}
123135
else {
124-
Decorations.highlight = undefined;
136+
Decorations.blameHighlight = undefined;
125137
}
138+
139+
Decorations.recentChangesHighlight && Decorations.recentChangesHighlight.dispose();
140+
141+
Decorations.recentChangesHighlight = window.createTextEditorDecorationType({
142+
gutterIconSize: 'contain',
143+
isWholeLine: true,
144+
overviewRulerLane: OverviewRulerLane.Right,
145+
dark: {
146+
backgroundColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.Line)
147+
? cfgTheme.dark.backgroundColor || themeDefaults.lineHighlight.dark.backgroundColor
148+
: undefined,
149+
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
150+
? this.context.asAbsolutePath('images/blame-dark.svg')
151+
: undefined,
152+
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
153+
? cfgTheme.dark.overviewRulerColor || themeDefaults.lineHighlight.dark.overviewRulerColor
154+
: undefined
155+
},
156+
light: {
157+
backgroundColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.Line)
158+
? cfgTheme.light.backgroundColor || themeDefaults.lineHighlight.light.backgroundColor
159+
: undefined,
160+
gutterIconPath: cfgChangesHighlight.locations.includes(LineHighlightLocations.Gutter)
161+
? this.context.asAbsolutePath('images/blame-light.svg')
162+
: undefined,
163+
overviewRulerColor: cfgChangesHighlight.locations.includes(LineHighlightLocations.OverviewRuler)
164+
? cfgTheme.light.overviewRulerColor || themeDefaults.lineHighlight.light.overviewRulerColor
165+
: undefined
166+
}
167+
});
126168
}
127169

128170
if (!Objects.areEquivalent(cfg.blame.file, this._config && this._config.blame.file) ||
171+
!Objects.areEquivalent(cfg.recentChanges.file, this._config && this._config.recentChanges.file) ||
129172
!Objects.areEquivalent(cfg.annotations, this._config && this._config.annotations) ||
130173
!Objects.areEquivalent(cfg.theme.annotations, this._config && this._config.theme.annotations)) {
131174
changed = true;
@@ -138,7 +181,12 @@ export class AnnotationController extends Disposable {
138181
for (const provider of this._annotationProviders.values()) {
139182
if (provider === undefined) continue;
140183

141-
provider.reset(this._whitespaceController);
184+
if (provider.annotationType === FileAnnotationType.RecentChanges) {
185+
provider.reset(Decorations.recentChangesAnnotation, Decorations.recentChangesHighlight);
186+
}
187+
else {
188+
provider.reset(Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController);
189+
}
142190
}
143191
}
144192
}
@@ -184,10 +232,15 @@ export class AnnotationController extends Disposable {
184232
let provider: AnnotationProviderBase | undefined = undefined;
185233
switch (type) {
186234
case FileAnnotationType.Gutter:
187-
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, this._whitespaceController, this.git, gitUri);
235+
provider = new GutterBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
188236
break;
237+
189238
case FileAnnotationType.Hover:
190-
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.annotation, Decorations.highlight, this._whitespaceController, this.git, gitUri);
239+
provider = new HoverBlameAnnotationProvider(this.context, editor, Decorations.blameAnnotation, Decorations.blameHighlight, this._whitespaceController, this.git, gitUri);
240+
break;
241+
242+
case FileAnnotationType.RecentChanges:
243+
provider = new RecentChangesAnnotationProvider(this.context, editor, undefined, Decorations.recentChangesHighlight!, this.git, gitUri);
191244
break;
192245
}
193246
if (provider === undefined || !(await provider.validate())) return false;
@@ -219,13 +272,17 @@ export class AnnotationController extends Disposable {
219272
}
220273

221274
async toggleAnnotations(editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number): Promise<boolean> {
222-
if (!editor || !editor.document || !this.git.isEditorBlameable(editor)) return false;
275+
if (!editor || !editor.document || type === FileAnnotationType.RecentChanges ? !this.git.isTrackable(editor.document.uri) : !this.git.isEditorBlameable(editor)) return false;
223276

224277
const provider = this._annotationProviders.get(editor.viewColumn || -1);
225278
if (provider === undefined) return this.showAnnotations(editor, type, shaOrLine);
226279

280+
const reopen = provider.annotationType !== type;
227281
await this.clear(provider.editor.viewColumn || -1);
228-
return false;
282+
283+
if (!reopen) return false;
284+
285+
return this.showAnnotations(editor, type, shaOrLine);
229286
}
230287

231288
private _onBlameabilityChanged(e: BlameabilityChangeEvent) {

0 commit comments

Comments
 (0)