diff --git a/internal/action/actions.go b/internal/action/actions.go index 7e3d452a..1fe8b7f0 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) } } @@ -657,7 +656,7 @@ func (h *BufPane) InsertNewline() bool { h.Buf.Remove(buffer.Loc{X: 0, Y: h.Cursor.Y - 1}, buffer.Loc{X: util.CharacterCount(line), Y: h.Cursor.Y - 1}) } } - h.Cursor.LastVisualX = h.Cursor.GetVisualX() + h.Cursor.StoreVisualX() h.Relocate() return true } @@ -687,7 +686,7 @@ func (h *BufPane) Backspace() bool { h.Buf.Remove(loc.Move(-1, h.Buf), loc) } } - h.Cursor.LastVisualX = h.Cursor.GetVisualX() + h.Cursor.StoreVisualX() h.Relocate() return true } @@ -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 { @@ -2058,35 +2061,20 @@ func (h *BufPane) SpawnCursorAtLoc(loc buffer.Loc) *buffer.Cursor { // SpawnMultiCursorUpN is not an action func (h *BufPane) SpawnMultiCursorUpN(n int) bool { lastC := h.Buf.GetCursor(h.Buf.NumCursors() - 1) - var c *buffer.Cursor - if !h.Buf.Settings["softwrap"].(bool) { - if n > 0 && lastC.Y == 0 { - return false - } - if n < 0 && lastC.Y+1 == h.Buf.LinesNum() { - return false - } - - h.Buf.DeselectCursors() - - c = buffer.NewCursor(h.Buf, buffer.Loc{lastC.X, lastC.Y - n}) - c.LastVisualX = lastC.LastVisualX - c.X = c.GetCharPosInLine(h.Buf.LineBytes(c.Y), c.LastVisualX) - c.Relocate() - } else { - vloc := h.VLocFromLoc(lastC.Loc) - sloc := h.Scroll(vloc.SLoc, -n) - if sloc == vloc.SLoc { - return false - } - - h.Buf.DeselectCursors() - - vloc.SLoc = sloc - vloc.VisualX = lastC.LastVisualX - c = buffer.NewCursor(h.Buf, h.LocFromVLoc(vloc)) - c.LastVisualX = lastC.LastVisualX + if n > 0 && lastC.Y == 0 { + return false } + if n < 0 && lastC.Y+1 == h.Buf.LinesNum() { + return false + } + + h.Buf.DeselectCursors() + + 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() h.Buf.AddCursor(c) h.Buf.SetCurCursor(h.Buf.NumCursors() - 1) diff --git a/internal/buffer/cursor.go b/internal/buffer/cursor.go index 7070dc23..d849f836 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,8 +67,9 @@ 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 = b.X, b.Y c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection + c.StoreVisualX() } // GotoLoc puts the cursor at the given cursor's location and gives @@ -73,8 +80,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) } @@ -100,7 +107,7 @@ func (c *Cursor) GetCharPosInLine(b []byte, visualPos int) int { // Start moves the cursor to the start of the line it is on func (c *Cursor) Start() { c.X = 0 - c.LastVisualX = c.GetVisualX() + c.StoreVisualX() } // StartOfText moves the cursor to the first non-whitespace rune of @@ -131,7 +138,7 @@ func (c *Cursor) IsStartOfText() bool { // End moves the cursor to the end of the line it is on func (c *Cursor) End() { c.X = util.CharacterCount(c.buf.LineBytes(c.Y)) - c.LastVisualX = c.GetVisualX() + c.StoreVisualX() } // CopySelection copies the user's selection to either "primary" @@ -615,5 +622,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/buffer/eventhandler.go b/internal/buffer/eventhandler.go index 10104f9c..e739f250 100644 --- a/internal/buffer/eventhandler.go +++ b/internal/buffer/eventhandler.go @@ -104,7 +104,7 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) { c.OrigSelection[0] = move(c.OrigSelection[0]) c.OrigSelection[1] = move(c.OrigSelection[1]) c.Relocate() - c.LastVisualX = c.GetVisualX() + c.StoreVisualX() } if useUndo { 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/comment/comment.lua b/runtime/plugins/comment/comment.lua index f86da945..ebb59626 100644 --- a/runtime/plugins/comment/comment.lua +++ b/runtime/plugins/comment/comment.lua @@ -107,7 +107,7 @@ function commentLine(bp, lineN, indentLen) bp.Cursor.Y = curpos.Y end bp.Cursor:Relocate() - bp.Cursor.LastVisualX = bp.Cursor:GetVisualX() + bp.Cursor:StoreVisualX() end function uncommentLine(bp, lineN, commentRegex) @@ -135,7 +135,7 @@ function uncommentLine(bp, lineN, commentRegex) end end bp.Cursor:Relocate() - bp.Cursor.LastVisualX = bp.Cursor:GetVisualX() + bp.Cursor:StoreVisualX() end function toggleCommentLine(bp, lineN, commentRegex) 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)