1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-10-16 09:27:37 +03:00

Replace paging config with an array of pagers

This commit is contained in:
Stefan Haller
2025-10-08 16:22:17 +02:00
parent 765c9eb85c
commit e44d6ec330
9 changed files with 191 additions and 78 deletions

View File

@@ -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: <c-s>

View File

@@ -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,8 +47,8 @@ git:
gui:
sidePanelWidth: 0.2 # gives you more space to show things side-by-side
git:
paging:
colorArg: never
pagers:
- colorArg: never
pager: ydiff -p cat -s --wrap --width={{columnWidth}}
```
@@ -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.)

View File

@@ -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{

View File

@@ -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",
}

View File

@@ -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)
}

View File

@@ -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: "<c-s>",

View File

@@ -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()
}

View File

@@ -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: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b",

View File

@@ -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": {