1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-02 23:27:32 +02:00

Merge branch 'master' into fix-display-of-renamed-files-in-tree-view

This commit is contained in:
Stefan Haller 2025-04-08 16:17:33 +02:00 committed by GitHub
commit b09251e27b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
83 changed files with 1007 additions and 317 deletions

View File

@ -148,7 +148,7 @@ Press `b` in the commits view to mark a commit as good/bad in order to begin a g
### Nuke the working tree
For when you really want to just get rid of anything that shows up when you run `git status` (and yes that includes dirty submodules) [kidpix style](https://www.youtube.com/watch?v=Ur7_A4JusMU), press `shift+d` to bring up the reset options menu and then select the 'nuke' option.
For when you really want to just get rid of anything that shows up when you run `git status` (and yes that includes dirty submodules) [kidpix style](https://www.youtube.com/watch?v=N4E2B_k2Bss), press `shift+d` to bring up the reset options menu and then select the 'nuke' option.
![Nuke working tree](../assets/demo/nuke_working_tree-compressed.gif)

View File

@ -442,6 +442,10 @@ os:
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
readFromClipboardCmd: ""
# A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands.
# See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands
shellFunctionsFile: ""
# If true, don't display introductory popups upon opening Lazygit.
disableStartupPopups: false
@ -740,6 +744,21 @@ The `editInTerminal` option is used to decide whether lazygit needs to suspend i
Contributions of new editor presets are welcome; see the `getPreset` function in [`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
## Using aliases or functions in shell commands
Lazygit has a command prompt (`:`) for quickly executing shell commands without having to quit lazygit or switch to a different terminal. Most people find it convenient to have their usual shell aliases or shell functions available at this prompt. To achieve this, put your alias definitions in a separate shell startup file (which you source from your normal startup file, i.e. from `.bashrc` or `.zshrc`), and then tell lazygit about this file like so:
```yml
os:
shellFunctionsFile: ~/.my_aliases.sh
```
For many people it might work well enough to use their entire shell config file (`~/.bashrc` or `~/.zshrc`) as the `shellFunctionsFile`, but these config files typically do a lot more than defining aliases (e.g. initialize the completion system, start an ssh-agent, etc.) and this may unnecessarily delay execution of shell commands.
When using zsh, aliases can't be used here, but functions can. It is easy to convert your existing aliases into functions, just change `alias l="ls -la"` to `l() ls -la`, for example. This way it will work as before both in the shell and in lazygit.
Note that the shell aliases file is not only used when executing shell commands, but also for [custom commands](Custom_Command_Keybindings.md), and when opening a file in the editor.
## Overriding default config file location
To override the default config directory, use `CONFIG_DIR="$HOME/.config/lazygit"`. This directory contains the config file in addition to some other files lazygit uses to keep track of state across sessions.

12
go.mod
View File

@ -13,9 +13,9 @@ require (
github.com/gookit/color v1.4.2
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac
github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
@ -37,7 +37,7 @@ require (
github.com/stretchr/testify v1.8.1
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
golang.org/x/sync v0.11.0
golang.org/x/sync v0.13.0
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
gopkg.in/yaml.v3 v3.0.1
)
@ -73,8 +73,8 @@ require (
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

24
go.sum
View File

@ -182,12 +182,12 @@ github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDL
github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws=
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 h1:meoUDZGF6jZAbhW5IBwj92mTqGmrOn+Cuu0jM7/aUcs=
github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac h1:vUNTiVEB9Bz16pTJ5kNgb/1HhnWdSA1P0GfFLUJeITI=
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647 h1:XpoXCyLGCB4mczPc1q/8dJefr6OB1xa7jyhH5a5eJqY=
github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
@ -436,8 +436,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -491,8 +491,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -502,8 +502,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -518,8 +518,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -34,12 +34,8 @@ func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj {
return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar)
func (self *gitCmdObjBuilder) NewShell(cmdStr string, shellFunctionsFile string) oscommands.ICmdObj {
return self.innerBuilder.NewShell(cmdStr, shellFunctionsFile).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) Quote(str string) string {

View File

@ -13,9 +13,9 @@ type ICmdObjBuilder interface {
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object.
New(args []string) ICmdObj
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
NewShell(commandStr string) ICmdObj
// Like NewShell, but uses the user's shell rather than "bash", and passes -i to it
NewInteractiveShell(commandStr string) ICmdObj
// shellFunctionsFile is an optional file path that will be sourced before executing the command. Callers should pass
// the value of UserConfig.OS.ShellFunctionsFile.
NewShell(commandStr string, shellFunctionsFile string) ICmdObj
// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
Quote(str string) string
}
@ -44,20 +44,16 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj {
}
}
func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
func (self *CmdObjBuilder) NewShell(commandStr string, shellFunctionsFile string) ICmdObj {
if len(shellFunctionsFile) > 0 {
commandStr = fmt.Sprintf("%ssource %s\n%s", self.platform.PrefixForShellFunctionsFile, shellFunctionsFile, commandStr)
}
quotedCommand := self.quotedCommandString(commandStr)
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
return self.New(cmdArgs)
}
func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj {
quotedCommand := self.quotedCommandString(commandStr + self.platform.InteractiveShellExit)
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand))
return self.New(cmdArgs)
}
func (self *CmdObjBuilder) quotedCommandString(commandStr string) string {
// Windows does not seem to like quotes around the command
if self.platform.OS == "windows" {

View File

@ -51,14 +51,11 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
}
var dummyPlatform = &Platform{
OS: "darwin",
Shell: "bash",
InteractiveShell: "bash",
ShellArg: "-c",
InteractiveShellArg: "-i",
InteractiveShellExit: "; exit $?",
OpenCommand: "open {{filename}}",
OpenLinkCommand: "open {{link}}",
OS: "darwin",
Shell: "bash",
ShellArg: "-c",
OpenCommand: "open {{filename}}",
OpenLinkCommand: "open {{link}}",
}
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {

View File

@ -35,14 +35,12 @@ type OSCommand struct {
// Platform stores the os state
type Platform struct {
OS string
Shell string
InteractiveShell string
ShellArg string
InteractiveShellArg string
InteractiveShellExit string
OpenCommand string
OpenLinkCommand string
OS string
Shell string
ShellArg string
PrefixForShellFunctionsFile string
OpenCommand string
OpenLinkCommand string
}
// NewOSCommand os command runner
@ -93,7 +91,7 @@ func (c *OSCommand) OpenFile(filename string) error {
"filename": c.Quote(filename),
}
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
return c.Cmd.NewShell(command).Run()
return c.Cmd.NewShell(command, c.UserConfig().OS.ShellFunctionsFile).Run()
}
func (c *OSCommand) OpenLink(link string) error {
@ -110,7 +108,7 @@ func (c *OSCommand) OpenLink(link string) error {
}
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
return c.Cmd.NewShell(command).Run()
return c.Cmd.NewShell(command, c.UserConfig().OS.ShellFunctionsFile).Run()
}
// Quote wraps a message in platform-specific quotation marks
@ -299,7 +297,7 @@ func (c *OSCommand) CopyToClipboard(str string) error {
cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
"text": c.Cmd.Quote(str),
})
return c.Cmd.NewShell(cmdStr).Run()
return c.Cmd.NewShell(cmdStr, c.UserConfig().OS.ShellFunctionsFile).Run()
}
return clipboard.WriteAll(str)
@ -310,7 +308,7 @@ func (c *OSCommand) PasteFromClipboard() (string, error) {
var err error
if c.UserConfig().OS.CopyToClipboardCmd != "" {
cmdStr := c.UserConfig().OS.ReadFromClipboardCmd
s, err = c.Cmd.NewShell(cmdStr).RunWithOutput()
s, err = c.Cmd.NewShell(cmdStr, c.UserConfig().OS.ShellFunctionsFile).RunWithOutput()
} else {
s, err = clipboard.ReadAll()
}
@ -360,5 +358,5 @@ func (c *OSCommand) UpdateWindowTitle() error {
return getWdErr
}
argString := fmt.Sprint("title ", filepath.Base(path), " - Lazygit")
return c.Cmd.NewShell(argString).Run()
return c.Cmd.NewShell(argString, c.UserConfig().OS.ShellFunctionsFile).Run()
}

View File

@ -12,27 +12,18 @@ import (
func GetPlatform() *Platform {
shell := getUserShell()
interactiveShell := shell
interactiveShellArg := "-i"
interactiveShellExit := "; exit $?"
if strings.HasSuffix(shell, "fish") {
interactiveShellExit = "; exit $status"
} else if !(strings.HasSuffix(shell, "bash") || strings.HasSuffix(shell, "zsh")) {
interactiveShell = "bash"
interactiveShellArg = ""
interactiveShellExit = ""
prefixForShellFunctionsFile := ""
if strings.HasSuffix(shell, "bash") {
prefixForShellFunctionsFile = "shopt -s expand_aliases\n"
}
return &Platform{
OS: runtime.GOOS,
Shell: "bash",
InteractiveShell: interactiveShell,
ShellArg: "-c",
InteractiveShellArg: interactiveShellArg,
InteractiveShellExit: interactiveShellExit,
OpenCommand: "open {{filename}}",
OpenLinkCommand: "open {{link}}",
OS: runtime.GOOS,
Shell: shell,
ShellArg: "-c",
PrefixForShellFunctionsFile: prefixForShellFunctionsFile,
OpenCommand: "open {{filename}}",
OpenLinkCommand: "open {{link}}",
}
}

View File

@ -2,11 +2,8 @@ package oscommands
func GetPlatform() *Platform {
return &Platform{
OS: "windows",
Shell: "cmd",
InteractiveShell: "cmd",
ShellArg: "/c",
InteractiveShellArg: "",
InteractiveShellExit: "",
OS: "windows",
Shell: "cmd",
ShellArg: "/c",
}
}

View File

@ -581,6 +581,18 @@ type OSConfig struct {
// Command for opening a link. Should contain "{{link}}".
OpenLink string `yaml:"openLink,omitempty"`
// CopyToClipboardCmd is the command for copying to clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
// ReadFromClipboardCmd is the command for reading the clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"`
// A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands
ShellFunctionsFile string `yaml:"shellFunctionsFile"`
// --------
// The following configs are all deprecated and kept for backward
@ -603,14 +615,6 @@ type OSConfig struct {
// OpenLinkCommand is the command for opening a link
// Deprecated: use OpenLink instead.
OpenLinkCommand string `yaml:"openLinkCommand,omitempty" jsonschema:"deprecated"`
// CopyToClipboardCmd is the command for copying to clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"`
// ReadFromClipboardCmd is the command for reading the clipboard.
// See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard
ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"`
}
type CustomCommandAfterHook struct {

View File

@ -55,17 +55,7 @@ func (self *ContextMgr) Replace(c types.Context) {
self.Activate(c, types.OnFocusOpts{})
}
func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) {
if len(opts) > 1 {
panic("cannot pass multiple opts to Push")
}
singleOpts := types.OnFocusOpts{}
if len(opts) > 0 {
// using triple dot but you should only ever pass one of these opt structs
singleOpts = opts[0]
}
func (self *ContextMgr) Push(c types.Context, opts types.OnFocusOpts) {
if !c.IsFocusable() {
return
}
@ -77,7 +67,7 @@ func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) {
}
if contextToActivate != nil {
self.Activate(contextToActivate, singleOpts)
self.Activate(contextToActivate, opts)
}
}

View File

@ -213,7 +213,7 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
return err
}
self.c.Context().Push(self.c.Contexts().LocalCommits)
self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{})
return nil
})
},

