From 6214abba9a4e6512cf4090f408c7b1174918aee4 Mon Sep 17 00:00:00 2001 From: Dmytro Maluka Date: Sun, 13 Oct 2024 23:12:04 +0200 Subject: [PATCH] Overhaul LastVisualX and GetVisualX() usage Restore the original meaning of LastVisualX before commit 6d13710d934d ("Implement moving cursor up/down within a wrapped line"): last visual x location of the cursor in a logical line in the buffer, not in a visual line on the screen (in other words, taking tabs and wide characters into account, but not taking softwrap into account). And add a separate LastWrappedVisualX field, similar to LastVisualX but taking softwrap into account as well. This allows tracking last x position at the same time for both cases when we care about softwrap and when we don't care about it. This can be useful, for example, for implementing cursor up/down movement actions that always move by logical lines, not by visual lines, even if softwrap is enabled (in addition to our default CursorUp and CursorDown actions that move by visual lines). Also this fixes a minor bug: in InsertTab(), when `tabstospaces` is enabled and we insert a tab, the amount of inserted spaces depends on the visual line wrapping (i.e. on the window width), which is probably not a good idea. --- internal/action/actions.go | 19 ++++++++++++------- internal/buffer/cursor.go | 17 ++++++++++++----- internal/display/bufwindow.go | 6 +++--- runtime/plugins/status/status.lua | 2 +- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/internal/action/actions.go b/internal/action/actions.go index ccfb686b..2ea8f472 100644 --- a/internal/action/actions.go +++ b/internal/action/actions.go @@ -170,10 +170,10 @@ func (h *BufPane) MoveCursorUp(n int) { if sloc == vloc.SLoc { // we are at the beginning of buffer h.Cursor.Loc = h.Buf.Start() - h.Cursor.LastVisualX = 0 + h.Cursor.StoreVisualX() } else { vloc.SLoc = sloc - vloc.VisualX = h.Cursor.LastVisualX + vloc.VisualX = h.Cursor.LastWrappedVisualX h.Cursor.Loc = h.LocFromVLoc(vloc) } } @@ -189,11 +189,10 @@ func (h *BufPane) MoveCursorDown(n int) { if sloc == vloc.SLoc { // we are at the end of buffer h.Cursor.Loc = h.Buf.End() - vloc = h.VLocFromLoc(h.Cursor.Loc) - h.Cursor.LastVisualX = vloc.VisualX + h.Cursor.StoreVisualX() } else { vloc.SLoc = sloc - vloc.VisualX = h.Cursor.LastVisualX + vloc.VisualX = h.Cursor.LastWrappedVisualX h.Cursor.Loc = h.LocFromVLoc(vloc) } } @@ -889,7 +888,7 @@ func (h *BufPane) InsertTab() bool { b := h.Buf indent := b.IndentString(util.IntOpt(b.Settings["tabsize"])) tabBytes := len(indent) - bytesUntilIndent := tabBytes - (h.Cursor.GetVisualX() % tabBytes) + bytesUntilIndent := tabBytes - (h.Cursor.GetVisualX(false) % tabBytes) b.Insert(h.Cursor.Loc, indent[:bytesUntilIndent]) h.Relocate() return true @@ -1275,6 +1274,7 @@ func (h *BufPane) Copy() bool { func (h *BufPane) CopyLine() bool { origLoc := h.Cursor.Loc origLastVisualX := h.Cursor.LastVisualX + origLastWrappedVisualX := h.Cursor.LastWrappedVisualX origSelection := h.Cursor.CurSelection nlines := h.selectLines() @@ -1291,6 +1291,7 @@ func (h *BufPane) CopyLine() bool { h.Cursor.Loc = origLoc h.Cursor.LastVisualX = origLastVisualX + h.Cursor.LastWrappedVisualX = origLastWrappedVisualX h.Cursor.CurSelection = origSelection h.Relocate() return true @@ -1360,6 +1361,7 @@ func (h *BufPane) DuplicateLine() bool { if h.Cursor.HasSelection() { origLoc := h.Cursor.Loc origLastVisualX := h.Cursor.LastVisualX + origLastWrappedVisualX := h.Cursor.LastWrappedVisualX origSelection := h.Cursor.CurSelection start := h.Cursor.CurSelection[0] @@ -1380,6 +1382,7 @@ func (h *BufPane) DuplicateLine() bool { h.Cursor.Loc = origLoc h.Cursor.LastVisualX = origLastVisualX + h.Cursor.LastWrappedVisualX = origLastWrappedVisualX h.Cursor.CurSelection = origSelection if start.Y < end.Y { @@ -2070,6 +2073,7 @@ func (h *BufPane) SpawnMultiCursorUpN(n int) bool { c = buffer.NewCursor(h.Buf, buffer.Loc{lastC.X, lastC.Y - n}) c.LastVisualX = lastC.LastVisualX + c.LastWrappedVisualX = lastC.LastWrappedVisualX c.X = c.GetCharPosInLine(h.Buf.LineBytes(c.Y), c.LastVisualX) c.Relocate() } else { @@ -2082,9 +2086,10 @@ func (h *BufPane) SpawnMultiCursorUpN(n int) bool { h.Buf.DeselectCursors() vloc.SLoc = sloc - vloc.VisualX = lastC.LastVisualX + vloc.VisualX = lastC.LastWrappedVisualX c = buffer.NewCursor(h.Buf, h.LocFromVLoc(vloc)) c.LastVisualX = lastC.LastVisualX + c.LastWrappedVisualX = lastC.LastWrappedVisualX } h.Buf.AddCursor(c) diff --git a/internal/buffer/cursor.go b/internal/buffer/cursor.go index 784f2d39..007fcbe5 100644 --- a/internal/buffer/cursor.go +++ b/internal/buffer/cursor.go @@ -20,8 +20,14 @@ type Cursor struct { buf *Buffer Loc - // Last cursor x position + // Last visual x position of the cursor. Used in cursor up/down movements + // for remembering the original x position when moving to a line that is + // shorter than current x position. LastVisualX int + // Similar to LastVisualX but takes softwrapping into account, i.e. last + // visual x position in a visual (wrapped) line on the screen, which may be + // different from the line in the buffer. + LastWrappedVisualX int // The current selection as a range of character numbers (inclusive) CurSelection [2]Loc @@ -61,7 +67,7 @@ func (c *Cursor) Buf() *Buffer { // Goto puts the cursor at the given cursor's location and gives // the current cursor its selection too func (c *Cursor) Goto(b Cursor) { - c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX + c.X, c.Y, c.LastVisualX, c.LastWrappedVisualX = b.X, b.Y, b.LastVisualX, b.LastWrappedVisualX c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection } @@ -73,8 +79,8 @@ func (c *Cursor) GotoLoc(l Loc) { } // GetVisualX returns the x value of the cursor in visual spaces -func (c *Cursor) GetVisualX() int { - if c.buf.GetVisualX != nil { +func (c *Cursor) GetVisualX(wrap bool) int { + if wrap && c.buf.GetVisualX != nil { return c.buf.GetVisualX(c.Loc) } @@ -615,5 +621,6 @@ func (c *Cursor) RuneUnder(x int) rune { } func (c *Cursor) StoreVisualX() { - c.LastVisualX = c.GetVisualX() + c.LastVisualX = c.GetVisualX(false) + c.LastWrappedVisualX = c.GetVisualX(true) } diff --git a/internal/display/bufwindow.go b/internal/display/bufwindow.go index 1bfc04fd..6315bcc6 100644 --- a/internal/display/bufwindow.go +++ b/internal/display/bufwindow.go @@ -58,7 +58,7 @@ func (w *BufWindow) SetBuffer(b *buffer.Buffer) { if option == "softwrap" || option == "wordwrap" { w.Relocate() for _, c := range w.Buf.GetCursors() { - c.LastVisualX = c.GetVisualX() + c.LastWrappedVisualX = c.GetVisualX(true) } } } @@ -160,7 +160,7 @@ func (w *BufWindow) updateDisplayInfo() { if w.bufWidth != prevBufWidth && w.Buf.Settings["softwrap"].(bool) { for _, c := range w.Buf.GetCursors() { - c.LastVisualX = c.GetVisualX() + c.LastWrappedVisualX = c.GetVisualX(true) } } } @@ -238,7 +238,7 @@ func (w *BufWindow) Relocate() bool { // horizontal relocation (scrolling) if !b.Settings["softwrap"].(bool) { - cx := activeC.GetVisualX() + cx := activeC.GetVisualX(false) rw := runewidth.RuneWidth(activeC.RuneUnder(activeC.X)) if rw == 0 { rw = 1 // tab or newline diff --git a/runtime/plugins/status/status.lua b/runtime/plugins/status/status.lua index 4942d396..ae1f77a6 100644 --- a/runtime/plugins/status/status.lua +++ b/runtime/plugins/status/status.lua @@ -21,7 +21,7 @@ function lines(b) end function vcol(b) - return tostring(b:GetActiveCursor():GetVisualX()) + return tostring(b:GetActiveCursor():GetVisualX(false)) end function bytes(b)