mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Support multiple pagers (#4953)
Allow configuring an array of pagers, and cycling through them with the `|` key (mnemonic: we are piping the output through something). I find this useful for switching between delta (which I prefer most of the time) and difftastic (which occasionally produces better output for certain kinds of changes). It could also be used to switch between delta in inline mode and in side-by-side mode.
This commit is contained in:
		| @@ -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> | ||||
|   | ||||
| @@ -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 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| @@ -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 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| @@ -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}} | ||||
| ``` | ||||
|  | ||||
|  | ||||
| @@ -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.) | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` 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 | | ||||
| | `` <esc> `` | Cancel |  | | ||||
| | `` ? `` | Open keybindings menu |  | | ||||
| | `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味 | ||||
| | `` R `` | 更新 | Gitの状態を更新します(`git status`、`git branch`などをバックグラウンドで実行してパネルの内容を更新します)。これは`git fetch`を実行しません。 | | ||||
| | `` + `` | 次の画面モード(通常/半分/全画面) |  | | ||||
| | `` _ `` | 前の画面モード |  | | ||||
| | `` | `` | Cycle pagers | Choose the next pager in the list of configured pagers | | ||||
| | `` <esc> `` | キャンセル |  | | ||||
| | `` ? `` | キーバインディングメニューを開く |  | | ||||
| | `` <c-s> `` | フィルターオプションを表示 | コミットログのフィルタリングオプションを表示し、フィルタに一致するコミットのみを表示します。 | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` 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 | | ||||
| | `` <esc> `` | 취소 |  | | ||||
| | `` ? `` | 매뉴 열기 |  | | ||||
| | `` <c-s> `` | View filter-by-path options | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` 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 | | ||||
| | `` <esc> `` | Annuleren |  | | ||||
| | `` ? `` | Open menu |  | | ||||
| | `` <c-s> `` | Bekijk scoping opties | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_ | ||||
| | `` 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 | | ||||
| | `` <esc> `` | Anuluj |  | | ||||
| | `` ? `` | Otwórz menu przypisań klawiszy |  | | ||||
| | `` <c-s> `` | Pokaż opcje filtrowania | Pokaż opcje filtrowania dziennika commitów, tak aby pokazywane były tylko commity pasujące do filtra. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` 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`. | | ||||
| | `` + `` | Next screen mode (normal/half/fullscreen) |  | | ||||
| | `` _ `` | Prev screen mode |  | | ||||
| | `` | `` | Cycle pagers | Choose the next pager in the list of configured pagers | | ||||
| | `` <esc> `` | Cancelar |  | | ||||
| | `` ? `` | Open keybindings menu |  | | ||||
| | `` <c-s> `` | View filter options | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _Связки клавиш_ | ||||
| | `` 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 | | ||||
| | `` <esc> `` | Отменить |  | | ||||
| | `` ? `` | Открыть меню |  | | ||||
| | `` <c-s> `` | Просмотреть параметры фильтрации по пути | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_ | ||||
| | `` R `` | 刷新 | 刷新git状态(即在后台上运行`git status`,`git branch`等命令以更新面板内容) 不会运行`git fetch` | | ||||
| | `` + `` | 下一屏模式(正常/半屏/全屏) |  | | ||||
| | `` _ `` | 上一屏模式 |  | | ||||
| | `` | `` | Cycle pagers | Choose the next pager in the list of configured pagers | | ||||
| | `` <esc> `` | 取消 |  | | ||||
| | `` ? `` | 打开菜单 |  | | ||||
| | `` <c-s> `` | 查看按路径过滤选项 | 查看用于过滤提交日志的选项,以便仅显示与过滤器匹配的提交。 | | ||||
|   | ||||
| @@ -24,6 +24,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B | ||||
| | `` 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 | | ||||
| | `` <esc> `` | 取消 |  | | ||||
| | `` ? `` | 開啟選單 |  | | ||||
| | `` <c-s> `` | 檢視篩選路徑選項 | View options for filtering the commit log, so that only commits matching the filter are shown. | | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/patch" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/common" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| @@ -59,6 +60,7 @@ func NewGitCommand( | ||||
| 	version *git_commands.GitVersion, | ||||
| 	osCommand *oscommands.OSCommand, | ||||
| 	gitConfig git_config.IGitConfig, | ||||
| 	pagerConfig *config.PagerConfig, | ||||
| ) (*GitCommand, error) { | ||||
| 	repoPaths, err := git_commands.GetRepoPaths(osCommand.Cmd, version) | ||||
| 	if err != nil { | ||||
| @@ -88,6 +90,7 @@ func NewGitCommand( | ||||
| 		gitConfig, | ||||
| 		repoPaths, | ||||
| 		repository, | ||||
| 		pagerConfig, | ||||
| 	), nil | ||||
| } | ||||
|  | ||||
| @@ -98,6 +101,7 @@ func NewGitCommandAux( | ||||
| 	gitConfig git_config.IGitConfig, | ||||
| 	repoPaths *git_commands.RepoPaths, | ||||
| 	repo *gogit.Repository, | ||||
| 	pagerConfig *config.PagerConfig, | ||||
| ) *GitCommand { | ||||
| 	cmd := NewGitCmdObjBuilder(cmn.Log, osCommand.Cmd) | ||||
|  | ||||
| @@ -108,7 +112,7 @@ func NewGitCommandAux( | ||||
| 	// common ones are: cmn, osCommand, dotGitDir, configCommands | ||||
| 	configCommands := git_commands.NewConfigCommands(cmn, gitConfig, repo) | ||||
|  | ||||
| 	gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands) | ||||
| 	gitCommon := git_commands.NewGitCommon(cmn, version, cmd, osCommand, repoPaths, repo, configCommands, pagerConfig) | ||||
|  | ||||
| 	fileLoader := git_commands.NewFileLoader(gitCommon, cmd, configCommands) | ||||
| 	statusCommands := git_commands.NewStatusCommands(gitCommon) | ||||
|   | ||||
| @@ -256,14 +256,14 @@ func (self *CommitCommands) AmendHeadCmdObj() *oscommands.CmdObj { | ||||
| func (self *CommitCommands) ShowCmdObj(hash string, filterPaths []string) *oscommands.CmdObj { | ||||
| 	contextSize := self.UserConfig().Git.DiffContextSize | ||||
|  | ||||
| 	extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig | ||||
| 	extDiffCmd := self.pagerConfig.GetExternalDiffCommand() | ||||
| 	useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() | ||||
| 	cmdArgs := NewGitCmd("show"). | ||||
| 		Config("diff.noprefix=false"). | ||||
| 		ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd). | ||||
| 		ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff"). | ||||
| 		Arg("--submodule"). | ||||
| 		Arg("--color="+self.UserConfig().Git.Paging.ColorArg). | ||||
| 		Arg("--color="+self.pagerConfig.GetColorArg()). | ||||
| 		Arg(fmt.Sprintf("--unified=%d", contextSize)). | ||||
| 		Arg("--stat"). | ||||
| 		Arg("--decorate"). | ||||
|   | ||||
| @@ -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{ | ||||
|   | ||||
| @@ -4,16 +4,18 @@ import ( | ||||
| 	gogit "github.com/jesseduffield/go-git/v5" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/oscommands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/common" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| ) | ||||
|  | ||||
| type GitCommon struct { | ||||
| 	*common.Common | ||||
| 	version   *GitVersion | ||||
| 	cmd       oscommands.ICmdObjBuilder | ||||
| 	os        *oscommands.OSCommand | ||||
| 	repoPaths *RepoPaths | ||||
| 	repo      *gogit.Repository | ||||
| 	config    *ConfigCommands | ||||
| 	version     *GitVersion | ||||
| 	cmd         oscommands.ICmdObjBuilder | ||||
| 	os          *oscommands.OSCommand | ||||
| 	repoPaths   *RepoPaths | ||||
| 	repo        *gogit.Repository | ||||
| 	config      *ConfigCommands | ||||
| 	pagerConfig *config.PagerConfig | ||||
| } | ||||
|  | ||||
| func NewGitCommon( | ||||
| @@ -24,14 +26,16 @@ func NewGitCommon( | ||||
| 	repoPaths *RepoPaths, | ||||
| 	repo *gogit.Repository, | ||||
| 	config *ConfigCommands, | ||||
| 	pagerConfig *config.PagerConfig, | ||||
| ) *GitCommon { | ||||
| 	return &GitCommon{ | ||||
| 		Common:    cmn, | ||||
| 		version:   version, | ||||
| 		cmd:       cmd, | ||||
| 		os:        osCommand, | ||||
| 		repoPaths: repoPaths, | ||||
| 		repo:      repo, | ||||
| 		config:    config, | ||||
| 		Common:      cmn, | ||||
| 		version:     version, | ||||
| 		cmd:         cmd, | ||||
| 		os:          osCommand, | ||||
| 		repoPaths:   repoPaths, | ||||
| 		repo:        repo, | ||||
| 		config:      config, | ||||
| 		pagerConfig: pagerConfig, | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,10 @@ | ||||
| package git_commands | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	gogit "github.com/jesseduffield/go-git/v5" | ||||
| 	"github.com/jesseduffield/go-git/v5/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/common" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| type ConfigCommands struct { | ||||
| @@ -31,26 +26,6 @@ func NewConfigCommands( | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *ConfigCommands) ConfiguredPager() string { | ||||
| 	if os.Getenv("GIT_PAGER") != "" { | ||||
| 		return os.Getenv("GIT_PAGER") | ||||
| 	} | ||||
| 	if os.Getenv("PAGER") != "" { | ||||
| 		return os.Getenv("PAGER") | ||||
| 	} | ||||
| 	output := self.gitConfig.Get("core.pager") | ||||
| 	return strings.Split(output, "\n")[0] | ||||
| } | ||||
|  | ||||
| func (self *ConfigCommands) GetPager(width int) string { | ||||
| 	templateValues := map[string]string{ | ||||
| 		"columnWidth": strconv.Itoa(width/2 - 6), | ||||
| 	} | ||||
|  | ||||
| 	pagerTemplate := string(self.UserConfig().Git.Paging.Pager) | ||||
| 	return utils.ResolvePlaceholderString(pagerTemplate, templateValues) | ||||
| } | ||||
|  | ||||
| type GpgConfigKey string | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -61,6 +61,10 @@ func buildGitCommon(deps commonDeps) *GitCommon { | ||||
| 		gitCommon.Common.SetUserConfig(config.GetDefaultConfig()) | ||||
| 	} | ||||
|  | ||||
| 	gitCommon.pagerConfig = config.NewPagerConfig(func() *config.UserConfig { | ||||
| 		return gitCommon.Common.UserConfig() | ||||
| 	}) | ||||
|  | ||||
| 	gitCommon.version = deps.gitVersion | ||||
| 	if gitCommon.version == nil { | ||||
| 		gitCommon.version = &GitVersion{2, 0, 0, ""} | ||||
|   | ||||
| @@ -19,9 +19,9 @@ func NewDiffCommands(gitCommon *GitCommon) *DiffCommands { | ||||
| // This is for generating diffs to be shown in the UI (e.g. rendering a range | ||||
| // diff to the main view). It uses a custom pager if one is configured. | ||||
| func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj { | ||||
| 	extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	extDiffCmd := self.pagerConfig.GetExternalDiffCommand() | ||||
| 	useExtDiff := extDiffCmd != "" | ||||
| 	useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig | ||||
| 	useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() | ||||
| 	ignoreWhitespace := self.UserConfig().Git.IgnoreWhitespaceInDiffView | ||||
|  | ||||
| 	return self.cmd.New( | ||||
| @@ -30,7 +30,7 @@ func (self *DiffCommands) DiffCmdObj(diffArgs []string) *oscommands.CmdObj { | ||||
| 			ConfigIf(useExtDiff, "diff.external="+extDiffCmd). | ||||
| 			ArgIfElse(useExtDiff || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff"). | ||||
| 			Arg("--submodule"). | ||||
| 			Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)). | ||||
| 			Arg(fmt.Sprintf("--color=%s", self.pagerConfig.GetColorArg())). | ||||
| 			ArgIf(ignoreWhitespace, "--ignore-all-space"). | ||||
| 			Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)). | ||||
| 			Arg(diffArgs...). | ||||
|   | ||||
| @@ -81,8 +81,8 @@ func (self *StashCommands) Hash(index int) (string, error) { | ||||
| } | ||||
|  | ||||
| func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj { | ||||
| 	extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig | ||||
| 	extDiffCmd := self.pagerConfig.GetExternalDiffCommand() | ||||
| 	useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() | ||||
|  | ||||
| 	// "-u" is the same as "--include-untracked", but the latter fails in older git versions for some reason | ||||
| 	cmdArgs := NewGitCmd("stash").Arg("show"). | ||||
| @@ -91,7 +91,7 @@ func (self *StashCommands) ShowStashEntryCmdObj(index int) *oscommands.CmdObj { | ||||
| 		Arg("-u"). | ||||
| 		ConfigIf(extDiffCmd != "", "diff.external="+extDiffCmd). | ||||
| 		ArgIfElse(extDiffCmd != "" || useExtDiffGitConfig, "--ext-diff", "--no-ext-diff"). | ||||
| 		Arg(fmt.Sprintf("--color=%s", self.UserConfig().Git.Paging.ColorArg)). | ||||
| 		Arg(fmt.Sprintf("--color=%s", self.pagerConfig.GetColorArg())). | ||||
| 		Arg(fmt.Sprintf("--unified=%d", self.UserConfig().Git.DiffContextSize)). | ||||
| 		ArgIf(self.UserConfig().Git.IgnoreWhitespaceInDiffView, "--ignore-all-space"). | ||||
| 		Arg(fmt.Sprintf("--find-renames=%d%%", self.UserConfig().Git.RenameSimilarityThreshold)). | ||||
|   | ||||
| @@ -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", | ||||
| 			} | ||||
|   | ||||
| @@ -258,7 +258,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool, | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool) *oscommands.CmdObj { | ||||
| 	colorArg := self.UserConfig().Git.Paging.ColorArg | ||||
| 	colorArg := self.pagerConfig.GetColorArg() | ||||
| 	if plain { | ||||
| 		colorArg = "never" | ||||
| 	} | ||||
| @@ -266,9 +266,9 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain | ||||
| 	contextSize := self.UserConfig().Git.DiffContextSize | ||||
| 	prevPath := node.GetPreviousPath() | ||||
| 	noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile() | ||||
| 	extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	extDiffCmd := self.pagerConfig.GetExternalDiffCommand() | ||||
| 	useExtDiff := extDiffCmd != "" && !plain | ||||
| 	useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig && !plain | ||||
| 	useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain | ||||
|  | ||||
| 	cmdArgs := NewGitCmd("diff"). | ||||
| 		ConfigIf(useExtDiff, "diff.external="+extDiffCmd). | ||||
| @@ -299,14 +299,14 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo | ||||
| func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool) *oscommands.CmdObj { | ||||
| 	contextSize := self.UserConfig().Git.DiffContextSize | ||||
|  | ||||
| 	colorArg := self.UserConfig().Git.Paging.ColorArg | ||||
| 	colorArg := self.pagerConfig.GetColorArg() | ||||
| 	if plain { | ||||
| 		colorArg = "never" | ||||
| 	} | ||||
|  | ||||
| 	extDiffCmd := self.UserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	extDiffCmd := self.pagerConfig.GetExternalDiffCommand() | ||||
| 	useExtDiff := extDiffCmd != "" && !plain | ||||
| 	useExtDiffGitConfig := self.UserConfig().Git.Paging.UseExternalDiffGitConfig && !plain | ||||
| 	useExtDiffGitConfig := self.pagerConfig.GetUseExternalDiffGitConfig() && !plain | ||||
|  | ||||
| 	cmdArgs := NewGitCmd("diff"). | ||||
| 		Config("diff.noprefix=false"). | ||||
|   | ||||
| @@ -310,6 +310,11 @@ func computeMigratedConfig(path string, content []byte, changes *ChangesSet) ([] | ||||
| 		return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err) | ||||
| 	} | ||||
|  | ||||
| 	err = migratePagers(&rootNode, changes) | ||||
| 	if err != nil { | ||||
| 		return nil, false, fmt.Errorf("Couldn't migrate config file at `%s`: %w", path, err) | ||||
| 	} | ||||
|  | ||||
| 	// Add more migrations here... | ||||
|  | ||||
| 	if reflect.DeepEqual(rootNode, originalCopy) { | ||||
| @@ -469,6 +474,37 @@ func migrateAllBranchesLogCmd(rootNode *yaml.Node, changes *ChangesSet) error { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func migratePagers(rootNode *yaml.Node, changes *ChangesSet) error { | ||||
| 	return yaml_utils.TransformNode(rootNode, []string{"git"}, func(gitNode *yaml.Node) error { | ||||
| 		pagingKeyNode, pagingValueNode := yaml_utils.LookupKey(gitNode, "paging") | ||||
| 		if pagingKeyNode == nil || pagingValueNode.Kind != yaml.MappingNode { | ||||
| 			// If there's no "paging" section (or it's not an object), there's nothing to do | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		pagersKeyNode, _ := yaml_utils.LookupKey(gitNode, "pagers") | ||||
| 		if pagersKeyNode != nil { | ||||
| 			// Conversely, if there *is* already a "pagers" array, we also have nothing to do. | ||||
| 			// This covers the case where the user keeps both the "paging" section and the "pagers" | ||||
| 			// array for the sake of easier testing of old versions. | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		pagingKeyNode.Value = "pagers" | ||||
| 		pagingContentCopy := pagingValueNode.Content | ||||
| 		pagingValueNode.Kind = yaml.SequenceNode | ||||
| 		pagingValueNode.Tag = "!!seq" | ||||
| 		pagingValueNode.Content = []*yaml.Node{{ | ||||
| 			Kind:    yaml.MappingNode, | ||||
| 			Content: pagingContentCopy, | ||||
| 		}} | ||||
|  | ||||
| 		changes.Add("Moved git.paging object to git.pagers array") | ||||
|  | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (c *AppConfig) GetDebug() bool { | ||||
| 	return c.debug | ||||
| } | ||||
|   | ||||
| @@ -1089,3 +1089,98 @@ func TestAllBranchesLogCmdMigrations(t *testing.T) { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPagerMigration(t *testing.T) { | ||||
| 	scenarios := []struct { | ||||
| 		name              string | ||||
| 		input             string | ||||
| 		expected          string | ||||
| 		expectedDidChange bool | ||||
| 		expectedChanges   []string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:              "Incomplete Configuration Passes uneventfully", | ||||
| 			input:             "git:", | ||||
| 			expectedDidChange: false, | ||||
| 			expectedChanges:   []string{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "No paging section", | ||||
| 			input: `git: | ||||
|   autoFetch: true | ||||
| `, | ||||
| 			expected: `git: | ||||
|   autoFetch: true | ||||
| `, | ||||
| 			expectedDidChange: false, | ||||
| 			expectedChanges:   []string{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Both paging and pagers exist", | ||||
| 			input: `git: | ||||
|   paging: | ||||
|     pager: delta --dark --paging=never | ||||
|   pagers: | ||||
|     - diff: diff-so-fancy | ||||
| `, | ||||
| 			expected: `git: | ||||
|   paging: | ||||
|     pager: delta --dark --paging=never | ||||
|   pagers: | ||||
|     - diff: diff-so-fancy | ||||
| `, | ||||
| 			expectedDidChange: false, | ||||
| 			expectedChanges:   []string{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "paging is not an object", | ||||
| 			input: `git: | ||||
|   paging: 5 | ||||
| `, | ||||
| 			expected: `git: | ||||
|   paging: 5 | ||||
| `, | ||||
| 			expectedDidChange: false, | ||||
| 			expectedChanges:   []string{}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "paging is moved to pagers array (keeping the order)", | ||||
| 			input: `git: | ||||
|   paging: | ||||
|     pager: delta --dark --paging=never | ||||
|   autoFetch: true | ||||
| `, | ||||
| 			expected: `git: | ||||
|   pagers: | ||||
|     - pager: delta --dark --paging=never | ||||
|   autoFetch: true | ||||
| `, | ||||
| 			expectedDidChange: true, | ||||
| 			expectedChanges:   []string{"Moved git.paging object to git.pagers array"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "paging is moved to pagers array even if empty", | ||||
| 			input: `git: | ||||
|   paging: {} | ||||
| `, | ||||
| 			expected: `git: | ||||
|   pagers: [{}] | ||||
| `, | ||||
| 			expectedDidChange: true, | ||||
| 			expectedChanges:   []string{"Moved git.paging object to git.pagers array"}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, s := range scenarios { | ||||
| 		t.Run(s.name, func(t *testing.T) { | ||||
| 			changes := NewChangesSet() | ||||
| 			actual, didChange, err := computeMigratedConfig("path doesn't matter", []byte(s.input), changes) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, s.expectedDidChange, didChange) | ||||
| 			if didChange { | ||||
| 				assert.Equal(t, s.expected, string(actual)) | ||||
| 			} | ||||
| 			assert.Equal(t, s.expectedChanges, changes.ToSliceFromOldest()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										82
									
								
								pkg/config/pager_config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								pkg/config/pager_config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| ) | ||||
|  | ||||
| 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(currentPagerConfig.Pager) | ||||
| 	return utils.ResolvePlaceholderString(pagerTemplate, templateValues) | ||||
| } | ||||
|  | ||||
| func (self *PagerConfig) GetColorArg() string { | ||||
| 	currentPagerConfig := self.currentPagerConfig() | ||||
| 	if currentPagerConfig == nil { | ||||
| 		return "always" | ||||
| 	} | ||||
|  | ||||
| 	colorArg := currentPagerConfig.ColorArg | ||||
| 	if colorArg == "" { | ||||
| 		return "always" | ||||
| 	} | ||||
| 	return colorArg | ||||
| } | ||||
|  | ||||
| func (self *PagerConfig) GetExternalDiffCommand() string { | ||||
| 	currentPagerConfig := self.currentPagerConfig() | ||||
| 	if currentPagerConfig == nil { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return currentPagerConfig.ExternalDiffCommand | ||||
| } | ||||
|  | ||||
| func (self *PagerConfig) GetUseExternalDiffGitConfig() bool { | ||||
| 	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) | ||||
| } | ||||
| @@ -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>", | ||||
|   | ||||
| @@ -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() | ||||
| } | ||||
|   | ||||
| @@ -69,6 +69,8 @@ type Gui struct { | ||||
| 	// this is the state of the GUI for the current repo | ||||
| 	State *GuiRepoState | ||||
|  | ||||
| 	pagerConfig *config.PagerConfig | ||||
|  | ||||
| 	CustomCommandsClient *custom_commands.Client | ||||
|  | ||||
| 	// this is a mapping of repos to gui states, so that we can restore the original | ||||
| @@ -169,6 +171,10 @@ func (self *StateAccessor) GetRepoState() types.IRepoStateAccessor { | ||||
| 	return self.gui.State | ||||
| } | ||||
|  | ||||
| func (self *StateAccessor) GetPagerConfig() *config.PagerConfig { | ||||
| 	return self.gui.pagerConfig | ||||
| } | ||||
|  | ||||
| func (self *StateAccessor) GetIsRefreshingFiles() bool { | ||||
| 	return self.gui.IsRefreshingFiles | ||||
| } | ||||
| @@ -307,6 +313,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context | ||||
| 		gui.gitVersion, | ||||
| 		gui.os, | ||||
| 		git_config.NewStdCachedGitConfig(gui.Log), | ||||
| 		gui.pagerConfig, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -653,7 +660,7 @@ func (gui *Gui) Contexts() *context.ContextTree { | ||||
| // NewGui builds a new gui handler | ||||
| func NewGui( | ||||
| 	cmn *common.Common, | ||||
| 	config config.AppConfigurer, | ||||
| 	configurer config.AppConfigurer, | ||||
| 	gitVersion *git_commands.GitVersion, | ||||
| 	updater *updates.Updater, | ||||
| 	showRecentRepos bool, | ||||
| @@ -663,7 +670,7 @@ func NewGui( | ||||
| 	gui := &Gui{ | ||||
| 		Common:               cmn, | ||||
| 		gitVersion:           gitVersion, | ||||
| 		Config:               config, | ||||
| 		Config:               configurer, | ||||
| 		Updater:              updater, | ||||
| 		statusManager:        status.NewStatusManager(), | ||||
| 		viewBufferManagerMap: map[string]*tasks.ViewBufferManager{}, | ||||
| @@ -713,7 +720,7 @@ func NewGui( | ||||
| 		credentialsHelper.PromptUserForCredential, | ||||
| 	) | ||||
|  | ||||
| 	osCommand := oscommands.NewOSCommand(cmn, config, oscommands.GetPlatform(), guiIO) | ||||
| 	osCommand := oscommands.NewOSCommand(cmn, configurer, oscommands.GetPlatform(), guiIO) | ||||
|  | ||||
| 	gui.os = osCommand | ||||
|  | ||||
| @@ -724,6 +731,8 @@ func NewGui( | ||||
| 	gui.BackgroundRoutineMgr = &BackgroundRoutineMgr{gui: gui} | ||||
| 	gui.stateAccessor = &StateAccessor{gui: gui} | ||||
|  | ||||
| 	gui.pagerConfig = config.NewPagerConfig(func() *config.UserConfig { return gui.UserConfig() }) | ||||
|  | ||||
| 	return gui, nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -45,8 +45,8 @@ func (gui *Gui) onResize() error { | ||||
| // command. | ||||
| func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error { | ||||
| 	width := view.InnerWidth() | ||||
| 	pager := gui.git.Config.GetPager(width) | ||||
| 	externalDiffCommand := gui.Config.GetUserConfig().Git.Paging.ExternalDiffCommand | ||||
| 	pager := gui.stateAccessor.GetPagerConfig().GetPagerCommand(width) | ||||
| 	externalDiffCommand := gui.stateAccessor.GetPagerConfig().GetExternalDiffCommand() | ||||
|  | ||||
| 	if pager == "" && externalDiffCommand == "" { | ||||
| 		// if we're not using a custom pager we don't need to use a pty | ||||
| @@ -58,7 +58,7 @@ func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error | ||||
| 		// Need to get the width and the pager again because the layout might have | ||||
| 		// changed the size of the view | ||||
| 		width = view.InnerWidth() | ||||
| 		pager = gui.git.Config.GetPager(width) | ||||
| 		pager := gui.stateAccessor.GetPagerConfig().GetPagerCommand(width) | ||||
|  | ||||
| 		cmdStr := strings.Join(cmd.Args, " ") | ||||
|  | ||||
|   | ||||
| @@ -356,6 +356,7 @@ type HasUrn interface { | ||||
| type IStateAccessor interface { | ||||
| 	GetRepoPathStack() *utils.StringStack | ||||
| 	GetRepoState() IRepoStateAccessor | ||||
| 	GetPagerConfig() *config.PagerConfig | ||||
| 	// tells us whether we're currently updating lazygit | ||||
| 	GetUpdating() bool | ||||
| 	SetUpdating(bool) | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user