Skip to content

Commit

Permalink
Merge pull request #1678 from sakurawald/fix-vi-jumplist
Browse files Browse the repository at this point in the history
fix: the vi jumplist-history-push should not destroy jumplist-history elements above the jumplist-index, when a new element is pushed.
cxxxr authored Dec 4, 2024
2 parents d71a593 + 5e0b3f7 commit e459971
Showing 4 changed files with 84 additions and 30 deletions.
6 changes: 6 additions & 0 deletions extensions/vi-mode/commands.lisp
Original file line number Diff line number Diff line change
@@ -123,6 +123,7 @@
:vi-append-line
:vi-open-below
:vi-open-above
:vi-jumps
:vi-jump-back
:vi-jump-next
:vi-a-word
@@ -1015,6 +1016,11 @@ on the same line or at eol if there are none."
(line-start p))))))
(move-to-column (current-point) column t)))

(define-command vi-jumps () ()
(line-end (current-point))
(lem:message-buffer (with-output-to-string (s)
(lem-vi-mode/jumplist::print-jumplist (current-jumplist) s))))

(define-command vi-jump-back (&optional (n 1)) (:universal)
(dotimes (i n)
(jump-back)))
16 changes: 10 additions & 6 deletions extensions/vi-mode/ex-command.lisp
Original file line number Diff line number Diff line change
@@ -67,12 +67,12 @@
(ex-write range filename t)))

(define-ex-command "^bn$" (range argument)
(declare (ignore range argument))
(lem:next-buffer))
(declare (ignore range argument))
(lem:next-buffer))

(define-ex-command "^bp$" (range argument)
(declare (ignore range argument))
(lem:previous-buffer))
(declare (ignore range argument))
(lem:previous-buffer))

(define-ex-command "^wq$" (range filename)
(ex-write-quit range filename nil t))
@@ -190,8 +190,8 @@
(declare (ignore range))
(lem:pipe-command
(format nil "~A ~A"
(subseq lem-vi-mode/ex-core:*command* 1)
command)))
(subseq lem-vi-mode/ex-core:*command* 1)
command)))

(define-ex-command "^(buffers|ls|files)$" (range argument)
(declare (ignore range argument))
@@ -259,3 +259,7 @@
(define-ex-command "^pwd?$" (range argument)
(declare (ignore range argument))
(lem:current-directory))

(define-ex-command "^jumps?$" (range argument)
(declare (ignore range argument))
(lem-vi-mode/commands:vi-jumps))
59 changes: 38 additions & 21 deletions extensions/vi-mode/jumplist.lisp
Original file line number Diff line number Diff line change
@@ -85,19 +85,20 @@

(defun jumplist-history-push (jumplist point)
(with-slots (history index current) jumplist
;; Delete the newer history
;; The jumplist-history is a list structure, for performance consideration, we use jumplist-current to track the item pointed by the jumplist-index.
;; When jumplist-index is 0, the jumplist-current should be nil. (Means we are NOT navigating the jumplist-history.)
;; If jumplist-index > 0 (means we are now navigating the jumplist-history), or in other words, the jumplist-current is NOT nil.
;; We only delete jumplist-current when jumplist-index > 0, since we don't want to destroy the jumplist while we are navigating back (C-o) and forth (C-o) the items in jumplist-history.
(when (< 0 index)
(loop repeat (1- index)
for point in history
do (delete-point point))
(setf history (nthcdr (1- index) history))
(when current
(delete-point current))
(setf index 0
current nil))

;; Set history of jumplist.
(setf history
(cons (copy-point point :left-inserting)
;; Remove points at the same line in the history
;; Remove the one existing equal point. (Only testing the same line for equality, ignoring the column-number to avoid jumplist spam. )
(remove point
history
:test (lambda (new-point point)
@@ -108,19 +109,37 @@
(delete-point point)
t))
:count 1)))

;; Keep only *max-jumplist-size* points (including `current`)
(delete-exceeded-elements history (1- *max-jumplist-size*))

point))

