diff --git a/internal/action/actions.go b/internal/action/actions.go index 28d0e53da1..d69b95e8fb 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -1068,6 +1068,9 @@ func (h *BufPane) ToggleHighlightSearch() bool { // UnhighlightSearch unhighlights all instances of the last used search term func (h *BufPane) UnhighlightSearch() bool { + if !h.Buf.HighlightSearch { + return false + } h.Buf.HighlightSearch = false return true } @@ -1163,7 +1166,9 @@ func (h *BufPane) DiffPrevious() bool { // Undo undoes the last action func (h *BufPane) Undo() bool { - h.Buf.Undo() + if !h.Buf.Undo() { + return false + } InfoBar.Message("Undid action") h.Relocate() return true @@ -1171,7 +1176,9 @@ func (h *BufPane) Undo() bool { // Redo redoes the last action func (h *BufPane) Redo() bool { - h.Buf.Redo() + if !h.Buf.Redo() { + return false + } InfoBar.Message("Redid action") h.Relocate() return true @@ -1570,10 +1577,9 @@ func (h *BufPane) ToggleRuler() bool { return true } -// ClearStatus clears the messenger bar +// ClearStatus clears the infobar. It is an alias for ClearInfo. func (h *BufPane) ClearStatus() bool { - InfoBar.Message("") - return true + return h.ClearInfo() } // ToggleHelp toggles the help screen @@ -1628,12 +1634,18 @@ func (h *BufPane) Escape() bool { // Deselect deselects on the current cursor func (h *BufPane) Deselect() bool { + if !h.Cursor.HasSelection() { + return false + } h.Cursor.Deselect(true) return true } // ClearInfo clears the infobar func (h *BufPane) ClearInfo() bool { + if InfoBar.Msg == "" { + return false + } InfoBar.Message("") return true } @@ -1724,6 +1736,10 @@ func (h *BufPane) AddTab() bool { // PreviousTab switches to the previous tab in the tab list func (h *BufPane) PreviousTab() bool { tabsLen := len(Tabs.List) + if tabsLen == 1 { + return false + } + a := Tabs.Active() + tabsLen Tabs.SetActive((a - 1) % tabsLen) @@ -1732,8 +1748,13 @@ func (h *BufPane) PreviousTab() bool { // NextTab switches to the next tab in the tab list func (h *BufPane) NextTab() bool { + tabsLen := len(Tabs.List) + if tabsLen == 1 { + return false + } + a := Tabs.Active() - Tabs.SetActive((a + 1) % len(Tabs.List)) + Tabs.SetActive((a + 1) % tabsLen) return true } @@ -1769,6 +1790,10 @@ func (h *BufPane) Unsplit() bool { // NextSplit changes the view to the next split func (h *BufPane) NextSplit() bool { + if len(h.tab.Panes) == 1 { + return false + } + a := h.tab.active if a < len(h.tab.Panes)-1 { a++ @@ -1783,6 +1808,10 @@ func (h *BufPane) NextSplit() bool { // PreviousSplit changes the view to the previous split func (h *BufPane) PreviousSplit() bool { + if len(h.tab.Panes) == 1 { + return false + } + a := h.tab.active if a > 0 { a-- @@ -1984,6 +2013,9 @@ func (h *BufPane) MouseMultiCursor(e *tcell.EventMouse) bool { // SkipMultiCursor moves the current multiple cursor to the next available position func (h *BufPane) SkipMultiCursor() bool { lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1) + if !lastC.HasSelection() { + return false + } sel := lastC.GetSelection() searchStart := lastC.CurSelection[1] @@ -2019,8 +2051,11 @@ func (h *BufPane) RemoveMultiCursor() bool { h.Buf.RemoveCursor(h.Buf.NumCursors() - 1) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) h.Buf.UpdateCursors() - } else { + } else if h.multiWord { h.multiWord = false + h.Cursor.Deselect(true) + } else { + return false } h.Relocate() return true @@ -2028,8 +2063,12 @@ func (h *BufPane) RemoveMultiCursor() bool { // RemoveAllMultiCursors removes all cursors except the base cursor func (h *BufPane) RemoveAllMultiCursors() bool { - h.Buf.ClearCursors() - h.multiWord = false + if h.Buf.NumCursors() > 1 || h.multiWord { + h.Buf.ClearCursors() + h.multiWord = false + } else { + return false + } h.Relocate() return true } diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index 14e0408950..4c5b885804 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -150,29 +150,31 @@ func BufMapEvent(k Event, action string) { actionfns = append(actionfns, afn) } bufAction := func(h *BufPane, te *tcell.EventMouse) bool { - cursors := h.Buf.GetCursors() - success := true for i, a := range actionfns { - innerSuccess := true - for j, c := range cursors { - if c == nil { - continue - } - h.Buf.SetCurCursor(c.Num) - h.Cursor = c - if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') { - innerSuccess = innerSuccess && h.execAction(a, names[i], j, te) - } else { - break + var success bool + if _, ok := MultiActions[names[i]]; ok { + success = true + for _, c := range h.Buf.GetCursors() { + h.Buf.SetCurCursor(c.Num) + h.Cursor = c + success = success && h.execAction(a, names[i], te) } + } else { + h.Buf.SetCurCursor(0) + h.Cursor = h.Buf.GetActiveCursor() + success = h.execAction(a, names[i], te) } + // if the action changed the current pane, update the reference h = MainTab().CurPane() - success = innerSuccess if h == nil { // stop, in case the current pane is not a BufPane break } + + if (!success && types[i] == '&') || (success && types[i] == '|') { + break + } } return true } @@ -562,36 +564,33 @@ func (h *BufPane) DoKeyEvent(e Event) bool { return more } -func (h *BufPane) execAction(action BufAction, name string, cursor int, te *tcell.EventMouse) bool { +func (h *BufPane) execAction(action BufAction, name string, te *tcell.EventMouse) bool { if name != "Autocomplete" && name != "CycleAutocompleteBack" { h.Buf.HasSuggestions = false } - _, isMulti := MultiActions[name] - if (!isMulti && cursor == 0) || isMulti { - if h.PluginCB("pre" + name) { - var success bool - switch a := action.(type) { - case BufKeyAction: - success = a(h) - case BufMouseAction: - success = a(h, te) - } - success = success && h.PluginCB("on"+name) + if !h.PluginCB("pre" + name) { + return false + } - if isMulti { - if recordingMacro { - if name != "ToggleMacro" && name != "PlayMacro" { - curmacro = append(curmacro, action) - } - } - } + var success bool + switch a := action.(type) { + case BufKeyAction: + success = a(h) + case BufMouseAction: + success = a(h, te) + } + success = success && h.PluginCB("on"+name) - return success + if _, ok := MultiActions[name]; ok { + if recordingMacro { + if name != "ToggleMacro" && name != "PlayMacro" { + curmacro = append(curmacro, action) + } } } - return false + return success } func (h *BufPane) completeAction(action string) { diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index 689d524d98..661ea8b546 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -1089,7 +1089,7 @@ func (b *Buffer) ClearCursors() { b.cursors = b.cursors[:1] b.UpdateCursors() b.curCursor = 0 - b.GetActiveCursor().ResetSelection() + b.GetActiveCursor().Deselect(true) } // MoveLinesUp moves the range of lines up one row diff --git a/internal/buffer/eventhandler.go b/internal/buffer/eventhandler.go index 53f6402530..f1fe2a0750 100644 --- a/internal/buffer/eventhandler.go +++ b/internal/buffer/eventhandler.go @@ -253,11 +253,11 @@ func (eh *EventHandler) Execute(t *TextEvent) { ExecuteTextEvent(t, eh.buf) } -// Undo the first event in the undo stack -func (eh *EventHandler) Undo() { +// Undo the first event in the undo stack. Returns false if the stack is empty. +func (eh *EventHandler) Undo() bool { t := eh.UndoStack.Peek() if t == nil { - return + return false } startTime := t.Time.UnixNano() / int64(time.Millisecond) @@ -266,15 +266,16 @@ func (eh *EventHandler) Undo() { for { t = eh.UndoStack.Peek() if t == nil { - return + break } if t.Time.UnixNano()/int64(time.Millisecond) < endTime { - return + break } eh.UndoOneEvent() } + return true } // UndoOneEvent undoes one event @@ -303,11 +304,11 @@ func (eh *EventHandler) UndoOneEvent() { eh.RedoStack.Push(t) } -// Redo the first event in the redo stack -func (eh *EventHandler) Redo() { +// Redo the first event in the redo stack. Returns false if the stack is empty. +func (eh *EventHandler) Redo() bool { t := eh.RedoStack.Peek() if t == nil { - return + return false } startTime := t.Time.UnixNano() / int64(time.Millisecond) @@ -316,15 +317,16 @@ func (eh *EventHandler) Redo() { for { t = eh.RedoStack.Peek() if t == nil { - return + break } if t.Time.UnixNano()/int64(time.Millisecond) > endTime { - return + break } eh.RedoOneEvent() } + return true } // RedoOneEvent redoes one event diff --git a/runtime/help/keybindings.md b/runtime/help/keybindings.md index 3198c8fbda..869af62d0d 100644 --- a/runtime/help/keybindings.md +++ b/runtime/help/keybindings.md @@ -242,6 +242,7 @@ ToggleDiffGutter ToggleRuler JumpLine ResetSearch +ClearInfo ClearStatus ShellMode CommandMode