View File

@ -6,6 +6,7 @@ import (
"path/filepath"
"strings"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@ -1194,13 +1195,36 @@ func filterNodesHaveUnstagedChanges(nodes []*filetree.FileNode) []*filetree.File
})
}
func findSubmoduleNode(nodes []*filetree.FileNode, submodules []*models.SubmoduleConfig) *models.File {
for _, node := range nodes {
submoduleNode := node.FindFirstFileBy(func(f *models.File) bool {
return f.IsSubmodule(submodules)
})
if submoduleNode != nil {
return submoduleNode
}
}
return nil
}
func (self *FilesController) canRemove(selectedNodes []*filetree.FileNode) *types.DisabledReason {
// Return disabled if the selection contains multiple changed items and includes a submodule change.
submodules := self.c.Model().Submodules
submoduleCount := lo.CountBy(selectedNodes, func(node *filetree.FileNode) bool {
return node.File != nil && node.File.IsSubmodule(submodules)
})
if submoduleCount > 0 && len(selectedNodes) > 1 {
return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupportedForSubmodules}
hasFiles := false
uniqueSelectedSubmodules := set.New[*models.SubmoduleConfig]()
for _, node := range selectedNodes {
_ = node.ForEachFile(func(f *models.File) error {
if submodule := f.SubmoduleConfig(submodules); submodule != nil {
uniqueSelectedSubmodules.Add(submodule)
} else {
hasFiles = true
}
return nil
})
if uniqueSelectedSubmodules.Len() > 0 && (hasFiles || uniqueSelectedSubmodules.Len() > 1) {
return &types.DisabledReason{Text: self.c.Tr.MultiSelectNotSupportedForSubmodules}
}
}
return nil
@ -1209,11 +1233,13 @@ func (self *FilesController) canRemove(selectedNodes []*filetree.FileNode) *type
func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error {
submodules := self.c.Model().Submodules
selectedNodes = normalisedSelectedNodes(selectedNodes)
// If we have one submodule then we must only have one submodule or `canRemove` would have
// returned an error
firstNode := selectedNodes[0]
if firstNode.File != nil && firstNode.File.IsSubmodule(submodules) {
submodule := firstNode.File.SubmoduleConfig(submodules)
submoduleNode := findSubmoduleNode(selectedNodes, submodules)
if submoduleNode != nil {
submodule := submoduleNode.SubmoduleConfig(submodules)
menuItems := []*types.MenuItem{
{
@ -1224,11 +1250,9 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error {
},
}
return self.c.Menu(types.CreateMenuOptions{Title: firstNode.GetPath(), Items: menuItems})
return self.c.Menu(types.CreateMenuOptions{Title: submoduleNode.GetPath(), Items: menuItems})
}
selectedNodes = normalisedSelectedNodes(selectedNodes)
discardAllChangesItem := types.MenuItem{
Label: self.c.Tr.DiscardAllChanges,
OnPress: func() error {

View File

@ -120,7 +120,7 @@ func (self *FilteringMenuAction) setFiltering() error {
repoState.SetScreenMode(types.SCREEN_HALF)
}
self.c.Context().Push(self.c.Contexts().LocalCommits)
self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{})
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() error {
self.c.Contexts().LocalCommits.SetSelection(0)

View File

@ -12,10 +12,6 @@ import (
"github.com/samber/lo"
)
type ICommitsHelper interface {
UpdateCommitPanelView(message string)
}
type CommitsHelper struct {
c *HelperCommon
@ -26,8 +22,6 @@ type CommitsHelper struct {
setCommitDescription func(string)
}
var _ ICommitsHelper = &CommitsHelper{}
func NewCommitsHelper(
c *HelperCommon,
getCommitSummary func() string,
@ -150,7 +144,7 @@ func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOp
self.UpdateCommitPanelView(opts.InitialMessage)
self.c.Context().Push(self.c.Contexts().CommitMessage)
self.c.Context().Push(self.c.Contexts().CommitMessage, types.OnFocusOpts{})
}
func (self *CommitsHelper) OnCommitSuccess() {

View File

@ -177,7 +177,7 @@ func (self *ConfirmationHelper) CreatePopupPanel(ctx goContext.Context, opts typ
self.c.State().GetRepoState().SetCurrentPopupOpts(&opts)
self.c.Context().Push(self.c.Contexts().Confirmation)
self.c.Context().Push(self.c.Contexts().Confirmation, types.OnFocusOpts{})
}
func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) {

View File

@ -6,12 +6,6 @@ import (
"github.com/samber/lo"
)
type IFilesHelper interface {
EditFiles(filenames []string) error
EditFileAtLine(filename string, lineNumber int) error
OpenFile(filename string) error
}
type FilesHelper struct {
c *HelperCommon
}
@ -22,8 +16,6 @@ func NewFilesHelper(c *HelperCommon) *FilesHelper {
}
}
var _ IFilesHelper = &FilesHelper{}
func (self *FilesHelper) EditFiles(filenames []string) error {
absPaths := lo.Map(filenames, func(filename string, _ int) string {
absPath, err := filepath.Abs(filename)
@ -71,11 +63,11 @@ func (self *FilesHelper) OpenDirInEditor(path string) error {
func (self *FilesHelper) callEditor(cmdStr string, suspend bool) error {
if suspend {
return self.c.RunSubprocessAndRefresh(
self.c.OS().Cmd.NewShell(cmdStr),
self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile),
)
}
return self.c.OS().Cmd.NewShell(cmdStr).Run()
return self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile).Run()
}
func (self *FilesHelper) OpenFile(filename string) error {

View File

@ -137,7 +137,7 @@ func (self *FixupHelper) HandleFindBaseCommitForFixupPress() error {
}
self.c.Contexts().LocalCommits.SetSelection(index)
self.c.Context().Push(self.c.Contexts().LocalCommits)
self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{})
return nil
}

View File

@ -6,11 +6,6 @@ import (
// this helper just wraps our hosting_service package
type IHostHelper interface {
GetPullRequestURL(from string, to string) (string, error)
GetCommitURL(commitHash string) (string, error)
}
type HostHelper struct {
c *HelperCommon
}

View File

@ -206,7 +206,7 @@ func (self *MergeAndRebaseHelper) PromptForConflictHandling() error {
{
Label: self.c.Tr.ViewConflictsMenuItem,
OnPress: func() error {
self.c.Context().Push(self.c.Contexts().Files)
self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{})
return nil
},
},
@ -357,7 +357,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error {
if err = self.ResetMarkedBaseCommit(); err != nil {
return err
}
self.c.Context().Push(self.c.Contexts().LocalCommits)
self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{})
return nil
},
},

View File

@ -62,7 +62,7 @@ func (self *MergeConflictsHelper) EscapeMerge() error {
// files context over it.
// So long as both places call OnUIThread, we're fine.
if self.c.Context().IsCurrent(self.c.Contexts().MergeConflicts) {
self.c.Context().Push(self.c.Contexts().Files)
self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{})
}
return nil
})
@ -93,7 +93,7 @@ func (self *MergeConflictsHelper) SwitchToMerge(path string) error {
}
}
self.c.Context().Push(self.c.Contexts().MergeConflicts)
self.c.Context().Push(self.c.Contexts().MergeConflicts, types.OnFocusOpts{})
return nil
}

View File

@ -9,10 +9,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type IPatchBuildingHelper interface {
ValidateNormalWorkingTreeState() (bool, error)
}
type PatchBuildingHelper struct {
c *HelperCommon
}

View File

@ -14,15 +14,6 @@ import (
"github.com/samber/lo"
)
type IRefsHelper interface {
CheckoutRef(ref string, options types.CheckoutRefOptions) error
GetCheckedOutRef() *models.Branch
CreateGitResetMenu(ref string) error
CreateCheckoutMenu(commit *models.Commit) error
ResetToRef(ref string, strength string, envVars []string) error
NewBranch(from string, fromDescription string, suggestedBranchname string) error
}
type RefsHelper struct {
c *HelperCommon
}
@ -35,8 +26,6 @@ func NewRefsHelper(
}
}
var _ IRefsHelper = &RefsHelper{}
func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions) error {
waitingStatus := options.WaitingStatus
if waitingStatus == "" {
@ -118,7 +107,7 @@ func (self *RefsHelper) CheckoutRemoteBranch(fullBranchName string, localBranchN
// Switch to the branches context _before_ starting to check out the
// branch, so that we see the inline status
if self.c.Context().Current() != self.c.Contexts().Branches {
self.c.Context().Push(self.c.Contexts().Branches)
self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
}
return self.CheckoutRef(branchName, types.CheckoutRefOptions{})
}
@ -334,7 +323,7 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest
refresh := func() error {
if self.c.Context().Current() != self.c.Contexts().Branches {
self.c.Context().Push(self.c.Contexts().Branches)
self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
}
self.c.Contexts().LocalCommits.SetSelection(0)

View File

@ -41,7 +41,7 @@ func (self *SearchHelper) OpenFilterPrompt(context types.IFilterableContext) err
self.OnPromptContentChanged("")
promptView.RenderTextArea()
self.c.Context().Push(self.c.Contexts().Search)
self.c.Context().Push(self.c.Contexts().Search, types.OnFocusOpts{})
return self.c.ResetKeybindings()
}
@ -58,7 +58,7 @@ func (self *SearchHelper) OpenSearchPrompt(context types.ISearchableContext) err
promptView.ClearTextArea()
promptView.RenderTextArea()
self.c.Context().Push(self.c.Contexts().Search)
self.c.Context().Push(self.c.Contexts().Search, types.OnFocusOpts{})
return self.c.ResetKeybindings()
}

View File

@ -114,7 +114,7 @@ func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) {
}
func (self *StagingHelper) handleStagingEscape() {
self.c.Context().Push(self.c.Contexts().Files)
self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{})
}
func (self *StagingHelper) secondaryStagingFocused() bool {

View File

@ -69,6 +69,6 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error {
self.c.PostRefreshUpdate(self.c.Contexts().SubCommits)
self.c.Context().Push(self.c.Contexts().SubCommits)
self.c.Context().Push(self.c.Contexts().SubCommits, types.OnFocusOpts{})
return nil
}

View File

@ -26,20 +26,10 @@ import (
// finding suggestions in this file, so that it's easy to see if a function already
// exists for fetching a particular model.
type ISuggestionsHelper interface {
GetRemoteSuggestionsFunc() func(string) []*types.Suggestion
GetBranchNameSuggestionsFunc() func(string) []*types.Suggestion
GetFilePathSuggestionsFunc() func(string) []*types.Suggestion
GetRemoteBranchesSuggestionsFunc(separator string) func(string) []*types.Suggestion
GetRefsSuggestionsFunc() func(string) []*types.Suggestion
}
type SuggestionsHelper struct {
c *HelperCommon
}
var _ ISuggestionsHelper = &SuggestionsHelper{}
func NewSuggestionsHelper(
c *HelperCommon,
) *SuggestionsHelper {

View File

@ -14,15 +14,6 @@ type UpstreamHelper struct {
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion
}
type IUpstreamHelper interface {
ParseUpstream(string) (string, string, error)
PromptForUpstreamWithInitialContent(*models.Branch, func(string) error) error
PromptForUpstreamWithoutInitialContent(*models.Branch, func(string) error) error
GetSuggestedRemote() string
}
var _ IUpstreamHelper = &UpstreamHelper{}
func NewUpstreamHelper(
c *HelperCommon,
getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion,

View File

@ -12,13 +12,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type IWorkingTreeHelper interface {
AnyStagedFiles() bool
AnyTrackedFiles() bool
IsWorkingTreeDirty() bool
FileForSubmodule(submodule *models.SubmoduleConfig) *models.File
}
type WorkingTreeHelper struct {
c *HelperCommon
refHelper *RefsHelper

View File

@ -12,11 +12,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
type IWorktreeHelper interface {
GetMainWorktreeName() string
GetCurrentWorktreeName() string
}
type WorktreeHelper struct {
c *HelperCommon
reposHelper *ReposHelper

View File

@ -56,7 +56,7 @@ func (self *JumpToSideWindowController) goToSideWindow(window string) func() err
context := self.c.Helpers().Window.GetContextForWindow(window)
self.c.Context().Push(context)
self.c.Context().Push(context, types.OnFocusOpts{})
return nil
}
}

View File

@ -179,7 +179,7 @@ func (self *ListController) HandleClick(opts gocui.ViewMouseBindingOpts) error {
func (self *ListController) pushContextIfNotFocused() error {
if !self.isFocused() {
self.c.Context().Push(self.context)
self.c.Context().Push(self.context, types.OnFocusOpts{})
}
return nil

View File

@ -608,6 +608,7 @@ func (self *LocalCommitsController) restoreSelectionRangeAndMode(selectionRangeA
})
if ok1 && ok2 {
self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, selectionRangeAndMode.mode)
self.context().HandleFocus(types.OnFocusOpts{})
}
}

View File

@ -79,7 +79,7 @@ func (self *QuitActions) Escape() error {
parentContext := currentContext.GetParentContext()
if parentContext != nil {
// TODO: think about whether this should be marked as a return rather than adding to the stack
self.c.Context().Push(parentContext)
self.c.Context().Push(parentContext, types.OnFocusOpts{})
return nil
}

View File

@ -129,7 +129,7 @@ func (self *RemotesController) enter(remote *models.Remote) error {
self.c.PostRefreshUpdate(remoteBranchesContext)
self.c.Context().Push(remoteBranchesContext)
self.c.Context().Push(remoteBranchesContext, types.OnFocusOpts{})
return nil
}

View File

@ -31,7 +31,7 @@ func (self *ShellCommandAction) Call() error {
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
return self.c.RunSubprocessAndRefresh(
self.c.OS().Cmd.NewInteractiveShell(command),
self.c.OS().Cmd.NewShell(command, self.c.UserConfig().OS.ShellFunctionsFile),
)
},
HandleDeleteSuggestion: func(index int) error {

View File

@ -69,7 +69,7 @@ func (self *SideWindowController) previousSideWindow() error {
context := self.c.Helpers().Window.GetContextForWindow(newWindow)
self.c.Context().Push(context)
self.c.Context().Push(context, types.OnFocusOpts{})
return nil
}
@ -93,6 +93,6 @@ func (self *SideWindowController) nextSideWindow() error {
context := self.c.Helpers().Window.GetContextForWindow(newWindow)
self.c.Context().Push(context)
self.c.Context().Push(context, types.OnFocusOpts{})
return nil
}

View File

@ -73,6 +73,6 @@ func (self *SnakeController) SetDirection(direction snake.Direction) func() erro
}
func (self *SnakeController) Escape() error {
self.c.Context().Push(self.c.Contexts().Submodules)
self.c.Context().Push(self.c.Contexts().Submodules, types.OnFocusOpts{})
return nil
}

View File

@ -180,7 +180,7 @@ func (self *StagingController) Escape() error {
func (self *StagingController) TogglePanel() error {
if self.otherContext.GetState() != nil {
self.c.Context().Push(self.otherContext)
self.c.Context().Push(self.otherContext, types.OnFocusOpts{})
}
return nil

View File

@ -112,7 +112,7 @@ func (self *StashController) handleStashApply(stashEntry *models.StashEntry) err
return err
}
if self.c.UserConfig().Gui.SwitchToFilesAfterStashApply {
self.c.Context().Push(self.c.Contexts().Files)
self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{})
}
return nil
}
@ -141,7 +141,7 @@ func (self *StashController) handleStashPop(stashEntry *models.StashEntry) error
return err
}
if self.c.UserConfig().Gui.SwitchToFilesAfterStashPop {
self.c.Context().Push(self.c.Contexts().Files)
self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{})
}
return nil
}

