Generalize jupyter-repl-history navigation functions

This commit is contained in:
Nathaniel Nicandro 2019-05-08 10:08:34 -05:00
parent ff0a228939
commit 107fa8042d
2 changed files with 58 additions and 49 deletions

View file

@ -962,19 +962,30 @@ buffer to display TEXT."
(font-lock-prepend-text-property
beg end 'font-lock-face 'jupyter-repl-traceback)))))))
(defun jupyter-repl-history--next (n)
"Helper function for `jupyter-repl-history-next'.
Rotates `jupyter-repl-history' N times in the forward direction,
towards newer history elements and returns the Nth history
element in that direction relative to the current REPL history.
If the sentinel value is found before rotating N times, return
nil."
(if (> n 0)
(unless (eq (ring-ref jupyter-repl-history -1) 'jupyter-repl-history)
(ring-insert jupyter-repl-history
(ring-remove jupyter-repl-history -1))
(jupyter-repl-history--next (1- n)))
(ring-ref jupyter-repl-history 0)))
(defun jupyter-repl-history--rotate (n)
"Rotate the REPL history ring N times.
The direction of rotation is determined by the sign of N. For N
positive rotate to newer history elements, for N negative rotate
to older elements.
Return nil if the sentinel value is found before completing the
required number of rotations, otherwise return the element
rotated to, i.e. the one at index 0."
(let (ifun cidx ridx)
(if (> n 0)
(setq ifun 'ring-insert cidx -1 ridx -1)
(setq ifun 'ring-insert-at-beginning cidx 1 ridx 0))
(cl-loop
repeat (abs n)
;; Check that the next index to rotate to is not the sentinel
if (eq (ring-ref jupyter-repl-history cidx) 'jupyter-repl-history)
return nil else do
;; if it isn't, remove an element at RIDX and insert it using IFUN back
;; into the history ring, thereby rotating the history
(funcall ifun jupyter-repl-history
(ring-remove jupyter-repl-history ridx))
;; after N successful rotations, return the element rotated to
finally return (ring-ref jupyter-repl-history 0))))
(defun jupyter-repl-history-next (&optional n)
"Go to the next history element.
@ -986,7 +997,7 @@ older history elements."
(or n (setq n 1))
(if (< n 0) (jupyter-repl-history-previous (- n))
(goto-char (point-max))
(let ((code (jupyter-repl-history--next n)))
(let ((code (jupyter-repl-history--rotate n)))
(if (and (null code) (equal (jupyter-repl-cell-code) ""))
(error "End of history")
(if (null code)
@ -995,21 +1006,6 @@ older history elements."
(jupyter-repl-replace-cell-code "")
(jupyter-repl-replace-cell-code code))))))
(defun jupyter-repl-history--previous (n)
"Helper function for `jupyter-repl-history-previous'.
Rotates `jupyter-repl-history' N times in the backward direction,
towards older history elements and returns the Nth history
element in that direction relative to the current REPL history.
If the sentinel value is found before rotating N times, return
nil."
(if (> n 0)
(unless (eq (ring-ref jupyter-repl-history 1) 'jupyter-repl-history)
(ring-insert-at-beginning
jupyter-repl-history (ring-remove jupyter-repl-history 0))
(jupyter-repl-history--previous (1- n)))
(unless (eq (ring-ref jupyter-repl-history 0) 'jupyter-repl-history)
(ring-ref jupyter-repl-history 0))))
(defun jupyter-repl-history-previous (&optional n)
"Go to the previous history element.
Similar to `jupyter-repl-history-next' but for older history
@ -1022,7 +1018,7 @@ elements."
(unless (equal (jupyter-repl-cell-code)
(ring-ref jupyter-repl-history 0))
(setq n (1- n)))
(let ((code (jupyter-repl-history--previous n)))
(let ((code (jupyter-repl-history--rotate (- n))))
(if (null code)
(error "Beginning of history")
(jupyter-repl-replace-cell-code code)))))
@ -1495,9 +1491,9 @@ the kernel `jupyter-current-client' is connected to."
"Wrap the input history search when search fails.
Go to the oldest history element for a forward search or to the
newest history element for a backward search."
(if isearch-forward
(jupyter-repl-history--previous (ring-length jupyter-repl-history))
(jupyter-repl-history--next (ring-length jupyter-repl-history)))
(jupyter-repl-history--rotate
(* (if isearch-forward -1 1)
(ring-length jupyter-repl-history)))
(jupyter-repl-replace-cell-code (ring-ref jupyter-repl-history 0))
(goto-char (if isearch-forward (jupyter-repl-cell-code-beginning-position)
(point-max))))
@ -1513,13 +1509,12 @@ in the appropriate direction, to the saved element."
(let ((elem (ring-ref jupyter-repl-history 0)))
(lambda (_cmd)
(when isearch-wrapped
(if isearch-forward
(jupyter-repl-history--next (ring-length jupyter-repl-history))
(jupyter-repl-history--previous (ring-length jupyter-repl-history))))
(while (not (eq (ring-ref jupyter-repl-history 0) elem))
(if isearch-forward
(jupyter-repl-history--previous 1)
(jupyter-repl-history--next 1)))
(jupyter-repl-history--rotate
(* (if isearch-forward 1 -1)
(ring-length jupyter-repl-history))))
(let ((dir (if isearch-forward -1 1)))
(while (not (eq (ring-ref jupyter-repl-history 0) elem))
(jupyter-repl-history--rotate dir)))
(jupyter-repl-replace-cell-code (ring-ref jupyter-repl-history 0)))))
(t
(let ((elem code))

View file

@ -1101,16 +1101,30 @@ last element being the newest element added to the history."
(ert-deftest jupyter-repl-history ()
:tags '(repl)
(jupyter-test-with-python-repl client
(jupyter-ert-info ("Rotating REPL history ring")
(let (jupyter-repl-history)
(ert-info ("Rotating REPL history ring")
(jupyter-test-set-dummy-repl-history)
(should (null (jupyter-repl-history--next 1)))
(should (equal (jupyter-repl-history--next 0) "3"))
(should (equal (jupyter-repl-history--previous 1) "2"))
(should (equal (jupyter-repl-history--next 1) "3"))
(should (null (jupyter-repl-history--previous 4)))
(should (equal (ring-ref jupyter-repl-history 0) "1")))
(ert-info ("Replacing cell contents with history")
(should (null (jupyter-repl-history--rotate 1)))
(ert-info ("No rotation")
(should (equal (ring-elements jupyter-repl-history)
'("3" "2" "1" jupyter-repl-history)))
(should (equal (jupyter-repl-history--rotate 0) "3"))
(should (equal (ring-elements jupyter-repl-history)
'("3" "2" "1" jupyter-repl-history))))
(ert-info ("Rotate to older elements")
(should (equal (jupyter-repl-history--rotate -1) "2"))
(should (equal (ring-elements jupyter-repl-history)
'("2" "1" jupyter-repl-history "3"))))
(ert-info ("Rotate to newer elements")
(should (equal (jupyter-repl-history--rotate 1) "3"))
(should (equal (ring-elements jupyter-repl-history)
'("3" "2" "1" jupyter-repl-history))))
(ert-info ("Rotations stop at sentinel")
(should (null (jupyter-repl-history--rotate -4)))
(should (equal (ring-elements jupyter-repl-history)
'("1" jupyter-repl-history "3" "2"))))))
(jupyter-test-with-python-repl client
(jupyter-ert-info ("Replacing cell contents with history")
(jupyter-test-set-dummy-repl-history)
(should (equal (jupyter-repl-cell-code) ""))
(jupyter-repl-history-previous)