From 3003683a24cf38dbd2eeeaee6244ad0c1bbe72ee Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 10 May 2026 03:48:44 -0500 Subject: fix(vterm): force a visible cursor in vterm-copy-mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vterm's C module sets `cursor-type' to nil whenever the underlying TUI sends DECTCEM (`\e[?25l') to hide the terminal cursor. Most full-screen TUIs do this on startup — Claude Code in an ai-vterm being a daily example. Once the cursor is hidden at the buffer level, vterm-copy-mode inherits that nil and the user can't see where point is when navigating to select text. Selection still works, but you're flying blind. Add a `vterm-copy-mode-hook' that forces `cursor-type' to a 3-pixel bar on entry and kills the buffer-local override on exit. The bar shape is drawn between characters rather than by inverting one, so heavy TUI face properties don't hide it either. On exit the live terminal goes back to whatever vterm's tracking says, so the TUI's chosen cursor state resumes. 4 ERT tests cover the hook's enter/exit behavior and confirm registration on `vterm-copy-mode-hook'. --- tests/test-vterm-copy-mode-cursor.el | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test-vterm-copy-mode-cursor.el (limited to 'tests') diff --git a/tests/test-vterm-copy-mode-cursor.el b/tests/test-vterm-copy-mode-cursor.el new file mode 100644 index 00000000..ee72a0bb --- /dev/null +++ b/tests/test-vterm-copy-mode-cursor.el @@ -0,0 +1,51 @@ +;;; test-vterm-copy-mode-cursor.el --- Tests for cursor visibility in vterm-copy-mode -*- lexical-binding: t; -*- + +;;; Commentary: +;; vterm's C module sets `cursor-type' to nil when the underlying TUI +;; sends DECTCEM (`\e[?25l'). Most full-screen TUIs (Claude Code, htop, +;; etc.) hide the cursor on startup. In `vterm-copy-mode' the user is +;; navigating the buffer, not watching the TUI, so the cursor must be +;; forced visible -- the hook in `vterm-config.el' handles that. On +;; exit, the buffer-local override is killed so the live terminal goes +;; back to the TUI's chosen cursor state. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(defvar vterm-copy-mode nil) +(require 'vterm-config) + +(ert-deftest test-vterm-copy-mode-cursor-restored-on-enter () + "Normal: entering copy-mode with cursor-type nil sets a visible cursor." + (with-temp-buffer + (setq-local cursor-type nil) + (let ((vterm-copy-mode t)) + (cj/--vterm-copy-mode-restore-cursor)) + (should (equal cursor-type '(bar . 3))))) + +(ert-deftest test-vterm-copy-mode-cursor-restored-when-prior-was-box () + "Boundary: entering copy-mode overrides any prior cursor-type with the bar." + (with-temp-buffer + (setq-local cursor-type 'box) + (let ((vterm-copy-mode t)) + (cj/--vterm-copy-mode-restore-cursor)) + (should (equal cursor-type '(bar . 3))))) + +(ert-deftest test-vterm-copy-mode-cursor-override-killed-on-exit () + "Normal: exiting copy-mode kills the buffer-local cursor-type override." + (with-temp-buffer + (setq-local cursor-type '(bar . 3)) + (should (local-variable-p 'cursor-type)) + (let ((vterm-copy-mode nil)) + (cj/--vterm-copy-mode-restore-cursor)) + (should-not (local-variable-p 'cursor-type)))) + +(ert-deftest test-vterm-copy-mode-cursor-hook-installed () + "Normal: the cursor-restoration hook is registered on vterm-copy-mode-hook." + (should (memq #'cj/--vterm-copy-mode-restore-cursor + vterm-copy-mode-hook))) + +(provide 'test-vterm-copy-mode-cursor) +;;; test-vterm-copy-mode-cursor.el ends here -- cgit v1.2.3