View File

@ -104,7 +104,7 @@ func (self *StatusController) onClick(opts gocui.ViewMouseBindingOpts) error {
return nil
}
self.c.Context().Push(self.Context())
self.c.Context().Push(self.Context(), types.OnFocusOpts{})
upstreamStatus := utils.Decolorise(presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now(), self.c.UserConfig()))
repoName := self.c.Git().RepoPaths.RepoName()

View File

@ -309,7 +309,7 @@ func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) erro
}
func (self *SubmodulesController) easterEgg() error {
self.c.Context().Push(self.c.Contexts().Snake)
self.c.Context().Push(self.c.Contexts().Snake, types.OnFocusOpts{})
return nil
}

View File

@ -91,7 +91,7 @@ func (self *SwitchToDiffFilesController) enter() error {
return err
}
self.c.Context().Push(commitFilesContext)
self.c.Context().Push(commitFilesContext, types.OnFocusOpts{})
return nil
}

View File

@ -115,7 +115,7 @@ func (self *TagsController) checkout(tag *models.Tag) error {
if err := self.c.Helpers().Refs.CheckoutRef(tag.FullRefName(), types.CheckoutRefOptions{}); err != nil {
return err
}
self.c.Context().Push(self.c.Contexts().Branches)
self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{})
return nil
}

