diff --git a/docs/Config.md b/docs/Config.md index 11fe29877..0cdc8ff83 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -319,26 +319,30 @@ gui: # Config relating to git git: + # Array of pagers. Each entry has the following format: + # + # # Value of the --color arg in the git diff command. Some pagers want + # # this to be set to 'always' and some want it set to 'never' + # colorArg: "always" + # + # # e.g. + # # diff-so-fancy + # # delta --dark --paging=never + # # ydiff -p cat -s --wrap --width={{columnWidth}} + # pager: "" + # + # # e.g. 'difft --color=always' + # externalDiffCommand: "" + # + # # If true, Lazygit will use git's `diff.external` config for paging. + # # The advantage over `externalDiffCommand` is that this can be + # # configured per file type in .gitattributes; see + # # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver. + # useExternalDiffGitConfig: false + # # See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md - paging: - # Value of the --color arg in the git diff command. Some pagers want this to be - # set to 'always' and some want it set to 'never' - colorArg: always - - # e.g. - # diff-so-fancy - # delta --dark --paging=never - # ydiff -p cat -s --wrap --width={{columnWidth}} - pager: "" - - # e.g. 'difft --color=always' - externalDiffCommand: "" - - # If true, Lazygit will use git's `diff.external` config for paging. The - # advantage over `externalDiffCommand` is that this can be configured per file - # type in .gitattributes; see - # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver. - useExternalDiffGitConfig: false + # for more information. + pagers: [] # Config relating to committing commit: @@ -638,6 +642,7 @@ keybinding: prevTab: '[' nextScreenMode: + prevScreenMode: _ + cyclePagers: '|' undo: z redo: Z filteringMenu: diff --git a/docs/Custom_Pagers.md b/docs/Custom_Pagers.md index 5e5d51372..c6616cecf 100644 --- a/docs/Custom_Pagers.md +++ b/docs/Custom_Pagers.md @@ -4,23 +4,27 @@ Lazygit supports custom pagers, [configured](/docs/Config.md) in the config.yml Support does not extend to Windows users, because we're making use of a package which doesn't have Windows support. However, see [below](#emulating-custom-pagers-on-windows) for a workaround. -## Default: +Multiple pagers are supported; you can cycle through them with the `|` key. This can be useful if you usually prefer a particular pager, but want to use a different one for certain kinds of diffs. + +Pagers are configured with the `pagers` array in the git section; here's an example for a multi-pager setup: ```yaml git: - paging: - colorArg: always + pagers: + - pager: delta --dark --paging=never + - pager: ydiff -p cat -s --wrap --width={{columnWidth}} + colorArg: never + - externalDiffCommand: difft --color=always ``` -the `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`. +The `colorArg` key is for whether you want the `--color=always` arg in your `git diff` command. Some pagers want it set to `always`, others want it set to `never`. The default is `always`, since that's what most pagers need. ## Delta: ```yaml git: - paging: - colorArg: always - pager: delta --dark --paging=never + pagers: + - pager: delta --dark --paging=never ``` ![](https://i.imgur.com/QJpQkF3.png) @@ -31,9 +35,8 @@ A cool feature of delta is --hyperlinks, which renders clickable links for the l ```yaml git: - paging: - colorArg: always - pager: diff-so-fancy + pagers: + - pager: diff-so-fancy ``` ![](https://i.imgur.com/rjH1TpT.png) @@ -44,9 +47,9 @@ git: gui: sidePanelWidth: 0.2 # gives you more space to show things side-by-side git: - paging: - colorArg: never - pager: ydiff -p cat -s --wrap --width={{columnWidth}} + pagers: + - colorArg: never + pager: ydiff -p cat -s --wrap --width={{columnWidth}} ``` ![](https://i.imgur.com/vaa8z0H.png) @@ -61,8 +64,8 @@ These can be used in lazygit by using the `externalDiffCommand` config; in the c ```yaml git: - paging: - externalDiffCommand: difft --color=always + pagers: + - externalDiffCommand: difft --color=always ``` The `colorArg` and `pager` options are not used in this case. @@ -71,16 +74,16 @@ You can add whatever extra arguments you prefer for your difftool; for instance ```yaml git: - paging: - externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off + pagers: + - externalDiffCommand: difft --color=always --display=inline --syntax-highlight=off ``` Instead of setting this command in lazygit's `externalDiffCommand` config, you can also tell lazygit to use the external diff command that is configured in git itself (`diff.external`), by using ```yaml git: - paging: - useExternalDiffGitConfig: true + pagers: + - useExternalDiffGitConfig: true ``` This can be useful if you also want to use it for diffs on the command line, and it also has the advantage that you can configure it per file type in `.gitattributes`; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver. @@ -106,8 +109,8 @@ In your lazygit config, use ```yml git: - paging: - externalDiffCommand: "C:/wherever/lazygit-pager.ps1" + pagers: + - externalDiffCommand: "C:/wherever/lazygit-pager.ps1" ``` The main limitation of this approach compared to a "real" pager is that renames are not displayed correctly; they are shown as if they were modifications of the old file. (This affects only the hunk headers; the diff itself is always correct.) diff --git a/pkg/commands/git_commands/commit_test.go b/pkg/commands/git_commands/commit_test.go index 8460164cc..6ea914c64 100644 --- a/pkg/commands/git_commands/commit_test.go +++ b/pkg/commands/git_commands/commit_test.go @@ -255,8 +255,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize uint64 similarityThreshold int ignoreWhitespace bool - extDiffCmd string - useExtDiffGitConfig bool + pagerConfig *config.PagingConfig expected []string } @@ -267,7 +266,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - extDiffCmd: "", + pagerConfig: nil, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"}, }, { @@ -276,7 +275,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - extDiffCmd: "", + pagerConfig: nil, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--", "file.txt"}, }, { @@ -285,7 +284,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 77, similarityThreshold: 50, ignoreWhitespace: false, - extDiffCmd: "", + pagerConfig: nil, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"}, }, { @@ -294,7 +293,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 33, ignoreWhitespace: false, - extDiffCmd: "", + pagerConfig: nil, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=33%", "--"}, }, { @@ -303,7 +302,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 77, similarityThreshold: 50, ignoreWhitespace: true, - extDiffCmd: "", + pagerConfig: nil, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--no-ext-diff", "--submodule", "--color=always", "--unified=77", "--stat", "--decorate", "-p", "1234567890", "--ignore-all-space", "--find-renames=50%", "--"}, }, { @@ -312,7 +311,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - extDiffCmd: "difft --color=always", + pagerConfig: &config.PagingConfig{ExternalDiffCommand: "difft --color=always"}, expected: []string{"-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"}, }, { @@ -321,7 +320,7 @@ func TestCommitShowCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - useExtDiffGitConfig: true, + pagerConfig: &config.PagingConfig{UseExternalDiffGitConfig: true}, expected: []string{"-C", "/path/to/worktree", "-c", "diff.noprefix=false", "show", "--ext-diff", "--submodule", "--color=always", "--unified=3", "--stat", "--decorate", "-p", "1234567890", "--find-renames=50%", "--"}, }, } @@ -329,11 +328,12 @@ func TestCommitShowCmdObj(t *testing.T) { for _, s := range scenarios { t.Run(s.testName, func(t *testing.T) { userConfig := config.GetDefaultConfig() - userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd + if s.pagerConfig != nil { + userConfig.Git.Pagers = []config.PagingConfig{*s.pagerConfig} + } userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace userConfig.Git.DiffContextSize = s.contextSize userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold - userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil) repoPaths := RepoPaths{ diff --git a/pkg/commands/git_commands/stash_test.go b/pkg/commands/git_commands/stash_test.go index 6b21c0b83..a942a4e98 100644 --- a/pkg/commands/git_commands/stash_test.go +++ b/pkg/commands/git_commands/stash_test.go @@ -103,8 +103,7 @@ func TestStashStashEntryCmdObj(t *testing.T) { contextSize uint64 similarityThreshold int ignoreWhitespace bool - extDiffCmd string - useExtDiffGitConfig bool + pagerConfig *config.PagingConfig expected []string } @@ -139,7 +138,7 @@ func TestStashStashEntryCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - extDiffCmd: "difft --color=always", + pagerConfig: &config.PagingConfig{ExternalDiffCommand: "difft --color=always"}, expected: []string{"git", "-C", "/path/to/worktree", "-c", "diff.external=difft --color=always", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"}, }, { @@ -148,7 +147,7 @@ func TestStashStashEntryCmdObj(t *testing.T) { contextSize: 3, similarityThreshold: 50, ignoreWhitespace: false, - useExtDiffGitConfig: true, + pagerConfig: &config.PagingConfig{UseExternalDiffGitConfig: true}, expected: []string{"git", "-C", "/path/to/worktree", "stash", "show", "-p", "--stat", "-u", "--ext-diff", "--color=always", "--unified=3", "--find-renames=50%", "refs/stash@{5}"}, }, { @@ -167,8 +166,9 @@ func TestStashStashEntryCmdObj(t *testing.T) { userConfig.Git.IgnoreWhitespaceInDiffView = s.ignoreWhitespace userConfig.Git.DiffContextSize = s.contextSize userConfig.Git.RenameSimilarityThreshold = s.similarityThreshold - userConfig.Git.Paging.ExternalDiffCommand = s.extDiffCmd - userConfig.Git.Paging.UseExternalDiffGitConfig = s.useExtDiffGitConfig + if s.pagerConfig != nil { + userConfig.Git.Pagers = []config.PagingConfig{*s.pagerConfig} + } repoPaths := RepoPaths{ worktreePath: "/path/to/worktree", } diff --git a/pkg/config/pager_config.go b/pkg/config/pager_config.go index 6d7d39739..e721da0e8 100644 --- a/pkg/config/pager_config.go +++ b/pkg/config/pager_config.go @@ -8,29 +8,75 @@ import ( type PagerConfig struct { getUserConfig func() *UserConfig + pagerIndex int } func NewPagerConfig(getUserConfig func() *UserConfig) *PagerConfig { return &PagerConfig{getUserConfig: getUserConfig} } +func (self *PagerConfig) currentPagerConfig() *PagingConfig { + pagers := self.getUserConfig().Git.Pagers + if len(pagers) == 0 { + return nil + } + + // Guard against the pager index being out of range, which can happen if the user + // has removed pagers from their config file while lazygit is running. + if self.pagerIndex >= len(pagers) { + self.pagerIndex = 0 + } + + return &pagers[self.pagerIndex] +} + func (self *PagerConfig) GetPagerCommand(width int) string { + currentPagerConfig := self.currentPagerConfig() + if currentPagerConfig == nil { + return "" + } + templateValues := map[string]string{ "columnWidth": strconv.Itoa(width/2 - 6), } - pagerTemplate := string(self.getUserConfig().Git.Paging.Pager) + pagerTemplate := string(currentPagerConfig.Pager) return utils.ResolvePlaceholderString(pagerTemplate, templateValues) } func (self *PagerConfig) GetColorArg() string { - return self.getUserConfig().Git.Paging.ColorArg + currentPagerConfig := self.currentPagerConfig() + if currentPagerConfig == nil { + return "always" + } + + colorArg := currentPagerConfig.ColorArg + if colorArg == "" { + return "always" + } + return colorArg } func (self *PagerConfig) GetExternalDiffCommand() string { - return self.getUserConfig().Git.Paging.ExternalDiffCommand + currentPagerConfig := self.currentPagerConfig() + if currentPagerConfig == nil { + return "" + } + return currentPagerConfig.ExternalDiffCommand } func (self *PagerConfig) GetUseExternalDiffGitConfig() bool { - return self.getUserConfig().Git.Paging.UseExternalDiffGitConfig + currentPagerConfig := self.currentPagerConfig() + if currentPagerConfig == nil { + return false + } + return currentPagerConfig.UseExternalDiffGitConfig +} + +func (self *PagerConfig) CyclePagers() { + self.pagerIndex = (self.pagerIndex + 1) % len(self.getUserConfig().Git.Pagers) +} + +func (self *PagerConfig) CurrentPagerIndex() (int, int) { + return self.pagerIndex, len(self.getUserConfig().Git.Pagers) } diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index a951de2e2..6635ff06a 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -233,8 +233,30 @@ type SpinnerConfig struct { } type GitConfig struct { - // See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md - Paging PagingConfig `yaml:"paging"` + // Array of pagers. Each entry has the following format: + // [dev] The following documentation is duplicated from the PagingConfig struct below. + // + // # Value of the --color arg in the git diff command. Some pagers want + // # this to be set to 'always' and some want it set to 'never' + // colorArg: "always" + // + // # e.g. + // # diff-so-fancy + // # delta --dark --paging=never + // # ydiff -p cat -s --wrap --width={{columnWidth}} + // pager: "" + // + // # e.g. 'difft --color=always' + // externalDiffCommand: "" + // + // # If true, Lazygit will use git's `diff.external` config for paging. + // # The advantage over `externalDiffCommand` is that this can be + // # configured per file type in .gitattributes; see + // # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver. + // useExternalDiffGitConfig: false + // + // See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md for more information. + Pagers []PagingConfig `yaml:"pagers"` // Config relating to committing Commit CommitConfig `yaml:"commit"` // Config relating to merging @@ -301,6 +323,7 @@ func (PagerType) JSONSchemaExtend(schema *jsonschema.Schema) { } } +// [dev] This documentation is duplicated in the GitConfig struct. If you make changes here, make them there too. type PagingConfig struct { // Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never' ColorArg string `yaml:"colorArg" jsonschema:"enum=always,enum=never"` @@ -441,6 +464,7 @@ type KeybindingUniversalConfig struct { PrevTab string `yaml:"prevTab"` NextScreenMode string `yaml:"nextScreenMode"` PrevScreenMode string `yaml:"prevScreenMode"` + CyclePagers string `yaml:"cyclePagers"` Undo string `yaml:"undo"` Redo string `yaml:"redo"` FilteringMenu string `yaml:"filteringMenu"` @@ -784,11 +808,6 @@ func GetDefaultConfig() *UserConfig { SwitchTabsWithPanelJumpKeys: false, }, Git: GitConfig{ - Paging: PagingConfig{ - ColorArg: "always", - Pager: "", - ExternalDiffCommand: "", - }, Commit: CommitConfig{ SignOff: false, AutoWrapCommitMessage: true, @@ -904,6 +923,7 @@ func GetDefaultConfig() *UserConfig { PrevTab: "[", NextScreenMode: "+", PrevScreenMode: "_", + CyclePagers: "|", Undo: "z", Redo: "Z", FilteringMenu: "", diff --git a/pkg/gui/controllers/global_controller.go b/pkg/gui/controllers/global_controller.go index 734e65516..1ae560685 100644 --- a/pkg/gui/controllers/global_controller.go +++ b/pkg/gui/controllers/global_controller.go @@ -1,6 +1,8 @@ package controllers import ( + "fmt" + "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -58,6 +60,13 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type Handler: opts.Guards.NoPopupPanel(self.prevScreenMode), Description: self.c.Tr.PrevScreenMode, }, + { + Key: opts.GetKey(opts.Config.Universal.CyclePagers), + Handler: opts.Guards.NoPopupPanel(self.cyclePagers), + GetDisabledReason: self.canCyclePagers, + Description: self.c.Tr.CyclePagers, + Tooltip: self.c.Tr.CyclePagersTooltip, + }, { Key: opts.GetKey(opts.Config.Universal.Return), Modifier: gocui.ModNone, @@ -171,6 +180,27 @@ func (self *GlobalController) prevScreenMode() error { return (&ScreenModeActions{c: self.c}).Prev() } +func (self *GlobalController) cyclePagers() error { + self.c.State().GetPagerConfig().CyclePagers() + if self.c.Context().CurrentSide().GetKey() == self.c.Context().Current().GetKey() { + self.c.Context().CurrentSide().HandleFocus(types.OnFocusOpts{}) + } + + current, total := self.c.State().GetPagerConfig().CurrentPagerIndex() + self.c.Toast(fmt.Sprintf("Selected pager %d of %d", current+1, total)) + return nil +} + +func (self *GlobalController) canCyclePagers() *types.DisabledReason { + _, total := self.c.State().GetPagerConfig().CurrentPagerIndex() + if total <= 1 { + return &types.DisabledReason{ + Text: self.c.Tr.CyclePagersDisabledReason, + } + } + return nil +} + func (self *GlobalController) createOptionsMenu() error { return (&OptionsMenuAction{c: self.c}).Call() } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 30123efaa..86ba163aa 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -585,6 +585,9 @@ type TranslationSet struct { ViewResetToUpstreamOptions string NextScreenMode string PrevScreenMode string + CyclePagers string + CyclePagersTooltip string + CyclePagersDisabledReason string StartSearch string StartFilter string Keybindings string @@ -1678,6 +1681,9 @@ func EnglishTranslationSet() *TranslationSet { ViewResetToUpstreamOptions: "View upstream reset options", NextScreenMode: "Next screen mode (normal/half/fullscreen)", PrevScreenMode: "Prev screen mode", + CyclePagers: "Cycle pagers", + CyclePagersTooltip: "Choose the next pager in the list of configured pagers", + CyclePagersDisabledReason: "No other pagers configured", StartSearch: "Search the current view by text", StartFilter: "Filter the current view by text", KeybindingsLegend: "Legend: `` means ctrl+b, `` means alt+b, `B` means shift+b", diff --git a/schema/config.json b/schema/config.json index 19554058b..db86f4f00 100644 --- a/schema/config.json +++ b/schema/config.json @@ -285,9 +285,12 @@ }, "GitConfig": { "properties": { - "paging": { - "$ref": "#/$defs/PagingConfig", - "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md" + "pagers": { + "items": { + "$ref": "#/$defs/PagingConfig" + }, + "type": "array", + "description": "Array of pagers. Each entry has the following format:\n\n # Value of the --color arg in the git diff command. Some pagers want\n # this to be set to 'always' and some want it set to 'never'\n colorArg: \"always\"\n\n # e.g.\n # diff-so-fancy\n # delta --dark --paging=never\n # ydiff -p cat -s --wrap --width={{columnWidth}}\n pager: \"\"\n\n # e.g. 'difft --color=always'\n externalDiffCommand: \"\"\n\n # If true, Lazygit will use git's `diff.external` config for paging.\n # The advantage over `externalDiffCommand` is that this can be\n # configured per file type in .gitattributes; see\n # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.\n useExternalDiffGitConfig: false\n\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md for more information." }, "commit": { "$ref": "#/$defs/CommitConfig", @@ -1464,6 +1467,10 @@ "type": "string", "default": "_" }, + "cyclePagers": { + "type": "string", + "default": "|" + }, "undo": { "type": "string", "default": "z" @@ -1667,13 +1674,11 @@ "always", "never" ], - "description": "Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never'", - "default": "always" + "description": "Value of the --color arg in the git diff command. Some pagers want this to be set to 'always' and some want it set to 'never'" }, "pager": { "type": "string", "description": "e.g.\ndiff-so-fancy\ndelta --dark --paging=never\nydiff -p cat -s --wrap --width={{columnWidth}}", - "default": "", "examples": [ "delta --dark --paging=never", "diff-so-fancy", @@ -1686,13 +1691,11 @@ }, "useExternalDiffGitConfig": { "type": "boolean", - "description": "If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.", - "default": false + "description": "If true, Lazygit will use git's `diff.external` config for paging. The advantage over `externalDiffCommand` is that this can be configured per file type in .gitattributes; see https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver." } }, "additionalProperties": false, - "type": "object", - "description": "See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md" + "type": "object" }, "RefresherConfig": { "properties": {