aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-24 00:06:01 -0400
committerCraig Jennings <c@cjennings.net>2026-06-24 00:06:01 -0400
commit83f2ac011f1afb085745ea527990eea32019fc00 (patch)
tree4916f84bbe9b99d3c3365ff5255ac6c28e36a8d8 /tests
parentbf947111dd6d894e30191148ee1d0314df56eb25 (diff)
downloaddotemacs-83f2ac011f1afb085745ea527990eea32019fc00.tar.gz
dotemacs-83f2ac011f1afb085745ea527990eea32019fc00.zip
feat(term): modified arrows enter copy-mode and carry direction
C-<arrow> and M-<arrow> in a ghostel buffer now enter copy-mode and move one step in that direction in a single stroke. The tmux path writes the arrow escape sequence into the pty so the copy cursor follows it; without tmux the same keys enter ghostel-copy-mode and move point. All eight keys join ghostel-keymap-exceptions and the semi-char map is rebuilt, so they reach Emacs instead of the terminal program. Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
Diffstat (limited to 'tests')
-rw-r--r--tests/test-term-tmux-history.el81
1 files changed, 81 insertions, 0 deletions
diff --git a/tests/test-term-tmux-history.el b/tests/test-term-tmux-history.el
index 4ad7fb79d..89b4875eb 100644
--- a/tests/test-term-tmux-history.el
+++ b/tests/test-term-tmux-history.el
@@ -355,5 +355,86 @@ Emacs region gets stuck in the ghostel buffer and tmux copy-mode's
begin-selection never starts."
(should (eq (keymap-lookup ghostel-mode-map "C-SPC") #'cj/term-send-C-SPC)))
+;; --------------------------- directional copy mode ---------------------------
+
+(ert-deftest test-term-copy-mode-step-tmux-sends-arrow ()
+ "Normal: in tmux, each direction writes its arrow escape sequence to the pty."
+ (dolist (case '((up . "\e[A") (down . "\e[B") (left . "\e[D") (right . "\e[C")))
+ (let ((sent nil))
+ (cl-letf (((symbol-function 'ghostel-send-string)
+ (lambda (s) (push s sent))))
+ (cj/--term-copy-mode-move-step (car case) t)
+ (should (equal sent (list (cdr case))))))))
+
+(ert-deftest test-term-copy-mode-step-nontmux-moves-point ()
+ "Normal: without tmux, up/down move by line and left/right move by char."
+ (with-temp-buffer
+ (insert "abc\ndef\nghi\n")
+ ;; up/down: land on line 2, step around.
+ (goto-char (point-min))
+ (forward-line 1)
+ (should (= (line-number-at-pos) 2))
+ (cj/--term-copy-mode-move-step 'up nil)
+ (should (= (line-number-at-pos) 1))
+ (cj/--term-copy-mode-move-step 'down nil)
+ (should (= (line-number-at-pos) 2))
+ ;; left/right: move by a single character.
+ (goto-char 2)
+ (cj/--term-copy-mode-move-step 'right nil)
+ (should (= (point) 3))
+ (cj/--term-copy-mode-move-step 'left nil)
+ (should (= (point) 2))))
+
+(ert-deftest test-term-copy-mode-step-unknown-direction-no-op ()
+ "Boundary: an unknown direction sends nothing in the tmux branch."
+ (let ((sent nil))
+ (cl-letf (((symbol-function 'ghostel-send-string)
+ (lambda (s) (push s sent))))
+ (cj/--term-copy-mode-move-step 'sideways t)
+ (should-not sent))))
+
+(ert-deftest test-term-copy-mode-up-command-enters-then-steps ()
+ "Normal: the bound command enters tmux copy-mode, then sends the arrow,
+so a single modified arrow both enters copy-mode and carries its direction."
+ (let ((agent (cj/test--make-fake-ghostel-buffer "agent [emacs.d]"))
+ (sent nil))
+ (unwind-protect
+ (with-current-buffer agent
+ (cl-letf (((symbol-function 'get-buffer-process)
+ (lambda (_buffer) 'fake-process))
+ ((symbol-function 'process-tty-name)
+ (lambda (_process &rest _) "/dev/pts/8"))
+ ((symbol-function 'ghostel-send-string)
+ (lambda (s) (push s sent))))
+ (test-term-tmux-history--with-tmux-mock
+ '((("list-clients" "-F" "#{client_tty}\t#{pane_id}") 0
+ "/dev/pts/8\t%8\n"))
+ (cj/term-copy-mode-up)
+ (should (equal (reverse sent) '("\C-b[\C-a" "\e[A"))))))
+ (when (buffer-live-p agent)
+ (kill-buffer agent)))))
+
+(ert-deftest test-term-copy-mode-arrows-bound-in-ghostel-map ()
+ "Normal: C-<arrow> and M-<arrow> both reach the directional copy-mode commands."
+ (dolist (case '(("C-<up>" . cj/term-copy-mode-up)
+ ("M-<up>" . cj/term-copy-mode-up)
+ ("C-<down>" . cj/term-copy-mode-down)
+ ("M-<down>" . cj/term-copy-mode-down)
+ ("C-<left>" . cj/term-copy-mode-left)
+ ("M-<left>" . cj/term-copy-mode-left)
+ ("C-<right>" . cj/term-copy-mode-right)
+ ("M-<right>" . cj/term-copy-mode-right)))
+ (should (eq (keymap-lookup ghostel-mode-map (car case)) (cdr case)))))
+
+(ert-deftest test-term-copy-mode-arrows-in-keymap-exceptions ()
+ "Regression: C-/M-<arrow> are in `ghostel-keymap-exceptions' and the rebuilt
+semi-char map no longer forwards them to the pty, so they reach Emacs and
+trigger copy-mode entry from inside a ghostel buffer."
+ (dolist (key '("C-<up>" "C-<down>" "C-<left>" "C-<right>"
+ "M-<up>" "M-<down>" "M-<left>" "M-<right>"))
+ (should (member key ghostel-keymap-exceptions)))
+ (should-not (eq (keymap-lookup ghostel-semi-char-mode-map "C-<up>")
+ 'ghostel--send-event)))
+
(provide 'test-term-tmux-history)
;;; test-term-tmux-history.el ends here