From 44566b4ec9adce9c44e8258799ef00d629a56615 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Tue, 19 Jun 2018 01:18:56 -0400 Subject: [PATCH] Revamps heatmap - median relative age & customizable cold/hot colors Closes #419 - Make blame heatmap color configurable --- CHANGELOG.md | 6 + README.md | 12 +- images/ss-heatmap.png | Bin 14729 -> 14719 bytes package.json | 18 + src/annotations/annotations.ts | 62 +- src/annotations/blameAnnotationProvider.ts | 65 +- src/annotations/fileAnnotationController.ts | 3 +- src/annotations/gutterBlameAnnotationProvider.ts | 10 +- src/annotations/heatmapBlameAnnotationProvider.ts | 7 +- src/ui/config.ts | 3 + src/ui/images/settings/heatmap.png | Bin 10473 -> 10475 bytes src/ui/settings/index.html | 723 ++++++++++++++++------ 12 files changed, 686 insertions(+), 223 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dec8dc1..83c23a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Added +- Adds completely revamped **heatmap** annotations + - The indicator's, now customizable, color will either be hot or cold based on the age of the most recent change (cold after 90 days by default) — closes [#419](https://github.com/eamodio/vscode-gitlens/issues/419) + - The indicator's brightness ranges from bright (newer) to dim (older) based on the relative age, which is calculated from the median age of all the changes in the file + - Adds `gitlens.heatmap.ageThreshold` setting to specify the age of the most recent change (in days) after which the gutter heatmap annotations will be cold rather than hot (i.e. will use `gitlens.heatmap.coldColor` instead of `gitlens.heatmap.hotColor`) + - Adds `gitlens.heatmap.coldColor` setting to specify the base color of the gutter heatmap annotations when the most recent change is older (cold) than the `gitlens.heatmap.ageThreshold` setting + - Adds `gitlens.heatmap.hotColor` setting to specify the base color of the gutter heatmap annotations when the most recent change is newer (hot) than the `gitlens.heatmap.ageThreshold` setting - Adds new branch history node under the **Repository Status** node in the *GitLens* explorer - Adds GitLab and Visual Studio Team Services icons to the remote nodes in the *GitLens* explorer — thanks to [PR #421](https://github.com/eamodio/vscode-gitlens/pull/421) by Maxim Pekurin ([@pmaxim25](https://github.com/pmaxim25)) diff --git a/README.md b/README.md index eace2d0..526c59c 100644 --- a/README.md +++ b/README.md @@ -342,8 +342,8 @@ An on-demand, [customizable](#gitlens-results-explorer-settings "Jump to the Git - Adds on-demand, [customizable](#gutter-blame-settings "Jump to the Gutter Blame settings"), and [themable](#themable-colors "Jump to the Themable Colors"), **gutter blame annotations** for the whole file - Contains the commit message and date, by [default](#gutter-blame-settings "Jump to the Gutter Blame settings") - - Adds a **heatmap** (age) indicator on right edge (by [default](#gutter-blame-settings "Jump to the Gutter Blame settings")) of the gutter to provide an easy, at-a-glance way to tell the age of a line ([optional](#gutter-blame-settings "Jump to the Gutter Blame settings"), on by default) - - Indicator ranges from bright yellow (newer) to dark brown (older) + - Adds a **heatmap** (age) indicator on right edge (by [default](#gutter-blame-settings "Jump to the Gutter Blame settings")) of the gutter to provide an easy, at-a-glance way to tell how recently lines were changed ([optional](#gutter-blame-settings "Jump to the Gutter Blame settings"), on by default) + - See the [gutter heatmap](#gutter-Heatmap "Jump to the Gutter Heatmap") section below for more details - Adds a *Toggle File Blame Annotations* command (`gitlens.toggleFileBlame`) with a shortcut of `alt+b` to toggle the blame annotations on and off - Press `Escape` to turn off the annotations @@ -353,8 +353,9 @@ An on-demand, [customizable](#gitlens-results-explorer-settings "Jump to the Git Gutter Heatmap

