|
12 | 12 | :listener-execute-function |
13 | 13 | :start-listener-mode |
14 | 14 | :refresh-prompt |
15 | | - :clamp-cursor-to-input-area)) |
| 15 | + :clamp-cursor-to-input-area |
| 16 | + :listener-previous-startswith-input |
| 17 | + :listener-next-startswith-input)) |
16 | 18 | (in-package :lem-tests/listener-mode) |
17 | 19 |
|
18 | 20 | (defun setup-listener-buffer () |
|
35 | 37 | (with-current-buffers () |
36 | 38 | (let ((buffer (setup-listener-buffer))) |
37 | 39 | (testing "cursor at input-start-point is not moved" |
38 | | - (move-point (current-point) (input-start-point buffer)) |
39 | | - (lem/listener-mode:clamp-cursor-to-input-area) |
40 | | - (ok (point= (current-point) (input-start-point buffer)))) |
| 40 | + (move-point (current-point) (input-start-point buffer)) |
| 41 | + (lem/listener-mode:clamp-cursor-to-input-area) |
| 42 | + (ok (point= (current-point) (input-start-point buffer)))) |
41 | 43 |
|
42 | 44 | (testing "cursor before input-start-point on same line is clamped" |
43 | | - (line-start (current-point)) |
44 | | - (ok (point< (current-point) (input-start-point buffer))) |
45 | | - (lem/listener-mode:clamp-cursor-to-input-area) |
46 | | - (ok (point= (current-point) (input-start-point buffer)))) |
| 45 | + (line-start (current-point)) |
| 46 | + (ok (point< (current-point) (input-start-point buffer))) |
| 47 | + (lem/listener-mode:clamp-cursor-to-input-area) |
| 48 | + (ok (point= (current-point) (input-start-point buffer)))) |
47 | 49 |
|
48 | 50 | (testing "cursor after input-start-point is not moved" |
49 | | - (buffer-end (current-point)) |
50 | | - (insert-string (current-point) "hello") |
51 | | - (let ((pos (copy-point (current-point) :temporary))) |
52 | | - (lem/listener-mode:clamp-cursor-to-input-area) |
53 | | - (ok (point= (current-point) pos)))) |
| 51 | + (buffer-end (current-point)) |
| 52 | + (insert-string (current-point) "hello") |
| 53 | + (let ((pos (copy-point (current-point) :temporary))) |
| 54 | + (lem/listener-mode:clamp-cursor-to-input-area) |
| 55 | + (ok (point= (current-point) pos)))) |
54 | 56 |
|
55 | 57 | (testing "cursor on previous line is not clamped" |
56 | | - ;; Add a newline in the input area so cursor can go to a previous line |
57 | | - (move-point (current-point) (input-start-point buffer)) |
58 | | - (character-offset (current-point) -1) |
59 | | - ;; cursor is now on a line before input-start-point's line |
60 | | - (unless (same-line-p (current-point) (input-start-point buffer)) |
61 | | - (lem/listener-mode:clamp-cursor-to-input-area) |
62 | | - (ok (not (point= (current-point) (input-start-point buffer)))))))))) |
| 58 | + ;; Add a newline in the input area so cursor can go to a previous line |
| 59 | + (move-point (current-point) (input-start-point buffer)) |
| 60 | + (character-offset (current-point) -1) |
| 61 | + ;; cursor is now on a line before input-start-point's line |
| 62 | + (unless (same-line-p (current-point) (input-start-point buffer)) |
| 63 | + (lem/listener-mode:clamp-cursor-to-input-area) |
| 64 | + (ok (not (point= (current-point) (input-start-point buffer)))))))))) |
| 65 | + |
| 66 | +;; make-history does not need pathname? |
| 67 | + |
| 68 | +(deftest listener-history-search-state-and-geometry |
| 69 | + (with-fake-interface () |
| 70 | + (with-current-buffers () |
| 71 | + (let* ((buffer (setup-listener-buffer)) |
| 72 | + (test-history (lem/common/history:make-history))) |
| 73 | + |
| 74 | + (setf (buffer-value buffer 'lem/listener-mode::%listener-history) |
| 75 | + test-history) |
| 76 | + |
| 77 | + (lem/common/history:add-history test-history "hello") |
| 78 | + (lem/common/history:add-history test-history "world") |
| 79 | + (lem/common/history:add-history test-history "hector") |
| 80 | + |
| 81 | + (testing "Successful previous match perfectly preserves prefix cursor offset" |
| 82 | + ;; Type our unsubmitted prefix "'he" |
| 83 | + (move-point (current-point) (input-start-point buffer)) |
| 84 | + (insert-string (current-point) "he") |
| 85 | + |
| 86 | + ;; Trigger the search |
| 87 | + (listener-previous-startswith-input) |
| 88 | + |
| 89 | + ;; Assert 1: The buffer text was completely replaced with the newest match |
| 90 | + (let ((full-input (points-to-string (input-start-point buffer) |
| 91 | + (buffer-end-point buffer)))) |
| 92 | + (ok (string= "hector" full-input))) |
| 93 | + |
| 94 | + ;; Assert 2: The cursor is resting exactly at the end of the "he" prefix. |
| 95 | + ;; This proves our linear offset math is working and preventing the timer crash! |
| 96 | + (let ((cursor-prefix (points-to-string (input-start-point buffer) |
| 97 | + (current-point)))) |
| 98 | + (ok (string= "he" cursor-prefix)))) |
| 99 | + |
| 100 | + (testing "State drift is prevented on failed searches and rollback succeeds" |
| 101 | + ;; We are currently viewing "hector". |
| 102 | + ;; Search previous again to hit the oldest match ("hello"). |
| 103 | + (listener-previous-startswith-input) |
| 104 | + (ok (string= "hello" (points-to-string (input-start-point buffer) |
| 105 | + (buffer-end-point buffer)))) |
| 106 | + |
| 107 | + ;; Now we force the failure state. Searching previous again will find nothing. |
| 108 | + ;; Because of our fix, the internal loop should rewind its phantom steps. |
| 109 | + (listener-previous-startswith-input) |
| 110 | + (ok (string= "hello" (points-to-string (input-start-point buffer) |
| 111 | + (buffer-end-point buffer)))) |
| 112 | + |
| 113 | + ;; If the rewind worked, stepping 'Next' exactly twice should traverse |
| 114 | + ;; perfectly back through "hector" and hit the Rollback state. |
| 115 | + (listener-next-startswith-input) |
| 116 | + (ok (string= "hector" (points-to-string (input-start-point buffer) |
| 117 | + (buffer-end-point buffer)))) |
| 118 | + |
| 119 | + ;; The final step Next: Rollback triggers! |
| 120 | + (listener-next-startswith-input) |
| 121 | + |
| 122 | + ;; Assert 3: The unsubmitted text was flawlessly restored |
| 123 | + (ok (string= "he" (points-to-string (input-start-point buffer) |
| 124 | + (buffer-end-point buffer)))) |
| 125 | + |
| 126 | + ;; Assert 4: The rollback explicitly clamped the cursor safely |
| 127 | + (let ((cursor-prefix (points-to-string (input-start-point buffer) |
| 128 | + (current-point)))) |
| 129 | + (ok (string= "he" cursor-prefix)))))))) |
0 commit comments