From e97d3bdee4f873e76018f1bb76f3752524105eb8 Mon Sep 17 00:00:00 2001 From: Sergio Bonfiglio Date: Thu, 30 Oct 2025 11:45:25 +0100 Subject: [PATCH 1/2] Shows uncommitted changes after blame calculation as the one calculated by the blame command --- package-lock.json | 4 ++++ src/Blame.ts | 24 +++++++++++++++++++++++- src/BlameManager.ts | 5 +++-- src/HeatMap.ts | 14 ++++++-------- src/HeatMapManager.ts | 23 ++++++++++++++++++----- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2ae248a..892cf27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1013,6 +1013,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz", "integrity": "sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.46.1", "@typescript-eslint/types": "5.46.1", @@ -1719,6 +1720,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2764,6 +2766,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", "dev": true, + "peer": true, "dependencies": { "@eslint/eslintrc": "^1.4.0", "@humanwhocodes/config-array": "^0.11.8", @@ -5684,6 +5687,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/Blame.ts b/src/Blame.ts index f2cae6b..15f1b8b 100644 --- a/src/Blame.ts +++ b/src/Blame.ts @@ -33,6 +33,11 @@ export interface BlamedDocument { export const ZERO_HASH = '0000000000000000000000000000000000000000'; +const NOT_COMMITTED_AUTHOR = { + name: 'Not Committed Yet', + email: 'not.committed.yet', +} + const createAuthor = (author: string, authorStyle: 'full' | 'first' | 'last'): BlamedAuthor => { const blamedAuthor = { name: author, displayName: author }; switch (authorStyle) { @@ -52,7 +57,7 @@ const createAuthor = (author: string, authorStyle: 'full' | 'first' | 'last'): B const createDate = (seconds: number, dateFormat: string): BlamedDate => { const d = new Date(seconds * 1000); return { - dateString: `${d.getFullYear()}${d.getMonth()}${d.getDate()}`, + dateString: `${d.getFullYear()}${d.getMonth()}${d.getDate()}`, date: d, localDate: parseDate(d, dateFormat), dateMillis: d.getTime(), @@ -60,6 +65,23 @@ const createDate = (seconds: number, dateFormat: string): BlamedDate => { }; }; + +export const createNotCommittedLine = (filename: string, line: string, linenumber: string): BlamedDocument => { + const authorStyle = Settings.getAuthorStyle(); + const dateFormat = Settings.getDateFormat(); + + return { + hash: ZERO_HASH, + author: createAuthor(NOT_COMMITTED_AUTHOR.name, authorStyle), + email: NOT_COMMITTED_AUTHOR.email, + codeline: line, + linenumber: linenumber, + date: createDate(Date.now() / 1000, dateFormat), + summary: `Version of ${filename} from ${filename}`, + filename: filename, + }; +}; + export const blame = async (document: vscode.TextDocument): Promise => { const blamed = (await blameFile(document.fileName)).split("\n"); log.trace('Got blamed file'); diff --git a/src/BlameManager.ts b/src/BlameManager.ts index fc3089d..f924b42 100644 --- a/src/BlameManager.ts +++ b/src/BlameManager.ts @@ -4,7 +4,7 @@ import * as vscode from 'vscode'; import DecorationManager from './DecorationManager'; import HeatMapManager from './HeatMapManager'; -import { BlamedDocument, blame } from './Blame'; +import { BlamedDocument, blame, createNotCommittedLine } from './Blame'; import { getFilename } from './Utils'; import ExtensionManager from './ExtensionManager'; import { blameFile } from './Git'; @@ -109,7 +109,8 @@ ${content} const add = event.contentChanges.find(change => change?.text.match(/\n/) && change?.range.start.line === change.range.end.line); const remove = event.contentChanges.find(change => change?.text === '' && change.range.start.line < change.range.end.line); if (add) { - this.blamedDocument.splice(add.range.start.line + 1, 0, { hash: '0' } as BlamedDocument); + const notCommittedLine = createNotCommittedLine(event.document.fileName, add.text, add.range.start.line.toString()); + this.blamedDocument.splice(add.range.start.line + 1, 0, notCommittedLine); } else if (remove) { this.blamedDocument.splice(remove.range.start.line + 1, 1); } diff --git a/src/HeatMap.ts b/src/HeatMap.ts index a5fdb58..92f10b3 100644 --- a/src/HeatMap.ts +++ b/src/HeatMap.ts @@ -6,9 +6,7 @@ import { BlamedDocument, BlamedDate } from './Blame'; import Settings from './Settings'; import { isDarkTheme } from './Utils'; -export interface IndexedHeatMap { - [key: string]: string | vscode.ThemeColor; -} +export type IndexedHeatMap = Map; type RGBC = { r: number; @@ -32,7 +30,7 @@ export const indexHeatColors = (blamedDocument: BlamedDocument[], heatColors: st distinctDates.sort((a, b) => b.dateMillis - a.dateMillis); - const indexedHeatMap = {} as IndexedHeatMap; + const indexedHeatMap = new Map(); const strategy = Settings.getHeatColorIndexStrategy(); @@ -41,26 +39,26 @@ export const indexHeatColors = (blamedDocument: BlamedDocument[], heatColors: st const last = distinctDates.pop(); if (first) { - indexedHeatMap[first.dateString] = heatColors[0]; + indexedHeatMap.set(first.dateMillis, heatColors[0]); } const commitPerColor = Math.max(1, Math.round(distinctDates.length / (heatColors.length - 2))); let colorIndex = 1; distinctDates.forEach((date, idx) => { - indexedHeatMap[date.dateString] = heatColors[colorIndex] || heatColors.at(-1)!; + indexedHeatMap.set(date.dateMillis, heatColors[colorIndex] || heatColors.at(-1)!); if (idx % commitPerColor === 0) { colorIndex++; } }); if (last) { - indexedHeatMap[last.dateString] = heatColors.at(-1)!; + indexedHeatMap.set(last.dateMillis, heatColors.at(-1)!); } } else { distinctDates.forEach((date, idx) => { if (idx < heatColors.length) { - indexedHeatMap[date.dateString] = heatColors[idx]; + indexedHeatMap.set(date.dateMillis, heatColors[idx]); } }); } diff --git a/src/HeatMapManager.ts b/src/HeatMapManager.ts index 34ab39e..fd57d3b 100644 --- a/src/HeatMapManager.ts +++ b/src/HeatMapManager.ts @@ -1,14 +1,15 @@ /** * License GPL-2.0 */ -import { generateHeatMapColors, IndexedHeatMap, indexHeatColors } from './HeatMap'; +import * as vscode from 'vscode'; import { BlamedDate, BlamedDocument } from './Blame'; -import { isDarkTheme } from './Utils'; +import { generateHeatMapColors, IndexedHeatMap, indexHeatColors } from './HeatMap'; import Settings from './Settings'; +import { isDarkTheme } from './Utils'; class HeatMapManager { - private heatMap: IndexedHeatMap = {}; + private heatMap: IndexedHeatMap = new Map(); private heatColors: string[] = []; private isDarkTheme: boolean = isDarkTheme(); @@ -20,8 +21,20 @@ class HeatMapManager { this.heatMap = indexHeatColors(document, this.heatColors); } - getHeatColor(date: BlamedDate) { - return this.heatMap[date.dateString] || this.heatColors.at(-1); + getHeatColor(date: BlamedDate): string | vscode.ThemeColor { + return this.heatMap.get(date.dateMillis) || this.getClosestColor(date); + } + + // Find the closest color for a given date. Used when the date is not directly indexed + // e.g.: for uncommitted/unsaved changes. + private getClosestColor(date: BlamedDate): string | vscode.ThemeColor { + const sortedKeys = Array.from(this.heatMap.keys()).sort((a, b) => b - a); + for (const key of sortedKeys) { + if (key <= date.dateMillis) { + return this.heatMap.get(key)!; + } + } + return this.heatColors.at(-1)!; } refreshColors() { From 8d2ac235b4110f8b313112ece856134b7f5363f6 Mon Sep 17 00:00:00 2001 From: Sergio Bonfiglio Date: Thu, 30 Oct 2025 12:04:23 +0100 Subject: [PATCH 2/2] fix: use getFileName() when created uncommitted lines --- src/BlameManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BlameManager.ts b/src/BlameManager.ts index f924b42..854af12 100644 --- a/src/BlameManager.ts +++ b/src/BlameManager.ts @@ -109,7 +109,7 @@ ${content} const add = event.contentChanges.find(change => change?.text.match(/\n/) && change?.range.start.line === change.range.end.line); const remove = event.contentChanges.find(change => change?.text === '' && change.range.start.line < change.range.end.line); if (add) { - const notCommittedLine = createNotCommittedLine(event.document.fileName, add.text, add.range.start.line.toString()); + const notCommittedLine = createNotCommittedLine(getFilename(event.document.fileName), add.text, add.range.start.line.toString()); this.blamedDocument.splice(add.range.start.line + 1, 0, notCommittedLine); } else if (remove) { this.blamedDocument.splice(remove.range.start.line + 1, 1);