Improve cycling through multiple pagers (#5678)

Lazygit lets you configure multiple pagers and switch between them with
the `|` key. The changes in this PR improve this for the case that you
have more than two.

- **You can see which pager you switched to.** The notification used to
just say "pager 2 of 3"; now it shows the pager's name, so you no longer
have to remember the order to know where you've landed.

- **You can name your pagers.** By default the name is taken from the
pager command, but you can set your own name in the config. This helps
when two entries run the same command with different options (for
example plain `delta` and `delta --side-by-side`).

- **You can cycle backwards.** Alongside `|`, which moves to the next
pager, the new `\` key moves to the previous one — so you can step back
instead of going all the way around the list to return to one you just
passed. This is especially useful when you have two pagers that you
alternate between often (e.g. `delta` and `delta --side-by-side`), but
also have several others in the list that you use only occasionally.

- **Invalid pager setups are caught early.** If an entry combines
options that can't be used together, lazygit now tells you about it on
startup instead of silently producing a broken diff.
This commit is contained in:
Stefan Haller 2026-06-07 16:00:08 +02:00 committed by GitHub
commit 8a7bef0975
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 341 additions and 15 deletions

View file

@ -342,6 +342,11 @@ gui:
git:
# Array of pagers. Each entry has the following format:
#
# # A name for the pager, shown in the notification when cycling pagers.
# # If not set, the name is derived from the first word of the pager
# # command (or of the external diff command).
# name: ""
#
# # 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"
@ -361,6 +366,9 @@ git:
# # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
# useExternalDiffGitConfig: false
#
# 'pager', 'externalDiffCommand', and 'useExternalDiffGitConfig' are mutually
# exclusive; set at most one per entry.
#
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md
# for more information.
pagers: []
@ -667,6 +675,7 @@ keybinding:
nextScreenMode: +
prevScreenMode: _
cyclePagers: '|'
cyclePagersReverse: \
undo: z
redo: Z
filteringMenu: <ctrl+s>

View file

@ -71,7 +71,7 @@ git:
- externalDiffCommand: difft --color=always
```
The `colorArg` and `pager` options are not used in this case.
The `colorArg` option is not used in this case.
You can add whatever extra arguments you prefer for your difftool; for instance
@ -91,6 +91,8 @@ git:
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.
`pager`, `externalDiffCommand`, and `useExternalDiffGitConfig` are alternative ways of producing the diff, so a pager entry may use at most one of them.
## Emulating custom pagers on Windows
There is a trick to emulate custom pagers on Windows using a Powershell script configured as an external diff command. It's not perfect, but certainly better than nothing. To do this, save the following script as `lazygit-pager.ps1` at a convenient place on your disk:

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | Refresh | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Next screen mode (normal/half/fullscreen) | |
| `` _ `` | Prev screen mode | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | Cancel | |
| `` ? `` | Open keybindings menu | |
| `` <ctrl+s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | 更新 | Gitの状態を更新します`git status`、`git branch`などをバックグラウンドで実行してパネルの内容を更新します)。これは`git fetch`を実行しません。 |
| `` + `` | 次の画面モード(通常/半分/全画面) | |
| `` _ `` | 前の画面モード | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | キャンセル | |
| `` ? `` | キーバインディングメニューを開く | |
| `` <ctrl+s> `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | 새로고침 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 다음 스크린 모드 (normal/half/fullscreen) | |
| `` _ `` | 이전 스크린 모드 | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | 취소 | |
| `` ? `` | 매뉴 열기 | |
| `` <ctrl+s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | Verversen | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Volgende scherm modus (normaal/half/groot) | |
| `` _ `` | Vorige scherm modus | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | Annuleren | |
| `` ? `` | Open menu | |
| `` <ctrl+s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | Odśwież | Odśwież stan git (tj. uruchom `git status`, `git branch`, itp. w tle, aby zaktualizować zawartość paneli). To nie uruchamia `git fetch`. |
| `` + `` | Następny tryb ekranu (normalny/półpełny/pełnoekranowy) | |
| `` _ `` | Poprzedni tryb ekranu | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | Anuluj | |
| `` ? `` | Otwórz menu przypisań klawiszy | |
| `` <ctrl+s> `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | Atualizar | Atualize o estado do git (ou seja, execute `git status`, `git branch`, etc em segundo plano para atualizar o conteúdo de painéis). Isso não executa `git fetch`. |
| `` + `` | Modo de tela seguinte (normal/metade/tela cheia) | |
| `` _ `` | Modo de tela anterior | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | Cancelar | |
| `` ? `` | Abrir o menu de atalhos do teclado | |
| `` <ctrl+s> `` | Ver opções de filtro | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | Обновить | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | Следующий режим экрана (нормальный/полуэкранный/полноэкранный) | |
| `` _ `` | Предыдущий режим экрана | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | Отменить | |
| `` ? `` | Открыть меню | |
| `` <ctrl+s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -23,6 +23,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` + `` | 下一屏模式(正常/半屏/全屏) | |
| `` _ `` | 上一屏模式 | |
| `` \| `` | 切换分页器 | 从已配置的分页器列表中选择下一个分页器 |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | 取消 | |
| `` ? `` | 打开菜单 | |
| `` <ctrl+s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |

View file

@ -22,7 +22,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
| `` R `` | 重新整理 | Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`. |
| `` + `` | 下一個螢幕模式(常規/半螢幕/全螢幕) | |
| `` _ `` | 上一個螢幕模式 | |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers |
| `` \| `` | Cycle pagers | Choose the next pager in the list of configured pagers. |
| `` \ `` | Cycle pagers (reverse) | Choose the previous pager in the list of configured pagers. |
| `` <esc> `` | 取消 | |
| `` ? `` | 開啟選單 | |
| `` <ctrl+s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. |

