1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-20 05:19:24 +02:00

stream output from certain git commands in command log panel

This commit is contained in:
Jesse Duffield 2021-10-24 10:43:48 +11:00
parent 01d82749b1
commit f704707d29
33 changed files with 873 additions and 214 deletions

4
go.mod
View File

@ -22,7 +22,7 @@ require (
github.com/integrii/flaggy v1.4.0
github.com/iriri/minimal/gitignore v0.3.2 // indirect
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
@ -43,7 +43,7 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0

4
go.sum
View File

@ -84,6 +84,8 @@ github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77 h1:MQUxSxVBT
github.com/jesseduffield/gocui v0.3.1-0.20211017091015-8bf4a4666b77/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f h1:JHrb78pj+gYC3KiJKL1WW6lYzlatBIF46oREn68plTM=
github.com/jesseduffield/gocui v0.3.1-0.20211017220056-b2fc03c74a6f/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0 h1:To4mMbu6oQpbbyHa4WtMTc/DHa9dqiRWZpDLMNK+Hdk=
github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/minimal v0.0.0-20211018110810-9cde264e6b1e h1:WZc73tBVMMhcO6zXyZBItLEF4jgBpBH0lFCZzDgrjDg=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
@ -195,6 +197,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk=
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 h1:SeSEfdIxyvwGJliREIJhRPPXvW6sDlLT+UQ3B0hD0NA=
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

View File

@ -127,7 +127,13 @@ func NewApp(config config.AppConfigurer, filterPath string) (*App, error) {
return app, err
}
app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr, app.Config, git_config.NewStdCachedGitConfig(app.Log))
app.GitCommand, err = commands.NewGitCommand(
app.Log,
app.OSCommand,
app.Tr,
app.Config,
git_config.NewStdCachedGitConfig(app.Log),
)
if err != nil {
return app, err
}

View File