View File

@ -38,7 +38,7 @@ func (gui *Gui) handleFocusCommandLog() error {
gui.c.State().SetShowExtrasWindow(true)
// TODO: is this necessary? Can't I just call 'return from context'?
gui.State.Contexts.CommandLog.SetParentContext(gui.c.Context().CurrentSide())
gui.c.Context().Push(gui.State.Contexts.CommandLog)
gui.c.Context().Push(gui.State.Contexts.CommandLog, types.OnFocusOpts{})
return nil
}
@ -58,6 +58,38 @@ func (gui *Gui) scrollDownExtra() error {
return nil
}
func (gui *Gui) pageUpExtrasPanel() error {
gui.Views.Extras.Autoscroll = false
gui.Views.Extras.ScrollUp(gui.Contexts().CommandLog.GetViewTrait().PageDelta())
return nil
}
func (gui *Gui) pageDownExtrasPanel() error {
gui.Views.Extras.Autoscroll = false
gui.Views.Extras.ScrollDown(gui.Contexts().CommandLog.GetViewTrait().PageDelta())
return nil
}
func (gui *Gui) goToExtrasPanelTop() error {
gui.Views.Extras.Autoscroll = false
gui.Views.Extras.ScrollUp(gui.Views.Extras.ViewLinesHeight())
return nil
}
func (gui *Gui) goToExtrasPanelBottom() error {
gui.Views.Extras.Autoscroll = true
gui.Views.Extras.ScrollDown(gui.Views.Extras.ViewLinesHeight())
return nil
}
func (gui *Gui) getCmdWriter() io.Writer {
return &prefixWriter{writer: gui.Views.Extras, prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.c.Tr.GitOutput)}
}

View File

