mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
I copied all files except dot files (.github and .gitignore), the _examples folder, and go.mod/go.sum. At some point we may want to copy the files back to the gocui repo when other clients (e.g. lazydocker) want to use the newer versions of them.
122 lines
3.9 KiB
Go
122 lines
3.9 KiB
Go
//go:build !windows
|
|
|
|
package gui
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/creack/pty"
|
|
"github.com/jesseduffield/lazygit/pkg/gocui"
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
func (gui *Gui) desiredPtySize(view *gocui.View) *pty.Winsize {
|
|
width, height := view.InnerSize()
|
|
|
|
return &pty.Winsize{Cols: uint16(width), Rows: uint16(height)}
|
|
}
|
|
|
|
func (gui *Gui) onResize() error {
|
|
gui.Mutexes.PtyMutex.Lock()
|
|
defer gui.Mutexes.PtyMutex.Unlock()
|
|
|
|
for viewName, ptmx := range gui.viewPtmxMap {
|
|
// TODO: handle resizing properly: we need to actually clear the main view
|
|
// and re-read the output from our pty. Or we could just re-run the original
|
|
// command from scratch
|
|
view, _ := gui.g.View(viewName)
|
|
if err := pty.Setsize(ptmx, gui.desiredPtySize(view)); err != nil {
|
|
return utils.WrapError(err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Some commands need to output for a terminal to active certain behaviour.
|
|
// For example, git won't invoke the GIT_PAGER env var unless it thinks it's
|
|
// talking to a terminal. We typically write cmd outputs straight to a view,
|
|
// which is just an io.Reader. the pty package lets us wrap a command in a
|
|
// pseudo-terminal meaning we'll get the behaviour we want from the underlying
|
|
// command.
|
|
func (gui *Gui) newPtyTask(view *gocui.View, cmd *exec.Cmd, prefix string) error {
|
|
width := view.InnerWidth()
|
|
pager := gui.stateAccessor.GetPagerConfig().GetPagerCommand(width)
|
|
externalDiffCommand := gui.stateAccessor.GetPagerConfig().GetExternalDiffCommand()
|
|
useExtDiffGitConfig := gui.stateAccessor.GetPagerConfig().GetUseExternalDiffGitConfig()
|
|
|
|
if pager == "" && externalDiffCommand == "" && !useExtDiffGitConfig {
|
|
// If we're not using a custom pager nor external diff command, then we don't need to use a pty
|
|
return gui.newCmdTask(view, cmd, prefix)
|
|
}
|
|
|
|
// Run the pty after layout so that it gets the correct size
|
|
gui.afterLayout(func() 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.stateAccessor.GetPagerConfig().GetPagerCommand(width)
|
|
|
|
cmdStr := strings.Join(cmd.Args, " ")
|
|
|
|
// This communicates to pagers that we're in a very simple
|
|
// terminal that they should not expect to have much capabilities.
|
|
// Moving the cursor, clearing the screen, or querying for colors are among such "advanced" capabilities.
|
|
// Context: https://github.com/jesseduffield/lazygit/issues/3419
|
|
cmd.Env = removeExistingTermEnvVars(cmd.Env)
|
|
cmd.Env = append(cmd.Env, "TERM=dumb")
|
|
|
|
cmd.Env = append(cmd.Env, "GIT_PAGER="+pager)
|
|
|
|
manager := gui.getManager(view)
|
|
|
|
var ptmx *os.File
|
|
start := func() (*exec.Cmd, io.Reader) {
|
|
var err error
|
|
ptmx, err = pty.StartWithSize(cmd, gui.desiredPtySize(view))
|
|
if err != nil {
|
|
gui.c.Log.Error(err)
|
|
}
|
|
|
|
gui.Mutexes.PtyMutex.Lock()
|
|
gui.viewPtmxMap[view.Name()] = ptmx
|
|
gui.Mutexes.PtyMutex.Unlock()
|
|
|
|
return cmd, ptmx
|
|
}
|
|
|
|
onClose := func() {
|
|
gui.Mutexes.PtyMutex.Lock()
|
|
ptmx.Close()
|
|
delete(gui.viewPtmxMap, view.Name())
|
|
gui.Mutexes.PtyMutex.Unlock()
|
|
}
|
|
|
|
linesToRead := gui.linesToReadFromCmdTask(view)
|
|
return manager.NewTask(manager.NewCmdTask(start, prefix, linesToRead, onClose), cmdStr)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func removeExistingTermEnvVars(env []string) []string {
|
|
return lo.Filter(env, func(envVar string, _ int) bool {
|
|
return !isTermEnvVar(envVar)
|
|
})
|
|
}
|
|
|
|
// Terminals set a variety of different environment variables
|
|
// to identify themselves to processes. This list should catch the most common among them.
|
|
func isTermEnvVar(envVar string) bool {
|
|
return strings.HasPrefix(envVar, "TERM=") ||
|
|
strings.HasPrefix(envVar, "TERM_PROGRAM=") ||
|
|
strings.HasPrefix(envVar, "TERM_PROGRAM_VERSION=") ||
|
|
strings.HasPrefix(envVar, "TERMINAL_EMULATOR=") ||
|
|
strings.HasPrefix(envVar, "TERMINAL_NAME=") ||
|
|
strings.HasPrefix(envVar, "TERMINAL_VERSION_")
|
|
}
|