Files
lazygit/pkg/integration/tests/diff/render_through_a_pipe.go
T
Stefan HallerandClaude Opus 5 087bdcd57f Render a diff without a pty on Windows
ConPTY doesn't carry a diff renderer's output to us as the renderer
wrote it. It parses the output into a screen buffer and re-encodes that
for the terminal side, and a sequence it can't represent there goes out
the moment it is parsed, separately from the text around it. The OSC
1717 records a renderer states its diff lines in therefore arrive
detached from the rows they describe, and the identity layer attributes
rows to the wrong diff line or to none.

Feed the renderer through a pipe there instead, so that its bytes reach
us unaltered. A stdin filter becomes a command of our own, since git
only invokes the one named by GIT_PAGER when it talks to a terminal; an
external diff renderer is git's own business either way and needs
nothing but the pipe.

Unix keeps the pty. A renderer reads the width to lay out to off it, so
taking it away would leave every configuration that doesn't name a
width rendering at whatever the renderer falls back to, and diff
renderers have worked on Unix far too long for that.
LAZYGIT_RENDER_WITHOUT_PTY asks for the piped path anyway, which is how
the integration tests cover it on a platform where they run at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 11:31:59 +02:00

50 lines
1.6 KiB
Go

package diff
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var RenderThroughAPipe = NewIntegrationTest(NewIntegrationTestArgs{
Description: "A stdin filter renders the diff when it is fed through a pipe rather than run in a pty",
ExtraCmdArgs: []string{},
Skip: false,
// This is how a render works on Windows, where a pty can't carry a
// renderer's output faithfully. Ask for it here so that the path is
// covered on the platforms the integration tests do run on.
ExtraEnvVars: map[string]string{"LAZYGIT_RENDER_WITHOUT_PTY": "1"},
SetupConfig: func(cfg *config.AppConfig) {
// Reports the width it was given, then passes the diff through. git
// only runs a filter of its own when it talks to a terminal, so the
// filter running at all says the pipeline was built here.
cfg.GetUserConfig().Git.DiffRenderers = []config.DiffRendererConfig{
{Command: `echo "rendered at {{width}} columns"; cat`},
}
},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "one\n")
shell.Commit("one")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Commits().
Focus().
Lines(
Contains("one").IsSelected(),
)
t.Views().Main().
// The width reaches the filter on its command line, since with no
// terminal it has nowhere to read it from.
Content(MatchesRegexp(`rendered at \d+ columns`)).
ContainsLines(
Equals("diff --git a/file1 b/file1"),
Contains("new file mode"),
Contains("index "),
Equals("--- /dev/null"),
Equals("+++ b/file1"),
Equals("@@ -0,0 +1 @@"),
Equals("+one"),
)
},
})