diff --git a/pkg/gui/controllers/toggle_whitespace_action.go b/pkg/gui/controllers/toggle_whitespace_action.go index 67bb59d86..33196182a 100644 --- a/pkg/gui/controllers/toggle_whitespace_action.go +++ b/pkg/gui/controllers/toggle_whitespace_action.go @@ -27,6 +27,12 @@ func (self *ToggleWhitespaceAction) Call() error { self.c.UserConfig().Git.IgnoreWhitespaceInDiffView = !self.c.UserConfig().Git.IgnoreWhitespaceInDiffView + // You toggle this to see whether what you are looking at is more than + // reindentation, so that is the thing to keep in front of you — even though + // ignoring whitespace, unlike the other ways of re-rendering a diff, can take + // the line away entirely along with the hunk or file it was in. + self.c.Helpers().DiffLine.PreserveDiffPositionOnRerender(self.c.Contexts().Normal.GetView()) + self.c.Helpers().DiffLine.PreserveDiffPositionOnRerender(self.c.Contexts().NormalSecondary.GetView()) self.c.Context().CurrentSide().HandleRenderToMain() return nil } diff --git a/pkg/integration/tests/main_view/keep_position_in_both_panes_when_ignoring_whitespace.go b/pkg/integration/tests/main_view/keep_position_in_both_panes_when_ignoring_whitespace.go new file mode 100644 index 000000000..c84ccde5d --- /dev/null +++ b/pkg/integration/tests/main_view/keep_position_in_both_panes_when_ignoring_whitespace.go @@ -0,0 +1,70 @@ +package main_view + +import ( + "fmt" + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var KeepPositionInBothPanesWhenIgnoringWhitespace = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ignoring whitespace keeps the place in the lower pane too, not only in the upper one", + ExtraCmdArgs: []string{}, + Skip: false, + Width: 120, + Height: 30, + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false + }, + SetupRepo: func(shell *Shell) { + lines := make([]string, 60) + for i := range lines { + lines[i] = fmt.Sprintf("line%02d", i+1) + } + shell.CreateFileAndAdd("file1", strings.Join(lines, "\n")+"\n") + shell.Commit("one") + + // Staged: real changes at lines 5, 25 and 45, and a whitespace-only one at 15, + // whose hunk goes when whitespace stops counting. + lines[4] = strings.ToUpper(lines[4]) + lines[14] = " " + lines[14] + lines[24] = strings.ToUpper(lines[24]) + lines[44] = strings.ToUpper(lines[44]) + shell.UpdateFile("file1", strings.Join(lines, "\n")+"\n") + shell.GitAddAll() + + // And one unstaged change, to split the file's diff across both panes. + lines[59] = strings.ToUpper(lines[59]) + shell.UpdateFile("file1", strings.Join(lines, "\n")+"\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + PressTab() + + t.Views().Secondary(). + IsFocused(). + Press(keys.Main.NextHunk). + Press(keys.Main.NextHunk). + Press(keys.Main.NextHunk). + SelectedLines( + Contains("-line45"), + ). + SelectedLineIdx(35). + OriginY(14). + Press(keys.Universal.ToggleWhitespaceInDiffView). + // The hunk above this one held nothing but a whitespace change, so it is + // gone and has taken nine lines of the lower pane's diff with it — leaving + // the line we were on where it was on the screen. + SelectedLines( + Contains("-line45"), + ). + SelectedLineIdx(26). + OriginY(5) + }, +}) diff --git a/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace.go b/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace.go new file mode 100644 index 000000000..9b9479369 --- /dev/null +++ b/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace.go @@ -0,0 +1,85 @@ +package main_view + +import ( + "fmt" + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var KeepPositionWhenIgnoringWhitespace = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ignoring whitespace keeps the line you were looking at where it was, even when it turns into a context line", + ExtraCmdArgs: []string{}, + Skip: false, + Width: 120, + Height: 30, + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false + }, + SetupRepo: func(shell *Shell) { + lines := make([]string, 40) + for i := range lines { + lines[i] = fmt.Sprintf("line%02d", i+1) + } + shell.CreateFileAndAdd("file1", strings.Join(lines, "\n")+"\n") + shell.Commit("one") + + // Real changes at lines 5 and 25, whitespace-only ones at 15, 27 and 35. The + // one at 27 shares a hunk with the change at 25, so ignoring whitespace turns + // it into a context line rather than taking its hunk away. + lines[4] = strings.ToUpper(lines[4]) + lines[14] = " " + lines[14] + lines[24] = strings.ToUpper(lines[24]) + lines[26] = lines[26] + " " + lines[34] = " " + lines[34] + shell.UpdateFile("file1", strings.Join(lines, "\n")+"\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + Press(keys.Main.NextHunk). + Press(keys.Main.NextHunk). + SelectedLines( + Contains("-line25"), + ). + SelectedLineIdx(26). + OriginY(14). + Press(keys.Universal.ToggleWhitespaceInDiffView). + // The hunk above this one held nothing but a whitespace change, so it is + // gone and has taken nine lines of diff with it. This is still the line we + // were on, on the row we were on (26 - 14 = 17 - 5). + SelectedLines( + Contains("-line25"), + ). + SelectedLineIdx(17). + OriginY(5). + // And back again, whitespace and all. + Press(keys.Universal.ToggleWhitespaceInDiffView). + SelectedLines( + Contains("-line25"), + ). + SelectedLineIdx(26). + OriginY(14). + // The whitespace-only change further down this hunk is a line of the file + // like any other: ignoring whitespace shows it as context instead of as a + // change, and that is still where we are. + Press(keys.Main.NextHunk). + Press(keys.Universal.NextItem). + SelectedLines( + Contains("+line27"), + ). + SelectedLineIdx(30). + OriginY(14). + Press(keys.Universal.ToggleWhitespaceInDiffView). + SelectedLines( + Contains(" line27"), + ). + SelectedLineIdx(20). + OriginY(4) + }, +}) diff --git a/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace_removes_it.go b/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace_removes_it.go new file mode 100644 index 000000000..1474d9042 --- /dev/null +++ b/pkg/integration/tests/main_view/keep_position_when_ignoring_whitespace_removes_it.go @@ -0,0 +1,81 @@ +package main_view + +import ( + "fmt" + "strings" + + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var KeepPositionWhenIgnoringWhitespaceRemovesIt = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ignoring whitespace where that takes the line you were on out of the diff lands on the nearest line it kept", + ExtraCmdArgs: []string{}, + Skip: false, + Width: 120, + Height: 30, + SetupConfig: func(cfg *config.AppConfig) { + cfg.GetUserConfig().Gui.UseHunkModeInStagingView = false + }, + SetupRepo: func(shell *Shell) { + lines := make([]string, 40) + for i := range lines { + lines[i] = fmt.Sprintf("line%02d", i+1) + } + shell.CreateFileAndAdd("file1", strings.Join(lines, "\n")+"\n") + shell.CreateFileAndAdd("file2", "one\ntwo\nthree\n") + shell.Commit("one") + + // Real changes at lines 5, 15 and 25, and a whitespace-only one at 35, far + // enough apart to be hunks of their own. + for _, i := range []int{5, 15, 25} { + lines[i-1] = strings.ToUpper(lines[i-1]) + } + lines[34] = " " + lines[34] + shell.UpdateFile("file1", strings.Join(lines, "\n")+"\n") + + // Nothing but reindentation, so ignoring whitespace leaves no diff at all. + shell.UpdateFile("file2", " one\n two\n three\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + SelectNextItem(). + SelectedLine(Contains("file1")). + Press(keys.Universal.FocusMainView) + + t.Views().Main(). + IsFocused(). + Press(keys.Main.NextHunk). + Press(keys.Main.NextHunk). + Press(keys.Main.NextHunk). + SelectedLines( + Contains("-line35"), + ). + SelectedLineIdx(35). + OriginY(14). + Press(keys.Universal.ToggleWhitespaceInDiffView). + // That hunk was a whitespace change and nothing else, so ignoring + // whitespace takes it — and the context around it — out of the diff + // entirely. The nearest line the diff kept is the last line of the hunk + // above, so that is where the selection lands; it goes back on the row it + // was on itself, which leaves everything above it exactly where it was. + SelectedLines( + Contains(" line28"), + ). + SelectedLineIdx(30). + OriginY(14). + // The whole diff can go this way, and then there is nothing to land on. + Press(keys.Universal.ToggleWhitespaceInDiffView). + PressEscape() + + t.Views().Files(). + IsFocused(). + SelectNextItem(). + SelectedLine(Contains("file2")). + Press(keys.Universal.ToggleWhitespaceInDiffView) + + t.Views().Main(). + Content(Equals("")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index c9bc6a721..57629dbce 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -371,8 +371,11 @@ var tests = []*components.IntegrationTest{ main_view.FileNavigationScrollsToTheTop, main_view.HideSelectionWhenChangesVanish, main_view.KeepPositionInBothPanesWhenChangingContextSize, + main_view.KeepPositionInBothPanesWhenIgnoringWhitespace, main_view.KeepPositionInBothPanesWhenSwitchingDiffRenderers, main_view.KeepPositionWhenChangingContextSize, + main_view.KeepPositionWhenIgnoringWhitespace, + main_view.KeepPositionWhenIgnoringWhitespaceRemovesIt, main_view.KeepPositionWhenSwitchingDiffRenderers, main_view.KeepPositionWhenTheSelectionIsOffScreen, main_view.KeepScrollWhenTheDiffCantBeRead,