Waiting for coverage results... If this message persists check your coverage integration. Go to coverage settings
stefanhaller merged move-to-next-stageable-line-when-staging-in-custom-patch into master
#4675
Last updated
No information.
pkg/commands/patch
pkg/gui/controllers
pkg/gui/patch_exploring
pkg/integration/tests/patch_building
pkg/commands/patch/format.go
Diff coverage
@@ -72,30 +72,22 @@ func (self *patchPresenter) format() string {
72 73lineIdx++ 74} 75appendFormattedLine := func(line string, style style.TextStyle) { 76formattedLine := self.formatLine( 77line, 78style, 79lineIdx, 80) 81 82appendLine(formattedLine) 83} 84 85for _, line := range self.patch.header { 86appendFormattedLine(line, theme.DefaultTextColor.SetBold()) 87} 88 89for _, hunk := range self.patch.hunks { 90appendLine( 91self.formatLine( 92hunk.formatHeaderStart(), 93style.FgCyan, 94lineIdx, 95) + 96// we're splitting the line into two parts: the diff header and the context 97// We explicitly pass 'included' as false here so that we're only tagging the 98// first half of the line as included if the line is indeed included. 99self.formatLineAux( 100hunk.headerContext, 101theme.DefaultTextColor,72 73lineIdx++ 74} 75 76for _, line := range self.patch.header { 77// always passing false for 'included' here because header lines are not part of the patch 1 hits78appendLine(self.formatLineAux(line, theme.DefaultTextColor.SetBold(), false)) 1 hits79} 80 81for _, hunk := range self.patch.hunks { 82appendLine( 83self.formatLineAux( 1 hits84hunk.formatHeaderStart(), 85style.FgCyan, 86false, 1 hits87) + 88// we're splitting the line into two parts: the diff header and the context 89// We explicitly pass 'included' as false for both because these are not part 1 hits90// of the actual patch 1 hits91self.formatLineAux( 92hunk.headerContext, 93theme.DefaultTextColor,@@ -104,7 +96,12 @@ func (self *patchPresenter) format() string {
104) 105 106for _, line := range hunk.bodyLines { 107appendFormattedLine(line.Content, self.patchLineStyle(line)) 108} 109} 96) 97 98for _, line := range hunk.bodyLines { 99style := self.patchLineStyle(line) 1 hits100if line.IsChange() { 2 hits101appendLine(self.formatLine(line.Content, style, lineIdx)) 1 hits102} else { 2 hits103appendLine(self.formatLineAux(line.Content, style, false)) 1 hits104} 1 hits105} 106} pkg/commands/patch/patch.go
Diff coverage
@@ -115,15 +115,22 @@ func (self *Patch) HunkContainingLine(idx int) int {
115return -1 116} 117 118// Returns the patch line index of the next change (i.e. addition or deletion). 119func (self *Patch) GetNextChangeIdx(idx int) int { 120idx = lo.Clamp(idx, 0, self.LineCount()-1) 121 122lines := self.Lines() 123 124for i, line := range lines[idx:] { 125if line.isChange() { 126return i + idx 127} 128} 115return -1 116} 117 118// Returns the patch line index of the next change (i.e. addition or deletion) 119// that matches the same "included" state, given the includedLines. If you don't 120// care about included states, pass nil for includedLines and false for included. 121func (self *Patch) GetNextChangeIdxOfSameIncludedState(idx int, includedLines []int, included bool) (int, bool) { 1 hits122idx = lo.Clamp(idx, 0, self.LineCount()-1) 123 124lines := self.Lines() 125 126isMatch := func(i int, line *PatchLine) bool { 2 hits127sameIncludedState := lo.Contains(includedLines, i) == included 1 hits128return line.IsChange() && sameIncludedState 1 hits129} 1 hits130 131for i, line := range lines[idx:] { 132if isMatch(i+idx, line) { 2 hits133return i + idx, true 1 hits134} 135} @@ -131,13 +138,18 @@ func (self *Patch) GetNextChangeIdx(idx int) int {
131// return the index of the last change 132for i := len(lines) - 1; i >= 0; i-- { 133line := lines[i] 134if line.isChange() { 135return i 136} 137} 138 139// should not be possible 140return 0 141} 142 143// Returns the length of the patch in lines138// return the index of the last change 139for i := len(lines) - 1; i >= 0; i-- { 140line := lines[i] 141if isMatch(i, line) { 2 hits142return i, true 1 hits143} 144} 145 146return 0, false Not covered147} 148 149// Returns the patch line index of the next change (i.e. addition or deletion). 150func (self *Patch) GetNextChangeIdx(idx int) int { 1 hits151result, _ := self.GetNextChangeIdxOfSameIncludedState(idx, nil, false) 1 hits152return result 1 hits153} 154 155// Returns the length of the patch in linespkg/commands/patch/patch_builder.go
Diff coverage
@@ -124,14 +124,6 @@ func (p *PatchBuilder) RemoveFile(filename string) error {
124return nil 125} 126 127func getIndicesForRange(first, last int) []int { 128indices := []int{} 129for i := first; i <= last; i++ { 130indices = append(indices, i) 131} 132return indices 133} 134 135func (p *PatchBuilder) getFileInfo(filename string) (*fileInfo, error) { 136info, ok := p.fileInfoMap[filename] 137if ok {124return nil 125} 126 127func (p *PatchBuilder) getFileInfo(filename string) (*fileInfo, error) { 128info, ok := p.fileInfoMap[filename] 129if ok {@@ -152,24 +144,24 @@ func (p *PatchBuilder) getFileInfo(filename string) (*fileInfo, error) {
152return info, nil 153} 154 155func (p *PatchBuilder) AddFileLineRange(filename string, firstLineIdx, lastLineIdx int) error { 156info, err := p.getFileInfo(filename) 157if err != nil { 158return err 159} 160info.mode = PART 161info.includedLineIndices = lo.Union(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx)) 162 163return nil 164} 165 166func (p *PatchBuilder) RemoveFileLineRange(filename string, firstLineIdx, lastLineIdx int) error { 167info, err := p.getFileInfo(filename) 168if err != nil { 169return err 170} 171info.mode = PART 172info.includedLineIndices, _ = lo.Difference(info.includedLineIndices, getIndicesForRange(firstLineIdx, lastLineIdx)) 173if len(info.includedLineIndices) == 0 { 174p.removeFile(info) 175}144return info, nil 145} 146 147func (p *PatchBuilder) AddFileLineRange(filename string, lineIndices []int) error { 1 hits148info, err := p.getFileInfo(filename) 149if err != nil { 150return err 151} 152info.mode = PART 153info.includedLineIndices = lo.Union(info.includedLineIndices, lineIndices) 1 hits154 155return nil 156} 157 158func (p *PatchBuilder) RemoveFileLineRange(filename string, lineIndices []int) error { Not covered159info, err := p.getFileInfo(filename) 160if err != nil { 161return err 162} 163info.mode = PART 164info.includedLineIndices, _ = lo.Difference(info.includedLineIndices, lineIndices) Not covered165if len(info.includedLineIndices) == 0 { 166p.removeFile(info) 167}pkg/commands/patch/patch_line.go
Diff coverage
@@ -18,7 +18,7 @@ type PatchLine struct {
18Content string // something like '+ hello' (note the first character is not removed) 19} 20 21func (self *PatchLine) isChange() bool { 22return self.Kind == ADDITION || self.Kind == DELETION 23} 18Content string // something like '+ hello' (note the first character is not removed) 19} 20 21func (self *PatchLine) IsChange() bool { 1 hits22return self.Kind == ADDITION || self.Kind == DELETION 23} pkg/gui/controllers/patch_building_controller.go
Diff coverage
@@ -126,7 +126,6 @@ func (self *PatchBuildingController) toggleSelection() error {
126self.context().GetMutex().Lock() 127defer self.context().GetMutex().Unlock() 128 129toggleFunc := self.c.Git().Patch.PatchBuilder.AddFileLineRange 130filename := self.c.Contexts().CommitFiles.GetSelectedPath() 131if filename == "" { 132return nil126self.context().GetMutex().Lock() 127defer self.context().GetMutex().Unlock() 128 129filename := self.c.Contexts().CommitFiles.GetSelectedPath() 130if filename == "" { 131return nil@@ -134,19 +133,26 @@ func (self *PatchBuildingController) toggleSelection() error {
134 135state := self.context().GetState() 136 137includedLineIndices, err := self.c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename) 138if err != nil { 139return err 140} 141currentLineIsStaged := lo.Contains(includedLineIndices, state.GetSelectedPatchLineIdx()) 142if currentLineIsStaged { 143toggleFunc = self.c.Git().Patch.PatchBuilder.RemoveFileLineRange 144} 145 146// add range of lines to those set for the file 147firstLineIdx, lastLineIdx := state.SelectedPatchRange() 148 149if err := toggleFunc(filename, firstLineIdx, lastLineIdx); err != nil { 150// might actually want to return an error here 151self.c.Log.Error(err) 152}133 134state := self.context().GetState() 135 136// Get added/deleted lines in the selected patch range 1 hits137lineIndicesToToggle := state.ChangeLinesInSelectedPatchRange() 1 hits138if len(lineIndicesToToggle) == 0 { 1 hits139// Only context lines or header lines selected, so nothing to do Not covered140return nil Not covered141} Not covered142 143includedLineIndices, err := self.c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename) 144if err != nil { 145return err 146} 147 148toggleFunc := self.c.Git().Patch.PatchBuilder.AddFileLineRange 1 hits149firstSelectedChangeLineIsStaged := lo.Contains(includedLineIndices, lineIndicesToToggle[0]) 1 hits150if firstSelectedChangeLineIsStaged { 1 hits151toggleFunc = self.c.Git().Patch.PatchBuilder.RemoveFileLineRange 152} 153 154// add range of lines to those set for the file 155if err := toggleFunc(filename, lineIndicesToToggle); err != nil { 1 hits 156// might actually want to return an error here 157self.c.Log.Error(err) 158}@@ -155,6 +161,8 @@ func (self *PatchBuildingController) toggleSelection() error {
155state.SetLineSelectMode() 156} 157 158return nil 159} 161state.SetLineSelectMode() 162} 163 164state.SelectNextStageableLineOfSameIncludedState(self.context().GetIncludedLineIndices(), firstSelectedChangeLineIsStaged) 1 hits165 1 hits166return nil 167} pkg/gui/patch_exploring/state.go
Diff coverage
@@ -282,6 +282,20 @@ func (s *State) SelectedPatchRange() (int, int) {
282return s.patchLineIndices[start], s.patchLineIndices[end] 283} 284 285func (s *State) CurrentLineNumber() int { 286return s.patch.LineNumberOfLine(s.patchLineIndices[s.selectedLineIdx]) 287}282return s.patchLineIndices[start], s.patchLineIndices[end] 283} 284 285// Returns the line indices of the selected patch range that are changes (i.e. additions or deletions) 286func (s *State) ChangeLinesInSelectedPatchRange() []int { 1 hits287viewStart, viewEnd := s.SelectedViewRange() 1 hits288patchStart, patchEnd := s.patchLineIndices[viewStart], s.patchLineIndices[viewEnd] 1 hits289lines := s.patch.Lines() 1 hits290indices := []int{} 1 hits291for i := patchStart; i <= patchEnd; i++ { 2 hits292if lines[i].IsChange() { 2 hits293indices = append(indices, i) 1 hits294} 1 hits295} 296return indices 1 hits297} 298 299func (s *State) CurrentLineNumber() int { 300return s.patch.LineNumberOfLine(s.patchLineIndices[s.selectedLineIdx]) 301}@@ -324,3 +338,11 @@ func wrapPatchLines(diff string, view *gocui.View) ([]int, []int) {
324view.Wrap, view.Editable, strings.TrimSuffix(diff, "\n"), view.InnerWidth(), view.TabWidth) 325return viewLineIndices, patchLineIndices 326} 338view.Wrap, view.Editable, strings.TrimSuffix(diff, "\n"), view.InnerWidth(), view.TabWidth) 339return viewLineIndices, patchLineIndices 340} 341 342func (s *State) SelectNextStageableLineOfSameIncludedState(includedLines []int, included bool) { 1 hits343_, lastLineIdx := s.SelectedPatchRange() 1 hits344patchLineIdx, found := s.patch.GetNextChangeIdxOfSameIncludedState(lastLineIdx+1, includedLines, included) 1 hits345if found { 2 hits346s.SelectLine(s.viewLineIndices[patchLineIdx]) 1 hits347} 1 hits348}pkg/integration/tests/patch_building/move_to_index_partial.go
pkg/integration/tests/patch_building/specific_selection.go
Diff coverage
@@ -66,18 +66,20 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
66Contains(` 1f`), 67). 68PressPrimaryAction(). 69// unlike in the staging panel, we don't remove lines from the patch building panel 70// upon 'adding' them. So the same lines will be selected 71SelectedLines( 72Contains(`@@ -1,6 +1,6 @@`), 73Contains(`-1a`), 74Contains(`+aa`), 75Contains(` 1b`), 76Contains(`-1c`), 77Contains(`+cc`), 78Contains(` 1d`), 79Contains(` 1e`), 80Contains(` 1f`), 81). 82Tap(func() { 83t.Views().Information().Content(Contains("Building patch"))66Contains(` 1f`), 67). 68PressPrimaryAction(). 69SelectedLines( 70Contains(`@@ -17,9 +17,9 @@`), 1 hits71Contains(` 1q`), 1 hits72Contains(` 1r`), 1 hits73Contains(` 1s`), 1 hits74Contains(`-1t`), 1 hits75Contains(`-1u`), 1 hits76Contains(`-1v`), 1 hits77Contains(`+tt`), 1 hits78Contains(`+uu`), 1 hits79Contains(`+vv`), 1 hits80Contains(` 1w`), 1 hits81Contains(` 1x`), 1 hits82Contains(` 1y`), 1 hits83). 84Tap(func() { 85t.Views().Information().Content(Contains("Building patch"))@@ -106,12 +108,21 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
106Contains("+2a"), 107). 108PressPrimaryAction(). 109NavigateToLine(Contains("+2c")). 110Press(keys.Universal.ToggleRangeSelect). 111NavigateToLine(Contains("+2e")). 112PressPrimaryAction(). 113NavigateToLine(Contains("+2g")). 114PressPrimaryAction(). 115Tap(func() { 116t.Views().Information().Content(Contains("Building patch")) 108Contains("+2a"), 109). 110PressPrimaryAction(). 111SelectedLines( 1 hits112Contains("+2b"), 1 hits113). 1 hits114NavigateToLine(Contains("+2c")). 115Press(keys.Universal.ToggleRangeSelect). 116NavigateToLine(Contains("+2e")). 117PressPrimaryAction(). 118SelectedLines( 1 hits119Contains("+2f"), 1 hits120). 1 hits121NavigateToLine(Contains("+2g")). 122PressPrimaryAction(). 123SelectedLines( 1 hits124Contains("+2h"), 1 hits125). 1 hits126Tap(func() { 127t.Views().Information().Content(Contains("Building patch"))