diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 2d87e444b180e..886510b00314c 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode'; +import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent, workspace, ConfigurationChangeEvent } from 'vscode'; import { Repository, Resource } from './repository'; -import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent, getCommitShortHash, truncate } from './util'; +import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent, truncate } from './util'; import { toMultiFileDiffEditorUris } from './uri'; import { AvatarQuery, AvatarQueryCommit, Branch, LogOptions, Ref, RefType } from './api/git'; import { emojify, ensureEmojis } from './emoji'; @@ -14,40 +14,6 @@ import { Commit } from './git'; import { OperationKind, OperationResult } from './operation'; import { ISourceControlHistoryItemDetailsProviderRegistry, provideSourceControlHistoryItemAvatar, provideSourceControlHistoryItemMessageLinks } from './historyItemDetailsProvider'; -function toSourceControlHistoryItemRef(repository: Repository, ref: Ref): SourceControlHistoryItemRef { - const rootUri = Uri.file(repository.root); - - switch (ref.type) { - case RefType.RemoteHead: - return { - id: `refs/remotes/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? l10n.t('Remote branch at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined, - revision: ref.commit, - icon: new ThemeIcon('cloud'), - category: l10n.t('remote branches') - }; - case RefType.Tag: - return { - id: `refs/tags/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? l10n.t('Tag at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined, - revision: ref.commit, - icon: new ThemeIcon('tag'), - category: l10n.t('tags') - }; - default: - return { - id: `refs/heads/${ref.name}`, - name: ref.name ?? '', - description: ref.commit ? getCommitShortHash(rootUri, ref.commit) : undefined, - revision: ref.commit, - icon: new ThemeIcon('git-branch'), - category: l10n.t('branches') - }; - } -} - function compareSourceControlHistoryItemRef(ref1: SourceControlHistoryItemRef, ref2: SourceControlHistoryItemRef): number { const getOrder = (ref: SourceControlHistoryItemRef): number => { if (ref.id.startsWith('refs/heads/')) { @@ -93,6 +59,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec private _HEAD: Branch | undefined; private _historyItemRefs: SourceControlHistoryItemRef[] = []; + private commitShortHashLength = 7; private historyItemDecorations = new Map(); private disposables: Disposable[] = []; @@ -102,12 +69,24 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec private readonly repository: Repository, private readonly logger: LogOutputChannel ) { + this.disposables.push(workspace.onDidChangeConfiguration(this.onDidChangeConfiguration)); + this.onDidChangeConfiguration(); + const onDidRunWriteOperation = filterEvent(repository.onDidRunOperation, e => !e.operation.readOnly); this.disposables.push(onDidRunWriteOperation(this.onDidRunWriteOperation, this)); this.disposables.push(window.registerFileDecorationProvider(this)); } + private onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { + if (e && !e.affectsConfiguration('git.commitShortHashLength')) { + return; + } + + const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); + this.commitShortHashLength = config.get('commitShortHashLength', 7); + } + private async onDidRunWriteOperation(result: OperationResult): Promise { if (!this.repository.HEAD) { this.logger.trace('[GitHistoryProvider][onDidRunWriteOperation] repository.HEAD is undefined'); @@ -119,7 +98,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec // Refs (alphabetically) const historyItemRefs = this.repository.refs - .map(ref => toSourceControlHistoryItemRef(this.repository, ref)) + .map(ref => this.toSourceControlHistoryItemRef(ref)) .sort((a, b) => a.id.localeCompare(b.id)); const delta = deltaHistoryItemRefs(this._historyItemRefs, historyItemRefs); @@ -241,13 +220,13 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec for (const ref of refs) { switch (ref.type) { case RefType.RemoteHead: - remoteBranches.push(toSourceControlHistoryItemRef(this.repository, ref)); + remoteBranches.push(this.toSourceControlHistoryItemRef(ref)); break; case RefType.Tag: - tags.push(toSourceControlHistoryItemRef(this.repository, ref)); + tags.push(this.toSourceControlHistoryItemRef(ref)); break; default: - branches.push(toSourceControlHistoryItemRef(this.repository, ref)); + branches.push(this.toSourceControlHistoryItemRef(ref)); break; } } @@ -319,7 +298,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec author: commit.authorName, authorEmail: commit.authorEmail, authorIcon: avatarUrl ? Uri.parse(avatarUrl) : new ThemeIcon('account'), - displayId: getCommitShortHash(Uri.file(this.repository.root), commit.hash), + displayId: truncate(commit.hash, this.commitShortHashLength, false), timestamp: commit.authorDate?.getTime(), statistics: commit.shortStat ?? { files: 0, insertions: 0, deletions: 0 }, references: references.length !== 0 ? references : undefined @@ -469,6 +448,38 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } } + private toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { + switch (ref.type) { + case RefType.RemoteHead: + return { + id: `refs/remotes/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? l10n.t('Remote branch at {0}', truncate(ref.commit, this.commitShortHashLength, false)) : undefined, + revision: ref.commit, + icon: new ThemeIcon('cloud'), + category: l10n.t('remote branches') + }; + case RefType.Tag: + return { + id: `refs/tags/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? l10n.t('Tag at {0}', truncate(ref.commit, this.commitShortHashLength, false)) : undefined, + revision: ref.commit, + icon: new ThemeIcon('tag'), + category: l10n.t('tags') + }; + default: + return { + id: `refs/heads/${ref.name}`, + name: ref.name ?? '', + description: ref.commit ? truncate(ref.commit, this.commitShortHashLength, false) : undefined, + revision: ref.commit, + icon: new ThemeIcon('git-branch'), + category: l10n.t('branches') + }; + } + } + dispose(): void { dispose(this.disposables); } diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 75b07d98b0cb4..76a3f92dde188 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -10,7 +10,7 @@ import { debounce } from './decorators'; import { emojify, ensureEmojis } from './emoji'; import { CommandCenter } from './commands'; import { OperationKind, OperationResult } from './operation'; -import { getCommitShortHash, truncate } from './util'; +import { truncate } from './util'; import { CommitShortStat } from './git'; import { provideSourceControlHistoryItemAvatar, provideSourceControlHistoryItemHoverCommands, provideSourceControlHistoryItemMessageLinks } from './historyItemDetailsProvider'; import { AvatarQuery, AvatarQueryCommit } from './api/git'; @@ -54,7 +54,7 @@ export class GitTimelineItem extends TimelineItem { return this.shortenRef(this.previousRef); } - setItemDetails(uri: Uri, hash: string | undefined, avatar: string | undefined, author: string, email: string | undefined, date: string, message: string, shortStat?: CommitShortStat, remoteSourceCommands: Command[] = []): void { + setItemDetails(uri: Uri, hash: string | undefined, shortHash: string | undefined, avatar: string | undefined, author: string, email: string | undefined, date: string, message: string, shortStat?: CommitShortStat, remoteSourceCommands: Command[] = []): void { this.tooltip = new MarkdownString('', true); this.tooltip.isTrusted = true; @@ -91,10 +91,10 @@ export class GitTimelineItem extends TimelineItem { this.tooltip.appendMarkdown(`${labels.join(', ')}\n\n`); } - if (hash) { + if (hash && shortHash) { this.tooltip.appendMarkdown(`---\n\n`); - this.tooltip.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(uri, hash)} \`](command:git.viewCommit?${encodeURIComponent(JSON.stringify([uri, hash]))} "${l10n.t('Open Commit')}")`); + this.tooltip.appendMarkdown(`[\`$(git-commit) ${shortHash} \`](command:git.viewCommit?${encodeURIComponent(JSON.stringify([uri, hash]))} "${l10n.t('Open Commit')}")`); this.tooltip.appendMarkdown(' '); this.tooltip.appendMarkdown(`[$(copy)](command:git.copyContentToClipboard?${encodeURIComponent(JSON.stringify(hash))} "${l10n.t('Copy Commit Hash')}")`); @@ -173,8 +173,6 @@ export class GitTimelineProvider implements TimelineProvider { ); } - const config = workspace.getConfiguration('git.timeline'); - // TODO@eamodio: Ensure that the uri is a file -- if not we could get the history of the repo? let limit: number | undefined; @@ -215,9 +213,11 @@ export class GitTimelineProvider implements TimelineProvider { const dateFormatter = new Intl.DateTimeFormat(env.language, { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }); - const dateType = config.get<'committed' | 'authored'>('date'); - const showAuthor = config.get('showAuthor'); - const showUncommitted = config.get('showUncommitted'); + const config = workspace.getConfiguration('git', Uri.file(repo.root)); + const dateType = config.get<'committed' | 'authored'>('timeline.date'); + const showAuthor = config.get('timeline.showAuthor'); + const showUncommitted = config.get('timeline.showUncommitted'); + const commitShortHashLength = config.get('commitShortHashLength') ?? 7; const openComparison = l10n.t('Open Comparison'); @@ -253,7 +253,7 @@ export class GitTimelineProvider implements TimelineProvider { const commitRemoteSourceCommands = !unpublishedCommits.has(c.hash) ? remoteHoverCommands : []; const messageWithLinks = await provideSourceControlHistoryItemMessageLinks(this.model, repo, message) ?? message; - item.setItemDetails(uri, c.hash, avatars?.get(c.hash), c.authorName!, c.authorEmail, dateFormatter.format(date), messageWithLinks, c.shortStat, commitRemoteSourceCommands); + item.setItemDetails(uri, c.hash, truncate(c.hash, commitShortHashLength, false), avatars?.get(c.hash), c.authorName!, c.authorEmail, dateFormatter.format(date), messageWithLinks, c.shortStat, commitRemoteSourceCommands); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -278,7 +278,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(uri, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); + item.setItemDetails(uri, undefined, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -300,7 +300,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(uri, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); + item.setItemDetails(uri, undefined, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 1ef59205883d4..28d8c27fbc5a1 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -291,8 +291,8 @@ export function detectUnicodeEncoding(buffer: Buffer): Encoding | null { return null; } -export function truncate(value: string, maxLength = 20): string { - return value.length <= maxLength ? value : `${value.substring(0, maxLength)}\u2026`; +export function truncate(value: string, maxLength = 20, ellipsis = true): string { + return value.length <= maxLength ? value : `${value.substring(0, maxLength)}${ellipsis ? '\u2026' : ''}`; } function normalizePath(path: string): string {