@ -109,13 +109,13 @@ func (self *Node[T]) SortChildren() {
self.Children = children
}
func (self *Node[T]) Some(test func(*Node[T]) bool) bool {
if test(self) {
func (self *Node[T]) Some(predicate func(*Node[T]) bool) bool {
if predicate(self) {
return true
}
for _, child := range self.Children {
if child.Some(test) {
if child.Some(predicate) {
return true
}
}
@ -123,14 +123,14 @@ func (self *Node[T]) Some(test func(*Node[T]) bool) bool {
return false
}
func (self *Node[T]) SomeFile(test func(*T) bool) bool {
func (self *Node[T]) SomeFile(predicate func(*T) bool) bool {
if self.IsFile() {
if test(self.File) {
if predicate(self.File) {
return true
}
} else {
for _, child := range self.Children {
if child.SomeFile(test) {
if child.SomeFile(predicate) {
return true
}
}
@ -139,13 +139,13 @@ func (self *Node[T]) SomeFile(test func(*T) bool) bool {
return false
}
func (self *Node[T]) Every(test func(*Node[T]) bool) bool {
if !test(self) {
func (self *Node[T]) Every(predicate func(*Node[T]) bool) bool {
if !predicate(self) {
return false
}
for _, child := range self.Children {
if !child.Every(test) {
if !child.Every(predicate) {
return false
}
}
@ -153,14 +153,14 @@ func (self *Node[T]) Every(test func(*Node[T]) bool) bool {
return true
}
func (self *Node[T]) EveryFile(test func(*T) bool) bool {
func (self *Node[T]) EveryFile(predicate func(*T) bool) bool {
if self.IsFile() {
if !test(self.File) {
if !predicate(self.File) {
return false
}
} else {
for _, child := range self.Children {
if !child.EveryFile(test) {
if !child.EveryFile(predicate) {
return false
}
}
@ -169,6 +169,22 @@ func (self *Node[T]) EveryFile(test func(*T) bool) bool {
return true
}
func (self *Node[T]) FindFirstFileBy(predicate func(*T) bool) *T {
if self.IsFile() {
if predicate(self.File) {
return self.File
}
} else {
for _, child := range self.Children {
if file := child.FindFirstFileBy(predicate); file != nil {
return file
}
}
}
return nil
}
func (self *Node[T]) Flatten(collapsedPaths *CollapsedPaths) []*Node[T] {
result := []*Node[T]{self}
@ -279,23 +295,23 @@ func (self *Node[T]) compressAux() *Node[T] {
return self
}
func (self *Node[T]) GetPathsMatching(test func(*Node[T]) bool) []string {
func (self *Node[T]) GetPathsMatching(predicate func(*Node[T]) bool) []string {
paths := []string{}
if test(self) {
if predicate(self) {
paths = append(paths, self.GetPath())
}
for _, child := range self.Children {
paths = append(paths, child.GetPathsMatching(test)...)
paths = append(paths, child.GetPathsMatching(predicate)...)
}
return paths
}
func (self *Node[T]) GetFilePathsMatching(test func(*T) bool) []string {
func (self *Node[T]) GetFilePathsMatching(predicate func(*T) bool) []string {
matchingFileNodes := lo.Filter(self.GetLeaves(), func(node *Node[T], _ int) bool {
return test(node.File)
return predicate(node.File)
})
return lo.Map(matchingFileNodes, func(node *Node[T], _ int) string {

View File

@ -109,6 +109,46 @@ func (gui *Gui) scrollDownConfirmationPanel() error {
return nil
}
func (gui *Gui) pageUpConfirmationPanel() error {
if gui.Views.Confirmation.Editable {
return nil
}
gui.Views.Confirmation.ScrollUp(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
return nil
}
func (gui *Gui) pageDownConfirmationPanel() error {
if gui.Views.Confirmation.Editable {
return nil
}
gui.Views.Confirmation.ScrollDown(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
return nil
}
func (gui *Gui) goToConfirmationPanelTop() error {
if gui.Views.Confirmation.Editable {
return nil
}
gui.Views.Confirmation.ScrollUp(gui.Views.Confirmation.ViewLinesHeight())
return nil
}
func (gui *Gui) goToConfirmationPanelBottom() error {
if gui.Views.Confirmation.Editable {
return nil
}
gui.Views.Confirmation.ScrollDown(gui.Views.Confirmation.ViewLinesHeight())
return nil
}
func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error {
return gui.handleCopySelectedSideContextItemToClipboardWithTruncation(-1)
}

View File

@ -394,7 +394,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context
}
}
gui.c.Context().Push(contextToPush)
gui.c.Context().Push(contextToPush, types.OnFocusOpts{})
return nil
}

View File

@ -260,6 +260,42 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
Key: gocui.MouseWheelDown,
Handler: self.scrollDownConfirmationPanel,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.NextPage),
Modifier: gocui.ModNone,
Handler: self.pageDownConfirmationPanel,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.PrevPage),
Modifier: gocui.ModNone,
Handler: self.pageUpConfirmationPanel,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.GotoTop),
Modifier: gocui.ModNone,
Handler: self.goToConfirmationPanelTop,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.GotoTopAlt),
Modifier: gocui.ModNone,
Handler: self.goToConfirmationPanelTop,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
Modifier: gocui.ModNone,
Handler: self.goToConfirmationPanelBottom,
},
{
ViewName: "confirmation",
Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt),
Modifier: gocui.ModNone,
Handler: self.goToConfirmationPanelBottom,
},
{
ViewName: "submodules",
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
@ -305,6 +341,42 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
Modifier: gocui.ModNone,
Handler: self.scrollDownExtra,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.NextPage),
Modifier: gocui.ModNone,
Handler: self.pageDownExtrasPanel,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.PrevPage),
Modifier: gocui.ModNone,
Handler: self.pageUpExtrasPanel,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.GotoTop),
Modifier: gocui.ModNone,
Handler: self.goToExtrasPanelTop,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.GotoTopAlt),
Modifier: gocui.ModNone,
Handler: self.goToExtrasPanelTop,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.GotoBottom),
Modifier: gocui.ModNone,
Handler: self.goToExtrasPanelBottom,
},
{
ViewName: "extras",
Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt),
Modifier: gocui.ModNone,
Handler: self.goToExtrasPanelBottom,
},
{
ViewName: "extras",
Tag: "navigation",

View File

@ -60,6 +60,6 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
gui.c.PostRefreshUpdate(gui.State.Contexts.Menu)
// TODO: ensure that if we're opened a menu from within a menu that it renders correctly
gui.c.Context().Push(gui.State.Contexts.Menu)
gui.c.Context().Push(gui.State.Contexts.Menu, types.OnFocusOpts{})
return nil
}

View File

@ -148,7 +148,7 @@ func (self *HandlerCreator) generateFindSuggestionsFunc(prompt *config.CustomCom
func (self *HandlerCreator) getCommandSuggestionsFn(command string) (func(string) []*types.Suggestion, error) {
lines := []*types.Suggestion{}
err := self.c.OS().Cmd.NewShell(command).RunAndProcessLines(func(line string) (bool, error) {
err := self.c.OS().Cmd.NewShell(command, self.c.UserConfig().OS.ShellFunctionsFile).RunAndProcessLines(func(line string) (bool, error) {
lines = append(lines, &types.Suggestion{Value: line, Label: line})
return false, nil
})
@ -259,7 +259,7 @@ func (self *HandlerCreator) finalHandler(customCommand config.CustomCommand, ses
return err
}
cmdObj := self.c.OS().Cmd.NewShell(cmdStr)
cmdObj := self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile)
if customCommand.Subprocess != nil && *customCommand.Subprocess {
return self.c.RunSubprocessAndRefresh(cmdObj)

View File

@ -286,7 +286,7 @@ type ListItem interface {
}
type IContextMgr interface {
Push(context Context, opts ...OnFocusOpts)
Push(context Context, opts OnFocusOpts)
Pop()
Replace(context Context)
Activate(context Context, opts OnFocusOpts)

View File

@ -72,7 +72,7 @@ func (gui *Gui) onViewTabClick(windowName string, tabIndex int) error {
return nil
}
gui.c.Context().Push(context)
gui.c.Context().Push(context, types.OnFocusOpts{})
return nil
}

View File

@ -134,6 +134,7 @@ func (gui *Gui) createAllViews() error {
gui.Views.Menu.Visible = false
gui.Views.Tooltip.Visible = false
gui.Views.Tooltip.AutoRenderHyperLinks = true
gui.Views.Information.BgColor = gocui.ColorDefault
gui.Views.Information.FgColor = gocui.ColorGreen

View File

@ -845,7 +845,7 @@ type TranslationSet struct {
NoItemSelected string
SelectedItemIsNotABranch string
SelectedItemDoesNotHaveFiles string
RangeSelectNotSupportedForSubmodules string
MultiSelectNotSupportedForSubmodules string
OldCherryPickKeyWarning string
CommandDoesNotSupportOpeningInEditor string
CustomCommands string
@ -1889,7 +1889,7 @@ func EnglishTranslationSet() *TranslationSet {
NoItemSelected: "No item selected",
SelectedItemIsNotABranch: "Selected item is not a branch",
SelectedItemDoesNotHaveFiles: "Selected item does not have files to view",
RangeSelectNotSupportedForSubmodules: "Range select not supported for submodules",
MultiSelectNotSupportedForSubmodules: "Multiselection not supported for submodules",
OldCherryPickKeyWarning: "The 'c' key is no longer the default key for copying commits to cherry pick. Please use `{{.copy}}` instead (and `{{.paste}}` to paste). The reason for this change is that the 'v' key for selecting a range of lines when staging is now also used for selecting a range of lines in any list view, meaning that we needed to find a new key for pasting commits, and if we're going to now use `{{.paste}}` for pasting commits, we may as well use `{{.copy}}` for copying them. If you want to configure the keybindings to get the old behaviour, set the following in your config:\n\nkeybinding:\n universal:\n toggleRangeSelect: <something other than v>\n commits:\n cherryPickCopy: 'c'\n pasteCommits: 'v'",
CommandDoesNotSupportOpeningInEditor: "This command doesn't support switching to the editor",
CustomCommands: "Custom commands",
@ -2082,6 +2082,7 @@ gui:
`,
"0.44.0": `- The gui.branchColors config option is deprecated; it will be removed in a future version. Please use gui.branchColorPatterns instead.
- The automatic coloring of branches starting with "feature/", "bugfix/", or "hotfix/" has been removed; if you want this, it's easy to set up using the new gui.branchColorPatterns option.`,
"0.49.0": `- Executing shell commands (with the ':' prompt) no longer uses an interactive shell, which means that if you want to use your shell aliases in this prompt, you need to do a little bit of setup work. See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands for details.`,
},
}
}

View File

@ -360,8 +360,8 @@ func (self *Shell) CloneIntoRemote(name string) *Shell {
}
func (self *Shell) CloneIntoSubmodule(submoduleName string, submodulePath string) *Shell {
self.Clone("other_repo")
self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../other_repo", submodulePath})
self.Clone(submoduleName)
self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../" + submoduleName, submodulePath})
return self
}

View File

@ -48,6 +48,9 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
Contains("<-- YOU ARE HERE --- commit 02").IsSelected(),
Contains("CI commit 01"),
).
Tap(func() {
t.Views().Main().Content(Contains("commit 02"))
}).
NavigateToLine(Contains("commit 06")).
Press(keys.Universal.Remove)

View File

@ -44,7 +44,7 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Main().Content(
Contains("-[submodule \"my_submodule_name\"]").
Contains("- path = my_submodule_path").
Contains("- url = ../other_repo"),
Contains("- url = ../my_submodule_name"),
)
t.FileSystem().PathNotPresent(gitDirSubmodulePath)

View File

@ -75,7 +75,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
Equals(" M my_submodule_path (submodule)"),
Equals(" ?? other_file").IsSelected(),
).
// Verify we can't use range select on submodules
// Verify we can't reset a submodule and file change at the same time.
Press(keys.Universal.ToggleRangeSelect).
SelectPreviousItem().
Lines(
@ -85,7 +85,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectToast(Contains("Disabled: Range select not supported for submodules"))
t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules"))
}).
Press(keys.Universal.ToggleRangeSelect).
Lines(

View File

@ -0,0 +1,126 @@
package submodule
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Reset submodule changes located in a nested folder.",
ExtraCmdArgs: []string{},
Skip: false,
SetupConfig: func(cfg *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("first commit")
shell.CreateDir("dir")
shell.CloneIntoSubmodule("submodule1", "dir/submodule1")
shell.CloneIntoSubmodule("submodule2", "dir/submodule2")
shell.GitAddAll()
shell.Commit("add submodules")
shell.CreateFile("dir/submodule1/file", "")
shell.CreateFile("dir/submodule2/file", "")
shell.CreateFile("dir/file", "")
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().Focus().
Lines(
Equals("▼ dir").IsSelected(),
Equals(" ?? file"),
Equals(" M submodule1 (submodule)"),
Equals(" M submodule2 (submodule)"),
).
// Verify we cannot reset the entire folder (has nested file and submodule changes).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules"))
}).
// Verify we cannot reset submodule + file or submodule + submodule via range select.
SelectNextItem().
Press(keys.Universal.ToggleRangeSelect).
SelectNextItem().
Lines(
Equals("▼ dir"),
Equals(" ?? file").IsSelected(),
Equals(" M submodule1 (submodule)").IsSelected(),
Equals(" M submodule2 (submodule)"),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules"))
}).
Press(keys.Universal.ToggleRangeSelect).
Press(keys.Universal.ToggleRangeSelect).
SelectNextItem().
Lines(
Equals("▼ dir"),
Equals(" ?? file"),
Equals(" M submodule1 (submodule)").IsSelected(),
Equals(" M submodule2 (submodule)").IsSelected(),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules"))
}).
// Reset the file change.
Press(keys.Universal.ToggleRangeSelect).
NavigateToLine(Contains("file")).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("Discard changes")).
Select(Contains("Discard all changes")).
Confirm()
}).
NavigateToLine(Contains("▼ dir")).
Lines(
Equals("▼ dir").IsSelected(),
Equals(" M submodule1 (submodule)"),
Equals(" M submodule2 (submodule)"),
).
// Verify we still cannot reset the entire folder (has two submodule changes).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules"))
}).
// Reset one of the submodule changes.
SelectNextItem().
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("dir/submodule1")).
Select(Contains("Stash uncommitted submodule changes and update")).
Confirm()
}).
NavigateToLine(Contains("▼ dir")).
Lines(
Equals("▼ dir").IsSelected(),
Equals(" M submodule2 (submodule)"),
).
// Now we can reset the folder (equivalent to resetting just the nested submodule change).
// Range selecting both the folder and submodule change is allowed.
Press(keys.Universal.ToggleRangeSelect).
SelectNextItem().
Lines(
Equals("▼ dir").IsSelected(),
Equals(" M submodule2 (submodule)").IsSelected(),
).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("dir/submodule2")).
Select(Contains("Stash uncommitted submodule changes and update")).
Cancel()
}).
// Or just selecting the folder itself.
NavigateToLine(Contains("▼ dir")).
Press(keys.Universal.Remove).
Tap(func() {
t.ExpectPopup().Menu().
Title(Equals("dir/submodule2")).
Select(Contains("Stash uncommitted submodule changes and update")).
Confirm()
}).
IsEmpty()
},
})

