Skip to content

Commit ba6cfc1

Browse files
committed
Handle pasting multi-line commit messages
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.
1 parent 46ebfbb commit ba6cfc1

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

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

0 commit comments

Comments
 (0)