Skip to content

Commit 3f68439

Browse files
author
Eric Amodio
committed
Refactors timeline to work better w/ multi sources
Separates the tree rendering from the data model cache Fixes many issues and simplifies the code
1 parent 64b65cf commit 3f68439

6 files changed

Lines changed: 499 additions & 382 deletions

File tree

extensions/git/src/git.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ interface MutableRemote extends Remote {
4545
isReadOnly: boolean;
4646
}
4747

48-
// TODO[ECA]: Move to git.d.ts once we are good with the api
48+
// TODO@eamodio: Move to git.d.ts once we are good with the api
4949
/**
5050
* Log file options.
5151
*/

extensions/git/src/timelineProvider.ts

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ dayjs.extend(advancedFormat);
1515

1616
const localize = nls.loadMessageBundle();
1717

18-
// TODO[ECA]: Localize or use a setting for date format
18+
// TODO@eamodio: Localize or use a setting for date format
1919

2020
export class GitTimelineItem extends TimelineItem {
2121
static is(item: TimelineItem): item is GitTimelineItem {
@@ -71,55 +71,55 @@ export class GitTimelineProvider implements TimelineProvider {
7171
readonly id = 'git-history';
7272
readonly label = localize('git.timeline.source', 'Git History');
7373

74-
private _disposable: Disposable;
74+
private disposable: Disposable;
7575

76-
private _repo: Repository | undefined;
77-
private _repoDisposable: Disposable | undefined;
78-
private _repoStatusDate: Date | undefined;
76+
private repo: Repository | undefined;
77+
private repoDisposable: Disposable | undefined;
78+
private repoStatusDate: Date | undefined;
7979

8080
constructor(private readonly _model: Model) {
81-
this._disposable = Disposable.from(
81+
this.disposable = Disposable.from(
8282
_model.onDidOpenRepository(this.onRepositoriesChanged, this),
8383
workspace.registerTimelineProvider(['file', 'git', 'gitlens-git'], this),
8484
);
8585
}
8686

8787
dispose() {
88-
this._disposable.dispose();
88+
this.disposable.dispose();
8989
}
9090

9191
async provideTimeline(uri: Uri, options: TimelineOptions, _token: CancellationToken): Promise<Timeline> {
9292
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
9393

9494
const repo = this._model.getRepository(uri);
9595
if (!repo) {
96-
this._repoDisposable?.dispose();
97-
this._repoStatusDate = undefined;
98-
this._repo = undefined;
96+
this.repoDisposable?.dispose();
97+
this.repoStatusDate = undefined;
98+
this.repo = undefined;
9999

100100
return { items: [] };
101101
}
102102

103-
if (this._repo?.root !== repo.root) {
104-
this._repoDisposable?.dispose();
103+
if (this.repo?.root !== repo.root) {
104+
this.repoDisposable?.dispose();
105105

106-
this._repo = repo;
107-
this._repoStatusDate = new Date();
108-
this._repoDisposable = Disposable.from(
106+
this.repo = repo;
107+
this.repoStatusDate = new Date();
108+
this.repoDisposable = Disposable.from(
109109
repo.onDidChangeRepository(uri => this.onRepositoryChanged(repo, uri)),
110110
repo.onDidRunGitStatus(() => this.onRepositoryStatusChanged(repo))
111111
);
112112
}
113113

114-
// TODO[ECA]: Ensure that the uri is a file -- if not we could get the history of the repo?
114+
// TODO@eamodio: Ensure that the uri is a file -- if not we could get the history of the repo?
115115

116116
let limit: number | undefined;
117117
if (options.limit !== undefined && typeof options.limit !== 'number') {
118118
try {
119-
const result = await this._model.git.exec(repo.root, ['rev-list', '--count', `${options.limit.cursor}..`, '--', uri.fsPath]);
119+
const result = await this._model.git.exec(repo.root, ['rev-list', '--count', `${options.limit.id}..`, '--', uri.fsPath]);
120120
if (!result.exitCode) {
121-
// Ask for 1 more than so we can determine if there are more commits
122-
limit = Number(result.stdout) + 1;
121+
// Ask for 2 more (1 for the limit commit and 1 for the next commit) than so we can determine if there are more commits
122+
limit = Number(result.stdout) + 2;
123123
}
124124
}
125125
catch {
@@ -130,21 +130,16 @@ export class GitTimelineProvider implements TimelineProvider {
130130
limit = options.limit === undefined ? undefined : options.limit + 1;
131131
}
132132

133-
134133
const commits = await repo.logFile(uri, {
135134
maxEntries: limit,
136135
hash: options.cursor,
137-
reverse: options.before,
138136
// sortByAuthorDate: true
139137
});
140138

141-
const more = limit === undefined || options.before ? false : commits.length >= limit;
139+
const more = limit === undefined ? false : commits.length >= limit;
142140
const paging = commits.length ? {
143141
more: more,
144-
cursors: {
145-
before: commits[0]?.hash,
146-
after: commits[commits.length - (more ? 1 : 2)]?.hash
147-
}
142+
cursor: more ? commits[commits.length - 1]?.hash : undefined
148143
} : undefined;
149144

150145
// If we asked for an extra commit, strip it off
@@ -171,16 +166,16 @@ export class GitTimelineProvider implements TimelineProvider {
171166
return item;
172167
});
173168

174-
if (options.cursor === undefined || options.before) {
169+
if (options.cursor === undefined) {
175170
const you = localize('git.timeline.you', 'You');
176171

177172
const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
178173
if (index) {
179-
const date = this._repoStatusDate ?? new Date();
174+
const date = this.repoStatusDate ?? new Date();
180175
dateFormatter = dayjs(date);
181176

182177
const item = new GitTimelineItem('~', 'HEAD', localize('git.timeline.stagedChanges', 'Staged Changes'), date.getTime(), 'index', 'git:file:index');
183-
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
178+
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
184179
item.iconPath = new (ThemeIcon as any)('git-commit');
185180
item.description = '';
186181
item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format('MMMM Do, YYYY h:mma'), Resource.getStatusText(index.type));
@@ -199,7 +194,7 @@ export class GitTimelineProvider implements TimelineProvider {
199194
dateFormatter = dayjs(date);
200195

201196
const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommited Changes'), date.getTime(), 'working', 'git:file:working');
202-
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
197+
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
203198
item.iconPath = new (ThemeIcon as any)('git-commit');
204199
item.description = '';
205200
item.detail = localize('git.timeline.detail', '{0} \u2014 {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format('MMMM Do, YYYY h:mma'), Resource.getStatusText(working.type));
@@ -222,7 +217,7 @@ export class GitTimelineProvider implements TimelineProvider {
222217
private onRepositoriesChanged(_repo: Repository) {
223218
// console.log(`GitTimelineProvider.onRepositoriesChanged`);
224219

225-
// TODO[ECA]: Being naive for now and just always refreshing each time there is a new repository
220+
// TODO@eamodio: Being naive for now and just always refreshing each time there is a new repository
226221
this.fireChanged();
227222
}
228223

@@ -236,7 +231,7 @@ export class GitTimelineProvider implements TimelineProvider {
236231
// console.log(`GitTimelineProvider.onRepositoryStatusChanged`);
237232

238233
// This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items
239-
this._repoStatusDate = new Date();
234+
this.repoStatusDate = new Date();
240235

241236
this.fireChanged();
242237
}

src/vs/vscode.proposed.d.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,12 +1874,9 @@ declare module 'vscode' {
18741874
export interface Timeline {
18751875
readonly paging?: {
18761876
/**
1877-
* A set of provider-defined cursors specifing the range of timeline items returned.
1877+
* A provider-defined cursor specifing the starting point of timeline items which are after the ones returned.
18781878
*/
1879-
readonly cursors: {
1880-
readonly before: string;
1881-
readonly after?: string
1882-
};
1879+
readonly cursor?: string
18831880

18841881
/**
18851882
* A flag which indicates whether there are more items that weren't returned.
@@ -1895,19 +1892,15 @@ declare module 'vscode' {
18951892

18961893
export interface TimelineOptions {
18971894
/**
1898-
* A provider-defined cursor specifing the range of timeline items that should be returned.
1895+
* A provider-defined cursor specifing the starting point of the timeline items that should be returned.
18991896
*/
19001897
cursor?: string;
19011898

19021899
/**
1903-
* A flag to specify whether the timeline items being requested should be before or after (default) the provided cursor.
1900+
* An optional maximum number timeline items or the all timeline items newer (inclusive) than the timestamp or id that should be returned.
1901+
* If `undefined` all timeline items should be returned.
19041902
*/
1905-
before?: boolean;
1906-
1907-
/**
1908-
* The maximum number or the ending cursor of timeline items that should be returned.
1909-
*/
1910-
limit?: number | { cursor: string };
1903+
limit?: number | { timestamp: number; id?: string };
19111904
}
19121905

19131906
export interface TimelineProvider {

0 commit comments

Comments
 (0)