View file

@ -46,6 +46,10 @@ e2e-tui *args:
e2e-all:
go test pkg/integration/clients/*.go
# Run some tests on the current commit, similar to what CI does.
check:
./scripts/check_commit.sh
bump-gocui:
scripts/bump_gocui.sh

View file

@ -2,6 +2,7 @@ package config
import (
"strconv"
"strings"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -77,6 +78,49 @@ func (self *PagerConfig) CyclePagers() {
self.pagerIndex = (self.pagerIndex + 1) % len(self.getUserConfig().Git.Pagers)
}
func (self *PagerConfig) CyclePagersBackward() {
n := len(self.getUserConfig().Git.Pagers)
self.pagerIndex = (self.pagerIndex - 1 + n) % n
}
func (self *PagerConfig) CurrentPagerIndex() (int, int) {
return self.pagerIndex, len(self.getUserConfig().Git.Pagers)
}
// CurrentPagerName returns a name for the current pager, suitable for showing
// to the user. It returns an empty string if no name can be derived; callers
// should substitute a localized fallback in that case.
func (self *PagerConfig) CurrentPagerName() string {
currentPagerConfig := self.currentPagerConfig()
if currentPagerConfig == nil {
return ""
}
return currentPagerConfig.displayName()
}
// CurrentPagerUsesGitConfigDiff reports whether the current pager defers to
// git's own external diff config. Such an entry has no name we can derive (the
// actual command may even vary per file via .gitattributes), so callers show a
// generic label rather than treating it like the default no-pager entry.
func (self *PagerConfig) CurrentPagerUsesGitConfigDiff() bool {
currentPagerConfig := self.currentPagerConfig()
return currentPagerConfig != nil && currentPagerConfig.UseExternalDiffGitConfig
}
func (self *PagingConfig) displayName() string {
if self.Name != "" {
return self.Name
}
if word := firstWord(string(self.Pager)); word != "" {
return word
}
return firstWord(self.ExternalDiffCommand)
}
func firstWord(command string) string {
fields := strings.Fields(command)
if len(fields) == 0 {
return ""
}
return fields[0]
}

View file

@ -0,0 +1,82 @@
package config
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCurrentPagerName(t *testing.T) {
scenarios := []struct {
name string
pager PagingConfig
expected string
}{
{
name: "explicit name takes precedence over the command",
pager: PagingConfig{Name: "delta side-by-side", Pager: "delta --side-by-side"},
expected: "delta side-by-side",
},
{
name: "derived from the first word of the pager command",
pager: PagingConfig{Pager: "delta --side-by-side"},
expected: "delta",
},
{
name: "surrounding whitespace in the command is ignored",
pager: PagingConfig{Pager: " diff-so-fancy "},
expected: "diff-so-fancy",
},
{
name: "falls back to the external diff command when there is no pager",
pager: PagingConfig{ExternalDiffCommand: "difft --color=always"},
expected: "difft",
},
{
name: "no name can be derived",
pager: PagingConfig{UseExternalDiffGitConfig: true},
expected: "",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
userConfig := &UserConfig{}
userConfig.Git.Pagers = []PagingConfig{s.pager}
config := NewPagerConfig(func() *UserConfig { return userConfig })
assert.Equal(t, s.expected, config.CurrentPagerName())
})
}
}
func TestCurrentPagerNameWithoutPagers(t *testing.T) {
config := NewPagerConfig(func() *UserConfig { return &UserConfig{} })
assert.Equal(t, "", config.CurrentPagerName())
}
func TestCyclePagers(t *testing.T) {
userConfig := &UserConfig{}
userConfig.Git.Pagers = []PagingConfig{{Name: "a"}, {Name: "b"}, {Name: "c"}}
config := NewPagerConfig(func() *UserConfig { return userConfig })
currentIndex := func() int {
index, _ := config.CurrentPagerIndex()
return index
}
assert.Equal(t, 0, currentIndex())
config.CyclePagers()
assert.Equal(t, 1, currentIndex())
config.CyclePagers()
assert.Equal(t, 2, currentIndex())
config.CyclePagers()
assert.Equal(t, 0, currentIndex(), "cycling forward past the last pager wraps to the first")
config.CyclePagersBackward()
assert.Equal(t, 2, currentIndex(), "cycling backward past the first pager wraps to the last")
config.CyclePagersBackward()
assert.Equal(t, 1, currentIndex())
}

View file

@ -256,6 +256,11 @@ type GitConfig struct {
// Array of pagers. Each entry has the following format:
// [dev] The following documentation is duplicated from the PagingConfig struct below.
//
// # A name for the pager, shown in the notification when cycling pagers.
// # If not set, the name is derived from the first word of the pager
// # command (or of the external diff command).
// name: ""
//
// # 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"
@ -275,6 +280,8 @@ type GitConfig struct {
// # https://git-scm.com/docs/gitattributes#_defining_an_external_diff_driver.
// useExternalDiffGitConfig: false
//
// 'pager', 'externalDiffCommand', and 'useExternalDiffGitConfig' are mutually exclusive; set at most one per entry.
//
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md for more information.
Pagers []PagingConfig `yaml:"pagers"`
// Config relating to committing
@ -345,6 +352,8 @@ 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 {
// A name for the pager, shown in the notification when cycling pagers. If not set, the name is derived from the first word of the pager command (or of the external diff command).
Name string `yaml:"name"`
// 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"`
// e.g.
@ -502,6 +511,7 @@ type KeybindingUniversalConfig struct {
NextScreenMode Keybinding `yaml:"nextScreenMode"`
PrevScreenMode Keybinding `yaml:"prevScreenMode"`
CyclePagers Keybinding `yaml:"cyclePagers"`
CyclePagersReverse Keybinding `yaml:"cyclePagersReverse"`
Undo Keybinding `yaml:"undo"`
Redo Keybinding `yaml:"redo"`
FilteringMenu Keybinding `yaml:"filteringMenu"`
@ -1010,6 +1020,7 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig {
NextScreenMode: Keybinding{"+"},
PrevScreenMode: Keybinding{"_"},
CyclePagers: Keybinding{"|"},
CyclePagersReverse: Keybinding{"\\"},
Undo: Keybinding{"z"},
Redo: Keybinding{"Z"},
FilteringMenu: Keybinding{"<ctrl+s>"},

View file

@ -46,6 +46,9 @@ func (config *UserConfig) Validate() error {
[]string{"always", "never", "when-maximised"}); err != nil {
return err
}
if err := validatePagers(config.Git.Pagers); err != nil {
return err
}
if err := validateKeybindings(config.Keybinding); err != nil {
return err
}
@ -71,6 +74,30 @@ func validateSpinner(spinner SpinnerConfig) error {
return nil
}
// validatePagers rejects pager entries that combine more than one diff
// mechanism. A pager (GIT_PAGER) formats the diff that git produces, whereas
// externalDiffCommand and useExternalDiffGitConfig change how git produces the
// diff in the first place; piping one through the other almost always yields
// garbled output, so we treat the three as mutually exclusive.
func validatePagers(pagers []PagingConfig) error {
for i, pager := range pagers {
count := 0
if pager.Pager != "" {
count++
}
if pager.ExternalDiffCommand != "" {
count++
}
if pager.UseExternalDiffGitConfig {
count++
}
if count > 1 {
return fmt.Errorf("git.pagers[%d]: at most one of 'pager', 'externalDiffCommand', and 'useExternalDiffGitConfig' may be set; they are mutually exclusive", i)
}
}
return nil
}
func validateEnum(name string, value string, allowedValues []string) error {
if slices.Contains(allowedValues, value) {
return nil

View file

@ -323,3 +323,34 @@ func TestUserConfigValidate_spinnerFrames(t *testing.T) {
})
}
}
func TestUserConfigValidate_pagers(t *testing.T) {
scenarios := []struct {
name string
pager PagingConfig
valid bool
}{
{name: "empty", pager: PagingConfig{}, valid: true},
{name: "pager only", pager: PagingConfig{Pager: "delta"}, valid: true},
{name: "external diff command only", pager: PagingConfig{ExternalDiffCommand: "difft"}, valid: true},
{name: "git config external diff only", pager: PagingConfig{UseExternalDiffGitConfig: true}, valid: true},
{name: "pager and external diff command", pager: PagingConfig{Pager: "delta", ExternalDiffCommand: "difft"}, valid: false},
{name: "pager and git config external diff", pager: PagingConfig{Pager: "delta", UseExternalDiffGitConfig: true}, valid: false},
{name: "both external diff mechanisms", pager: PagingConfig{ExternalDiffCommand: "difft", UseExternalDiffGitConfig: true}, valid: false},
{name: "all three", pager: PagingConfig{Pager: "delta", ExternalDiffCommand: "difft", UseExternalDiffGitConfig: true}, valid: false},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
config := GetDefaultConfig()
config.Git.Pagers = []PagingConfig{s.pager}
err := config.Validate()
if s.valid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
}

View file

@ -1,10 +1,11 @@
package controllers
import (
"fmt"
"strconv"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type GlobalController struct {
@ -67,6 +68,13 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
Description: self.c.Tr.CyclePagers,
Tooltip: self.c.Tr.CyclePagersTooltip,
},
{
Keys: opts.GetKeys(opts.Config.Universal.CyclePagersReverse),
Handler: opts.Guards.NoPopupPanel(self.cyclePagersBackward),
GetDisabledReason: self.canCyclePagers,
Description: self.c.Tr.CyclePagersReverse,
Tooltip: self.c.Tr.CyclePagersReverseTooltip,
},
{
Keys: opts.GetKeys(opts.Config.Universal.Return),
Handler: self.escape,
@ -158,6 +166,19 @@ func (self *GlobalController) prevScreenMode() error {
func (self *GlobalController) cyclePagers() error {
self.c.State().GetPagerConfig().CyclePagers()
self.onPagerChanged()
return nil
}
func (self *GlobalController) cyclePagersBackward() error {
self.c.State().GetPagerConfig().CyclePagersBackward()
self.onPagerChanged()
return nil
}
// onPagerChanged re-renders the main view so the newly selected pager takes
// effect, and shows a toast naming it.
func (self *GlobalController) onPagerChanged() {
currentSide := self.c.Context().CurrentSide()
currentKey := self.c.Context().Current().GetKey()
if currentSide.GetKey() == currentKey ||
@ -166,9 +187,21 @@ func (self *GlobalController) cyclePagers() error {
currentSide.HandleRenderToMain()
}
current, total := self.c.State().GetPagerConfig().CurrentPagerIndex()
self.c.Toast(fmt.Sprintf("Selected pager %d of %d", current+1, total))
return nil
pagerConfig := self.c.State().GetPagerConfig()
current, total := pagerConfig.CurrentPagerIndex()
name := pagerConfig.CurrentPagerName()
if name == "" {
if pagerConfig.CurrentPagerUsesGitConfigDiff() {
name = self.c.Tr.ExternalDiffPagerName
} else {
name = self.c.Tr.DefaultPagerName
}
}
self.c.Toast(utils.ResolvePlaceholderString(self.c.Tr.SelectedPager, map[string]string{
"name": name,
"current": strconv.Itoa(current + 1),
"total": strconv.Itoa(total),
}))
}
func (self *GlobalController) canCyclePagers() *types.DisabledReason {

View file

@ -607,7 +607,12 @@ type TranslationSet struct {
PrevScreenMode string
CyclePagers string
CyclePagersTooltip string
CyclePagersReverse string
CyclePagersReverseTooltip string
CyclePagersDisabledReason string
SelectedPager string
DefaultPagerName string
ExternalDiffPagerName string
StartSearch string
StartFilter string
SelectRemoteRepository string
@ -1737,8 +1742,13 @@ func EnglishTranslationSet() *TranslationSet {
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",
CyclePagersTooltip: "Choose the next pager in the list of configured pagers.",
CyclePagersReverse: "Cycle pagers (reverse)",
CyclePagersReverseTooltip: "Choose the previous pager in the list of configured pagers.",
CyclePagersDisabledReason: "No other pagers configured",
SelectedPager: "Pager: {{.name}} ({{.current}} of {{.total}})",
DefaultPagerName: "(default)",
ExternalDiffPagerName: "(external diff)",
StartSearch: "Search the current view by text",
StartFilter: "Filter the current view by text",
SelectRemoteRepository: "Select base repository for pull requests",

View file

@ -0,0 +1,45 @@
package diff
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var CyclePagers = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Cycle forwards and backwards through configured pagers",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().Git.Pagers = []config.PagingConfig{
// an explicit name overrides the derived one
{Name: "custom name", Pager: "cat"},
// no name, so it's derived from the first word of the command
{Pager: "cat -n"},
// neither name nor command, so it falls back to the default label
{},
}
},
SetupRepo: func(shell *Shell) {
shell.CreateNCommits(1)
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Press(keys.Universal.CyclePagers)
t.ExpectToast(Equals("Pager: cat (2 of 3)"))
t.Views().Commits().Press(keys.Universal.CyclePagers)
t.ExpectToast(Equals("Pager: (default) (3 of 3)"))
// cycling forward past the last pager wraps around to the first
t.Views().Commits().Press(keys.Universal.CyclePagers)
t.ExpectToast(Equals("Pager: custom name (1 of 3)"))
// cycling backward past the first pager wraps around to the last
t.Views().Commits().Press(keys.Universal.CyclePagersReverse)
t.ExpectToast(Equals("Pager: (default) (3 of 3)"))
t.Views().Commits().Press(keys.Universal.CyclePagersReverse)
t.ExpectToast(Equals("Pager: cat (2 of 3)"))
},
})

View file

@ -210,6 +210,7 @@ var tests = []*components.IntegrationTest{
demo.Undo,
demo.WorktreeCreateFromBranches,
diff.CopyToClipboard,
diff.CyclePagers,
diff.Diff,
diff.DiffAndApplyPatch,
diff.DiffCommits,

View file

@ -321,7 +321,7 @@
"$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."
"description": "Array of pagers. Each entry has the following format:\n\n # A name for the pager, shown in the notification when cycling pagers.\n # If not set, the name is derived from the first word of the pager\n # command (or of the external diff command).\n name: \"\"\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\n'pager', 'externalDiffCommand', and 'useExternalDiffGitConfig' are mutually exclusive; set at most one per entry.\n\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Custom_Pagers.md for more information."
},
"commit": {
"$ref": "#/$defs/CommitConfig",
@ -3127,6 +3127,20 @@
],
"default": "|"
},
"cyclePagersReverse": {
"oneOf": [
{
"type": "string"
},
{
"items": {
"type": "string"
},
"type": "array"
}
],
"default": "\\"
},
"undo": {
"oneOf": [
{
@ -3488,6 +3502,10 @@
},
"PagingConfig": {
"properties": {
"name": {
"type": "string",
"description": "A name for the pager, shown in the notification when cycling pagers. If not set, the name is derived from the first word of the pager command (or of the external diff command)."
},
"colorArg": {
"type": "string",
"enum": [