mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 23:56:24 -04:00
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:
commit
8a7bef0975
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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> `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 |
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
4
justfile
4
justfile
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
82
pkg/config/pager_config_test.go
Normal file
82
pkg/config/pager_config_test.go
Normal 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())
|
||||
}
|
||||
|
|
@ -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>"},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
45
pkg/integration/tests/diff/cycle_pagers.go
Normal file
45
pkg/integration/tests/diff/cycle_pagers.go
Normal 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)"))
|
||||
},
|
||||
})
|
||||
|
|
@ -210,6 +210,7 @@ var tests = []*components.IntegrationTest{
|
|||
demo.Undo,
|
||||
demo.WorktreeCreateFromBranches,
|
||||
diff.CopyToClipboard,
|
||||
diff.CyclePagers,
|
||||
diff.Diff,
|
||||
diff.DiffAndApplyPatch,
|
||||
diff.DiffCommits,
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
Loading…
Reference in a new issue