mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-28 04:23:32 +01:00
Polishes the timeline UI/UX
Cleans up API and removes some unused features (e.g. paging)
Adds date formatting
Adds loading progress and message
Removes lots of console.logs 😁
Adds titles to diffs
This commit is contained in:
@@ -3,10 +3,20 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dayjs from 'dayjs';
|
||||
import * as advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import * as relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode';
|
||||
import { Model } from './model';
|
||||
import { Repository } from './repository';
|
||||
import { debounce } from './decorators';
|
||||
import { Status } from './api/git';
|
||||
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
// TODO[ECA]: Localize all the strings
|
||||
// TODO[ECA]: Localize or use a setting for date format
|
||||
|
||||
export class GitTimelineProvider implements TimelineProvider {
|
||||
private _onDidChange = new EventEmitter<Uri | undefined>();
|
||||
@@ -21,45 +31,47 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
|
||||
private _repo: Repository | undefined;
|
||||
private _repoDisposable: Disposable | undefined;
|
||||
private _repoStatusDate: Date | undefined;
|
||||
|
||||
constructor(private readonly _model: Model) {
|
||||
this._disposable = workspace.registerTimelineProvider('*', this);
|
||||
this._disposable = Disposable.from(
|
||||
_model.onDidOpenRepository(this.onRepositoriesChanged, this),
|
||||
workspace.registerTimelineProvider('*', this),
|
||||
);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
|
||||
async provideTimeline(uri: Uri, _since: number, _token: CancellationToken): Promise<TimelineItem[]> {
|
||||
console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
|
||||
async provideTimeline(uri: Uri, _token: CancellationToken): Promise<TimelineItem[]> {
|
||||
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
|
||||
|
||||
const repo = this._model.getRepository(uri);
|
||||
if (!repo) {
|
||||
console.log(`GitTimelineProvider.provideTimeline: repo NOT found`);
|
||||
|
||||
this._repoDisposable?.dispose();
|
||||
this._repoStatusDate = undefined;
|
||||
this._repo = undefined;
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log(`GitTimelineProvider.provideTimeline: repo found`);
|
||||
|
||||
if (this._repo?.root !== repo.root) {
|
||||
this._repoDisposable?.dispose();
|
||||
|
||||
this._repo = repo;
|
||||
this._repoStatusDate = new Date();
|
||||
this._repoDisposable = Disposable.from(
|
||||
repo.onDidChangeRepository(() => this.onRepositoryChanged(), this)
|
||||
repo.onDidChangeRepository(this.onRepositoryChanged, this),
|
||||
repo.onDidRunGitStatus(this.onRepositoryStatusChanged, this)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Ensure that the uri is a file -- if not we could get the history of the repo?
|
||||
// TODO[ECA]: Ensure that the uri is a file -- if not we could get the history of the repo?
|
||||
|
||||
const commits = await repo.logFile(uri, { maxEntries: 10 });
|
||||
|
||||
console.log(`GitTimelineProvider.provideTimeline: commits=${commits.length}`);
|
||||
const commits = await repo.logFile(uri);
|
||||
|
||||
let dateFormatter: dayjs.Dayjs;
|
||||
const items = commits.map<TimelineItem>(c => {
|
||||
let message = c.message;
|
||||
|
||||
@@ -68,13 +80,15 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
message = `${message.substring(0, index)} \u2026`;
|
||||
}
|
||||
|
||||
dateFormatter = dayjs(c.authorDate);
|
||||
|
||||
return {
|
||||
id: c.hash,
|
||||
date: c.authorDate?.getTime() ?? 0,
|
||||
timestamp: c.authorDate?.getTime() ?? 0,
|
||||
iconPath: new ThemeIcon('git-commit'),
|
||||
label: message,
|
||||
description: `${c.authorName} (${c.authorEmail}) \u2022 ${c.hash.substr(0, 8)}`,
|
||||
detail: `${c.authorName} (${c.authorEmail})\n${c.authorDate}\n\n${c.message}`,
|
||||
description: `${dateFormatter.fromNow()} \u2022 ${c.authorName}`,
|
||||
detail: `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`,
|
||||
command: {
|
||||
title: 'Open Diff',
|
||||
command: 'git.openDiff',
|
||||
@@ -85,16 +99,42 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
|
||||
const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
|
||||
if (index) {
|
||||
const date = this._repoStatusDate ?? new Date();
|
||||
dateFormatter = dayjs(date);
|
||||
|
||||
let status;
|
||||
switch (index.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
status = 'Modified';
|
||||
break;
|
||||
case Status.INDEX_ADDED:
|
||||
status = 'Added';
|
||||
break;
|
||||
case Status.INDEX_DELETED:
|
||||
status = 'Deleted';
|
||||
break;
|
||||
case Status.INDEX_RENAMED:
|
||||
status = 'Renamed';
|
||||
break;
|
||||
case Status.INDEX_COPIED:
|
||||
status = 'Copied';
|
||||
break;
|
||||
default:
|
||||
status = '';
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
items.push({
|
||||
id: '~',
|
||||
// TODO: Fix the date
|
||||
date: Date.now(),
|
||||
timestamp: date.getTime(),
|
||||
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
|
||||
iconPath: new ThemeIcon('git-commit'),
|
||||
label: 'Staged Changes',
|
||||
description: '',
|
||||
detail: '',
|
||||
description: `${dateFormatter.fromNow()} \u2022 You`,
|
||||
detail: `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`,
|
||||
command: {
|
||||
title: 'Open Diff',
|
||||
title: 'Open Comparison',
|
||||
command: 'git.openDiff',
|
||||
arguments: [uri, '~']
|
||||
}
|
||||
@@ -104,10 +144,23 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
return items;
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
private onRepositoriesChanged(_repo: Repository) {
|
||||
// console.log(`GitTimelineProvider.onRepositoriesChanged`);
|
||||
|
||||
// TODO[ECA]: Being naive for now and just always refreshing each time there is a new repository
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
private onRepositoryChanged() {
|
||||
console.log(`GitTimelineProvider.onRepositoryChanged`);
|
||||
// console.log(`GitTimelineProvider.onRepositoryChanged`);
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
private onRepositoryStatusChanged() {
|
||||
// This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items
|
||||
this._repoStatusDate = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user