@ -1,6 +1,9 @@
package commands
import (
"io"
"io/ioutil"
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/config"
@ -17,10 +20,11 @@ func NewDummyGitCommand() *GitCommand {
func NewDummyGitCommandWithOSCommand(osCommand *oscommands.OSCommand) *GitCommand {
newAppConfig := config.NewDummyAppConfig()
return &GitCommand{
Log: utils.NewDummyLog(),
OSCommand: osCommand,
Tr: i18n.NewTranslationSet(utils.NewDummyLog(), newAppConfig.GetUserConfig().Gui.Language),
Config: newAppConfig,
GitConfig: git_config.NewFakeGitConfig(map[string]string{}),
Log: utils.NewDummyLog(),
OSCommand: osCommand,
Tr: i18n.NewTranslationSet(utils.NewDummyLog(), newAppConfig.GetUserConfig().Gui.Language),
Config: newAppConfig,
GitConfig: git_config.NewFakeGitConfig(map[string]string{}),
GetCmdWriter: func() io.Writer { return ioutil.Discard },
}
}

View File

@ -1,6 +1,7 @@
package commands
import (
"io"
"io/ioutil"
"os"
"path/filepath"
@ -40,6 +41,11 @@ type GitCommand struct {
// Push to current determines whether the user has configured to push to the remote branch of the same name as the current or not
PushToCurrent bool
// this is just a view that we write to when running certain commands.
// Coincidentally at the moment it's the same view that OnRunCommand logs to
// but that need not always be the case.
GetCmdWriter func() io.Writer
}
// NewGitCommand it runs git commands
@ -77,6 +83,7 @@ func NewGitCommand(
DotGitDir: dotGitDir,
PushToCurrent: pushToCurrent,
GitConfig: gitConfig,
GetCmdWriter: func() io.Writer { return ioutil.Discard },
}
gitCommand.PatchManager = patch.NewPatchManager(log, gitCommand.ApplyPatch, gitCommand.ShowFileDiff)

View File

@ -0,0 +1,153 @@
package oscommands
import (
"bufio"
"bytes"
"io"
"os/exec"
"regexp"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// DetectUnamePass detect a username / password / passphrase question in a command
// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
func (c *OSCommand) DetectUnamePass(cmdObj ICmdObj, writer io.Writer, promptUserForCredential func(string) string) error {
ttyText := ""
errMessage := c.RunCommandWithOutputLive(cmdObj, writer, func(word string) string {
ttyText = ttyText + " " + word
prompts := map[string]string{
`.+'s password:`: "password",
`Password\s*for\s*'.+':`: "password",
`Username\s*for\s*'.+':`: "username",
`Enter\s*passphrase\s*for\s*key\s*'.+':`: "passphrase",
}
for pattern, askFor := range prompts {
if match, _ := regexp.MatchString(pattern, ttyText); match {
ttyText = ""
return promptUserForCredential(askFor)
}
}
return ""
})
return errMessage
}
// Due to a lack of pty support on windows we have RunCommandWithOutputLiveWrapper being defined
// separate for windows and other OS's
func (c *OSCommand) RunCommandWithOutputLive(cmdObj ICmdObj, writer io.Writer, handleOutput func(string) string) error {
return RunCommandWithOutputLiveWrapper(c, cmdObj, writer, handleOutput)
}
type cmdHandler struct {
stdoutPipe io.Reader
stdinPipe io.Writer
close func() error
}
// RunCommandWithOutputLiveAux runs a command and return every word that gets written in stdout
// Output is a function that executes by every word that gets read by bufio
// As return of output you need to give a string that will be written to stdin
// NOTE: If the return data is empty it won't write anything to stdin
func RunCommandWithOutputLiveAux(
c *OSCommand,
cmdObj ICmdObj,
writer io.Writer,
// handleOutput takes a word from stdout and returns a string to be written to stdin.
// See DetectUnamePass above for how this is used to check for a username/password request
handleOutput func(string) string,
startCmd func(cmd *exec.Cmd) (*cmdHandler, error),
) error {
c.Log.WithField("command", cmdObj.ToString()).Info("RunCommand")
c.LogCommand(cmdObj.ToString(), true)
cmd := cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8").GetCmd()
var stderr bytes.Buffer
cmd.Stderr = io.MultiWriter(writer, &stderr)
handler, err := startCmd(cmd)
if err != nil {
return err
}
defer func() {
if closeErr := handler.close(); closeErr != nil {
c.Log.Error(closeErr)
}
}()
tr := io.TeeReader(handler.stdoutPipe, writer)
go utils.Safe(func() {
scanner := bufio.NewScanner(tr)
scanner.Split(scanWordsWithNewLines)
for scanner.Scan() {
text := scanner.Text()
output := strings.Trim(text, " ")
toInput := handleOutput(output)
if toInput != "" {
_, _ = handler.stdinPipe.Write([]byte(toInput))
}
}
})
err = cmd.Wait()
if err != nil {
return errors.New(stderr.String())
}
return nil
}
// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
// For specific comments about this function take a look at: bufio.ScanWords
func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if isSpace(r) {
return i + width, data[start:i], nil
}
}
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
return start, nil, nil
}
// isSpace is also copied from the bufio package and has been modified to also captures new lines
// For specific comments about this function take a look at: bufio.isSpace
func isSpace(r rune) bool {
if r <= '\u00FF' {
switch r {
case ' ', '\t', '\v', '\f':
return true
case '\u0085', '\u00A0':
return true
}
return false
}
if '\u2000' <= r && r <= '\u200a' {
return true
}
switch r {
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
return true
}
return false
}

View File

@ -4,95 +4,34 @@
package oscommands
import (
"bufio"
"bytes"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/utils"
"io"
"os/exec"
"github.com/creack/pty"
)
// RunCommandWithOutputLiveWrapper runs a command and return every word that gets written in stdout
// Output is a function that executes by every word that gets read by bufio
// As return of output you need to give a string that will be written to stdin
// NOTE: If the return data is empty it won't written anything to stdin
func RunCommandWithOutputLiveWrapper(c *OSCommand, cmdObj ICmdObj, output func(string) string) error {
c.Log.WithField("command", cmdObj.ToString()).Info("RunCommand")
c.LogCommand(cmdObj.ToString(), true)
cmd := cmdObj.AddEnvVars("LANG=en_US.UTF-8", "LC_ALL=en_US.UTF-8").GetCmd()
func RunCommandWithOutputLiveWrapper(
c *OSCommand,
cmdObj ICmdObj,
writer io.Writer,
output func(string) string,
) error {
return RunCommandWithOutputLiveAux(
c,
cmdObj,
writer,
output,
func(cmd *exec.Cmd) (*cmdHandler, error) {
ptmx, err := pty.Start(cmd)
if err != nil {
return nil, err
}
var stderr bytes.Buffer
cmd.Stderr = &stderr
ptmx, err := pty.Start(cmd)
if err != nil {
return err
}
go utils.Safe(func() {
scanner := bufio.NewScanner(ptmx)
scanner.Split(scanWordsWithNewLines)
for scanner.Scan() {
toOutput := strings.Trim(scanner.Text(), " ")
_, _ = ptmx.WriteString(output(toOutput))
}
})
err = cmd.Wait()
ptmx.Close()
if err != nil {
return errors.New(stderr.String())
}
return nil
}
// scanWordsWithNewLines is a copy of bufio.ScanWords but this also captures new lines
// For specific comments about this function take a look at: bufio.ScanWords
func scanWordsWithNewLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
if isSpace(r) {
return i + width, data[start:i], nil
}
}
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
return start, nil, nil
}
// isSpace is also copied from the bufio package and has been modified to also captures new lines
// For specific comments about this function take a look at: bufio.isSpace
func isSpace(r rune) bool {
if r <= '\u00FF' {
switch r {
case ' ', '\t', '\v', '\f':
return true
case '\u0085', '\u00A0':
return true
}
return false
}
if '\u2000' <= r && r <= '\u200a' {
return true
}
switch r {
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
return true
}
return false
return &cmdHandler{
stdoutPipe: ptmx,
stdinPipe: ptmx,
close: ptmx.Close,
}, nil
},
)
}

View File

@ -3,8 +3,61 @@
package oscommands
// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there
// TODO: Remove this hack and replace it with a proper way to run commands live on windows
func RunCommandWithOutputLiveWrapper(c *OSCommand, cmdObj ICmdObj, output func(string) string) error {
return c.RunCommand(cmdObj.ToString())
import (
"bytes"
"io"
"os/exec"
"sync"
)
type Buffer struct {
b bytes.Buffer
m sync.Mutex
}
func (b *Buffer) Read(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Read(p)
}
func (b *Buffer) Write(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Write(p)
}
// RunCommandWithOutputLiveWrapper runs a command live but because of windows compatibility this command can't be ran there
// TODO: Remove this hack and replace it with a proper way to run commands live on windows. We still have an issue where if a password is requested, the request for a password is written straight to stdout because we can't control the stdout of a subprocess of a subprocess. Keep an eye on https://github.com/creack/pty/pull/109
func RunCommandWithOutputLiveWrapper(
c *OSCommand,
cmdObj ICmdObj,
writer io.Writer,
output func(string) string,
) error {
return RunCommandWithOutputLiveAux(
c,
cmdObj,
writer,
output,
func(cmd *exec.Cmd) (*cmdHandler, error) {
stdoutReader, stdoutWriter := io.Pipe()
cmd.Stdout = stdoutWriter
buf := &Buffer{}
cmd.Stdin = buf
if err := cmd.Start(); err != nil {
return nil, err
}
// because we don't yet have windows support for a pty, we instead just
// pass our standard stream handlers and because there's no pty to close
// we pass a no-op function for that.
return &cmdHandler{
stdoutPipe: stdoutReader,
stdinPipe: buf,
close: func() error { return nil },
}, nil
},
)
}

View File

@ -7,7 +7,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"sync"
@ -217,38 +216,6 @@ func (c *OSCommand) ShellCommandFromString(commandStr string) *exec.Cmd {
return c.ExecutableFromString(shellCommand)
}
// RunCommandWithOutputLive runs RunCommandWithOutputLiveWrapper
func (c *OSCommand) RunCommandWithOutputLive(cmdObj ICmdObj, output func(string) string) error {
return RunCommandWithOutputLiveWrapper(c, cmdObj, output)
}
// DetectUnamePass detect a username / password / passphrase question in a command
// promptUserForCredential is a function that gets executed when this function detect you need to fillin a password or passphrase
// The promptUserForCredential argument will be "username", "password" or "passphrase" and expects the user's password/passphrase or username back
func (c *OSCommand) DetectUnamePass(cmdObj ICmdObj, promptUserForCredential func(string) string) error {
ttyText := ""
errMessage := c.RunCommandWithOutputLive(cmdObj, func(word string) string {
ttyText = ttyText + " " + word
prompts := map[string]string{
`.+'s password:`: "password",
`Password\s*for\s*'.+':`: "password",
`Username\s*for\s*'.+':`: "username",
`Enter\s*passphrase\s*for\s*key\s*'.+':`: "passphrase",
}
for pattern, askFor := range prompts {
if match, _ := regexp.MatchString(pattern, ttyText); match {
ttyText = ""
return promptUserForCredential(askFor)
}
}
return ""
})
return errMessage
}
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(formatString string, formatArgs ...interface{}) error {
_, err := c.RunCommandWithOutput(formatString, formatArgs...)

View File

@ -2,6 +2,8 @@ package commands
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
func (c *GitCommand) AddRemote(name string, url string) error {
@ -23,7 +25,11 @@ func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error
func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, promptUserForCredential func(string) string) error {
command := fmt.Sprintf("git push %s --delete %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(command)
return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
func (c *GitCommand) DetectUnamePass(cmdObj oscommands.ICmdObj, promptUserForCredential func(string) string) error {
return c.OSCommand.DetectUnamePass(cmdObj, c.GetCmdWriter(), promptUserForCredential)
}
// CheckRemoteBranchExists Returns remote branch

View File

@ -38,7 +38,7 @@ func (c *GitCommand) Push(opts PushOpts) error {
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
return c.OSCommand.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
type FetchOptions struct {
@ -59,7 +59,7 @@ func (c *GitCommand) Fetch(opts FetchOptions) error {
}
cmdObj := c.NewCmdObjFromStr(cmdStr)
return c.OSCommand.DetectUnamePass(cmdObj, func(question string) string {
return c.DetectUnamePass(cmdObj, func(question string) string {
if opts.PromptUserForCredential != nil {
return opts.PromptUserForCredential(question)
}
@ -95,17 +95,17 @@ func (c *GitCommand) Pull(opts PullOptions) error {
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
cmdObj := c.NewCmdObjFromStr(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:")
return c.OSCommand.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
return c.DetectUnamePass(cmdObj, opts.PromptUserForCredential)
}
func (c *GitCommand) FastForward(branchName string, remoteName string, remoteBranchName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s %s:%s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(remoteBranchName), c.OSCommand.Quote(branchName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}
func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git fetch %s", c.OSCommand.Quote(remoteName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}

View File

@ -15,5 +15,5 @@ func (c *GitCommand) DeleteTag(tagName string) error {
func (c *GitCommand) PushTag(remoteName string, tagName string, promptUserForCredential func(string) string) error {
cmdStr := fmt.Sprintf("git push %s %s", c.OSCommand.Quote(remoteName), c.OSCommand.Quote(tagName))
cmdObj := c.NewCmdObjFromStr(cmdStr)
return c.OSCommand.DetectUnamePass(cmdObj, promptUserForCredential)
return c.DetectUnamePass(cmdObj, promptUserForCredential)
}

View File

@ -22,9 +22,9 @@ func (gui *Gui) handleCommitConfirm() error {
cmdStr := gui.GitCommand.CommitCmdStr(message, flags)
gui.OnRunCommand(oscommands.NewCmdLogEntry(cmdStr, gui.Tr.Spans.Commit, true))
_ = gui.returnFromContext()
return gui.withGpgHandling(cmdStr, gui.Tr.CommittingStatus, func() error {
gui.Views.CommitMessage.ClearTextArea()
_ = gui.returnFromContext()
return nil
})
}

View File

@ -1,5 +1,11 @@
package gui
import (
"io"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) handleCreateExtrasMenuPanel() error {
menuItems := []*menuItem{
{
@ -48,3 +54,28 @@ func (gui *Gui) scrollDownExtra() error {
return nil
}
func (gui *Gui) getCmdWriter() io.Writer {
return &prefixWriter{writer: gui.Views.Extras, prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.Tr.GitOutput)}
}
// Ensures that the first write is preceded by writing a prefix.
// This allows us to say 'Git output:' before writing the actual git output.
// We could just write directly to the view in this package before running the command but we already have code in the commands package that writes to the same view beforehand (with the command it's about to run) so things would be out of order.
type prefixWriter struct {
prefix string
prefixWritten bool
writer io.Writer
}
func (self *prefixWriter) Write(p []byte) (n int, err error) {
if !self.prefixWritten {
self.prefixWritten = true
// assuming we can write this prefix in one go
_, err = self.writer.Write([]byte(self.prefix))
if err != nil {
return
}
}
return self.writer.Write(p)
}

View File

@ -1,5 +1,11 @@
package gui
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
// Currently there is a bug where if we switch to a subprocess from within
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
// fix this bug, or just stop running subprocesses from within there, given that
@ -19,23 +25,38 @@ func (gui *Gui) withGpgHandling(cmdStr string, waitingStatus string, onSuccess f
return err
}
if err != nil {
return err
}
return err
} else {
return gui.WithWaitingStatus(waitingStatus, func() error {
err := gui.OSCommand.RunCommand(cmdStr)
if err != nil {
return err
} else if onSuccess != nil {
if err := onSuccess(); err != nil {
return err
}
}
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
})
return gui.RunAndStream(cmdStr, waitingStatus, onSuccess)
}
return nil
}
func (gui *Gui) RunAndStream(cmdStr string, waitingStatus string, onSuccess func() error) error {
return gui.WithWaitingStatus(waitingStatus, func() error {
cmd := gui.OSCommand.ShellCommandFromString(cmdStr)
cmd.Env = append(cmd.Env, "TERM=dumb")
cmdWriter := gui.getCmdWriter()
cmd.Stdout = cmdWriter
cmd.Stderr = cmdWriter
if err := cmd.Run(); err != nil {
if _, err := cmd.Stdout.Write([]byte(fmt.Sprintf("%s\n", style.FgRed.Sprint(err.Error())))); err != nil {
gui.Log.Error(err)
}
_ = gui.refreshSidePanels(refreshOptions{mode: ASYNC})
return gui.surfaceError(
fmt.Errorf(
gui.Tr.GitCommandFailed, gui.Config.GetUserConfig().Keybinding.Universal.ExtrasMenu,
),
)
}
if onSuccess != nil {
if err := onSuccess(); err != nil {
return err
}
}
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
})
}

View File

@ -123,12 +123,15 @@ func (gui *Gui) createAllViews() error {
gui.Views.Extras.FgColor = theme.GocuiDefaultTextColor
gui.Views.Extras.Autoscroll = true
gui.Views.Extras.Wrap = true
gui.printCommandLogHeader()
if _, err := gui.g.SetCurrentView(gui.defaultSideContext().GetViewName()); err != nil {
return err
}
gui.GitCommand.GetCmdWriter = gui.getCmdWriter
return nil
}

View File

@ -434,6 +434,8 @@ type TranslationSet struct {
NoConfigFileFoundErr string
LcLoadingFileSuggestions string
MustSpecifyOriginError string
GitOutput string
GitCommandFailed string
Spans Spans
}
@ -961,6 +963,8 @@ func englishTranslationSet() TranslationSet {
NoConfigFileFoundErr: "No config file found",
LcLoadingFileSuggestions: "loading file suggestions",
MustSpecifyOriginError: "Must specify a remote if specifying a branch",
GitOutput: "Git output:",
GitCommandFailed: "Git command failed. Check command log for details (open with %s)",
Spans: Spans{
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
CheckoutCommit: "Checkout commit",

View File

@ -29,12 +29,15 @@ type (
fontEffect int
)
type instruction struct {
kind int
param1 int
param2 int
toWrite []rune
}
type instruction interface{ isInstruction() }
type eraseInLineFromCursor struct{}
func (self eraseInLineFromCursor) isInstruction() {}
type noInstruction struct{}
func (self noInstruction) isInstruction() {}
const (
stateNone escapeState = iota
@ -87,7 +90,7 @@ func newEscapeInterpreter(mode OutputMode) *escapeInterpreter {
curFgColor: ColorDefault,
curBgColor: ColorDefault,
mode: mode,
instruction: instruction{kind: NONE},
instruction: noInstruction{},
}
return ei
}
@ -101,7 +104,7 @@ func (ei *escapeInterpreter) reset() {
}
func (ei *escapeInterpreter) instructionRead() {
ei.instruction.kind = NONE
ei.instruction = noInstruction{}
}
// parseOne parses a rune. If isEscape is true, it means that the rune is part
@ -137,6 +140,8 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
ei.csiParam = append(ei.csiParam, "")
case ch == 'm':
ei.csiParam = append(ei.csiParam, "0")
case ch == 'K':
// fall through
default:
return false, errCSIParseError
}
@ -168,12 +173,20 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
ei.csiParam = nil
return true, nil
case ch == 'K':
p, err := strconv.Atoi(ei.csiParam[0])
if err != nil {
return false, errCSIParseError
p := 0
if len(ei.csiParam) != 0 && ei.csiParam[0] != "" {
p, err = strconv.Atoi(ei.csiParam[0])
if err != nil {
return false, errCSIParseError
}
}
if p == 0 {
ei.instruction = eraseInLineFromCursor{}
} else {
// non-zero values of P not supported
ei.instruction = noInstruction{}
}
ei.instruction.kind = ERASE_IN_LINE
ei.instruction.param1 = p
ei.state = stateNone
ei.csiParam = nil

View File

@ -1041,7 +1041,6 @@ func (g *Gui) draw(v *View) error {
Screen.HideCursor()
}
v.clearRunes()
if err := v.draw(); err != nil {
return err
}

View File

@ -359,7 +359,32 @@ func (v *View) Dimensions() (int, int, int, int) {
// Size returns the number of visible columns and rows in the View.
func (v *View) Size() (x, y int) {
return v.x1 - v.x0 - 1, v.y1 - v.y0 - 1
return v.Width(), v.Height()
}
func (v *View) Width() int {
return v.x1 - v.x0 - 1
}
func (v *View) Height() int {
return v.y1 - v.y0 - 1
}
// if a view has a frame, that leaves less space for its writeable area
func (v *View) InnerWidth() int {
return v.Width() - v.frameOffset()
}
func (v *View) InnerHeight() int {
return v.Height() - v.frameOffset()
}
func (v *View) frameOffset() int {
if v.Frame {
return 1
} else {
return 0
}
}
// Name returns the name of the view.
@ -576,12 +601,14 @@ func (v *View) writeRunes(p []rune) {
case '\r':
v.wx = 0
default:
cells := v.parseInput(r)
moveCursor, cells := v.parseInput(r)
if cells == nil {
continue
}
v.writeCells(v.wx, v.wy, cells)
v.wx += len(cells)
if moveCursor {
v.wx += len(cells)
}
}
}
}
@ -589,8 +616,9 @@ func (v *View) writeRunes(p []rune) {
// parseInput parses char by char the input written to the View. It returns nil
// while processing ESC sequences. Otherwise, it returns a cell slice that
// contains the processed data.
func (v *View) parseInput(ch rune) []cell {
func (v *View) parseInput(ch rune) (bool, []cell) {
cells := []cell{}
moveCursor := true
isEscape, err := v.ei.parseOne(ch)
if err != nil {
@ -604,25 +632,32 @@ func (v *View) parseInput(ch rune) []cell {
}
v.ei.reset()
} else {
if isEscape {
return nil
}
repeatCount := 1
if ch == '\t' {
if _, ok := v.ei.instruction.(eraseInLineFromCursor); ok {
// fill rest of line
v.ei.instructionRead()
repeatCount = v.InnerWidth() - v.wx
ch = ' '
moveCursor = false
} else if isEscape {
// do not output anything
return moveCursor, nil
} else if ch == '\t' {
// fill tab-sized space
ch = ' '
repeatCount = 4
}
c := cell{
fgColor: v.ei.curFgColor,
bgColor: v.ei.curBgColor,
chr: ch,
}
for i := 0; i < repeatCount; i++ {
c := cell{
fgColor: v.ei.curFgColor,
bgColor: v.ei.curBgColor,
chr: ch,
}
cells = append(cells, c)
}
}
return cells
return moveCursor, cells
}
// Read reads data into p from the current reading position set by SetReadPos.
@ -767,6 +802,8 @@ func (v *View) IsTainted() bool {
// draw re-draws the view's contents.
func (v *View) draw() error {
v.clearRunes()
if !v.Visible {
return nil
}
@ -809,8 +846,9 @@ func (v *View) draw() error {
}
}
if v.Autoscroll && len(v.viewLines) > maxY {
v.oy = len(v.viewLines) - maxY
visibleViewLinesHeight := v.viewLineLengthIgnoringTrailingBlankLines()
if v.Autoscroll && visibleViewLinesHeight > maxY {
v.oy = visibleViewLinesHeight - maxY
}
y := 0
for i, vline := range v.viewLines {
@ -858,6 +896,18 @@ func (v *View) draw() error {
return nil
}
// if autoscroll is enabled but we only have a single row of cells shown to the
// user, we don't want to scroll to the final line if it contains no text. So
// this tells us the view lines height when we ignore any trailing blank lines
func (v *View) viewLineLengthIgnoringTrailingBlankLines() int {
for i := len(v.viewLines) - 1; i >= 0; i-- {
if len(v.viewLines[i].line) > 0 {
return i + 1
}
}
return 0
}
func (v *View) isPatternMatchedRune(x, y int) (bool, bool) {
searchStringLength := len(v.searcher.searchString)
for i, pos := range v.searcher.searchPositions {
@ -1008,23 +1058,6 @@ func indexFunc(r rune) bool {
return r == ' ' || r == 0
}
// SetLine changes the contents of an existing line.
func (v *View) SetLine(y int, text string) error {
if y < 0 || y >= len(v.lines) {
err := ErrInvalidPoint
return err
}
v.tainted = true
line := make([]cell, 0)
for _, r := range text {
c := v.parseInput(r)
line = append(line, c...)
}
v.lines[y] = line
return nil
}
// SetHighlight toggles highlighting of separate lines, for custom lists
// or multiple selection in views.
func (v *View) SetHighlight(y int, on bool) error {
@ -1129,14 +1162,10 @@ func (v *View) RenderTextArea() {
fmt.Fprint(v, v.TextArea.GetContent())
cursorX, cursorY := v.TextArea.GetCursorXY()
prevOriginX, prevOriginY := v.Origin()
width, height := v.Size()
width, height := v.InnerWidth(), v.InnerHeight()
frameAdjustment := 0
if v.Frame {
frameAdjustment = -1
}
newViewCursorX, newOriginX := updatedCursorAndOrigin(prevOriginX, width+frameAdjustment, cursorX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(prevOriginY, height+frameAdjustment, cursorY)
newViewCursorX, newOriginX := updatedCursorAndOrigin(prevOriginX, width, cursorX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(prevOriginY, height, cursorY)
_ = v.SetCursor(newViewCursorX, newViewCursorY)
_ = v.SetOrigin(newOriginX, newOriginY)

View File

@ -34,3 +34,25 @@ func ParseUnixCredentials(m *SocketControlMessage) (*Ucred, error) {
ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0]))
return &ucred, nil
}
// PktInfo4 encodes Inet4Pktinfo into a socket control message of type IP_PKTINFO.
func PktInfo4(info *Inet4Pktinfo) []byte {
b := make([]byte, CmsgSpace(SizeofInet4Pktinfo))
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = SOL_IP
h.Type = IP_PKTINFO
h.SetLen(CmsgLen(SizeofInet4Pktinfo))
*(*Inet4Pktinfo)(h.data(0)) = *info
return b
}
// PktInfo6 encodes Inet6Pktinfo into a socket control message of type IPV6_PKTINFO.
func PktInfo6(info *Inet6Pktinfo) []byte {
b := make([]byte, CmsgSpace(SizeofInet6Pktinfo))
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
h.Level = SOL_IPV6
h.Type = IPV6_PKTINFO
h.SetLen(CmsgLen(SizeofInet6Pktinfo))
*(*Inet6Pktinfo)(h.data(0)) = *info
return b
}

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
// +build go1.9
//go:build windows && go1.9
// +build windows,go1.9
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
// +build windows
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build generate
// +build generate
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows && race
// +build windows,race
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows && !race
// +build windows,!race
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
// +build windows
package windows
@ -235,3 +236,4 @@ type QUERY_SERVICE_LOCK_STATUS struct {
//sys NotifyServiceStatusChange(service Handle, notifyMask uint32, notifier *SERVICE_NOTIFY) (ret error) = advapi32.NotifyServiceStatusChangeW
//sys SubscribeServiceChangeNotifications(service Handle, eventType uint32, callback uintptr, callbackCtx uintptr, subscription *uintptr) (ret error) = sechost.SubscribeServiceChangeNotifications?
//sys UnsubscribeServiceChangeNotifications(subscription uintptr) = sechost.UnsubscribeServiceChangeNotifications?
//sys RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) = advapi32.RegisterServiceCtrlHandlerExW

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
// +build windows
package windows

View File

@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build windows
// +build windows
// Package windows contains an interface to the low-level operating system

View File

@ -401,6 +401,11 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys LoadResource(module Handle, resInfo Handle) (resData Handle, err error) = kernel32.LoadResource
//sys LockResource(resData Handle) (addr uintptr, err error) = kernel32.LockResource
// Version APIs
//sys GetFileVersionInfoSize(filename string, zeroHandle *Handle) (bufSize uint32, err error) = version.GetFileVersionInfoSizeW
//sys GetFileVersionInfo(filename string, handle uint32, bufSize uint32, buffer unsafe.Pointer) (err error) = version.GetFileVersionInfoW
//sys VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) = version.VerQueryValueW
// Process Status API (PSAPI)
//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
//sys EnumProcessModules(process Handle, module *Handle, cb uint32, cbNeeded *uint32) (err error) = psapi.EnumProcessModules
@ -418,11 +423,16 @@ func NewCallbackCDecl(fn interface{}) uintptr {
//sys RtlInitString(destinationString *NTString, sourceString *byte) = ntdll.RtlInitString
//sys NtCreateFile(handle *Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, allocationSize *int64, attributes uint32, share uint32, disposition uint32, options uint32, eabuffer uintptr, ealength uint32) (ntstatus error) = ntdll.NtCreateFile
//sys NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, iosb *IO_STATUS_BLOCK, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (ntstatus error) = ntdll.NtCreateNamedPipeFile
//sys NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtSetInformationFile
//sys RtlDosPathNameToNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFileNamePart *uint16, relativeName *RTL_RELATIVE_NAME) (ntstatus error) = ntdll.RtlDosPathNameToNtPathName_U_WithStatus
//sys RtlDosPathNameToRelativeNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFileNamePart *uint16, relativeName *RTL_RELATIVE_NAME) (ntstatus error) = ntdll.RtlDosPathNameToRelativeNtPathName_U_WithStatus
//sys RtlDefaultNpAcl(acl **ACL) (ntstatus error) = ntdll.RtlDefaultNpAcl
//sys NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32, retLen *uint32) (ntstatus error) = ntdll.NtQueryInformationProcess
//sys NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32) (ntstatus error) = ntdll.NtSetInformationProcess
//sys NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32, retLen *uint32) (ntstatus error) = ntdll.NtQuerySystemInformation
//sys NtSetSystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32) (ntstatus error) = ntdll.NtSetSystemInformation
//sys RtlAddFunctionTable(functionTable *RUNTIME_FUNCTION, entryCount uint32, baseAddress uintptr) (ret bool) = ntdll.RtlAddFunctionTable
//sys RtlDeleteFunctionTable(functionTable *RUNTIME_FUNCTION) (ret bool) = ntdll.RtlDeleteFunctionTable
// syscall interface implementation for other packages

View File

@ -2366,6 +2366,12 @@ type LIST_ENTRY struct {
Blink *LIST_ENTRY
}
type RUNTIME_FUNCTION struct {
BeginAddress uint32
EndAddress uint32
UnwindData uint32
}
type LDR_DATA_TABLE_ENTRY struct {
reserved1 [2]uintptr
InMemoryOrderLinks LIST_ENTRY
@ -2556,6 +2562,60 @@ const (
FILE_PIPE_SERVER_END = 0x00000001
)
const (
// FileInformationClass for NtSetInformationFile
FileBasicInformation = 4
FileRenameInformation = 10
FileDispositionInformation = 13
FilePositionInformation = 14
FileEndOfFileInformation = 20
FileValidDataLengthInformation = 39
FileShortNameInformation = 40
FileIoPriorityHintInformation = 43
FileReplaceCompletionInformation = 61
FileDispositionInformationEx = 64
FileCaseSensitiveInformation = 71
FileLinkInformation = 72
FileCaseSensitiveInformationForceAccessCheck = 75
FileKnownFolderInformation = 76
// Flags for FILE_RENAME_INFORMATION
FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001
FILE_RENAME_POSIX_SEMANTICS = 0x00000002
FILE_RENAME_SUPPRESS_PIN_STATE_INHERITANCE = 0x00000004
FILE_RENAME_SUPPRESS_STORAGE_RESERVE_INHERITANCE = 0x00000008
FILE_RENAME_NO_INCREASE_AVAILABLE_SPACE = 0x00000010
FILE_RENAME_NO_DECREASE_AVAILABLE_SPACE = 0x00000020
FILE_RENAME_PRESERVE_AVAILABLE_SPACE = 0x00000030
FILE_RENAME_IGNORE_READONLY_ATTRIBUTE = 0x00000040
FILE_RENAME_FORCE_RESIZE_TARGET_SR = 0x00000080
FILE_RENAME_FORCE_RESIZE_SOURCE_SR = 0x00000100
FILE_RENAME_FORCE_RESIZE_SR = 0x00000180
// Flags for FILE_DISPOSITION_INFORMATION_EX
FILE_DISPOSITION_DO_NOT_DELETE = 0x00000000
FILE_DISPOSITION_DELETE = 0x00000001
FILE_DISPOSITION_POSIX_SEMANTICS = 0x00000002
FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK = 0x00000004
FILE_DISPOSITION_ON_CLOSE = 0x00000008
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010
// Flags for FILE_CASE_SENSITIVE_INFORMATION
FILE_CS_FLAG_CASE_SENSITIVE_DIR = 0x00000001
// Flags for FILE_LINK_INFORMATION
FILE_LINK_REPLACE_IF_EXISTS = 0x00000001
FILE_LINK_POSIX_SEMANTICS = 0x00000002
FILE_LINK_SUPPRESS_STORAGE_RESERVE_INHERITANCE = 0x00000008
FILE_LINK_NO_INCREASE_AVAILABLE_SPACE = 0x00000010
FILE_LINK_NO_DECREASE_AVAILABLE_SPACE = 0x00000020
FILE_LINK_PRESERVE_AVAILABLE_SPACE = 0x00000030
FILE_LINK_IGNORE_READONLY_ATTRIBUTE = 0x00000040
FILE_LINK_FORCE_RESIZE_TARGET_SR = 0x00000080
FILE_LINK_FORCE_RESIZE_SOURCE_SR = 0x00000100
FILE_LINK_FORCE_RESIZE_SR = 0x00000180
)
// ProcessInformationClasses for NtQueryInformationProcess and NtSetInformationProcess.
const (
ProcessBasicInformation = iota
@ -2672,6 +2732,203 @@ type PROCESS_BASIC_INFORMATION struct {
InheritedFromUniqueProcessId uintptr
}
// SystemInformationClasses for NtQuerySystemInformation and NtSetSystemInformation
const (
SystemBasicInformation = iota
SystemProcessorInformation
SystemPerformanceInformation
SystemTimeOfDayInformation
SystemPathInformation
SystemProcessInformation
SystemCallCountInformation
SystemDeviceInformation
SystemProcessorPerformanceInformation
SystemFlagsInformation
SystemCallTimeInformation
SystemModuleInformation
SystemLocksInformation
SystemStackTraceInformation
SystemPagedPoolInformation
SystemNonPagedPoolInformation
SystemHandleInformation
SystemObjectInformation
SystemPageFileInformation
SystemVdmInstemulInformation
SystemVdmBopInformation
SystemFileCacheInformation
SystemPoolTagInformation
SystemInterruptInformation
SystemDpcBehaviorInformation
SystemFullMemoryInformation
SystemLoadGdiDriverInformation
SystemUnloadGdiDriverInformation
SystemTimeAdjustmentInformation
SystemSummaryMemoryInformation
SystemMirrorMemoryInformation
SystemPerformanceTraceInformation
systemObsolete0
SystemExceptionInformation
SystemCrashDumpStateInformation
SystemKernelDebuggerInformation
SystemContextSwitchInformation
SystemRegistryQuotaInformation
SystemExtendServiceTableInformation
SystemPrioritySeperation
SystemVerifierAddDriverInformation
SystemVerifierRemoveDriverInformation
SystemProcessorIdleInformation
SystemLegacyDriverInformation
SystemCurrentTimeZoneInformation
SystemLookasideInformation
SystemTimeSlipNotification
SystemSessionCreate
SystemSessionDetach
SystemSessionInformation
SystemRangeStartInformation
SystemVerifierInformation
SystemVerifierThunkExtend
SystemSessionProcessInformation
SystemLoadGdiDriverInSystemSpace
SystemNumaProcessorMap
SystemPrefetcherInformation
SystemExtendedProcessInformation
SystemRecommendedSharedDataAlignment
SystemComPlusPackage
SystemNumaAvailableMemory
SystemProcessorPowerInformation
SystemEmulationBasicInformation
SystemEmulationProcessorInformation
SystemExtendedHandleInformation
SystemLostDelayedWriteInformation
SystemBigPoolInformation
SystemSessionPoolTagInformation
SystemSessionMappedViewInformation
SystemHotpatchInformation
SystemObjectSecurityMode
SystemWatchdogTimerHandler
SystemWatchdogTimerInformation
SystemLogicalProcessorInformation
SystemWow64SharedInformationObsolete
SystemRegisterFirmwareTableInformationHandler
SystemFirmwareTableInformation
SystemModuleInformationEx
SystemVerifierTriageInformation
SystemSuperfetchInformation
SystemMemoryListInformation
SystemFileCacheInformationEx
SystemThreadPriorityClientIdInformation
SystemProcessorIdleCycleTimeInformation
SystemVerifierCancellationInformation
SystemProcessorPowerInformationEx
SystemRefTraceInformation
SystemSpecialPoolInformation
SystemProcessIdInformation
SystemErrorPortInformation
SystemBootEnvironmentInformation
SystemHypervisorInformation
SystemVerifierInformationEx
SystemTimeZoneInformation
SystemImageFileExecutionOptionsInformation
SystemCoverageInformation
SystemPrefetchPatchInformation
SystemVerifierFaultsInformation
SystemSystemPartitionInformation
SystemSystemDiskInformation
SystemProcessorPerformanceDistribution
SystemNumaProximityNodeInformation
SystemDynamicTimeZoneInformation
SystemCodeIntegrityInformation
SystemProcessorMicrocodeUpdateInformation
SystemProcessorBrandString
SystemVirtualAddressInformation
SystemLogicalProcessorAndGroupInformation
SystemProcessorCycleTimeInformation
SystemStoreInformation
SystemRegistryAppendString
SystemAitSamplingValue
SystemVhdBootInformation
SystemCpuQuotaInformation
SystemNativeBasicInformation
systemSpare1
SystemLowPriorityIoInformation
SystemTpmBootEntropyInformation
SystemVerifierCountersInformation
SystemPagedPoolInformationEx
SystemSystemPtesInformationEx
SystemNodeDistanceInformation
SystemAcpiAuditInformation
SystemBasicPerformanceInformation
SystemQueryPerformanceCounterInformation
SystemSessionBigPoolInformation
SystemBootGraphicsInformation
SystemScrubPhysicalMemoryInformation
SystemBadPageInformation
SystemProcessorProfileControlArea
SystemCombinePhysicalMemoryInformation
SystemEntropyInterruptTimingCallback
SystemConsoleInformation
SystemPlatformBinaryInformation
SystemThrottleNotificationInformation
SystemHypervisorProcessorCountInformation
SystemDeviceDataInformation
SystemDeviceDataEnumerationInformation
SystemMemoryTopologyInformation
SystemMemoryChannelInformation
SystemBootLogoInformation
SystemProcessorPerformanceInformationEx
systemSpare0
SystemSecureBootPolicyInformation
SystemPageFileInformationEx
SystemSecureBootInformation
SystemEntropyInterruptTimingRawInformation
SystemPortableWorkspaceEfiLauncherInformation
SystemFullProcessInformation
SystemKernelDebuggerInformationEx
SystemBootMetadataInformation
SystemSoftRebootInformation
SystemElamCertificateInformation
SystemOfflineDumpConfigInformation
SystemProcessorFeaturesInformation
SystemRegistryReconciliationInformation
SystemEdidInformation
SystemManufacturingInformation
SystemEnergyEstimationConfigInformation
SystemHypervisorDetailInformation
SystemProcessorCycleStatsInformation
SystemVmGenerationCountInformation
SystemTrustedPlatformModuleInformation
SystemKernelDebuggerFlags
SystemCodeIntegrityPolicyInformation
SystemIsolatedUserModeInformation
SystemHardwareSecurityTestInterfaceResultsInformation
SystemSingleModuleInformation
SystemAllowedCpuSetsInformation
SystemDmaProtectionInformation
SystemInterruptCpuSetsInformation
SystemSecureBootPolicyFullInformation
SystemCodeIntegrityPolicyFullInformation
SystemAffinitizedInterruptProcessorInformation
SystemRootSiloInformation
)
type RTL_PROCESS_MODULE_INFORMATION struct {
Section Handle
MappedBase uintptr
ImageBase uintptr
ImageSize uint32
Flags uint32
LoadOrderIndex uint16
InitOrderIndex uint16
LoadCount uint16
OffsetToFileName uint16
FullPathName [256]byte
}
type RTL_PROCESS_MODULES struct {
NumberOfModules uint32
Modules [1]RTL_PROCESS_MODULE_INFORMATION
}
// Constants for LocalAlloc flags.
const (
LMEM_FIXED = 0x0
@ -2766,6 +3023,22 @@ var (
RT_MANIFEST ResourceID = 24
)
type VS_FIXEDFILEINFO struct {
Signature uint32
StrucVersion uint32
FileVersionMS uint32
FileVersionLS uint32
ProductVersionMS uint32
ProductVersionLS uint32
FileFlagsMask uint32
FileFlags uint32
FileOS uint32
FileType uint32
FileSubtype uint32
FileDateMS uint32
FileDateLS uint32
}
type COAUTHIDENTITY struct {
User *uint16
UserLength uint32

View File

@ -51,6 +51,7 @@ var (
modshell32 = NewLazySystemDLL("shell32.dll")
moduser32 = NewLazySystemDLL("user32.dll")
moduserenv = NewLazySystemDLL("userenv.dll")
modversion = NewLazySystemDLL("version.dll")
modwintrust = NewLazySystemDLL("wintrust.dll")
modws2_32 = NewLazySystemDLL("ws2_32.dll")
modwtsapi32 = NewLazySystemDLL("wtsapi32.dll")
@ -124,6 +125,7 @@ var (
procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW")
procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW")
procRegisterEventSourceW = modadvapi32.NewProc("RegisterEventSourceW")
procRegisterServiceCtrlHandlerExW = modadvapi32.NewProc("RegisterServiceCtrlHandlerExW")
procReportEventW = modadvapi32.NewProc("ReportEventW")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
@ -364,9 +366,14 @@ var (
procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo")
procNtCreateFile = modntdll.NewProc("NtCreateFile")
procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile")
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procNtQueryInformationProcess = modntdll.NewProc("NtQueryInformationProcess")
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
procNtSetInformationProcess = modntdll.NewProc("NtSetInformationProcess")
procNtSetSystemInformation = modntdll.NewProc("NtSetSystemInformation")
procRtlAddFunctionTable = modntdll.NewProc("RtlAddFunctionTable")
procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl")
procRtlDeleteFunctionTable = modntdll.NewProc("RtlDeleteFunctionTable")
procRtlDosPathNameToNtPathName_U_WithStatus = modntdll.NewProc("RtlDosPathNameToNtPathName_U_WithStatus")
procRtlDosPathNameToRelativeNtPathName_U_WithStatus = modntdll.NewProc("RtlDosPathNameToRelativeNtPathName_U_WithStatus")
procRtlGetCurrentPeb = modntdll.NewProc("RtlGetCurrentPeb")
@ -402,6 +409,9 @@ var (
procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock")
procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock")
procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW")
procGetFileVersionInfoSizeW = modversion.NewProc("GetFileVersionInfoSizeW")
procGetFileVersionInfoW = modversion.NewProc("GetFileVersionInfoW")
procVerQueryValueW = modversion.NewProc("VerQueryValueW")
procWinVerifyTrustEx = modwintrust.NewProc("WinVerifyTrustEx")
procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW")
procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW")
@ -1055,6 +1065,15 @@ func RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Hand
return
}
func RegisterServiceCtrlHandlerEx(serviceName *uint16, handlerProc uintptr, context uintptr) (handle Handle, err error) {
r0, _, e1 := syscall.Syscall(procRegisterServiceCtrlHandlerExW.Addr(), 3, uintptr(unsafe.Pointer(serviceName)), uintptr(handlerProc), uintptr(context))
handle = Handle(r0)
if handle == 0 {
err = errnoErr(e1)
}
return
}
func ReportEvent(log Handle, etype uint16, category uint16, eventId uint32, usrSId uintptr, numStrings uint16, dataSize uint32, strings **uint16, rawData *byte) (err error) {
r1, _, e1 := syscall.Syscall9(procReportEventW.Addr(), 9, uintptr(log), uintptr(etype), uintptr(category), uintptr(eventId), uintptr(usrSId), uintptr(numStrings), uintptr(dataSize), uintptr(unsafe.Pointer(strings)), uintptr(unsafe.Pointer(rawData)))
if r1 == 0 {
@ -3152,6 +3171,14 @@ func NtCreateNamedPipeFile(pipe *Handle, access uint32, oa *OBJECT_ATTRIBUTES, i
return
}
func NtSetInformationFile(handle Handle, iosb *IO_STATUS_BLOCK, inBuffer *byte, inBufferLen uint32, class uint32) (ntstatus error) {
r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferLen), uintptr(class), 0)
if r0 != 0 {
ntstatus = NTStatus(r0)
}
return
}
func NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32, retLen *uint32) (ntstatus error) {
r0, _, _ := syscall.Syscall6(procNtQueryInformationProcess.Addr(), 5, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), uintptr(unsafe.Pointer(retLen)), 0)
if r0 != 0 {
@ -3160,6 +3187,14 @@ func NtQueryInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe
return
}
func NtQuerySystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32, retLen *uint32) (ntstatus error) {
r0, _, _ := syscall.Syscall6(procNtQuerySystemInformation.Addr(), 4, uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen), uintptr(unsafe.Pointer(retLen)), 0, 0)
if r0 != 0 {
ntstatus = NTStatus(r0)
}
return
}
func NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.Pointer, procInfoLen uint32) (ntstatus error) {
r0, _, _ := syscall.Syscall6(procNtSetInformationProcess.Addr(), 4, uintptr(proc), uintptr(procInfoClass), uintptr(procInfo), uintptr(procInfoLen), 0, 0)
if r0 != 0 {
@ -3168,6 +3203,20 @@ func NtSetInformationProcess(proc Handle, procInfoClass int32, procInfo unsafe.P
return
}
func NtSetSystemInformation(sysInfoClass int32, sysInfo unsafe.Pointer, sysInfoLen uint32) (ntstatus error) {
r0, _, _ := syscall.Syscall(procNtSetSystemInformation.Addr(), 3, uintptr(sysInfoClass), uintptr(sysInfo), uintptr(sysInfoLen))
if r0 != 0 {
ntstatus = NTStatus(r0)
}
return
}
func RtlAddFunctionTable(functionTable *RUNTIME_FUNCTION, entryCount uint32, baseAddress uintptr) (ret bool) {
r0, _, _ := syscall.Syscall(procRtlAddFunctionTable.Addr(), 3, uintptr(unsafe.Pointer(functionTable)), uintptr(entryCount), uintptr(baseAddress))
ret = r0 != 0
return
}
func RtlDefaultNpAcl(acl **ACL) (ntstatus error) {
r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(acl)), 0, 0)
if r0 != 0 {
@ -3176,6 +3225,12 @@ func RtlDefaultNpAcl(acl **ACL) (ntstatus error) {
return
}
func RtlDeleteFunctionTable(functionTable *RUNTIME_FUNCTION) (ret bool) {
r0, _, _ := syscall.Syscall(procRtlDeleteFunctionTable.Addr(), 1, uintptr(unsafe.Pointer(functionTable)), 0, 0)
ret = r0 != 0
return
}
func RtlDosPathNameToNtPathName(dosName *uint16, ntName *NTUnicodeString, ntFileNamePart *uint16, relativeName *RTL_RELATIVE_NAME) (ntstatus error) {
r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U_WithStatus.Addr(), 4, uintptr(unsafe.Pointer(dosName)), uintptr(unsafe.Pointer(ntName)), uintptr(unsafe.Pointer(ntFileNamePart)), uintptr(unsafe.Pointer(relativeName)), 0, 0)
if r0 != 0 {
@ -3449,6 +3504,58 @@ func GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) {
return
}
func GetFileVersionInfoSize(filename string, zeroHandle *Handle) (bufSize uint32, err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(filename)
if err != nil {
return
}
return _GetFileVersionInfoSize(_p0, zeroHandle)
}
func _GetFileVersionInfoSize(filename *uint16, zeroHandle *Handle) (bufSize uint32, err error) {
r0, _, e1 := syscall.Syscall(procGetFileVersionInfoSizeW.Addr(), 2, uintptr(unsafe.Pointer(filename)), uintptr(unsafe.Pointer(zeroHandle)), 0)
bufSize = uint32(r0)
if bufSize == 0 {
err = errnoErr(e1)
}
return
}
func GetFileVersionInfo(filename string, handle uint32, bufSize uint32, buffer unsafe.Pointer) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(filename)
if err != nil {
return
}
return _GetFileVersionInfo(_p0, handle, bufSize, buffer)
}
func _GetFileVersionInfo(filename *uint16, handle uint32, bufSize uint32, buffer unsafe.Pointer) (err error) {
r1, _, e1 := syscall.Syscall6(procGetFileVersionInfoW.Addr(), 4, uintptr(unsafe.Pointer(filename)), uintptr(handle), uintptr(bufSize), uintptr(buffer), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func VerQueryValue(block unsafe.Pointer, subBlock string, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(subBlock)
if err != nil {
return
}
return _VerQueryValue(block, _p0, pointerToBufferPointer, bufSize)
}
func _VerQueryValue(block unsafe.Pointer, subBlock *uint16, pointerToBufferPointer unsafe.Pointer, bufSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procVerQueryValueW.Addr(), 4, uintptr(block), uintptr(unsafe.Pointer(subBlock)), uintptr(pointerToBufferPointer), uintptr(unsafe.Pointer(bufSize)), 0, 0)
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func WinVerifyTrustEx(hwnd HWND, actionId *GUID, data *WinTrustData) (ret error) {
r0, _, _ := syscall.Syscall(procWinVerifyTrustEx.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(actionId)), uintptr(unsafe.Pointer(data)))
if r0 != 0 {

4
vendor/modules.txt vendored
View File

@ -164,7 +164,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.20211017220056-b2fc03c74a6f
# github.com/jesseduffield/gocui v0.3.1-0.20211024041248-681a61c53ed0
## explicit
github.com/jesseduffield/gocui
# github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
@ -258,7 +258,7 @@ golang.org/x/crypto/ssh/knownhosts
golang.org/x/net/context
golang.org/x/net/internal/socks
golang.org/x/net/proxy
# golang.org/x/sys v0.0.0-20211015200801-69063c4bb744
# golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70
## explicit
golang.org/x/sys/cpu
golang.org/x/sys/internal/unsafeheader