v0.8.0: fix cursor off-by-one in word-wrap — use < instead of <=, incf accum len instead of 1+len
The position-cursor function now uses cursor-line/cursor-col stored by view-input instead of recomputing from scratch, guaranteeing alignment with the rendered text. The boundary check uses (< pos (+ accum len)) to avoid falsely matching the first character of the next wrapped line. Removed the speculative reset-recovery code that sent cursor to end when pos was 0, since that broke legitimate navigation to the beginning of the input.
This commit is contained in:
@@ -218,7 +218,7 @@ and current sidebar mode (:auto/:visible/:hidden)."
|
||||
;; Speaker lines for all input rows
|
||||
(dotimes (r panel-rows)
|
||||
(cl-tty.backend:draw-text fb hpad (+ panel-top r) "│" (theme-color :input-prompt) nil))
|
||||
;; Draw each wrapped input line
|
||||
;; Draw each wrapped input line, tracking display position of cursor
|
||||
(let ((accum 0) (cl 0) (cc 0))
|
||||
(dotimes (i n-lines)
|
||||
(let* ((line (nth i lines))
|
||||
@@ -226,9 +226,9 @@ and current sidebar mode (:auto/:visible/:hidden)."
|
||||
(len (length line)))
|
||||
(when (>= row (- h 4)) (return))
|
||||
(cl-tty.backend:draw-text fb (+ hpad 2) row line input-fg nil)
|
||||
(when (and (>= pos accum) (<= pos (+ accum len)))
|
||||
(when (and (>= pos accum) (or (< pos (+ accum len)) (= i (1- n-lines))))
|
||||
(setf cl i cc (- pos accum)))
|
||||
(incf accum (1+ len))))
|
||||
(incf accum len)))
|
||||
(setf (st :cursor-line) cl (st :cursor-col) cc))
|
||||
;; Hint bar at h-2: F:/MCP: on left, token gauge + keybindings on right
|
||||
(let* ((focal (or (st :foveal-id) "-"))
|
||||
@@ -343,35 +343,26 @@ and current sidebar mode (:auto/:visible/:hidden)."
|
||||
(setf (st :dirty) (list nil nil nil))))
|
||||
|
||||
(defun position-cursor (fb w h)
|
||||
"Draw cursor at the input insertion point using reverse video (Emacs-style)."
|
||||
(let* ((sw (if (sidebar-visible-p w) (or (st :sidebar-width) 42) 0))
|
||||
(cw (- w sw))
|
||||
(hpad 2)
|
||||
(text (input-string))
|
||||
"Draw cursor at the input insertion point using reverse video (Emacs-style).
|
||||
Uses cursor-line/cursor-col stored by view-input to stay aligned with rendering."
|
||||
(let* ((text (input-string))
|
||||
(text-len (length text))
|
||||
(pos (or (st :cursor-pos) 0))
|
||||
(prompt-w (- cw (* 2 hpad) 2))
|
||||
(cl (or (st :cursor-line) 0))
|
||||
(cc (or (st :cursor-col) 0))
|
||||
(hpad 2)
|
||||
(sw (if (sidebar-visible-p w) (or (st :sidebar-width) 42) 0))
|
||||
(cw (- w sw))
|
||||
(inner-w (- cw (* 2 hpad)))
|
||||
(prompt-w (- inner-w 2))
|
||||
(lines (cl-tty.box:word-wrap text prompt-w))
|
||||
(n-lines (max 1 (length lines)))
|
||||
(cl 0) (cc 0) (accum 0))
|
||||
;; Find which wrapped line the cursor falls on
|
||||
(dotimes (i n-lines)
|
||||
(let ((len (length (nth i lines))))
|
||||
(when (and (>= pos accum) (or (<= pos (+ accum len))
|
||||
(= i (1- n-lines))))
|
||||
(setf cl i cc (- pos accum)))
|
||||
(incf accum (1+ len))))
|
||||
;; If text exists but pos is 0, move cursor to end (recovery for pos reset)
|
||||
(when (and (plusp text-len) (zerop pos))
|
||||
(setf pos text-len
|
||||
cl (1- n-lines)
|
||||
cc (length (car (last lines)))))
|
||||
(let* ((panel-rows (max 4 (+ n-lines 2)))
|
||||
(panel-rows (max 4 (+ n-lines 2)))
|
||||
(panel-top (- h 4 panel-rows -1))
|
||||
(cx (+ hpad 2 cc))
|
||||
(cy (+ panel-top 1 cl))
|
||||
(bg-i (theme-color :bg-input))
|
||||
(input-fg (theme-color :input-fg)))
|
||||
(let ((cx (+ hpad 2 cc))
|
||||
(cy (+ panel-top 1 cl)))
|
||||
(if (< pos text-len)
|
||||
(let ((ch (char text pos)))
|
||||
(cl-tty.backend:draw-text fb cx cy (string ch) bg-i input-fg))
|
||||
|
||||
Reference in New Issue
Block a user