mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Merge 42b54e2c53 into 3f6be3b3ee
This commit is contained in:
commit
db93acb923
|
|
@ -277,6 +277,15 @@ func (c *OSCommand) PipeCommands(cmdObjs ...*CmdObj) error {
|
|||
}
|
||||
|
||||
func (c *OSCommand) CopyToClipboard(str string) error {
|
||||
c.logCopyToClipboard(str)
|
||||
return c.writeToClipboard(str)
|
||||
}
|
||||
|
||||
func (c *OSCommand) CopyToClipboardQuiet(str string) error {
|
||||
return c.writeToClipboard(str)
|
||||
}
|
||||
|
||||
func (c *OSCommand) logCopyToClipboard(str string) {
|
||||
escaped := strings.ReplaceAll(str, "\n", "\\n")
|
||||
truncated := utils.TruncateWithEllipsis(escaped, 40)
|
||||
|
||||
|
|
@ -287,6 +296,9 @@ func (c *OSCommand) CopyToClipboard(str string) error {
|
|||
},
|
||||
)
|
||||
c.LogCommand(msg, false)
|
||||
}
|
||||
|
||||
func (c *OSCommand) writeToClipboard(str string) error {
|
||||
if c.UserConfig().OS.CopyToClipboardCmd != "" {
|
||||
cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{
|
||||
"text": c.Cmd.Quote(str),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,190 @@ func (gui *Gui) LogAction(action string) {
|
|||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) gitOutputBlocksFromView() []string {
|
||||
if gui.Views.Extras == nil {
|
||||
return nil
|
||||
}
|
||||
return gitOutputBlocksFromCommandLogLines(
|
||||
gui.Views.Extras.BufferLines(),
|
||||
gui.c.Tr.GitOutput,
|
||||
gui.isCopyToClipboardLogLine,
|
||||
)
|
||||
}
|
||||
|
||||
func (gui *Gui) lastGitOutput() string {
|
||||
blocks := gui.gitOutputBlocksFromView()
|
||||
if len(blocks) == 0 {
|
||||
return ""
|
||||
}
|
||||
return blocks[len(blocks)-1]
|
||||
}
|
||||
|
||||
func (gui *Gui) allGitOutput() string {
|
||||
return strings.Join(gui.gitOutputBlocksFromView(), "\n\n")
|
||||
}
|
||||
|
||||
func (gui *Gui) hasGitOutput() bool {
|
||||
return gui.lastGitOutput() != ""
|
||||
}
|
||||
|
||||
func (gui *Gui) hasCommandLogEntries() bool {
|
||||
return gui.commandLogContent() != ""
|
||||
}
|
||||
|
||||
func (gui *Gui) commandLogContent() string {
|
||||
if gui.Views.Extras == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
introLine := gui.commandLogIntroLine()
|
||||
var filtered []string
|
||||
for _, line := range gui.Views.Extras.BufferLines() {
|
||||
trimmed := strings.TrimSpace(line)
|
||||
if trimmed == "" {
|
||||
if len(filtered) > 0 {
|
||||
filtered = append(filtered, "")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if trimmed == introLine {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmed, gui.c.Tr.RandomTip+":") {
|
||||
continue
|
||||
}
|
||||
if gui.isCopyToClipboardLogLine(line) {
|
||||
continue
|
||||
}
|
||||
if gui.isCreateFileLogLine(line) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, line)
|
||||
}
|
||||
|
||||
return strings.TrimRight(strings.Join(filtered, "\n"), "\n")
|
||||
}
|
||||
|
||||
func (gui *Gui) commandLogIntroLine() string {
|
||||
return strings.TrimSpace(fmt.Sprintf(
|
||||
gui.c.Tr.CommandLogHeader,
|
||||
gui.c.UserConfig().Keybinding.Universal.ExtrasMenu,
|
||||
))
|
||||
}
|
||||
|
||||
func (gui *Gui) isCopyToClipboardLogLine(line string) bool {
|
||||
return logLineMatchesTemplate(line, gui.c.Tr.Log.CopyToClipboard, "{{.str}}")
|
||||
}
|
||||
|
||||
func (gui *Gui) isCreateFileLogLine(line string) bool {
|
||||
return logLineMatchesTemplate(line, gui.c.Tr.Log.CreateFileWithContent, "{{.path}}")
|
||||
}
|
||||
|
||||
func logLineMatchesTemplate(line string, template string, placeholder string) bool {
|
||||
parts := strings.Split(template, placeholder)
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
trimmed := strings.TrimSpace(line)
|
||||
return strings.HasPrefix(trimmed, parts[0]) && strings.HasSuffix(trimmed, parts[1])
|
||||
}
|
||||
|
||||
func gitOutputBlocksFromCommandLogLines(lines []string, gitOutputHeader string, isCopyToClipboardLogLine func(string) bool) []string {
|
||||
var blocks []string
|
||||
|
||||
for i, line := range lines {
|
||||
if line != gitOutputHeader {
|
||||
continue
|
||||
}
|
||||
|
||||
block := commandLogEntryBeforeGitOutput(lines, i, gitOutputHeader, isCopyToClipboardLogLine)
|
||||
if len(block) > 0 {
|
||||
block = append(block, "")
|
||||
}
|
||||
block = append(block, gitOutputHeader)
|
||||
block = append(block, gitOutputLinesAfterHeader(lines, i+1, gitOutputHeader, isCopyToClipboardLogLine)...)
|
||||
|
||||
if trimmed := strings.TrimRight(strings.Join(block, "\n"), "\n"); trimmed != "" {
|
||||
blocks = append(blocks, trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
return blocks
|
||||
}
|
||||
|
||||
func commandLogEntryBeforeGitOutput(lines []string, headerIdx int, gitOutputHeader string, isCopyToClipboardLogLine func(string) bool) []string {
|
||||
i := headerIdx - 1
|
||||
for i >= 0 && lines[i] == "" {
|
||||
i--
|
||||
}
|
||||
|
||||
var commands []string
|
||||
for i >= 0 && strings.HasPrefix(lines[i], " ") && !isCopyToClipboardLogLine(lines[i]) {
|
||||
commands = append([]string{lines[i]}, commands...)
|
||||
i--
|
||||
}
|
||||
|
||||
if len(commands) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i >= 0 && lines[i] == "" {
|
||||
i--
|
||||
}
|
||||
|
||||
var entry []string
|
||||
if i >= 0 && !strings.HasPrefix(lines[i], " ") && lines[i] != gitOutputHeader {
|
||||
entry = append(entry, lines[i])
|
||||
}
|
||||
entry = append(entry, commands...)
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
func gitOutputLinesAfterHeader(lines []string, startIdx int, gitOutputHeader string, isCopyToClipboardLogLine func(string) bool) []string {
|
||||
output := make([]string, 0, len(lines)-startIdx)
|
||||
|
||||
for i := startIdx; i < len(lines); i++ {
|
||||
line := lines[i]
|
||||
if line == gitOutputHeader {
|
||||
break
|
||||
}
|
||||
if isCopyToClipboardLogLine(line) {
|
||||
continue
|
||||
}
|
||||
if isStartOfNewCommandLogEntry(lines, i, isCopyToClipboardLogLine) {
|
||||
break
|
||||
}
|
||||
output = append(output, line)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func isStartOfNewCommandLogEntry(lines []string, i int, isCopyToClipboardLogLine func(string) bool) bool {
|
||||
line := lines[i]
|
||||
if line == "" || strings.HasPrefix(line, " ") {
|
||||
return false
|
||||
}
|
||||
|
||||
for j := i + 1; j < len(lines); j++ {
|
||||
if lines[j] == "" {
|
||||
continue
|
||||
}
|
||||
if isCopyToClipboardLogLine(lines[j]) {
|
||||
continue
|
||||
}
|
||||
return isLazygitCommandLogLine(lines[j], isCopyToClipboardLogLine)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isLazygitCommandLogLine(line string, isCopyToClipboardLogLine func(string) bool) bool {
|
||||
return isCopyToClipboardLogLine(line) || strings.HasPrefix(line, " git ")
|
||||
}
|
||||
|
||||
func (gui *Gui) LogCommand(cmdStr string, commandLine bool) {
|
||||
if gui.Views.Extras == nil {
|
||||
return
|
||||
|
|
|
|||
140
pkg/gui/command_log_panel_test.go
Normal file
140
pkg/gui/command_log_panel_test.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const gitOutputHeader = "Git output:"
|
||||
|
||||
func englishCopyToClipboardLogLineMatcher() func(string) bool {
|
||||
return func(line string) bool {
|
||||
return logLineMatchesTemplate(line, "Copying '{{.str}}' to clipboard", "{{.str}}")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksFromCommandLogLines(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
"",
|
||||
gitOutputHeader,
|
||||
"line1",
|
||||
"line2",
|
||||
}
|
||||
|
||||
assert.Equal(t, []string{"Push\n git push\n\nGit output:\nline1\nline2"}, gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher()))
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksIncludeIndentedStderr(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
gitOutputHeader,
|
||||
" at foo.go:10",
|
||||
" at bar.go:20",
|
||||
"hook failed",
|
||||
}
|
||||
|
||||
assert.Equal(t, []string{"Push\n git push\n\nGit output:\n at foo.go:10\n at bar.go:20\nhook failed"}, gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher()))
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksIncludeToolErrorWithIndentedContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
gitOutputHeader,
|
||||
"Error: validation failed",
|
||||
" line 42: syntax error",
|
||||
"more output",
|
||||
"Stage file",
|
||||
" git add foo",
|
||||
gitOutputHeader,
|
||||
"second command output",
|
||||
}
|
||||
|
||||
assert.Equal(t, []string{
|
||||
"Push\n git push\n\nGit output:\nError: validation failed\n line 42: syntax error\nmore output",
|
||||
"Stage file\n git add foo\n\nGit output:\nsecond command output",
|
||||
}, gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher()))
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksSkipCopyNotifications(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
gitOutputHeader,
|
||||
"hook line",
|
||||
" Copying 'hook line' to clipboard",
|
||||
"hook line 2",
|
||||
}
|
||||
|
||||
assert.Equal(t, []string{"Push\n git push\n\nGit output:\nhook line\nhook line 2"}, gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher()))
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksEndAtNextCommandLogEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
gitOutputHeader,
|
||||
"first command output",
|
||||
"Stage file",
|
||||
" git add foo",
|
||||
"",
|
||||
gitOutputHeader,
|
||||
"second command output",
|
||||
}
|
||||
|
||||
assert.Equal(t, []string{
|
||||
"Push\n git push\n\nGit output:\nfirst command output",
|
||||
"Stage file\n git add foo\n\nGit output:\nsecond command output",
|
||||
}, gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher()))
|
||||
}
|
||||
|
||||
func TestGitOutputBlocksMultipleBlocksJoined(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := []string{
|
||||
"Push",
|
||||
" git push",
|
||||
gitOutputHeader,
|
||||
"first command",
|
||||
"Pull",
|
||||
" git pull",
|
||||
gitOutputHeader,
|
||||
"second command",
|
||||
}
|
||||
|
||||
blocks := gitOutputBlocksFromCommandLogLines(lines, gitOutputHeader, englishCopyToClipboardLogLineMatcher())
|
||||
assert.Equal(t, "Push\n git push\n\nGit output:\nfirst command\n\nPull\n git pull\n\nGit output:\nsecond command", joinGitOutputBlocks(blocks))
|
||||
}
|
||||
|
||||
func TestLogLineMatchesCopyToClipboardTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
matcher := englishCopyToClipboardLogLineMatcher()
|
||||
assert.True(t, matcher(" Copying 'hook line' to clipboard"))
|
||||
assert.False(t, matcher("Push"))
|
||||
}
|
||||
|
||||
func joinGitOutputBlocks(blocks []string) string {
|
||||
result := ""
|
||||
for i, block := range blocks {
|
||||
if i > 0 {
|
||||
result += "\n\n"
|
||||
}
|
||||
result += block
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ func (self *MenuViewModel) GetDisplayStrings(_ int, _ int) [][]string {
|
|||
|
||||
return lo.Map(menuItems, func(item *types.MenuItem, _ int) []string {
|
||||
displayStrings := item.LabelColumns
|
||||
if item.DisabledReason != nil {
|
||||
if item.DisabledReasonAtUse() != nil {
|
||||
displayStrings[0] = style.FgDefault.SetStrikethrough().Sprint(displayStrings[0])
|
||||
}
|
||||
|
||||
|
|
@ -282,13 +282,15 @@ func (self *MenuContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Bin
|
|||
}
|
||||
|
||||
func (self *MenuContext) OnMenuPress(selectedItem *types.MenuItem) error {
|
||||
if selectedItem != nil && selectedItem.DisabledReason != nil {
|
||||
if selectedItem.DisabledReason.ShowErrorInPanel {
|
||||
return errors.New(selectedItem.DisabledReason.Text)
|
||||
}
|
||||
if selectedItem != nil {
|
||||
if disabledReason := selectedItem.DisabledReasonAtUse(); disabledReason != nil {
|
||||
if disabledReason.ShowErrorInPanel {
|
||||
return errors.New(disabledReason.Text)
|
||||
}
|
||||
|
||||
self.c.ErrorToast(self.c.Tr.DisabledMenuItemPrefix + selectedItem.DisabledReason.Text)
|
||||
return nil
|
||||
self.c.ErrorToast(self.c.Tr.DisabledMenuItemPrefix + disabledReason.Text)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
self.c.Context().Pop()
|
||||
|
|
|
|||
|
|
@ -488,11 +488,11 @@ func (self *ConfirmationHelper) IsPopupPanelFocused() bool {
|
|||
|
||||
func (self *ConfirmationHelper) TooltipForMenuItem(menuItem *types.MenuItem) string {
|
||||
tooltip := menuItem.Tooltip
|
||||
if menuItem.DisabledReason != nil && menuItem.DisabledReason.Text != "" {
|
||||
if disabledReason := menuItem.DisabledReasonAtUse(); disabledReason != nil && disabledReason.Text != "" {
|
||||
if tooltip != "" {
|
||||
tooltip += "\n\n"
|
||||
}
|
||||
tooltip += style.FgRed.Sprintf(self.c.Tr.DisabledMenuItemPrefix) + menuItem.DisabledReason.Text
|
||||
tooltip += style.FgRed.Sprintf(self.c.Tr.DisabledMenuItemPrefix) + disabledReason.Text
|
||||
}
|
||||
return tooltip
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package gui
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
|
|
@ -10,6 +13,20 @@ import (
|
|||
)
|
||||
|
||||
func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
||||
noGitOutputDisabledReason := func() *types.DisabledReason {
|
||||
if gui.hasGitOutput() {
|
||||
return nil
|
||||
}
|
||||
return &types.DisabledReason{Text: gui.c.Tr.NoGitOutputToCopy}
|
||||
}
|
||||
|
||||
noCommandLogDisabledReason := func() *types.DisabledReason {
|
||||
if gui.hasCommandLogEntries() {
|
||||
return nil
|
||||
}
|
||||
return &types.DisabledReason{Text: gui.c.Tr.NoCommandLogToOpenInEditor}
|
||||
}
|
||||
|
||||
return gui.c.Menu(types.CreateMenuOptions{
|
||||
Title: gui.c.Tr.CommandLog,
|
||||
Items: []*types.MenuItem{
|
||||
|
|
@ -33,10 +50,79 @@ func (gui *Gui) handleCreateExtrasMenuPanel() error {
|
|||
Keys: []gocui.Key{gocui.NewKeyRune('f')},
|
||||
OnPress: gui.handleFocusCommandLog,
|
||||
},
|
||||
{
|
||||
Label: gui.c.Tr.CopyGitOutputToClipboard,
|
||||
Keys: []gocui.Key{gocui.NewKeyRune('c')},
|
||||
OnPress: gui.handleCopyLastGitOutputToClipboard,
|
||||
GetDisabledReason: noGitOutputDisabledReason,
|
||||
},
|
||||
{
|
||||
Label: gui.c.Tr.CopyAllGitOutputToClipboard,
|
||||
Keys: []gocui.Key{gocui.NewKeyRune('a')},
|
||||
OnPress: gui.handleCopyAllGitOutputToClipboard,
|
||||
GetDisabledReason: noGitOutputDisabledReason,
|
||||
},
|
||||
{
|
||||
Label: gui.c.Tr.OpenCommandLogInEditor,
|
||||
Keys: []gocui.Key{gocui.NewKeyRune('o')},
|
||||
OnPress: gui.handleOpenCommandLogInEditor,
|
||||
GetDisabledReason: noCommandLogDisabledReason,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCopyLastGitOutputToClipboard() error {
|
||||
output := gui.lastGitOutput()
|
||||
if output == "" {
|
||||
return errors.New(gui.c.Tr.NoGitOutputToCopy)
|
||||
}
|
||||
|
||||
if err := gui.os.CopyToClipboardQuiet(output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.c.Toast(gui.c.Tr.GitOutputCopiedToClipboard)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCopyAllGitOutputToClipboard() error {
|
||||
output := gui.allGitOutput()
|
||||
if output == "" {
|
||||
return errors.New(gui.c.Tr.NoGitOutputToCopy)
|
||||
}
|
||||
|
||||
if err := gui.os.CopyToClipboardQuiet(output); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.c.Toast(gui.c.Tr.GitOutputCopiedToClipboard)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenCommandLogInEditor() error {
|
||||
content := gui.commandLogContent()
|
||||
if content == "" {
|
||||
return errors.New(gui.c.Tr.NoCommandLogToOpenInEditor)
|
||||
}
|
||||
|
||||
filepath := filepath.Join(
|
||||
gui.os.GetTempDir(),
|
||||
gui.c.Git().RepoPaths.RepoName(),
|
||||
time.Now().Format("Jan _2 15.04.05.000000000")+"-command-log.txt",
|
||||
)
|
||||
if err := gui.os.CreateFileWithContent(filepath, content); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.Helpers().Files.EditFiles([]string{filepath}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.c.Toast(gui.c.Tr.CommandLogOpenedInEditor)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFocusCommandLog() error {
|
||||
gui.c.State().SetShowExtrasWindow(true)
|
||||
// TODO: is this necessary? Can't I just call 'return from context'?
|
||||
|
|
@ -94,7 +180,10 @@ func (gui *Gui) goToExtrasPanelBottom() error {
|
|||
}
|
||||
|
||||
func (gui *Gui) getCmdWriter() io.Writer {
|
||||
return &prefixWriter{writer: gui.Views.Extras, prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.c.Tr.GitOutput)}
|
||||
return &prefixWriter{
|
||||
writer: gui.Views.Extras,
|
||||
prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.c.Tr.GitOutput),
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures that the first write is preceded by writing a prefix.
|
||||
|
|
|
|||
|
|
@ -229,3 +229,11 @@ func (self *guiCommon) WithInlineStatus(item types.HasUrn, operation types.ItemO
|
|||
self.gui.helpers.InlineStatus.WithInlineStatus(helpers.InlineStatusOpts{Item: item, Operation: operation, ContextKey: contextKey}, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *guiCommon) LastGitOutput() string {
|
||||
return self.gui.lastGitOutput()
|
||||
}
|
||||
|
||||
func (self *guiCommon) AllGitOutput() string {
|
||||
return self.gui.allGitOutput()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,9 @@ type IGuiCommon interface {
|
|||
|
||||
ResetKeybindings()
|
||||
|
||||
LastGitOutput() string
|
||||
AllGitOutput() string
|
||||
|
||||
// hopefully we can remove this once we've moved all our keybinding stuff out of the gui god struct.
|
||||
GetInitialKeybindingsWithCustomCommands() ([]*Binding, []*gocui.ViewMouseBinding)
|
||||
|
||||
|
|
@ -333,6 +336,10 @@ type MenuItem struct {
|
|||
// and refuse to invoke the command
|
||||
DisabledReason *DisabledReason
|
||||
|
||||
// If non-nil, evaluated when rendering and invoking the menu item. Takes
|
||||
// precedence over DisabledReason when set.
|
||||
GetDisabledReason func() *DisabledReason
|
||||
|
||||
// Can be used to group menu items into sections with headers. MenuItems
|
||||
// with the same Section should be contiguous, and will automatically get a
|
||||
// section header. If nil, the item is not part of a section.
|
||||
|
|
@ -348,6 +355,14 @@ func (self *MenuItem) ID() string {
|
|||
return self.Label
|
||||
}
|
||||
|
||||
func (self *MenuItem) DisabledReasonAtUse() *DisabledReason {
|
||||
if self.GetDisabledReason != nil {
|
||||
return self.GetDisabledReason()
|
||||
}
|
||||
|
||||
return self.DisabledReason
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
CommitFiles []*models.CommitFile
|
||||
Files []*models.File
|
||||
|
|
|
|||
|
|
@ -787,6 +787,13 @@ type TranslationSet struct {
|
|||
CommandLog string
|
||||
ToggleShowCommandLog string
|
||||
FocusCommandLog string
|
||||
CopyGitOutputToClipboard string
|
||||
CopyAllGitOutputToClipboard string
|
||||
NoGitOutputToCopy string
|
||||
GitOutputCopiedToClipboard string
|
||||
OpenCommandLogInEditor string
|
||||
NoCommandLogToOpenInEditor string
|
||||
CommandLogOpenedInEditor string
|
||||
CommandLogHeader string
|
||||
RandomTip string
|
||||
ToggleWhitespaceInDiffView string
|
||||
|
|
@ -1949,6 +1956,13 @@ func EnglishTranslationSet() *TranslationSet {
|
|||
CantSwitchWhileOperationInProgress: "Can't switch repositories while an operation is in progress",
|
||||
ToggleShowCommandLog: "Toggle show/hide command log",
|
||||
FocusCommandLog: "Focus command log",
|
||||
CopyGitOutputToClipboard: "Copy last git output to clipboard",
|
||||
CopyAllGitOutputToClipboard: "Copy all git outputs to clipboard",
|
||||
NoGitOutputToCopy: "No git output to copy",
|
||||
GitOutputCopiedToClipboard: "Git output copied to clipboard",
|
||||
OpenCommandLogInEditor: "Open command log in editor",
|
||||
NoCommandLogToOpenInEditor: "No command log to open in editor",
|
||||
CommandLogOpenedInEditor: "Command log opened in editor",
|
||||
CommandLogHeader: "You can hide/focus this panel by pressing '%s'\n",
|
||||
RandomTip: "Random tip",
|
||||
ToggleWhitespaceInDiffView: "Toggle whitespace",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package misc
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CopyAllGitOutputToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Copy all streamed git outputs from the command log to the clipboard",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().OS.CopyToClipboardCmd = "printf '%s' {{text}} > clipboard"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
|
||||
shell.CloneIntoRemote("origin")
|
||||
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
|
||||
shell.EmptyCommit("two")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Universal.Push)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
|
||||
t.GlobalPress(keys.Universal.ExtrasMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Command log")).
|
||||
Select(Contains("Copy all git outputs to clipboard")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Equals("Git output copied to clipboard"))
|
||||
|
||||
t.FileSystem().FileContent("clipboard",
|
||||
Contains("master -> master").
|
||||
Contains("git push").
|
||||
Contains("Push"))
|
||||
},
|
||||
})
|
||||
45
pkg/integration/tests/misc/copy_git_output_to_clipboard.go
Normal file
45
pkg/integration/tests/misc/copy_git_output_to_clipboard.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package misc
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var CopyGitOutputToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Copy streamed git output from the command log to the clipboard",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().OS.CopyToClipboardCmd = "printf '%s' {{text}} > clipboard"
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
|
||||
shell.CloneIntoRemote("origin")
|
||||
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
|
||||
shell.EmptyCommit("two")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Universal.Push)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
|
||||
t.GlobalPress(keys.Universal.ExtrasMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Command log")).
|
||||
Select(Contains("Copy last git output to clipboard")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Equals("Git output copied to clipboard"))
|
||||
|
||||
t.FileSystem().FileContent("clipboard",
|
||||
Contains("master -> master").
|
||||
Contains("git push").
|
||||
Contains("Push"))
|
||||
},
|
||||
})
|
||||
47
pkg/integration/tests/misc/open_command_log_in_editor.go
Normal file
47
pkg/integration/tests/misc/open_command_log_in_editor.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package misc
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var OpenCommandLogInEditor = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Open the full command log in the user's editor",
|
||||
ExtraCmdArgs: []string{},
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.GetUserConfig().OS.Edit = "cp {{filename}} editor-output.txt"
|
||||
config.GetUserConfig().Gui.ShowRandomTip = false
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
|
||||
shell.CloneIntoRemote("origin")
|
||||
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
|
||||
shell.EmptyCommit("two")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Press(keys.Universal.Push)
|
||||
|
||||
t.Views().Status().Content(Equals("✓ repo → master"))
|
||||
|
||||
t.GlobalPress(keys.Universal.ExtrasMenu)
|
||||
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Command log")).
|
||||
Select(Contains("Open command log in editor")).
|
||||
Confirm()
|
||||
|
||||
t.ExpectToast(Equals("Command log opened in editor"))
|
||||
|
||||
t.FileSystem().FileContent("editor-output.txt",
|
||||
Contains("Push").
|
||||
Contains("git push").
|
||||
Contains("master -> master").
|
||||
DoesNotContain("You can hide/focus"))
|
||||
},
|
||||
})
|
||||
|
|
@ -355,13 +355,16 @@ var tests = []*components.IntegrationTest{
|
|||
interactive_rebase.SwapWithConflict,
|
||||
interactive_rebase.ViewFilesOfTodoEntries,
|
||||
misc.ConfirmOnQuit,
|
||||
misc.CopyAllGitOutputToClipboard,
|
||||
misc.CopyConfirmationMessageToClipboard,
|
||||
misc.CopyGitOutputToClipboard,
|
||||
misc.CopyToClipboard,
|
||||
misc.DirenvApprovesEnvrc,
|
||||
misc.DirenvLoadedOnRepoSwitch,
|
||||
misc.DirenvUnloadsOnBlockedEnvrc,
|
||||
misc.FilterRecentRepos,
|
||||
misc.InitialOpen,
|
||||
misc.OpenCommandLogInEditor,
|
||||
misc.RecentReposOnLaunch,
|
||||
misc.StartInGitDir,
|
||||
patch_building.Apply,
|
||||
|
|
|
|||
Loading…
Reference in a new issue