View File

@ -346,6 +346,7 @@ var tests = []*components.IntegrationTest{
submodule.Remove,
submodule.RemoveNested,
submodule.Reset,
submodule.ResetFolder,
sync.FetchPrune,
sync.FetchWhenSortedByDate,
sync.ForcePush,

View File

@ -1551,6 +1551,18 @@
"type": "string",
"description": "Command for opening a link. Should contain \"{{link}}\"."
},
"copyToClipboardCmd": {
"type": "string",
"description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard"
},
"readFromClipboardCmd": {
"type": "string",
"description": "ReadFromClipboardCmd is the command for reading the clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard"
},
"shellFunctionsFile": {
"type": "string",
"description": "A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands"
},
"editCommand": {
"type": "string",
"description": "EditCommand is the command for editing a file.\nDeprecated: use Edit instead. Note that semantics are different:\nEditCommand is just the command itself, whereas Edit contains a\n\"{{filename}}\" variable."
@ -1566,14 +1578,6 @@
"openLinkCommand": {
"type": "string",
"description": "OpenLinkCommand is the command for opening a link\nDeprecated: use OpenLink instead."
},
"copyToClipboardCmd": {
"type": "string",
"description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard"
},
"readFromClipboardCmd": {
"type": "string",
"description": "ReadFromClipboardCmd is the command for reading the clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard"
}
},
"additionalProperties": false,

View File

@ -39,6 +39,10 @@ func (s *Set[T]) Includes(value T) bool {
return s.hashMap[value]
}
func (s *Set[T]) Len() int {
return len(s.hashMap)
}
// output slice is not necessarily in the same order that items were added
func (s *Set[T]) ToSlice() []T {
return maps.Keys(s.hashMap)

View File

@ -822,6 +822,8 @@ func (v *View) writeRunes(p []rune) {
if v.pendingNewline {
finishLine()
} else {
v.autoRenderHyperlinksInCurrentLine()
}
v.updateSearchPositions()

View File

@ -18,7 +18,7 @@ import (
type token struct{}
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
// the same overall task. A Group should not be reused for different tasks.
//
// A zero Group is valid, has no limit on the number of active goroutines,
// and does not cancel on error.
@ -46,7 +46,7 @@ func (g *Group) done() {
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := withCancelCause(ctx)
ctx, cancel := context.WithCancelCause(ctx)
return &Group{cancel: cancel}, ctx
}
@ -61,6 +61,7 @@ func (g *Group) Wait() error {
}
// Go calls the given function in a new goroutine.
// The first call to Go must happen before a Wait.
// It blocks until the new goroutine can be added without the number of
// active goroutines in the group exceeding the configured limit.
//

View File

@ -1,13 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
return context.WithCancelCause(parent)
}

View File

@ -1,14 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
ctx, cancel := context.WithCancel(parent)
return ctx, func(error) { cancel() }
}

12
vendor/golang.org/x/sys/cpu/cpu.go generated vendored
View File

@ -149,6 +149,18 @@ var ARM struct {
_ CacheLinePad
}
// The booleans in Loong64 contain the correspondingly named cpu feature bit.
// The struct is padded to avoid false sharing.
var Loong64 struct {
_ CacheLinePad
HasLSX bool // support 128-bit vector extension
HasLASX bool // support 256-bit vector extension
HasCRC32 bool // support CRC instruction
HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction
HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} instruction
_ CacheLinePad
}
// MIPS64X contains the supported CPU features of the current mips64/mips64le
// platforms. If the current platform is not mips64/mips64le or the current
// operating system is not Linux then all feature flags are false.