(defun jumplist-history-back (jumplist)
(with-slots (index current) jumplist
(when (zerop index)
(setf current (copy-point (current-point) :left-inserting)))
(ignore-some-conditions (jumplist-invalid-index)
(multiple-value-bind (point new-index)
(jumplist-find-backward jumplist (1+ index))
(setf index new-index)
point))))
;; NOTE: In vim, the jump-back (C-o) command will save (current-point) as the first element of jumplist-history, if jumplist-current is nil (which means that we did n't use C-i before, and history-index = 0, and now we are ready to enter jumplist navigating mode.), so that it's possible to back to this location in the future.
(let ((enter-jumplist-navigating-mode-p nil))
(when (zerop index)
(setf current (copy-point (current-point) :left-inserting))
(setf enter-jumplist-navigating-mode-p t))

(ignore-some-conditions (jumplist-invalid-index)
(multiple-value-bind (point new-index)

(if enter-jumplist-navigating-mode-p
(progn
;; Ensure (current-point) is the first element of jumplist-history.
(jumplist-history-push jumplist current)
;; Because we have pushed a new element, so the original index is outdated.
(jumplist-find-backward jumplist (+ 2 index)))
(progn
(jumplist-find-backward jumplist (1+ index))))

;; Update the value of jumplist-index, making the jumplist-stack-pointer points current.
(setf index new-index)

point))
)))

(defun jumplist-history-next (jumplist)
(with-slots (index current) jumplist
@@ -159,14 +178,12 @@
(enough-namestring (buffer-filename buffer))
(buffer-name buffer))))
(with-slots (index current) jumplist
(loop with results = (list (if (and current
(alive-point-p current))
(list (= 0 index)
(abs (- 0 index))
(line-number-at-point current)
(point-column current)
(buffer-identifier (point-buffer current)))
(list (= 0 index)
(loop with results = (if (and current
(alive-point-p current))
;; Initialize with empty list, means we are in jumplist navigating mode. (C-o is used at least once.)
(list)
;; Initialize with a NIL entry, to mean we are not at jumplist navigating mode. (jumplist-current = nil and jumplist-index = 0)
(list (list (= 0 index)
(abs (- 0 index))
nil nil nil)))
with dead-count = 0
33 changes: 30 additions & 3 deletions extensions/vi-mode/tests/jumplist.lisp
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@
(lines " 3: (1, 0) NIL"
" 2: (2, 0) NIL"
" 1: (3, 0) NIL"
"> 0: NIL")))
"> 0: (4, 0) NIL")))
(ok (not (signals (jumplist-history-next jumplist))))))))

(deftest jumplist-delete-newer-history
@@ -107,8 +107,10 @@
(jumplist-history-push jumplist (copy-point (buffer-end-point (current-buffer))))
(ok (equal (with-output-to-string (s)
(print-jumplist jumplist s))
(lines " 3: (1, 0) NIL"
" 2: (2, 0) NIL"
(lines " 5: (1, 0) NIL"
" 4: (2, 0) NIL"
" 3: (3, 0) NIL"
" 2: (4, 0) NIL"
" 1: (6, 0) NIL"
"> 0: NIL")))))))

@@ -147,3 +149,28 @@
(lines " 2: (2, 0) NIL"
" 1: (3, 0) NIL"
"> 0: NIL"))))))))

(deftest jumplist-push-current-point-into-jumplist-before-jump-history-back
(with-fake-interface ()
(with-vi-buffer (#?"second-location\n[s]tart\n\nfirst-location\n\n\nthird-location")
(let ((jumplist (current-jumplist)))
(cmd "2gg")
(cmd "4gg")
(cmd "gg")
(cmd "G")
(cmd "gg")
(ok (equal (with-output-to-string (s)
(print-jumplist jumplist s))
(lines " 4: (2, 0) NIL"
" 3: (4, 0) NIL"
" 2: (1, 0) NIL"
" 1: (7, 0) NIL"
"> 0: NIL")))
(cmd "<C-o>")
(ok (equal (with-output-to-string (s)
(print-jumplist jumplist s))
(lines " 2: (2, 0) NIL"
" 1: (4, 0) NIL"
"> 0: (7, 0) NIL"
" 1: (1, 0) NIL")))))))

0 comments on commit e459971

Please sign in to comment.