From 95ea6814ceb57013f28d3ccfe7e1e725ff0658ad Mon Sep 17 00:00:00 2001 From: cutelisp Date: Thu, 24 Jul 2025 08:26:40 +0100 Subject: [PATCH] Add Horizontal Scroll Actions --- internal/action/actions.go | 28 ++++++++++++++++++++++++++++ internal/action/bufpane.go | 2 ++ internal/display/infowindow.go | 1 + internal/display/softwrap.go | 25 +++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/internal/action/actions.go b/internal/action/actions.go index 94fe3cf769..2646ce4844 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -35,6 +35,22 @@ func (h *BufPane) ScrollDown(n int) { h.SetView(v) } +// ScrollLeft is not an action +func (h *BufPane) ScrollLeft(n int) bool { + v := h.GetView() + v.StartCol = h.HScroll(v.StartCol, -n) + h.SetView(v) + return true +} + +// ScrollRight is not an action +func (h *BufPane) ScrollRight(n int) bool { + v := h.GetView() + v.StartCol = h.HScroll(v.StartCol, n) + h.SetView(v) + return true +} + // ScrollAdjust can be used to shift the view so that the last line is at the // bottom if the user has scrolled past the last line. func (h *BufPane) ScrollAdjust() { @@ -159,6 +175,18 @@ func (h *BufPane) ScrollDownAction() bool { return true } +// ScrollDownAction scrolls the view left +func (h *BufPane) ScrollLeftAction() bool { + h.ScrollLeft(util.IntOpt(h.Buf.Settings["scrollspeed"])) + return true +} + +// ScrollUpAction scrolls the view right +func (h *BufPane) ScrollRightAction() bool { + h.ScrollRight(util.IntOpt(h.Buf.Settings["scrollspeed"])) + return true +} + // Center centers the view on the cursor func (h *BufPane) Center() bool { v := h.GetView() diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index b4030a956f..c06bda15e3 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -838,6 +838,8 @@ var BufKeyActions = map[string]BufKeyAction{ "Suspend": (*BufPane).Suspend, "ScrollUp": (*BufPane).ScrollUpAction, "ScrollDown": (*BufPane).ScrollDownAction, + "ScrollLeft": (*BufPane).ScrollLeftAction, + "ScrollRight": (*BufPane).ScrollRightAction, "SpawnMultiCursor": (*BufPane).SpawnMultiCursor, "SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp, "SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown, diff --git a/internal/display/infowindow.go b/internal/display/infowindow.go index 878d40ff3e..24c84ae56d 100644 --- a/internal/display/infowindow.go +++ b/internal/display/infowindow.go @@ -83,6 +83,7 @@ func (i *InfoWindow) BufView() View { } } +func (i *InfoWindow) HScroll(sc, n int) int { return sc } func (i *InfoWindow) Scroll(s SLoc, n int) SLoc { return s } func (i *InfoWindow) Diff(s1, s2 SLoc) int { return 0 } func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc { return SLoc{0, 0} } diff --git a/internal/display/softwrap.go b/internal/display/softwrap.go index 1460f88304..4c82545d28 100644 --- a/internal/display/softwrap.go +++ b/internal/display/softwrap.go @@ -60,6 +60,7 @@ type VLoc struct { } type SoftWrap interface { + HScroll(sc, n int) int Scroll(s SLoc, n int) SLoc Diff(s1, s2 SLoc) int SLocFromLoc(loc buffer.Loc) SLoc @@ -297,6 +298,30 @@ func (w *BufWindow) Scroll(s SLoc, n int) SLoc { return w.scroll(s, n) } +// HScroll returns the column after moving n columns relative to sc +// i.e. the result of scrolling n columns left/right. n can be negative, +// which means scrolling left. The returned column is guaranteed to be +// within the buffer boundaries. +func (w *BufWindow) HScroll(sc, n int) int { + if w.Buf.Settings["softwrap"].(bool) { + return sc + } + if n <= 0 { + return util.Max(0, sc+n) + } + + lw := 0 // longest line width + for i := 0; i < w.Buf.LinesNum(); i++ { + cc := util.CharacterCount(w.Buf.LineBytes(i)) + lw = util.Max(lw, cc) + } + + OFFSET := 3 + maxSc := lw - w.BufView().Width + OFFSET + + return util.Min(sc+n, maxSc) +} + // Diff returns the difference (the vertical distance) between two SLocs. func (w *BufWindow) Diff(s1, s2 SLoc) int { if !w.Buf.Settings["softwrap"].(bool) {