diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 448c451d3..3d58e6aec 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -5,11 +5,11 @@ import ( "path/filepath" "strings" "time" + "unicode" "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" - "github.com/samber/lo" ) type CommitsHelper struct { @@ -48,6 +48,15 @@ func (self *CommitsHelper) SplitCommitMessageAndDescription(message string) (str return summary, description } +// joinCommitMessageAndDescription is the inverse of +// SplitCommitMessageAndDescription. +func (self *CommitsHelper) joinCommitMessageAndDescription(summary string, description string) string { + if len(description) == 0 { + return summary + } + return summary + "\n\n" + description +} + // SplitPreservedCommitMessage splits a message in our preservation format // (summary and description joined by a single "\n") into summary and description. // It is lossless: round-tripping through JoinCommitMessageAndUnwrappedDescription @@ -103,9 +112,7 @@ func TryRemoveHardLineBreaks(message string, autoWrapWidth int) string { } func (self *CommitsHelper) SwitchToEditor() error { - message := lo.Ternary(len(self.getCommitDescription()) == 0, - self.getCommitSummary(), - self.getCommitSummary()+"\n\n"+self.getCommitDescription()) + message := self.joinCommitMessageAndDescription(self.getCommitSummary(), self.getCommitDescription()) filepath := filepath.Join(self.c.OS().GetTempDir(), self.c.Git().RepoPaths.RepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".msg") err := self.c.OS().CreateFileWithContent(filepath, message) if err != nil { @@ -242,12 +249,27 @@ func (self *CommitsHelper) OpenCommitMenu(suggestionFunc func(string) []*types.S }, Keys: menuKey('c'), }, + { + Label: self.c.Tr.CopyCommitMessageToClipboard, + OnPress: func() error { + return self.copyCommitMessageToClipboard() + }, + Keys: menuKey('C'), + }, { Label: self.c.Tr.PasteCommitMessageFromClipboard, OnPress: func() error { return self.pasteCommitMessageFromClipboard() }, - Keys: menuKey('p'), + Keys: menuKey('V'), + }, + { + Label: self.c.Tr.ClearCommitMessage, + OnPress: func() error { + self.setSummaryAndDescriptionInView("", "") + return nil + }, + Keys: menuKey('d'), }, } return self.c.Menu(types.CreateMenuOptions{ @@ -271,6 +293,30 @@ func (self *CommitsHelper) addCoAuthor(suggestionFunc func(string) []*types.Sugg return nil } +func (self *CommitsHelper) copyCommitMessageToClipboard() error { + // The panel keeps the space at the end of each line that it wraps. Git + // strips trailing whitespace when it commits, so do the same here; this + // makes the copied message look like the committed one. + message := stripTrailingWhitespace( + self.joinCommitMessageAndDescription(self.getCommitSummary(), self.getCommitDescription())) + + self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard) + if err := self.c.OS().CopyToClipboard(message); err != nil { + return err + } + + self.c.Toast(self.c.Tr.CommitMessageCopiedToClipboard) + return nil +} + +func stripTrailingWhitespace(message string) string { + lines := strings.Split(message, "\n") + for i, line := range lines { + lines[i] = strings.TrimRightFunc(line, unicode.IsSpace) + } + return strings.Join(lines, "\n") +} + func (self *CommitsHelper) pasteCommitMessageFromClipboard() error { message, err := self.c.OS().PasteFromClipboard() if err != nil { diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 3d4689d8a..d8aad8b99 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -765,8 +765,10 @@ type TranslationSet struct { CopyCommitHashToClipboard string CommitHash string CommitURL string + CopyCommitMessageToClipboard string PasteCommitMessageFromClipboard string SurePasteCommitMessage string + ClearCommitMessage string CommitMessage string CommitMessageBody string CommitSubject string @@ -2002,8 +2004,10 @@ func EnglishTranslationSet() *TranslationSet { CopyCommitHashToClipboard: "Copy abbreviated commit hash to clipboard", CommitHash: "Commit hash", CommitURL: "Commit URL", + CopyCommitMessageToClipboard: "Copy commit message to clipboard", PasteCommitMessageFromClipboard: "Paste commit message from clipboard", SurePasteCommitMessage: "Pasting will overwrite the current commit message, continue?", + ClearCommitMessage: "Clear commit message", CommitMessage: "Commit message (subject and body)", CommitMessageBody: "Commit message body", CommitSubject: "Commit subject", diff --git a/pkg/integration/tests/commit/clear_commit_message.go b/pkg/integration/tests/commit/clear_commit_message.go new file mode 100644 index 000000000..1c5909357 --- /dev/null +++ b/pkg/integration/tests/commit/clear_commit_message.go @@ -0,0 +1,39 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ClearCommitMessage = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Clear the commit message in the commit message panel", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file", "file content") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + Focus(). + Press(keys.Files.CommitChanges) + + t.ExpectPopup().CommitMessagePanel(). + Type("subject"). + SwitchToDescription(). + Type("first line"). + AddNewline(). + Type("second line"). + SwitchToSummary(). + OpenCommitMenu() + + t.ExpectPopup().Menu().Title(Equals("Commit Menu")). + Select(Contains("Clear commit message")). + Confirm() + + t.ExpectPopup().CommitMessagePanel(). + Content(Equals("")). + SwitchToDescription(). + Content(Equals("")) + }, +}) diff --git a/pkg/integration/tests/commit/copy_commit_message_from_panel.go b/pkg/integration/tests/commit/copy_commit_message_from_panel.go new file mode 100644 index 000000000..12da37d46 --- /dev/null +++ b/pkg/integration/tests/commit/copy_commit_message_from_panel.go @@ -0,0 +1,44 @@ +package commit + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var CopyCommitMessageFromPanel = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Copy the commit message from the commit message panel 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.CreateFileAndAdd("file", "file content") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + Focus(). + Press(keys.Files.CommitChanges) + + t.ExpectPopup().CommitMessagePanel(). + Type("subject"). + SwitchToDescription(). + Type("This line is longer than the auto-wrap width, so the clipboard gets it with a line break"). + AddNewline(). + Type("second line"). + SwitchToSummary(). + OpenCommitMenu() + + t.ExpectPopup().Menu().Title(Equals("Commit Menu")). + Select(Contains("Copy commit message to clipboard")). + Confirm() + + t.ExpectToast(Equals("Commit message copied to clipboard")) + + t.FileSystem().FileContent("../clipboard", Equals( + "subject\n\nThis line is longer than the auto-wrap width, so the clipboard gets it\nwith a line break\nsecond line")) + + t.ExpectPopup().CommitMessagePanel(). + Content(Equals("subject")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 6d66a1080..c6ee9564f 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -142,6 +142,7 @@ var tests = []*components.IntegrationTest{ commit.CheckoutFileFromCommit, commit.CheckoutFileFromRangeSelectionOfCommits, commit.CheckoutFileWithLocalModifications, + commit.ClearCommitMessage, commit.Commit, commit.CommitAllWithNestedRepository, commit.CommitDuringSquashMerge, @@ -156,6 +157,7 @@ var tests = []*components.IntegrationTest{ commit.CommitWithNonMatchingBranchName, commit.CommitWithPrefix, commit.CopyAuthorToClipboard, + commit.CopyCommitMessageFromPanel, commit.CopyMessageBodyToClipboard, commit.CopyTagToClipboard, commit.CreateAmendCommit,