22
vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cpu
// HWCAP bits. These are exposed by the Linux kernel.
const (
hwcap_LOONGARCH_LSX = 1 << 4
hwcap_LOONGARCH_LASX = 1 << 5
)
func doinit() {
// TODO: Features that require kernel support like LSX and LASX can
// be detected here once needed in std library or by the compiler.
Loong64.HasLSX = hwcIsSet(hwCap, hwcap_LOONGARCH_LSX)
Loong64.HasLASX = hwcIsSet(hwCap, hwcap_LOONGARCH_LASX)
}
func hwcIsSet(hwc uint, val uint) bool {
return hwc&val != 0
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64
//go:build linux && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64
package cpu

View File

@ -8,5 +8,43 @@ package cpu
const cacheLineSize = 64
// Bit fields for CPUCFG registers, Related reference documents:
// https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_cpucfg
const (
// CPUCFG1 bits
cpucfg1_CRC32 = 1 << 25
// CPUCFG2 bits
cpucfg2_LAM_BH = 1 << 27
cpucfg2_LAMCAS = 1 << 28
)
func initOptions() {
options = []option{
{Name: "lsx", Feature: &Loong64.HasLSX},
{Name: "lasx", Feature: &Loong64.HasLASX},
{Name: "crc32", Feature: &Loong64.HasCRC32},
{Name: "lam_bh", Feature: &Loong64.HasLAM_BH},
{Name: "lamcas", Feature: &Loong64.HasLAMCAS},
}
// The CPUCFG data on Loong64 only reflects the hardware capabilities,
// not the kernel support status, so features such as LSX and LASX that
// require kernel support cannot be obtained from the CPUCFG data.
//
// These features only require hardware capability support and do not
// require kernel specific support, so they can be obtained directly
// through CPUCFG
cfg1 := get_cpucfg(1)
cfg2 := get_cpucfg(2)
Loong64.HasCRC32 = cfgIsSet(cfg1, cpucfg1_CRC32)
Loong64.HasLAMCAS = cfgIsSet(cfg2, cpucfg2_LAMCAS)
Loong64.HasLAM_BH = cfgIsSet(cfg2, cpucfg2_LAM_BH)
}
func get_cpucfg(reg uint32) uint32
func cfgIsSet(cfg uint32, val uint32) bool {
return cfg&val != 0
}

13
vendor/golang.org/x/sys/cpu/cpu_loong64.s generated vendored Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
// func get_cpucfg(reg uint32) uint32
TEXT ·get_cpucfg(SB), NOSPLIT|NOFRAME, $0
MOVW reg+0(FP), R5
// CPUCFG R5, R4 = 0x00006ca4
WORD $0x00006ca4
MOVW R4, ret+8(FP)
RET

View File

@ -13,7 +13,7 @@ import "strconv"
// https://golang.org/cl/209597.
func parseRelease(rel string) (major, minor, patch int, ok bool) {
// Strip anything after a dash or plus.
for i := 0; i < len(rel); i++ {
for i := range len(rel) {
if rel[i] == '-' || rel[i] == '+' {
rel = rel[:i]
break
@ -21,7 +21,7 @@ func parseRelease(rel string) (major, minor, patch int, ok bool) {
}
next := func() (int, bool) {
for i := 0; i < len(rel); i++ {
for i := range len(rel) {
if rel[i] == '.' {
ver, err := strconv.Atoi(rel[:i])
rel = rel[i+1:]

View File

@ -602,7 +602,150 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI
return
}
//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error)
// sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error)
const minIovec = 8
func Readv(fd int, iovs [][]byte) (n int, err error) {
if !darwinKernelVersionMin(11, 0, 0) {
return 0, ENOSYS
}
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
n, err = readv(fd, iovecs)
readvRacedetect(iovecs, n, err)
return n, err
}
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
if !darwinKernelVersionMin(11, 0, 0) {
return 0, ENOSYS
}
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
n, err = preadv(fd, iovecs, offset)
readvRacedetect(iovecs, n, err)
return n, err
}
func Writev(fd int, iovs [][]byte) (n int, err error) {
if !darwinKernelVersionMin(11, 0, 0) {
return 0, ENOSYS
}
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
n, err = writev(fd, iovecs)
writevRacedetect(iovecs, n)
return n, err
}
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
if !darwinKernelVersionMin(11, 0, 0) {
return 0, ENOSYS
}
iovecs := make([]Iovec, 0, minIovec)
iovecs = appendBytes(iovecs, iovs)
if raceenabled {
raceReleaseMerge(unsafe.Pointer(&ioSync))
}
n, err = pwritev(fd, iovecs, offset)
writevRacedetect(iovecs, n)
return n, err
}
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
for _, b := range bs {
var v Iovec
v.SetLen(len(b))
if len(b) > 0 {
v.Base = &b[0]
} else {
v.Base = (*byte)(unsafe.Pointer(&_zero))
}
vecs = append(vecs, v)
}
return vecs
}
func writevRacedetect(iovecs []Iovec, n int) {
if !raceenabled {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
n -= m
if m > 0 {
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
}
}
}
func readvRacedetect(iovecs []Iovec, n int, err error) {
if !raceenabled {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
n -= m
if m > 0 {
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
}
}
if err == nil {
raceAcquire(unsafe.Pointer(&ioSync))
}
}
func darwinMajorMinPatch() (maj, min, patch int, err error) {
var un Utsname
err = Uname(&un)
if err != nil {
return
}
var mmp [3]int
c := 0
Loop:
for _, b := range un.Release[:] {
switch {
case b >= '0' && b <= '9':
mmp[c] = 10*mmp[c] + int(b-'0')
case b == '.':
c++
if c > 2 {
return 0, 0, 0, ENOTSUP
}
case b == 0:
break Loop
default:
return 0, 0, 0, ENOTSUP
}
}
if c != 2 {
return 0, 0, 0, ENOTSUP
}
return mmp[0], mmp[1], mmp[2], nil
}
func darwinKernelVersionMin(maj, min, patch int) bool {
actualMaj, actualMin, actualPatch, err := darwinMajorMinPatch()
if err != nil {
return false
}
return actualMaj > maj || actualMaj == maj && (actualMin > min || actualMin == min && actualPatch >= patch)
}
//sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
@ -705,3 +848,7 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI
//sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
//sys munmap(addr uintptr, length uintptr) (err error)
//sys readv(fd int, iovecs []Iovec) (n int, err error)
//sys preadv(fd int, iovecs []Iovec, offset int64) (n int, err error)
//sys writev(fd int, iovecs []Iovec) (n int, err error)
//sys pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error)

View File

@ -13,6 +13,7 @@ package unix
import (
"encoding/binary"
"slices"
"strconv"
"syscall"
"time"
@ -417,7 +418,7 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
return nil, 0, EINVAL
}
sa.raw.Family = AF_UNIX
for i := 0; i < n; i++ {
for i := range n {
sa.raw.Path[i] = int8(name[i])
}
// length is family (uint16), name, NUL.
@ -507,7 +508,7 @@ func (sa *SockaddrL2) sockaddr() (unsafe.Pointer, _Socklen, error) {
psm := (*[2]byte)(unsafe.Pointer(&sa.raw.Psm))
psm[0] = byte(sa.PSM)
psm[1] = byte(sa.PSM >> 8)
for i := 0; i < len(sa.Addr); i++ {
for i := range len(sa.Addr) {
sa.raw.Bdaddr[i] = sa.Addr[len(sa.Addr)-1-i]
}
cid := (*[2]byte)(unsafe.Pointer(&sa.raw.Cid))
@ -589,11 +590,11 @@ func (sa *SockaddrCAN) sockaddr() (unsafe.Pointer, _Socklen, error) {
sa.raw.Family = AF_CAN
sa.raw.Ifindex = int32(sa.Ifindex)
rx := (*[4]byte)(unsafe.Pointer(&sa.RxID))
for i := 0; i < 4; i++ {
for i := range 4 {
sa.raw.Addr[i] = rx[i]
}
tx := (*[4]byte)(unsafe.Pointer(&sa.TxID))
for i := 0; i < 4; i++ {
for i := range 4 {
sa.raw.Addr[i+4] = tx[i]
}
return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil
@ -618,11 +619,11 @@ func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) {
sa.raw.Family = AF_CAN
sa.raw.Ifindex = int32(sa.Ifindex)
n := (*[8]byte)(unsafe.Pointer(&sa.Name))
for i := 0; i < 8; i++ {
for i := range 8 {
sa.raw.Addr[i] = n[i]
}
p := (*[4]byte)(unsafe.Pointer(&sa.PGN))
for i := 0; i < 4; i++ {
for i := range 4 {
sa.raw.Addr[i+8] = p[i]
}
sa.raw.Addr[12] = sa.Addr
@ -911,7 +912,7 @@ func (sa *SockaddrIUCV) sockaddr() (unsafe.Pointer, _Socklen, error) {
// These are EBCDIC encoded by the kernel, but we still need to pad them
// with blanks. Initializing with blanks allows the caller to feed in either
// a padded or an unpadded string.
for i := 0; i < 8; i++ {
for i := range 8 {
sa.raw.Nodeid[i] = ' '
sa.raw.User_id[i] = ' '
sa.raw.Name[i] = ' '
@ -1148,7 +1149,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
var user [8]byte
var name [8]byte
for i := 0; i < 8; i++ {
for i := range 8 {
user[i] = byte(pp.User_id[i])
name[i] = byte(pp.Name[i])
}
@ -1173,11 +1174,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
Ifindex: int(pp.Ifindex),
}
name := (*[8]byte)(unsafe.Pointer(&sa.Name))
for i := 0; i < 8; i++ {
for i := range 8 {
name[i] = pp.Addr[i]
}
pgn := (*[4]byte)(unsafe.Pointer(&sa.PGN))
for i := 0; i < 4; i++ {
for i := range 4 {
pgn[i] = pp.Addr[i+8]
}
addr := (*[1]byte)(unsafe.Pointer(&sa.Addr))
@ -1188,11 +1189,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
Ifindex: int(pp.Ifindex),
}
rx := (*[4]byte)(unsafe.Pointer(&sa.RxID))
for i := 0; i < 4; i++ {
for i := range 4 {
rx[i] = pp.Addr[i]
}
tx := (*[4]byte)(unsafe.Pointer(&sa.TxID))
for i := 0; i < 4; i++ {
for i := range 4 {
tx[i] = pp.Addr[i+4]
}
return sa, nil
@ -2216,10 +2217,7 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
m := min(int(iovecs[i].Len), n)
n -= m
if m > 0 {
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
@ -2270,10 +2268,7 @@ func writevRacedetect(iovecs []Iovec, n int) {
return
}
for i := 0; n > 0 && i < len(iovecs); i++ {
m := int(iovecs[i].Len)
if m > n {
m = n
}
m := min(int(iovecs[i].Len), n)
n -= m
if m > 0 {
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
@ -2320,12 +2315,7 @@ func isGroupMember(gid int) bool {
return false
}
for _, g := range groups {
if g == gid {
return true
}
}
return false
return slices.Contains(groups, gid)
}
func isCapDacOverrideSet() bool {

View File

@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readv(fd int, iovecs []Iovec) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_readv_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_preadv_trampoline_addr uintptr
//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func writev(fd int, iovecs []Iovec) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_writev_trampoline_addr uintptr
//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_pwritev_trampoline_addr uintptr
//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstat(fd int, stat *Stat_t) (err error) {
_, _, e1 := syscall_syscall(libc_fstat64_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {

View File

@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0
GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8
DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB)
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readv(SB)
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_preadv(SB)
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_writev(SB)
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_pwritev(SB)
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
TEXT libc_fstat64_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fstat64(SB)
GLOBL ·libc_fstat64_trampoline_addr(SB), RODATA, $8

View File

@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func readv(fd int, iovecs []Iovec) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_readv_trampoline_addr uintptr
//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_preadv_trampoline_addr uintptr
//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func writev(fd int, iovecs []Iovec) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_writev_trampoline_addr uintptr
//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
var _p0 unsafe.Pointer
if len(iovecs) > 0 {
_p0 = unsafe.Pointer(&iovecs[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
n = int(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
var libc_pwritev_trampoline_addr uintptr
//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstat(fd int, stat *Stat_t) (err error) {
_, _, e1 := syscall_syscall(libc_fstat_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
if e1 != 0 {

View File

@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0
GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8
DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB)
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_readv(SB)
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_preadv(SB)
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_writev(SB)
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_pwritev(SB)
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
TEXT libc_fstat_trampoline<>(SB),NOSPLIT,$0-0
JMP libc_fstat(SB)
GLOBL ·libc_fstat_trampoline_addr(SB), RODATA, $8

View File

@ -1074,6 +1074,7 @@ const (
IP_ADD_MEMBERSHIP = 0xc
IP_DROP_MEMBERSHIP = 0xd
IP_PKTINFO = 0x13
IP_MTU_DISCOVER = 0x47
IPV6_V6ONLY = 0x1b
IPV6_UNICAST_HOPS = 0x4
@ -1083,6 +1084,7 @@ const (
IPV6_JOIN_GROUP = 0xc
IPV6_LEAVE_GROUP = 0xd
IPV6_PKTINFO = 0x13
IPV6_MTU_DISCOVER = 0x47
MSG_OOB = 0x1
MSG_PEEK = 0x2
@ -1132,6 +1134,15 @@ const (
WSASYS_STATUS_LEN = 128
)
// enum PMTUD_STATE from ws2ipdef.h
const (
IP_PMTUDISC_NOT_SET = 0
IP_PMTUDISC_DO = 1
IP_PMTUDISC_DONT = 2
IP_PMTUDISC_PROBE = 3
IP_PMTUDISC_MAX = 4
)
type WSABuf struct {
Len uint32
Buf *byte
@ -1146,6 +1157,22 @@ type WSAMsg struct {
Flags uint32
}
type WSACMSGHDR struct {
Len uintptr
Level int32
Type int32
}
type IN_PKTINFO struct {
Addr [4]byte
Ifindex uint32
}
type IN6_PKTINFO struct {
Addr [16]byte
Ifindex uint32
}
// Flags for WSASocket
const (
WSA_FLAG_OVERLAPPED = 0x01

View File

@ -44,6 +44,8 @@ type Terminal struct {
// bytes, as an index into |line|). If it returns ok=false, the key
// press is processed normally. Otherwise it returns a replacement line
// and the new cursor position.
//
// This will be disabled during ReadPassword.
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
// Escape contains a pointer to the escape codes for this terminal.
@ -692,6 +694,8 @@ func (t *Terminal) Write(buf []byte) (n int, err error) {
// ReadPassword temporarily changes the prompt and reads a password, without
// echo, from the terminal.
//
// The AutoCompleteCallback is disabled during this call.
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
t.lock.Lock()
defer t.lock.Unlock()
@ -699,6 +703,11 @@ func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
oldPrompt := t.prompt
t.prompt = []rune(prompt)
t.echo = false
oldAutoCompleteCallback := t.AutoCompleteCallback
t.AutoCompleteCallback = nil
defer func() {
t.AutoCompleteCallback = oldAutoCompleteCallback
}()
line, err = t.readLine()

20
vendor/modules.txt vendored
View File

@ -120,7 +120,7 @@ github.com/integrii/flaggy
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
## explicit
github.com/jbenet/go-context/io
# github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
# github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918
## explicit; go 1.18
github.com/jesseduffield/generics/maps
github.com/jesseduffield/generics/set
@ -168,7 +168,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
# github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac
# github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647
## explicit; go 1.12
github.com/jesseduffield/gocui
# github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
@ -307,20 +307,20 @@ golang.org/x/exp/slices
golang.org/x/net/context
golang.org/x/net/internal/socks
golang.org/x/net/proxy
# golang.org/x/sync v0.11.0
## explicit; go 1.18
# golang.org/x/sync v0.13.0
## explicit; go 1.23.0
golang.org/x/sync/errgroup
# golang.org/x/sys v0.30.0
## explicit; go 1.18
# golang.org/x/sys v0.32.0
## explicit; go 1.23.0
golang.org/x/sys/cpu
golang.org/x/sys/plan9
golang.org/x/sys/unix
golang.org/x/sys/windows
# golang.org/x/term v0.29.0
## explicit; go 1.18
# golang.org/x/term v0.31.0
## explicit; go 1.23.0
golang.org/x/term
# golang.org/x/text v0.22.0
## explicit; go 1.18
# golang.org/x/text v0.24.0
## explicit; go 1.23.0
golang.org/x/text/encoding
golang.org/x/text/encoding/internal/identifier
golang.org/x/text/runes