mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 21:46:49 -04:00
Renderers like delta and difftastic pick their colors for either a dark
or a light background, and they can't find out which one the terminal
has. Lazygit runs them with TERM=dumb, in a pty that doesn't answer
their queries. So the colors come from the config, and when the
terminal switches between dark and light, the diff keeps the ones it
has.
Add {{colorScheme}} to the commands of diff renderers. It is 'dark' or
'light', going by gui.colorScheme, or by the terminal if that is
'auto'. It can be passed to delta as --{{colorScheme}} and to
difftastic as --background={{colorScheme}}; other renderers can choose
between options with a template expression. When the terminal switches
between dark and light, render the diff again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
249 lines
8.7 KiB
Go
249 lines
8.7 KiB
Go
package config
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/jesseduffield/lazygit/pkg/i18n"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestCurrentDiffRendererName(t *testing.T) {
|
|
tr := i18n.EnglishTranslationSet()
|
|
|
|
scenarios := []struct {
|
|
name string
|
|
diffRendererConfig DiffRendererConfig
|
|
expected string
|
|
}{
|
|
{
|
|
name: "explicit name takes precedence over the command",
|
|
diffRendererConfig: DiffRendererConfig{Name: "delta side-by-side", Command: "delta --side-by-side"},
|
|
expected: "delta side-by-side",
|
|
},
|
|
{
|
|
name: "derived from the first word of the stdinFilter command",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --side-by-side"},
|
|
expected: "delta",
|
|
},
|
|
{
|
|
name: "surrounding whitespace in the command is ignored",
|
|
diffRendererConfig: DiffRendererConfig{Command: " diff-so-fancy "},
|
|
expected: "diff-so-fancy",
|
|
},
|
|
{
|
|
name: "derived from the first word of the extDiff command",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --color=always"},
|
|
expected: "difft",
|
|
},
|
|
{
|
|
name: "no name can be derived for external diff",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff"},
|
|
expected: tr.ExternalDiffDiffRendererName,
|
|
},
|
|
{
|
|
name: "derived from first argument of rawGit args",
|
|
diffRendererConfig: DiffRendererConfig{Type: "rawGit", Args: []string{"--color-words"}},
|
|
expected: "--color-words",
|
|
},
|
|
{
|
|
name: "no name can be derived for raw diff",
|
|
diffRendererConfig: DiffRendererConfig{Type: "rawGit"},
|
|
expected: tr.DefaultDiffRendererName,
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
userConfig := &UserConfig{}
|
|
userConfig.Git.DiffRenderers = []DiffRendererConfig{s.diffRendererConfig}
|
|
config := NewDiffRendererConfigManager(func() *UserConfig { return userConfig })
|
|
|
|
assert.Equal(t, s.expected, config.CurrentDiffRendererName(tr))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetStdinFilterCommand(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
diffRendererConfig DiffRendererConfig
|
|
width int
|
|
lightBackground bool
|
|
expected string
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "a command without template variables is passed through",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --paging=never"},
|
|
width: 120,
|
|
expected: "delta --paging=never",
|
|
},
|
|
{
|
|
name: "the width the diff is rendered at",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --width={{width}}"},
|
|
width: 120,
|
|
expected: "delta --width=120",
|
|
},
|
|
{
|
|
name: "the width of one side of a side-by-side rendering",
|
|
diffRendererConfig: DiffRendererConfig{Command: "ydiff -p cat -w {{columnWidth}}"},
|
|
width: 120,
|
|
expected: "ydiff -p cat -w 54",
|
|
},
|
|
{
|
|
name: "a template variable can also be written with a leading dot",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --width={{.width}}"},
|
|
width: 120,
|
|
expected: "delta --width=120",
|
|
},
|
|
{
|
|
name: "the color scheme on a dark background",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --{{colorScheme}}"},
|
|
width: 120,
|
|
expected: "delta --dark",
|
|
},
|
|
{
|
|
name: "the color scheme on a light background",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --{{colorScheme}}"},
|
|
width: 120,
|
|
lightBackground: true,
|
|
expected: "delta --light",
|
|
},
|
|
{
|
|
name: "the command can choose between options by the color scheme",
|
|
diffRendererConfig: DiffRendererConfig{Command: `delta --syntax-theme={{if eq .colorScheme "light"}}GitHub{{else}}Dracula{{end}}`},
|
|
width: 120,
|
|
lightBackground: true,
|
|
expected: "delta --syntax-theme=GitHub",
|
|
},
|
|
{
|
|
name: "the command can use template expressions",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta{{if gt .width 100}} --side-by-side{{end}}"},
|
|
width: 120,
|
|
expected: "delta --side-by-side",
|
|
},
|
|
{
|
|
name: "an unknown template variable is an error",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --width={{.widht}}"},
|
|
width: 120,
|
|
expectedError: "can't use the command 'delta --width={{.widht}}'",
|
|
},
|
|
{
|
|
name: "an unknown template variable without a leading dot is an error too",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --width={{widht}}"},
|
|
width: 120,
|
|
expectedError: "can't use the command 'delta --width={{widht}}'",
|
|
},
|
|
{
|
|
name: "nothing is returned for a renderer of another type",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --width={{width}}"},
|
|
width: 120,
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
userConfig := &UserConfig{}
|
|
userConfig.Git.DiffRenderers = []DiffRendererConfig{s.diffRendererConfig}
|
|
config := NewDiffRendererConfigManager(func() *UserConfig { return userConfig })
|
|
|
|
command, err := config.GetStdinFilterCommand(DiffRendererValues{Width: s.width, LightBackground: s.lightBackground})
|
|
if s.expectedError != "" {
|
|
assert.ErrorContains(t, err, s.expectedError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, s.expected, command)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetExternalDiffCommand(t *testing.T) {
|
|
scenarios := []struct {
|
|
name string
|
|
diffRendererConfig DiffRendererConfig
|
|
expected string
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "a command without template variables is passed through",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --color=always"},
|
|
expected: "difft --color=always",
|
|
},
|
|
{
|
|
name: "the width the diff is rendered at",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --width={{width}}"},
|
|
expected: "difft --width=120",
|
|
},
|
|
{
|
|
name: "the color scheme",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --background={{colorScheme}}"},
|
|
expected: "difft --background=dark",
|
|
},
|
|
{
|
|
name: "the width alongside the diff context size",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --width={{width}} --context={{diffContext}}"},
|
|
expected: "difft --width=120 --context=3",
|
|
},
|
|
{
|
|
name: "a variable of stdin filters is an error",
|
|
diffRendererConfig: DiffRendererConfig{Type: "extDiff", Command: "difft --width={{columnWidth}}"},
|
|
expectedError: "can't use the command 'difft --width={{columnWidth}}'",
|
|
},
|
|
{
|
|
name: "nothing is returned for a renderer of another type",
|
|
diffRendererConfig: DiffRendererConfig{Command: "delta --width={{width}}"},
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, s := range scenarios {
|
|
t.Run(s.name, func(t *testing.T) {
|
|
userConfig := &UserConfig{}
|
|
userConfig.Git.DiffRenderers = []DiffRendererConfig{s.diffRendererConfig}
|
|
config := NewDiffRendererConfigManager(func() *UserConfig { return userConfig })
|
|
|
|
command, err := config.GetExternalDiffCommand(DiffRendererValues{Width: 120, DiffContext: 3})
|
|
if s.expectedError != "" {
|
|
assert.ErrorContains(t, err, s.expectedError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, s.expected, command)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCurrentDiffRendererNameWithoutDiffRenderers(t *testing.T) {
|
|
config := NewDiffRendererConfigManager(func() *UserConfig { return &UserConfig{} })
|
|
|
|
tr := i18n.EnglishTranslationSet()
|
|
assert.Equal(t, tr.DefaultDiffRendererName, config.CurrentDiffRendererName(tr))
|
|
}
|
|
|
|
func TestCycleDiffRenderers(t *testing.T) {
|
|
userConfig := &UserConfig{}
|
|
userConfig.Git.DiffRenderers = []DiffRendererConfig{{Name: "a"}, {Name: "b"}, {Name: "c"}}
|
|
config := NewDiffRendererConfigManager(func() *UserConfig { return userConfig })
|
|
|
|
currentIndex := func() int {
|
|
index, _ := config.CurrentDiffRendererIndex()
|
|
return index
|
|
}
|
|
|
|
assert.Equal(t, 0, currentIndex())
|
|
|
|
config.CycleDiffRenderers()
|
|
assert.Equal(t, 1, currentIndex())
|
|
config.CycleDiffRenderers()
|
|
assert.Equal(t, 2, currentIndex())
|
|
config.CycleDiffRenderers()
|
|
assert.Equal(t, 0, currentIndex(), "cycling forward past the last diff renderer wraps to the first")
|
|
|
|
config.CycleDiffRenderersBackward()
|
|
assert.Equal(t, 2, currentIndex(), "cycling backward past the first diff renderer wraps to the last")
|
|
config.CycleDiffRenderersBackward()
|
|
assert.Equal(t, 1, currentIndex())
|
|
}
|