From e22e3e729360d29e15b6bf3e0301b9a9eff4770e Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:47:43 +0100 Subject: [PATCH] Git - improve timeline hover (#237365) --- extensions/git/src/blame.ts | 6 +-- extensions/git/src/commands.ts | 8 ++-- extensions/git/src/git.ts | 5 ++ extensions/git/src/timelineProvider.ts | 47 +++++++++++++++---- .../contrib/timeline/browser/timelinePane.ts | 2 +- 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index a39781cdc41..c4ca348eef9 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -265,9 +265,9 @@ export class GitBlameController { markdownString.appendMarkdown(`\n\n---\n\n`); } - markdownString.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(documentUri, blameInformationOrCommit.hash)} \`](command:git.blameStatusBarItem.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`); + markdownString.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(documentUri, blameInformationOrCommit.hash)} \`](command:git.viewCommit2?${encodeURIComponent(JSON.stringify([documentUri, blameInformationOrCommit.hash]))} "${l10n.t('View Commit')}")`); markdownString.appendMarkdown(' '); - markdownString.appendMarkdown(`[$(copy)](command:git.blameStatusBarItem.copyContent?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`); + markdownString.appendMarkdown(`[$(copy)](command:git.copyContentToClipboard?${encodeURIComponent(JSON.stringify(blameInformationOrCommit.hash))} "${l10n.t('Copy Commit Hash')}")`); markdownString.appendMarkdown('  |  '); markdownString.appendMarkdown(`[$(gear)](command:workbench.action.openSettings?%5B%22git.blame%22%5D "${l10n.t('Open Settings')}")`); @@ -702,7 +702,7 @@ class GitBlameStatusBarItem { this._statusBarItem.tooltip = this._controller.getBlameInformationHover(window.activeTextEditor.document.uri, blameInformation[0].blameInformation); this._statusBarItem.command = { title: l10n.t('View Commit'), - command: 'git.blameStatusBarItem.viewCommit', + command: 'git.viewCommit2', arguments: [window.activeTextEditor.document.uri, blameInformation[0].blameInformation.hash] } satisfies Command; } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f7476a23214..ab344ad5023 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -4346,8 +4346,8 @@ export class CommandCenter { env.clipboard.writeText(historyItem.message); } - @command('git.blameStatusBarItem.viewCommit', { repository: true }) - async viewStatusBarCommit(repository: Repository, historyItemId: string): Promise { + @command('git.viewCommit2', { repository: true }) + async viewCommit2(repository: Repository, historyItemId: string): Promise { if (!repository || !historyItemId) { return; } @@ -4365,8 +4365,8 @@ export class CommandCenter { await commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources }); } - @command('git.blameStatusBarItem.copyContent') - async blameStatusBarCopyContent(content: string): Promise { + @command('git.copyContentToClipboard') + async copyContentToClipboard(content: string): Promise { if (typeof content !== 'string') { return; } diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 5bdfd655dbc..0ca691fda64 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -62,6 +62,7 @@ export interface LogFileOptions { /** Optional. Specifies whether to start retrieving log entries in reverse order. */ readonly reverse?: boolean; readonly sortByAuthorDate?: boolean; + readonly shortStats?: boolean; } function parseVersion(raw: string): string { @@ -1290,6 +1291,10 @@ export class Repository { } } + if (options?.shortStats) { + args.push('--shortstat'); + } + if (options?.sortByAuthorDate) { args.push('--author-date-order'); } diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 5788ecc53dd..4f5c53f9e50 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -10,6 +10,8 @@ import { debounce } from './decorators'; import { emojify, ensureEmojis } from './emoji'; import { CommandCenter } from './commands'; import { OperationKind, OperationResult } from './operation'; +import { getCommitShortHash } from './util'; +import { CommitShortStat } from './git'; export class GitTimelineItem extends TimelineItem { static is(item: TimelineItem): item is GitTimelineItem { @@ -48,18 +50,46 @@ export class GitTimelineItem extends TimelineItem { return this.shortenRef(this.previousRef); } - setItemDetails(author: string, email: string | undefined, date: string, message: string): void { + setItemDetails(uri: Uri, hash: string | undefined, author: string, email: string | undefined, date: string, message: string, shortStat?: CommitShortStat): void { this.tooltip = new MarkdownString('', true); + this.tooltip.isTrusted = true; + this.tooltip.supportHtml = true; if (email) { const emailTitle = l10n.t('Email'); - this.tooltip.appendMarkdown(`$(account) [**${author}**](mailto:${email} "${emailTitle} ${author}")\n\n`); + this.tooltip.appendMarkdown(`$(account) [**${author}**](mailto:${email} "${emailTitle} ${author}")`); } else { - this.tooltip.appendMarkdown(`$(account) **${author}**\n\n`); + this.tooltip.appendMarkdown(`$(account) **${author}**`); } - this.tooltip.appendMarkdown(`$(history) ${date}\n\n`); - this.tooltip.appendMarkdown(message); + this.tooltip.appendMarkdown(`, $(history) ${date}\n\n`); + this.tooltip.appendMarkdown(`${message}\n\n`); + + if (shortStat) { + this.tooltip.appendMarkdown(`---\n\n`); + + if (shortStat.insertions) { + this.tooltip.appendMarkdown(`${shortStat.insertions === 1 ? + l10n.t('{0} insertion{1}', shortStat.insertions, '(+)') : + l10n.t('{0} insertions{1}', shortStat.insertions, '(+)')}`); + } + + if (shortStat.deletions) { + this.tooltip.appendMarkdown(`, ${shortStat.deletions === 1 ? + l10n.t('{0} deletion{1}', shortStat.deletions, '(-)') : + l10n.t('{0} deletions{1}', shortStat.deletions, '(-)')}`); + } + + this.tooltip.appendMarkdown(`\n\n`); + } + + if (hash) { + this.tooltip.appendMarkdown(`---\n\n`); + + this.tooltip.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(uri, hash)} \`](command:git.viewCommit2?${encodeURIComponent(JSON.stringify([uri, hash]))} "${l10n.t('View Commit')}")`); + this.tooltip.appendMarkdown(' '); + this.tooltip.appendMarkdown(`[$(copy)](command:git.copyContentToClipboard?${encodeURIComponent(JSON.stringify(hash))} "${l10n.t('Copy Commit Hash')}")`); + } } private shortenRef(ref: string): string { @@ -153,6 +183,7 @@ export class GitTimelineProvider implements TimelineProvider { maxEntries: limit, hash: options.cursor, follow: true, + shortStats: true, // sortByAuthorDate: true }); @@ -184,7 +215,7 @@ export class GitTimelineProvider implements TimelineProvider { item.description = c.authorName; } - item.setItemDetails(c.authorName!, c.authorEmail, dateFormatter.format(date), message); + item.setItemDetails(uri, c.hash, c.authorName!, c.authorEmail, dateFormatter.format(date), message, c.shortStat); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -209,7 +240,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); + item.setItemDetails(uri, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -231,7 +262,7 @@ export class GitTimelineProvider implements TimelineProvider { const item = new GitTimelineItem('', index ? '~' : 'HEAD', l10n.t('Uncommitted Changes'), date.getTime(), 'working', 'git:file:working'); item.iconPath = new ThemeIcon('circle-outline'); item.description = ''; - item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); + item.setItemDetails(uri, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index b97f9201953..7e18e20482c 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -1162,7 +1162,7 @@ class TimelineTreeRenderer implements ITreeRenderer