Overhaul LastVisualX and GetVisualX() usage

Restore the original meaning of LastVisualX before commit 6d13710d93
("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.
This commit is contained in:
Dmytro Maluka
2024-10-13 23:12:04 +02:00
parent 85afb6eb87
commit 6214abba9a
4 changed files with 28 additions and 16 deletions

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)