aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-06 12:47:24 -0500
committerCraig Jennings <c@cjennings.net>2026-06-06 12:47:24 -0500
commit110644a48ad63353cfe19110248d3515b4537b58 (patch)
tree9afc7211f9321b5cfecfdd5a2328818f34792d11
parent0946c42f5caaf715786780b7be8205e6ac289d8a (diff)
downloaddotemacs-110644a48ad63353cfe19110248d3515b4537b58.tar.gz
dotemacs-110644a48ad63353cfe19110248d3515b4537b58.zip
fix(term): land copy-mode cursor at column 0
Entering copy-mode from C-; x c left the cursor at the live column, far right after a prompt, so scrolling up ran the cursor up the right edge instead of the left. In the tmux branch I append C-a after C-b [, which tmux's emacs copy-mode reads as start-of-line. It has to go after C-b [: before copy-mode is active, C-a would hit the shell's readline instead. In the non-tmux ghostel-copy-mode branch I call beginning-of-line after entering the view, for the same column-0 result. Both branches now run the cursor up the left edge. The non-tmux test asserts ghostel-copy-mode runs before beginning-of-line, since the move only repositions inside an active copy view. Its tracker variable is dwim-order, not calls, to avoid clashing with the variable the tmux mock macro binds.
-rw-r--r--modules/term-config.el24
-rw-r--r--tests/test-term-tmux-history.el23
2 files changed, 32 insertions, 15 deletions
diff --git a/modules/term-config.el b/modules/term-config.el
index 7cd386dc..2daebe9b 100644
--- a/modules/term-config.el
+++ b/modules/term-config.el
@@ -29,10 +29,12 @@
;; Two ways to lift text out of a terminal, both with the same key story:
;; - C-; x c enters copy-mode via `cj/term-copy-mode-dwim'. When a tmux
;; client is attached (typical -- `cj/term-launch-tmux' auto-starts tmux),
-;; sends tmux's prefix C-b [ so the user lands in tmux's own copy-mode with
-;; the full pane history available. Without tmux, falls back to
+;; sends tmux's prefix C-b [ then C-a, so the user lands in tmux's own
+;; copy-mode with the full pane history and the cursor at column 0 (so
+;; scrolling up runs up the left, not the right). Without tmux, falls back to
;; `ghostel-copy-mode' (read-only standard-Emacs navigation over the
-;; scrollback; M-w copies and stays, q / C-g exit).
+;; scrollback; M-w copies and stays, q / C-g exit) and moves point to the
+;; start of the line for the same column-0 reason.
;; - C-; x h captures the current tmux pane's full history into a temporary
;; Emacs buffer.
;; In both copy surfaces, M-w copies the active region and stays open so several
@@ -190,13 +192,19 @@ cheap boolean predicate."
"Enter copy-mode using the engine appropriate to this terminal.
When tmux is attached, write tmux's default prefix sequence (C-b [) into the
-pty so the user lands in tmux's copy-mode with the full pane history. Without
-tmux, falls through to `ghostel-copy-mode', a read-only standard-Emacs view of
-the scrollback (M-w copies and stays, q / C-g exit)."
+pty so the user lands in tmux's copy-mode with the full pane history, then
+C-a to land the cursor at the start of the line. Without the trailing C-a
+the copy cursor inherits the live column (far right after a prompt) and
+scrolling up runs up the right edge; tmux's emacs copy-mode binds C-a to
+start-of-line, so column 0 makes it run up the left. Without tmux, falls
+through to `ghostel-copy-mode' (a read-only standard-Emacs view of the
+scrollback; M-w copies and stays, q / C-g exit), then moves point to the
+start of the line for the same column-0 reason."
(interactive)
(if (cj/term--in-tmux-p)
- (ghostel-send-string "\C-b[")
- (ghostel-copy-mode)))
+ (ghostel-send-string "\C-b[\C-a")
+ (ghostel-copy-mode)
+ (beginning-of-line)))
;; ----------------------------- ghostel package -------------------------------
diff --git a/tests/test-term-tmux-history.el b/tests/test-term-tmux-history.el
index b7ab5351..51e9725c 100644
--- a/tests/test-term-tmux-history.el
+++ b/tests/test-term-tmux-history.el
@@ -260,8 +260,11 @@ ghostel-mode terminal."
(should-not tmux-called)))))
(ert-deftest test-term-copy-mode-dwim-sends-tmux-prefix-when-attached ()
- "Normal: with tmux attached, dwim writes C-b [ into the pty so tmux enters
-its own copy-mode against the full pane history."
+ "Normal: with tmux attached, dwim writes C-b [ then C-a into the pty so
+tmux enters its own copy-mode and lands the cursor at the start of the
+line. Without the trailing C-a the cursor inherits the live column (far
+right after a prompt) and scrolling up runs up the right edge; start-of-line
+puts it at column 0 so it runs up the left."
(let ((agent (cj/test--make-fake-ghostel-buffer "agent [emacs.d]"))
(sent nil)
(copy-mode-called nil))
@@ -279,16 +282,20 @@ its own copy-mode against the full pane history."
'((("list-clients" "-F" "#{client_tty}\t#{pane_id}") 0
"/dev/pts/8\t%8\n"))
(cj/term-copy-mode-dwim)
- (should (equal sent '("\C-b[")))
+ (should (equal sent '("\C-b[\C-a")))
(should-not copy-mode-called))))
(when (buffer-live-p agent)
(kill-buffer agent)))))
(ert-deftest test-term-copy-mode-dwim-falls-back-without-tmux ()
- "Boundary: without tmux, dwim calls `ghostel-copy-mode' and sends nothing."
+ "Boundary: without tmux, dwim calls `ghostel-copy-mode' then moves point
+to the start of the line and sends nothing to the pty. The
+`beginning-of-line' must run after `ghostel-copy-mode' so it repositions
+inside the copy view; column 0 keeps the cursor on the left edge while
+scrolling, parity with the tmux branch's trailing C-a."
(let ((agent (cj/test--make-fake-ghostel-buffer "agent [emacs.d]"))
(sent nil)
- (copy-mode-called nil))
+ (dwim-order nil))
(unwind-protect
(with-current-buffer agent
(cl-letf (((symbol-function 'get-buffer-process)
@@ -298,13 +305,15 @@ its own copy-mode against the full pane history."
((symbol-function 'ghostel-send-string)
(lambda (s) (push s sent)))
((symbol-function 'ghostel-copy-mode)
- (lambda () (setq copy-mode-called t))))
+ (lambda () (push 'copy-mode dwim-order)))
+ ((symbol-function 'beginning-of-line)
+ (lambda (&optional _n) (push 'beginning-of-line dwim-order))))
(test-term-tmux-history--with-tmux-mock
'((("list-clients" "-F" "#{client_tty}\t#{pane_id}") 1
"no server running"))
(cj/term-copy-mode-dwim)
(should-not sent)
- (should copy-mode-called))))
+ (should (equal (reverse dwim-order) '(copy-mode beginning-of-line))))))
(when (buffer-live-p agent)
(kill-buffer agent)))))