Skip to content

Commit 3915cb6

Browse files
authored
Fix pasting multi-line text into commit message panel (#4234)
- **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
2 parents 3012306 + ba6cfc1 commit 3915cb6

File tree

7 files changed

+105
-6
lines changed

7 files changed

+105
-6
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
github.com/integrii/flaggy v1.4.0
1717
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
1818
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
19-
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe
19+
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
2020
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
2121
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
2222
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e

Diff for: go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
188188
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
189189
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
190190
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
191-
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe h1:lNTwIp53mU5pfKYFinIsbUsd6mNxMit4IXcJUnn1Pc0=
192-
github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
191+
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951 h1:7/3M0yosAM9/aLAjTfzSJWhsWjT860ZVe4T76RPwE2k=
192+
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
193193
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
194194
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
195195
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=

Diff for: pkg/gui/controllers/commit_description_controller.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (self *CommitDescriptionController) GetKeybindings(opts types.KeybindingsOp
2828
bindings := []*types.Binding{
2929
{
3030
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
31-
Handler: self.switchToCommitMessage,
31+
Handler: self.handleTogglePanel,
3232
},
3333
{
3434
Key: opts.GetKey(opts.Config.Universal.Return),
@@ -75,6 +75,32 @@ func (self *CommitDescriptionController) switchToCommitMessage() error {
7575
return nil
7676
}
7777

78+
func (self *CommitDescriptionController) handleTogglePanel() error {
79+
// The default keybinding for this action is "<tab>", which means that we
80+
// also get here when pasting multi-line text that contains tabs. In that
81+
// case we don't want to toggle the panel, but insert the tab as a character
82+
// (somehow, see below).
83+
//
84+
// Only do this if the TogglePanel command is actually mapped to "<tab>"
85+
// (the default). If it's not, we can only hope that it's mapped to some
86+
// ctrl key or fn key, which is unlikely to occur in pasted text. And if
87+
// they mapped some *other* command to "<tab>", then we're totally out of
88+
// luck.
89+
if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "<tab>" {
90+
// Handling tabs in pasted commit messages is not optimal, but hopefully
91+
// good enough for now. We simply insert 4 spaces without worrying about
92+
// column alignment. This works well enough for leading indentation,
93+
// which is common in pasted code snippets.
94+
view := self.Context().GetView()
95+
for range 4 {
96+
view.Editor.Edit(view, gocui.KeySpace, ' ', 0)
97+
}
98+
return nil
99+
}
100+
101+
return self.switchToCommitMessage()
102+
}
103+
78104
func (self *CommitDescriptionController) close() error {
79105
self.c.Helpers().Commits.CloseCommitMessagePanel()
80106
return nil

Diff for: pkg/gui/controllers/commit_message_controller.go

+41-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
4848
},
4949
{
5050
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
51-
Handler: self.switchToCommitDescription,
51+
Handler: self.handleTogglePanel,
5252
},
5353
{
5454
Key: opts.GetKey(opts.Config.CommitMessage.CommitMenu),
@@ -105,6 +105,32 @@ func (self *CommitMessageController) switchToCommitDescription() error {
105105
return nil
106106
}
107107

108+
func (self *CommitMessageController) handleTogglePanel() error {
109+
// The default keybinding for this action is "<tab>", which means that we
110+
// also get here when pasting multi-line text that contains tabs. In that
111+
// case we don't want to toggle the panel, but insert the tab as a character
112+
// (somehow, see below).
113+
//
114+
// Only do this if the TogglePanel command is actually mapped to "<tab>"
115+
// (the default). If it's not, we can only hope that it's mapped to some
116+
// ctrl key or fn key, which is unlikely to occur in pasted text. And if
117+
// they mapped some *other* command to "<tab>", then we're totally out of
118+
// luck.
119+
if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "<tab>" {
120+
// It is unlikely that a pasted commit message contains a tab in the
121+
// subject line, so it shouldn't matter too much how we handle it.
122+
// Simply insert 4 spaces instead; all that matters is that we don't
123+
// switch to the description panel.
124+
view := self.context().GetView()
125+
for range 4 {
126+
view.Editor.Edit(view, gocui.KeySpace, ' ', 0)
127+
}
128+
return nil
129+
}
130+
131+
return self.switchToCommitDescription()
132+
}
133+
108134
func (self *CommitMessageController) handleCommitIndexChange(value int) error {
109135
currentIndex := self.context().GetSelectedIndex()
110136
newIndex := currentIndex + value
@@ -140,6 +166,20 @@ func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, e
140166
}
141167

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

Diff for: vendor/github.com/jesseduffield/gocui/gui.go

+24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: vendor/github.com/jesseduffield/gocui/tcell_driver.go

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: vendor/modules.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
171171
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
172172
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
173173
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
174-
# github.com/jesseduffield/gocui v0.3.1-0.20250207131741-38a8ffbf24fe
174+
# github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
175175
## explicit; go 1.12
176176
github.com/jesseduffield/gocui
177177
# github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a

0 commit comments

Comments
 (0)