-- Adds an on-demand **heatmap** to the edge of the gutter to show the relative age of a line - - Indicator ranges from bright yellow (newer) to dark brown (older) +- Adds an on-demand **heatmap** to the edge of the gutter to show how recently lines were changed + - The indicator's [customizable](#gutter-heatmap-settings "Jump to the Gutter Heatmap settings") color will either be hot or cold based on the age of the most recent change (cold after 90 days by [default](#gutter-heatmap-settings "Jump to the Gutter Heatmap settings")) + - The indicator's brightness ranges from bright (newer) to dim (older) based on the relative age, which is calculated from the median age of all the changes in the file - Adds *Toggle File Heatmap Annotations* command (`gitlens.toggleFileHeatmap`) to toggle the heatmap on and off - Press `Escape` to turn off the annotations @@ -717,6 +718,9 @@ See also [Explorer Settings](#explorer-settings "Jump to the Explorer settings") |Name | Description |-----|------------ +|`gitlens.heatmap.ageThreshold`|Specifies the age of the most recent change (in days) after which the gutter heatmap annotations will be cold rather than hot (i.e. will use `gitlens.heatmap.coldColor` instead of `gitlens.heatmap.hotColor`) +|`gitlens.heatmap.coldColor`|Specifies the base color of the gutter heatmap annotations when the most recent change is older (cold) than the `gitlens.heatmap.ageThreshold` setting +|`gitlens.heatmap.hotColor`|Specifies the base color of the gutter heatmap annotations when the most recent change is newer (hot) than the `gitlens.heatmap.ageThreshold` setting |`gitlens.heatmap.toggleMode`|Specifies how the gutter heatmap annotations will be toggled
`file` - toggle each file individually
`window` - toggle the window, i.e. all files at once ### Hover Settings diff --git a/images/ss-heatmap.png b/images/ss-heatmap.png index 7e7fc182ab9062c470077b6d56cfd2e6a53beebf..3227a228265f7ab5523c1e33710bcad530fd546b 100644 GIT binary patch delta 12088 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 Oo2ru5gAWQ%Uj9El6_jxR delta 12135 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 h0Qj|wt3K1mSaGMV!^*\#\*\_\-\+\.]/g; // 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___'; +let computedHeatmapColor: { + color: string, + rgb: string +}; + export class Annotations { - static applyHeatmap(decoration: DecorationOptions, date: Date, now: number) { - const color = this.getHeatmapColor(now, date); + static applyHeatmap(decoration: DecorationOptions, date: Date, heatmap: ComputedHeatmap) { + const color = this.getHeatmapColor(date, heatmap); (decoration.renderOptions!.before! as any).borderColor = color; } - private static getHeatmapColor(now: number, date: Date) { - const days = Dates.dateDaysFromNow(date, now); - - if (days <= 2) return '#ffeca7'; - if (days <= 7) return '#ffdd8c'; - if (days <= 14) return '#ffdd7c'; - if (days <= 30) return '#fba447'; - if (days <= 60) return '#f68736'; - if (days <= 90) return '#f37636'; - if (days <= 180) return '#ca6632'; - if (days <= 365) return '#c0513f'; - if (days <= 730) return '#a2503a'; - return '#793738'; + private static getHeatmapColor(date: Date, heatmap: ComputedHeatmap) { + const baseColor = heatmap.cold + ? heatmap.colors.cold + : heatmap.colors.hot; + + const age = heatmap.computeAge(date); + if (age === 0) return baseColor; + + if (computedHeatmapColor === undefined || computedHeatmapColor.color !== baseColor) { + let rgba = toRgba(baseColor); + if (rgba == null) { + rgba = toRgba(heatmap.cold ? defaultHeatmapColdColor : defaultHeatmapHotColor)!; + } + + const [r, g, b] = rgba; + computedHeatmapColor = { + color: baseColor, + rgb: `${r}, ${g}, ${b}` + }; + } + + return `rgba(${computedHeatmapColor.rgb}, ${(1 - (age / 10)).toFixed(2)})`; } private static getHoverCommandBar(commit: GitCommit, hasRemote: boolean, annotationType?: FileAnnotationType, line: number = 0) { @@ -213,14 +239,14 @@ export class Annotations { } as IRenderOptions; } - static heatmap(commit: GitCommit, now: number, renderOptions: IRenderOptions): DecorationOptions { + static heatmap(commit: GitCommit, heatmap: ComputedHeatmap, renderOptions: IRenderOptions): DecorationOptions { const decoration = { renderOptions: { before: { ...renderOptions } } as DecorationInstanceRenderOptions } as DecorationOptions; - Annotations.applyHeatmap(decoration, commit.date, now); + Annotations.applyHeatmap(decoration, commit.date, heatmap); return decoration; } diff --git a/src/annotations/blameAnnotationProvider.ts b/src/annotations/blameAnnotationProvider.ts index 97dbf0a..852389e 100644 --- a/src/annotations/blameAnnotationProvider.ts +++ b/src/annotations/blameAnnotationProvider.ts @@ -2,7 +2,7 @@ import { Arrays, Iterables } from '../system'; import { CancellationToken, Disposable, Hover, HoverProvider, languages, Position, Range, TextDocument, TextEditor, TextEditorDecorationType } from 'vscode'; import { AnnotationProviderBase } from './annotationProvider'; -import { Annotations } from './annotations'; +import { Annotations, ComputedHeatmap } from './annotations'; import { Container } from '../container'; import { GitDocumentState, TrackedDocument } from '../trackers/gitDocumentTracker'; import { GitBlame, GitCommit, GitUri } from '../gitService'; @@ -91,6 +91,69 @@ export abstract class BlameAnnotationProviderBase extends AnnotationProviderBase return blame; } + protected getComputedHeatmap(blame: GitBlame): ComputedHeatmap { + const dates = []; + + let commit; + let previousSha; + for (const l of blame.lines) { + if (previousSha === l.sha) continue; + previousSha = l.sha; + + commit = blame.commits.get(l.sha); + if (commit === undefined) continue; + + dates.push(commit.date); + } + + dates.sort((a, b) => a.getTime() - b.getTime()); + + const half = Math.floor(dates.length / 2); + const median = dates.length % 2 + ? dates[half].getTime() + : (dates[half - 1].getTime() + dates[half].getTime()) / 2.0; + + const lookup: number[] = []; + + const newest = dates[dates.length - 1].getTime(); + let step = (newest - median) / 5; + for (let i = 5; i > 0; i--) { + lookup.push(median + (step * i)); + } + + lookup.push(median); + + const oldest = dates[0].getTime(); + step = (median - oldest) / 4; + for (let i = 1; i <= 4; i++) { + lookup.push(median - (step * i)); + } + + const d = new Date(); + d.setDate(d.getDate() - (Container.config.heatmap.ageThreshold || 90)); + + return { + cold: newest < d.getTime(), + colors: { + cold: Container.config.heatmap.coldColor, + hot: Container.config.heatmap.hotColor + }, + median: median, + newest: newest, + oldest: oldest, + computeAge: (date: Date) => { + const time = date.getTime(); + let index = 0; + for (let i = 0; i < lookup.length; i++) { + index = i; + if (time >= lookup[i]) break; + } + + return index; + } + }; + } + registerHoverProviders(providers: { details: boolean, changes: boolean }) { if (!Container.config.hovers.enabled || !Container.config.hovers.annotations.enabled || (!providers.details && !providers.changes)) return; diff --git a/src/annotations/fileAnnotationController.ts b/src/annotations/fileAnnotationController.ts index 23a0e26..36cf8a5 100644 --- a/src/annotations/fileAnnotationController.ts +++ b/src/annotations/fileAnnotationController.ts @@ -161,6 +161,7 @@ export class FileAnnotationController extends Disposable { if (configuration.changed(e, configuration.name('blame').value) || configuration.changed(e, configuration.name('recentChanges').value) || + configuration.changed(e, configuration.name('heatmap').value) || configuration.changed(e, configuration.name('hovers').value)) { // Since the configuration has changed -- reset any visible annotations for (const provider of this._annotationProviders.values()) { @@ -416,7 +417,7 @@ export class FileAnnotationController extends Disposable { this._keyboardScope = undefined; } - private async showAnnotationsCore(currentProvider: AnnotationProviderBase | undefined, editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number, progress?: Progress<{ message: string}>): Promise { + private async showAnnotationsCore(currentProvider: AnnotationProviderBase | undefined, editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number, progress?: Progress<{ message: string }>): Promise { if (progress !== undefined) { let annotationsLabel = 'annotations'; switch (type) { diff --git a/src/annotations/gutterBlameAnnotationProvider.ts b/src/annotations/gutterBlameAnnotationProvider.ts index 005d2fd..96e9e05 100644 --- a/src/annotations/gutterBlameAnnotationProvider.ts +++ b/src/annotations/gutterBlameAnnotationProvider.ts @@ -33,7 +33,6 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { tokenOptions: tokenOptions }; - const now = Date.now(); const avatars = cfg.avatars; const gravatarDefault = Container.config.defaultGravatarsStyle; const separateLines = cfg.separateLines; @@ -48,6 +47,11 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { let gutter: DecorationOptions | undefined; let previousSha: string | undefined; + let computedHeatmap; + if (cfg.heatmap.enabled) { + computedHeatmap = this.getComputedHeatmap(blame); + } + for (const l of blame.lines) { const line = l.line; @@ -106,8 +110,8 @@ export class GutterBlameAnnotationProvider extends BlameAnnotationProviderBase { gutter = Annotations.gutter(commit, cfg.format, options, renderOptions); - if (cfg.heatmap.enabled) { - Annotations.applyHeatmap(gutter, commit.date, now); + if (computedHeatmap !== undefined) { + Annotations.applyHeatmap(gutter, commit.date, computedHeatmap); } gutter.range = new Range(line, 0, line, 0); diff --git a/src/annotations/heatmapBlameAnnotationProvider.ts b/src/annotations/heatmapBlameAnnotationProvider.ts index 86a1250..c5f6330 100644 --- a/src/annotations/heatmapBlameAnnotationProvider.ts +++ b/src/annotations/heatmapBlameAnnotationProvider.ts @@ -17,7 +17,6 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase const start = process.hrtime(); - const now = Date.now(); const renderOptions = Annotations.heatmapRenderOptions(); this.decorations = []; @@ -26,6 +25,8 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase let commit: GitBlameCommit | undefined; let heatmap: DecorationOptions | undefined; + const computedHeatmap = this.getComputedHeatmap(blame); + for (const l of blame.lines) { const line = l.line; @@ -44,7 +45,7 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase commit = blame.commits.get(l.sha); if (commit === undefined) continue; - heatmap = Annotations.heatmap(commit, now, renderOptions); + heatmap = Annotations.heatmap(commit, computedHeatmap, renderOptions); heatmap.range = new Range(line, 0, line, 0); this.decorations.push(heatmap); @@ -62,4 +63,4 @@ export class HeatmapBlameAnnotationProvider extends BlameAnnotationProviderBase this.selection(shaOrLine, blame); return true; } -} \ No newline at end of file +} diff --git a/src/ui/config.ts b/src/ui/config.ts index 37f4b50..992ed57 100644 --- a/src/ui/config.ts +++ b/src/ui/config.ts @@ -299,6 +299,9 @@ export interface IConfig { gitExplorer: IGitExplorerConfig; heatmap: { + ageThreshold: number; + coldColor: string; + hotColor: string; toggleMode: AnnotationsToggleMode; }; diff --git a/src/ui/images/settings/heatmap.png b/src/ui/images/settings/heatmap.png index 2f1cdb17b45d873bdd30a8562716c53ac3654976..b57c4af300cfe6c9a875c6aa7e097d2578681d80 100644 GIT binary patch literal 10475 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 b7+xkQX_`Z#m7xV#_8=7n4f$%>XF>k~{YLT0 diff --git a/src/ui/settings/index.html b/src/ui/settings/index.html index 85678ce..7b1290f 100644 --- a/src/ui/settings/index.html +++ b/src/ui/settings/index.html @@ -2,7 +2,7 @@ - + @@ -12,7 +12,7 @@
- + @@ -97,13 +98,13 @@