Skip to content

Commit

Permalink
Fix pasting multi-line text into commit message panel (#4234)
Browse files Browse the repository at this point in the history
- **PR Description**

When pasting a multi-line commit message into the subject field of the
commit editor, we would interpret the first newline as the confirmation
for closing the editor, and then all remaining characters as whatever
command they are bound to, resulting in executing all sorts of arbitrary
commands.

Now we recognize this being a paste, and interpret the first newline as
moving to the description.

Also, prevent tabs in the pasted content from switching to the
respective other panel; simply insert four spaces instead, which should
be good enough for the leading indentation in pasted code snippets, for
example.

Finally, disable pasting text into non-editable views; my assumption is
that this is always a mistake, as it would execute arbitrary commands
depending on what's in the clipboard.

This depends on the terminal emulator supporting bracketed paste; I
didn't find one on Mac that doesn't (I tested with Terminal.app, iTerm2,
Ghostty, kitty, Alacritty, WezTerm, and VSCode's builtin terminal. It
works well in all of them).

I couldn't get it to work in Windows Terminal though, and I don't
understand why, as it does seem to support bracketed paste (it works in
bash).

Fixes #3151
Fixes #4066
Fixes #4216
  • Loading branch information
stefanhaller authored Feb 10, 2025
2 parents 3012306 + ba6cfc1 commit 3915cb6
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 6 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe h1:lNTwIp53mU5pfKYFinIsbUsd6mNxMit4IXcJUnn1Pc0=
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951 h1:7/3M0yosAM9/aLAjTfzSJWhsWjT860ZVe4T76RPwE2k=
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
Expand Down
28 changes: 27 additions & 1 deletion pkg/gui/controllers/commit_description_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (self *CommitDescriptionController) GetKeybindings(opts types.KeybindingsOp
bindings := []*types.Binding{
{
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
Handler: self.switchToCommitMessage,
Handler: self.handleTogglePanel,
},
{
Key: opts.GetKey(opts.Config.Universal.Return),
Expand Down Expand Up @@ -75,6 +75,32 @@ func (self *CommitDescriptionController) switchToCommitMessage() error {
return nil
}

func (self *CommitDescriptionController) handleTogglePanel() error {
// The default keybinding for this action is "<tab>", which means that we
// also get here when pasting multi-line text that contains tabs. In that
// case we don't want to toggle the panel, but insert the tab as a character
// (somehow, see below).
//
// Only do this if the TogglePanel command is actually mapped to "<tab>"
// (the default). If it's not, we can only hope that it's mapped to some
// ctrl key or fn key, which is unlikely to occur in pasted text. And if
// they mapped some *other* command to "<tab>", then we're totally out of
// luck.
if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "<tab>" {
// Handling tabs in pasted commit messages is not optimal, but hopefully
// good enough for now. We simply insert 4 spaces without worrying about
// column alignment. This works well enough for leading indentation,
// which is common in pasted code snippets.
view := self.Context().GetView()
for range 4 {
view.Editor.Edit(view, gocui.KeySpace, ' ', 0)
}
return nil
}

return self.switchToCommitMessage()
}

func (self *CommitDescriptionController) close() error {
self.c.Helpers().Commits.CloseCommitMessagePanel()
return nil
Expand Down
42 changes: 41 additions & 1 deletion pkg/gui/controllers/commit_message_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
},
{
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
Handler: self.switchToCommitDescription,
Handler: self.handleTogglePanel,
},
{
Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu),
Expand Down Expand Up @@ -105,6 +105,32 @@ func (self *CommitMessageController) switchToCommitDescription() error {
return nil
}

func (self *CommitMessageController) handleTogglePanel() error {
// The default keybinding for this action is "<tab>", which means that we
// also get here when pasting multi-line text that contains tabs. In that
// case we don't want to toggle the panel, but insert the tab as a character
// (somehow, see below).
//
// Only do this if the TogglePanel command is actually mapped to "<tab>"
// (the default). If it's not, we can only hope that it's mapped to some
// ctrl key or fn key, which is unlikely to occur in pasted text. And if
// they mapped some *other* command to "<tab>", then we're totally out of
// luck.
if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "<tab>" {
// It is unlikely that a pasted commit message contains a tab in the
// subject line, so it shouldn't matter too much how we handle it.
// Simply insert 4 spaces instead; all that matters is that we don't
// switch to the description panel.
view := self.context().GetView()
for range 4 {
view.Editor.Edit(view, gocui.KeySpace, ' ', 0)
}
return nil
}

return self.switchToCommitDescription()
}

func (self *CommitMessageController) handleCommitIndexChange(value int) error {
currentIndex := self.context().GetSelectedIndex()
newIndex := currentIndex + value
Expand Down Expand Up @@ -140,6 +166,20 @@ func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, e
}

func (self *CommitMessageController) confirm() error {
// The default keybinding for this action is "<enter>", which means that we
// also get here when pasting multi-line text that contains newlines. In
// that case we don't want to confirm the commit, but switch to the
// description panel instead so that the rest of the pasted text goes there.
//
// Only do this if the SubmitEditorText command is actually mapped to
// "<enter>" (the default). If it's not, we can only hope that it's mapped
// to some ctrl key or fn key, which is unlikely to occur in pasted text.
// And if they mapped some *other* command to "<enter>", then we're totally
// out of luck.
if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.SubmitEditorText == "<enter>" {
return self.switchToCommitDescription()
}

return self.c.Helpers().Commits.HandleCommitConfirm()
}

Expand Down
24 changes: 24 additions & 0 deletions vendor/github.com/jesseduffield/gocui/gui.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions vendor/github.com/jesseduffield/gocui/tcell_driver.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,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.20250207131741-38a8ffbf24fe
# github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
## explicit; go 1.12
github.com/jesseduffield/gocui
# github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
Expand Down

0 comments on commit 3915cb6

Please sign in to comment.