From ba6cfc1f85b152d8a8e23e720988f85a4a0c590b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 5 Feb 2025 10:11:00 +0100 Subject: [PATCH] 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. --- .../commit_description_controller.go | 28 ++++++++++++- .../controllers/commit_message_controller.go | 42 ++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/pkg/gui/controllers/commit_description_controller.go b/pkg/gui/controllers/commit_description_controller.go index aea6cfbdf..4337a1e4b 100644 --- a/pkg/gui/controllers/commit_description_controller.go +++ b/pkg/gui/controllers/commit_description_controller.go @@ -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), @@ -75,6 +75,32 @@ func (self *CommitDescriptionController) switchToCommitMessage() error { return nil } +func (self *CommitDescriptionController) handleTogglePanel() error { + // The default keybinding for this action is "", 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 "" + // (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 "", then we're totally out of + // luck. + if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "" { + // 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 diff --git a/pkg/gui/controllers/commit_message_controller.go b/pkg/gui/controllers/commit_message_controller.go index 28168ef18..6f0773801 100644 --- a/pkg/gui/controllers/commit_message_controller.go +++ b/pkg/gui/controllers/commit_message_controller.go @@ -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), @@ -105,6 +105,32 @@ func (self *CommitMessageController) switchToCommitDescription() error { return nil } +func (self *CommitMessageController) handleTogglePanel() error { + // The default keybinding for this action is "", 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 "" + // (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 "", then we're totally out of + // luck. + if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.TogglePanel == "" { + // 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 @@ -140,6 +166,20 @@ func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, e } func (self *CommitMessageController) confirm() error { + // The default keybinding for this action is "", 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 + // "" (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 "", then we're totally + // out of luck. + if self.c.GocuiGui().IsPasting && self.c.UserConfig().Keybinding.Universal.SubmitEditorText == "" { + return self.switchToCommitDescription() + } + return self.c.Helpers().Commits.HandleCommitConfirm() }