Skip to content

Commit

Permalink
Remove hard line breaks when rewording commits
Browse files Browse the repository at this point in the history
... and when recalling a commit message from an old commit by pressing up-arrow.

This is necessary because committing turns our soft line breaks into real ones,
but when rewording we want to turn them back into soft ones again, so that it's
possible to insert words at the beginning of a paragraph and have everything
rewrap nicely.

This is only a best effort; the algorithm only removes those hard line breaks
that can be removed without changing the way the message looks. This works well
when the previous commit message was wrapped at the same width, which for most
users should be the most common case; but if it wasn't, the result is not great.
Specifically, if the old wrap width was smaller, some hard line breaks just
won't be removed; if it was wider though, you'll get an unpleasant comb effect
with alternating long and short lines. In such a case it's best to switch to the
editor and use whatever wrapping features you have there (e.g. alt-Q).
  • Loading branch information
stefanhaller committed Dec 20, 2023
1 parent dc494cd commit 85a9dac
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 2 deletions.
4 changes: 3 additions & 1 deletion pkg/gui/controllers/commit_message_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)

Expand Down Expand Up @@ -119,7 +120,8 @@ func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, e
}
return false, self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
}
self.c.Helpers().Commits.UpdateCommitPanelView(commitMessage)
self.c.Helpers().Commits.UpdateCommitPanelView(
helpers.TryRemoveHardLineBreaks(commitMessage, self.c.UserConfig.Git.Commit.WrapCommitMessageAt))
return true, nil
}

Expand Down
30 changes: 30 additions & 0 deletions pkg/gui/controllers/helpers/commits_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"
"time"

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/samber/lo"
)
Expand Down Expand Up @@ -63,6 +64,35 @@ func (self *CommitsHelper) JoinCommitMessageAndUnwrappedDescription() string {
return self.getCommitSummary() + "\n" + self.getUnwrappedCommitDescription()
}

func TryRemoveHardLineBreaks(message string, autoWrapWidth int) string {
if autoWrapWidth <= 0 {
return message
}

messageRunes := []rune(message)
lastHardLineStart := 0
for i, r := range messageRunes {
if r == '\n' {
// Try to make this a soft linebreak by turning it into a space, and
// checking whether it still wraps to the same result then.
messageRunes[i] = ' '

_, cursorMapping := gocui.AutoWrapContent(messageRunes[lastHardLineStart:], autoWrapWidth)

// Look at the cursorMapping to check whether auto-wrapping inserted
// a line break. If it did, there will be a cursorMapping entry with
// Orig pointing to the position after the inserted line break.
if len(cursorMapping) == 0 || cursorMapping[0].Orig != i-lastHardLineStart+1 {
// It didn't, so change it back to a newline
messageRunes[i] = '\n'
}
lastHardLineStart = i + 1
}
}

return string(messageRunes)
}

func (self *CommitsHelper) SwitchToEditor() error {
if !self.c.Contexts().CommitMessage.CanSwitchToEditor() {
return nil
Expand Down
41 changes: 41 additions & 0 deletions pkg/gui/controllers/helpers/commits_helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package helpers

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestTryRemoveHardLineBreaks(t *testing.T) {
scenarios := []struct {
name string
message string
autoWrapWidth int
expectedResult string
}{
{
name: "empty",
message: "",
autoWrapWidth: 7,
expectedResult: "",
},
{
name: "all line breaks are needed",
message: "abc\ndef\n\nxyz",
autoWrapWidth: 7,
expectedResult: "abc\ndef\n\nxyz",
},
{
name: "some can be unwrapped",
message: "123\nabc def\nghi jkl\nmno\n456\n",
autoWrapWidth: 7,
expectedResult: "123\nabc def ghi jkl mno\n456\n",
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
actualResult := TryRemoveHardLineBreaks(s.message, s.autoWrapWidth)
assert.Equal(t, s.expectedResult, actualResult)
})
}
}
3 changes: 2 additions & 1 deletion pkg/gui/controllers/local_commits_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,11 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
return self.c.Error(err)
}

unwrappedMessage := helpers.TryRemoveHardLineBreaks(commitMessage, self.c.UserConfig.Git.Commit.WrapCommitMessageAt)
return self.c.Helpers().Commits.OpenCommitMessagePanel(
&helpers.OpenCommitMessagePanelOpts{
CommitIndex: self.context().GetSelectedLineIdx(),
InitialMessage: commitMessage,
InitialMessage: unwrappedMessage,
SummaryTitle: self.c.Tr.Actions.RewordCommit,
DescriptionTitle: self.c.Tr.CommitDescriptionTitle,
PreserveMessage: false,
Expand Down

0 comments on commit 85a9dac

Please sign in to comment.