@@ -9,22 +9,53 @@ import HunkView from './hunk-view'
9
9
const EMPTY_SET = new Set ( )
10
10
11
11
export default class FilePatchView {
12
- constructor ( { filePatch, repository, stagingStatus, registerHunkView} ) {
12
+ constructor ( { filePatch, repository, stagingStatus, selectionMode , registerHunkView} ) {
13
13
this . filePatch = filePatch
14
14
this . repository = repository
15
15
this . filePatchSubscriptions = new CompositeDisposable (
16
16
this . filePatch . onDidUpdate ( this . didUpdateFilePatch . bind ( this ) ) ,
17
17
this . filePatch . onDidDestroy ( this . didDestroyFilePatch . bind ( this ) )
18
18
)
19
19
this . stagingStatus = stagingStatus
20
- this . selectedLines = null
21
- this . selectedHunk = null
20
+ this . selectedHunk = filePatch . getHunks ( ) [ 0 ]
21
+ this . selectedHunkIndex = 0
22
+ this . setInitialSelection ( this . selectedHunk )
22
23
this . registerHunkView = registerHunkView
23
24
this . emitter = new Emitter ( )
25
+ this . selectionMode = selectionMode || 'hunk'
24
26
etch . initialize ( this )
27
+ this . subscriptions = atom . commands . add ( this . element , {
28
+ 'git:toggle-patch-selection-mode' : this . togglePatchSelectionMode . bind ( this ) ,
29
+ 'git:focus-next-hunk' : this . focusNextHunk . bind ( this )
30
+ } )
25
31
}
26
32
27
- update ( { filePatch, repository, stagingStatus} ) {
33
+ setInitialSelection ( hunk ) {
34
+ if ( hunk ) {
35
+ if ( this . selectionMode === 'hunk' ) {
36
+ this . selectNonContextLines ( hunk . getLines ( ) )
37
+ } else {
38
+ this . selectNonContextLines ( [ this . getFirstNonContextLine ( hunk ) ] )
39
+ }
40
+ } else {
41
+ this . selectNonContextLines ( [ ] )
42
+ }
43
+ }
44
+
45
+ selectNonContextLines ( lines ) {
46
+ this . selectedLines = new Set ( lines . filter ( l => l . isChanged ( ) ) )
47
+ }
48
+
49
+ focusNextHunk ( ) {
50
+ const hunks = this . filePatch . getHunks ( )
51
+ let index = hunks . indexOf ( this . selectedHunk )
52
+ this . selectedHunk = ++ index < hunks . length ? hunks [ index ] : hunks [ 0 ]
53
+ this . selectedHunkIndex = index < hunks . length ? index : 0
54
+ this . setInitialSelection ( this . selectedHunk )
55
+ return etch . update ( this )
56
+ }
57
+
58
+ update ( { filePatch, repository, stagingStatus, selectionMode} ) {
28
59
if ( this . filePatchSubscriptions ) this . filePatchSubscriptions . dispose ( )
29
60
30
61
this . filePatch = filePatch
@@ -34,21 +65,26 @@ export default class FilePatchView {
34
65
)
35
66
this . repository = repository
36
67
this . stagingStatus = stagingStatus
68
+ this . selectionMode = selectionMode || this . selectionMode
69
+ this . selectedHunk = filePatch . getHunks ( ) [ 0 ]
70
+ this . selectedHunkIndex = 0
71
+ this . setInitialSelection ( this . selectedHunk )
37
72
this . emitter . emit ( 'did-change-title' , this . getTitle ( ) )
38
73
return etch . update ( this )
39
74
}
40
75
41
76
destroy ( ) {
42
77
this . emitter . emit ( 'did-destroy' )
78
+ this . subscriptions . dispose ( )
43
79
return etch . destroy ( this )
44
80
}
45
81
46
82
render ( ) {
47
83
let stageButtonLabelPrefix = this . stagingStatus === 'unstaged' ? 'Stage' : 'Unstage'
48
84
return (
49
- < div className = 'git-FilePatchView' > { this . filePatch . getHunks ( ) . map ( ( hunk ) => {
85
+ < div className = 'git-FilePatchView' tabIndex = '-1' > { this . filePatch . getHunks ( ) . map ( ( hunk ) => {
50
86
const isSelected = hunk === this . selectedHunk
51
- const selectedLines = ( isSelected && this . selectedLines != null ) ? this . selectedLines : EMPTY_SET
87
+ const selectedLines = isSelected ? this . selectedLines : EMPTY_SET
52
88
return (
53
89
< HunkView
54
90
hunk = { hunk }
@@ -64,6 +100,20 @@ export default class FilePatchView {
64
100
)
65
101
}
66
102
103
+ getFirstNonContextLine ( hunk ) {
104
+ const lines = hunk . getLines ( )
105
+ for ( let i = 0 ; i < lines . length ; i ++ ) {
106
+ const line = lines [ i ]
107
+ if ( line . isChanged ( ) ) return line
108
+ }
109
+ }
110
+
111
+ togglePatchSelectionMode ( ) {
112
+ this . selectionMode = this . selectionMode === 'hunk' ? 'hunkLine' : 'hunk'
113
+ this . setInitialSelection ( this . selectedHunk )
114
+ return etch . update ( this )
115
+ }
116
+
67
117
getTitle ( ) {
68
118
let title = this . stagingStatus === 'staged' ? 'Staged' : 'Unstaged'
69
119
title += ' Changes: '
@@ -80,12 +130,17 @@ export default class FilePatchView {
80
130
}
81
131
82
132
didSelectLinesForHunk ( hunk , selectedLines ) {
83
- this . selectedLines = selectedLines
84
133
this . selectedHunk = hunk
134
+ this . selectedHunkIndex = this . filePatch . getHunks ( ) . indexOf ( hunk )
135
+ if ( this . selectionMode === 'hunk' ) {
136
+ this . selectNonContextLines ( hunk . getLines ( ) )
137
+ } else {
138
+ this . selectNonContextLines ( [ ...selectedLines ] )
139
+ }
85
140
etch . update ( this )
86
141
}
87
142
88
- didClickStageButtonForHunk ( hunk ) {
143
+ async didClickStageButtonForHunk ( hunk ) {
89
144
// TODO: Test the behavior of this line, which ensure we only attempt to
90
145
// stage the selected lines if we clicked the stage button on the hunk
91
146
// containing them.
@@ -95,26 +150,37 @@ export default class FilePatchView {
95
150
if ( this . stagingStatus === 'unstaged' ) {
96
151
if ( this . selectedLines && clickedSelectedHunk ) {
97
152
patchToApply = this . filePatch . getStagePatchForLines ( this . selectedLines )
98
- this . selectedLines = null
153
+ this . selectedLines = EMPTY_SET
99
154
} else {
100
155
patchToApply = this . filePatch . getStagePatchForHunk ( hunk )
101
156
}
102
157
} else if ( this . stagingStatus === 'staged' ) {
103
158
if ( this . selectedLines && clickedSelectedHunk ) {
104
159
patchToApply = this . filePatch . getUnstagePatchForLines ( this . selectedLines )
105
- this . selectedLines = null
160
+ this . selectedLines = EMPTY_SET
106
161
} else {
107
162
patchToApply = this . filePatch . getUnstagePatchForHunk ( hunk )
108
163
}
109
164
} else {
110
165
throw new Error ( `Unknown stagingStatus: ${ this . stagingStatus } ` )
111
166
}
112
167
113
- return this . repository . applyPatchToIndex ( patchToApply )
168
+ await this . repository . applyPatchToIndex ( patchToApply )
169
+ return etch . update ( this )
114
170
}
115
171
116
172
didUpdateFilePatch ( ) {
117
- etch . update ( this )
173
+ const hunks = this . filePatch . getHunks ( )
174
+ if ( hunks . includes ( this . selectedHunk ) ) {
175
+ // retain existing selectedHunk
176
+ } else if ( hunks [ this . selectedHunkIndex ] ) {
177
+ this . selectedHunk = hunks [ this . selectedHunkIndex ]
178
+ } else {
179
+ this . selectedHunkIndex = hunks . length - 1
180
+ this . selectedHunk = hunks [ this . selectedHunkIndex ]
181
+ }
182
+ this . setInitialSelection ( this . selectedHunk )
183
+ return etch . update ( this )
118
184
}
119
185
120
186
didDestroyFilePatch ( ) {
0 commit comments