3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
+ import * as dayjs from 'dayjs' ;
7
+ import * as advancedFormat from 'dayjs/plugin/advancedFormat' ;
8
+ import * as relativeTime from 'dayjs/plugin/relativeTime' ;
6
9
import { CancellationToken , Disposable , Event , EventEmitter , ThemeIcon , TimelineItem , TimelineProvider , Uri , workspace } from 'vscode' ;
7
10
import { Model } from './model' ;
8
11
import { Repository } from './repository' ;
9
12
import { debounce } from './decorators' ;
13
+ import { Status } from './api/git' ;
14
+
15
+ dayjs . extend ( advancedFormat ) ;
16
+ dayjs . extend ( relativeTime ) ;
17
+
18
+ // TODO[ECA]: Localize all the strings
19
+ // TODO[ECA]: Localize or use a setting for date format
10
20
11
21
export class GitTimelineProvider implements TimelineProvider {
12
22
private _onDidChange = new EventEmitter < Uri | undefined > ( ) ;
@@ -21,45 +31,47 @@ export class GitTimelineProvider implements TimelineProvider {
21
31
22
32
private _repo : Repository | undefined ;
23
33
private _repoDisposable : Disposable | undefined ;
34
+ private _repoStatusDate : Date | undefined ;
24
35
25
36
constructor ( private readonly _model : Model ) {
26
- this . _disposable = workspace . registerTimelineProvider ( '*' , this ) ;
37
+ this . _disposable = Disposable . from (
38
+ _model . onDidOpenRepository ( this . onRepositoriesChanged , this ) ,
39
+ workspace . registerTimelineProvider ( '*' , this ) ,
40
+ ) ;
27
41
}
28
42
29
43
dispose ( ) {
30
44
this . _disposable . dispose ( ) ;
31
45
}
32
46
33
- async provideTimeline ( uri : Uri , _since : number , _token : CancellationToken ) : Promise < TimelineItem [ ] > {
34
- console . log ( `GitTimelineProvider.provideTimeline: uri=${ uri } state=${ this . _model . state } ` ) ;
47
+ async provideTimeline ( uri : Uri , _token : CancellationToken ) : Promise < TimelineItem [ ] > {
48
+ // console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
35
49
36
50
const repo = this . _model . getRepository ( uri ) ;
37
51
if ( ! repo ) {
38
- console . log ( `GitTimelineProvider.provideTimeline: repo NOT found` ) ;
39
-
40
52
this . _repoDisposable ?. dispose ( ) ;
53
+ this . _repoStatusDate = undefined ;
41
54
this . _repo = undefined ;
42
55
43
56
return [ ] ;
44
57
}
45
58
46
- console . log ( `GitTimelineProvider.provideTimeline: repo found` ) ;
47
-
48
59
if ( this . _repo ?. root !== repo . root ) {
49
60
this . _repoDisposable ?. dispose ( ) ;
50
61
51
62
this . _repo = repo ;
63
+ this . _repoStatusDate = new Date ( ) ;
52
64
this . _repoDisposable = Disposable . from (
53
- repo . onDidChangeRepository ( ( ) => this . onRepositoryChanged ( ) , this )
65
+ repo . onDidChangeRepository ( this . onRepositoryChanged , this ) ,
66
+ repo . onDidRunGitStatus ( this . onRepositoryStatusChanged , this )
54
67
) ;
55
68
}
56
69
57
- // TODO: Ensure that the uri is a file -- if not we could get the history of the repo?
58
-
59
- const commits = await repo . logFile ( uri , { maxEntries : 10 } ) ;
70
+ // TODO[ECA]: Ensure that the uri is a file -- if not we could get the history of the repo?
60
71
61
- console . log ( `GitTimelineProvider.provideTimeline: commits= ${ commits . length } ` ) ;
72
+ const commits = await repo . logFile ( uri ) ;
62
73
74
+ let dateFormatter : dayjs . Dayjs ;
63
75
const items = commits . map < TimelineItem > ( c => {
64
76
let message = c . message ;
65
77
@@ -68,13 +80,15 @@ export class GitTimelineProvider implements TimelineProvider {
68
80
message = `${ message . substring ( 0 , index ) } \u2026` ;
69
81
}
70
82
83
+ dateFormatter = dayjs ( c . authorDate ) ;
84
+
71
85
return {
72
86
id : c . hash ,
73
- date : c . authorDate ?. getTime ( ) ?? 0 ,
87
+ timestamp : c . authorDate ?. getTime ( ) ?? 0 ,
74
88
iconPath : new ThemeIcon ( 'git-commit' ) ,
75
89
label : message ,
76
- description : `${ c . authorName } ( ${ c . authorEmail } ) \u2022 ${ c . hash . substr ( 0 , 8 ) } ` ,
77
- detail : `${ c . authorName } (${ c . authorEmail } )\n ${ c . authorDate } \n\n${ c . message } ` ,
90
+ description : `${ dateFormatter . fromNow ( ) } \u2022 ${ c . authorName } ` ,
91
+ 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 } ` ,
78
92
command : {
79
93
title : 'Open Diff' ,
80
94
command : 'git.openDiff' ,
@@ -85,16 +99,42 @@ export class GitTimelineProvider implements TimelineProvider {
85
99
86
100
const index = repo . indexGroup . resourceStates . find ( r => r . resourceUri . fsPath === uri . fsPath ) ;
87
101
if ( index ) {
102
+ const date = this . _repoStatusDate ?? new Date ( ) ;
103
+ dateFormatter = dayjs ( date ) ;
104
+
105
+ let status ;
106
+ switch ( index . type ) {
107
+ case Status . INDEX_MODIFIED :
108
+ status = 'Modified' ;
109
+ break ;
110
+ case Status . INDEX_ADDED :
111
+ status = 'Added' ;
112
+ break ;
113
+ case Status . INDEX_DELETED :
114
+ status = 'Deleted' ;
115
+ break ;
116
+ case Status . INDEX_RENAMED :
117
+ status = 'Renamed' ;
118
+ break ;
119
+ case Status . INDEX_COPIED :
120
+ status = 'Copied' ;
121
+ break ;
122
+ default :
123
+ status = '' ;
124
+ break ;
125
+ }
126
+
127
+
88
128
items . push ( {
89
129
id : '~' ,
90
- // TODO: Fix the date
91
- date : Date . now ( ) ,
130
+ timestamp : date . getTime ( ) ,
131
+ // TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
92
132
iconPath : new ThemeIcon ( 'git-commit' ) ,
93
133
label : 'Staged Changes' ,
94
- description : '' ,
95
- detail : '' ,
134
+ description : ` ${ dateFormatter . fromNow ( ) } \u2022 You` ,
135
+ detail : `You \u2014 Index\n ${ dateFormatter . fromNow ( ) } ( ${ dateFormatter . format ( 'MMMM Do, YYYY h:mma' ) } )\n ${ status } ` ,
96
136
command : {
97
- title : 'Open Diff ' ,
137
+ title : 'Open Comparison ' ,
98
138
command : 'git.openDiff' ,
99
139
arguments : [ uri , '~' ]
100
140
}
@@ -104,10 +144,23 @@ export class GitTimelineProvider implements TimelineProvider {
104
144
return items ;
105
145
}
106
146
147
+ @debounce ( 500 )
148
+ private onRepositoriesChanged ( _repo : Repository ) {
149
+ // console.log(`GitTimelineProvider.onRepositoriesChanged`);
150
+
151
+ // TODO[ECA]: Being naive for now and just always refreshing each time there is a new repository
152
+ this . _onDidChange . fire ( ) ;
153
+ }
154
+
107
155
@debounce ( 500 )
108
156
private onRepositoryChanged ( ) {
109
- console . log ( `GitTimelineProvider.onRepositoryChanged` ) ;
157
+ // console.log(`GitTimelineProvider.onRepositoryChanged`);
110
158
111
159
this . _onDidChange . fire ( ) ;
112
160
}
161
+
162
+ private onRepositoryStatusChanged ( ) {
163
+ // This is crappy, but for now just save the last time a status was run and use that as the timestamp for staged items
164
+ this . _repoStatusDate = new Date ( ) ;
165
+ }
113
166
}
0 commit comments