From 554b32db5da9630fa24fb2abf3f93f21b03ff7a0 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 9 May 2026 11:58:52 -0500 Subject: feat(vterm): F12 toggle that excludes claude and preserves geometry vterm-toggle picked the most-recently-selected vterm buffer as F12's toggle target. After using F9 on a claude vterm, the most-recent vterm IS claude, so F12 ended up toggling claude, which has its own F9 / C-F9 / M-F9 surface in ai-vterm.el and shouldn't be affected. The display rule also had a hard-coded `(window-height . 0.7)` that overrode mouse-resize and orientation flips on every toggle. I replaced the F12 binding with `cj/vterm-toggle` in `eshell-vterm-config.el`, mirroring the pattern shipped in ai-vterm.el: - `cj/--vterm-toggle-buffer-p` excludes claude-prefixed buffers from F12's candidate set. - `cj/--vterm-toggle-capture-state` records direction + body size at toggle-off. - `cj/--vterm-toggle-display-saved` replays via `(body-columns . N)` / `(body-lines . N)` cons forms with the cardinal direction mapped to its frame-edge variant (`right` -> `rightmost`, `below` -> `bottom`, etc.) so vterm always lands at the captured edge regardless of selected window. - `cj/vterm-toggle` uses `delete-window` (with `one-window-p` guard) on toggle-off so buffer-move scenarios don't leak ghost windows. Default direction is `'below` to match F12's traditional bottom split. The vterm-toggle package stays installed so `M-x vterm-toggle` still works. Only the F12 binding changes. 19 new tests across three files: buffer-filter, dispatch, display. Full make test green. Tradeoff: ~150 lines of geometry helpers and capture/display action logic are duplicated from ai-vterm.el. Worth extracting into a shared module now that two consumers exist. I filed it as a follow-up rather than blocking this ship. --- tests/test-vterm-toggle--buffer-filter.el | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/test-vterm-toggle--buffer-filter.el (limited to 'tests/test-vterm-toggle--buffer-filter.el') diff --git a/tests/test-vterm-toggle--buffer-filter.el b/tests/test-vterm-toggle--buffer-filter.el new file mode 100644 index 00000000..8deb9066 --- /dev/null +++ b/tests/test-vterm-toggle--buffer-filter.el @@ -0,0 +1,103 @@ +;;; test-vterm-toggle--buffer-filter.el --- Tests for F12's buffer filter -*- lexical-binding: t; -*- + +;;; Commentary: +;; Three closely-related helpers determine which vterm buffers F12 +;; manages: the predicate `cj/--vterm-toggle-buffer-p', the MRU list +;; `cj/--vterm-toggle-buffers', and the per-frame window finder +;; `cj/--vterm-toggle-displayed-window'. All three exclude claude- +;; prefixed buffers so claude has its own F9 surface. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'eshell-vterm-config) + +(defun test-vterm-toggle--cleanup () + "Kill any test-prefixed vterm-style buffers left behind." + (dolist (b (buffer-list)) + (let ((name (buffer-name b))) + (when (or (string-prefix-p "*test-vterm" name) + (string-prefix-p "claude [" name)) + (kill-buffer b))))) + +(defun test-vterm-toggle--make-vterm-buffer (name) + "Create BUFFER with vterm-mode for testing. +Avoids actually launching a vterm process by manually setting major-mode." + (let ((buf (get-buffer-create name))) + (with-current-buffer buf + (setq-local major-mode 'vterm-mode)) + buf)) + +(ert-deftest test-vterm-toggle--buffer-p-accepts-vterm-mode () + "Normal: a vterm-mode buffer with non-claude name qualifies." + (test-vterm-toggle--cleanup) + (let ((buf (test-vterm-toggle--make-vterm-buffer "*test-vterm-1*"))) + (unwind-protect + (should (cj/--vterm-toggle-buffer-p buf)) + (kill-buffer buf)))) + +(ert-deftest test-vterm-toggle--buffer-p-rejects-claude () + "Boundary: claude-prefixed vterm buffers are excluded from F12's set." + (test-vterm-toggle--cleanup) + (let ((buf (test-vterm-toggle--make-vterm-buffer "claude [project-a]"))) + (unwind-protect + (should-not (cj/--vterm-toggle-buffer-p buf)) + (kill-buffer buf)))) + +(ert-deftest test-vterm-toggle--buffer-p-rejects-non-vterm () + "Boundary: a regular buffer (not vterm-mode, no vterm name prefix) -> nil." + (test-vterm-toggle--cleanup) + (let ((buf (get-buffer-create "*test-vterm-regular*"))) + (unwind-protect + (should-not (cj/--vterm-toggle-buffer-p buf)) + (kill-buffer buf)))) + +(ert-deftest test-vterm-toggle--buffer-p-rejects-dead-buffer () + "Boundary: nil and dead buffers -> nil." + (should-not (cj/--vterm-toggle-buffer-p nil)) + (let ((buf (test-vterm-toggle--make-vterm-buffer "*test-vterm-dead*"))) + (kill-buffer buf) + (should-not (cj/--vterm-toggle-buffer-p buf)))) + +(ert-deftest test-vterm-toggle--buffers-filters-claude () + "Normal: returns vterm buffers but excludes claude-prefixed ones." + (test-vterm-toggle--cleanup) + (let ((normal (test-vterm-toggle--make-vterm-buffer "*test-vterm-normal*")) + (claude (test-vterm-toggle--make-vterm-buffer "claude [for-test]"))) + (unwind-protect + (let ((result (cj/--vterm-toggle-buffers))) + (should (memq normal result)) + (should-not (memq claude result))) + (kill-buffer normal) + (kill-buffer claude)))) + +(ert-deftest test-vterm-toggle--displayed-window-finds-vterm () + "Normal: vterm in a window -> returns that window." + (test-vterm-toggle--cleanup) + (let ((vt (test-vterm-toggle--make-vterm-buffer "*test-vterm-shown*"))) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (let ((win (split-window-right))) + (set-window-buffer win vt) + (let ((result (cj/--vterm-toggle-displayed-window))) + (should (windowp result)) + (should (eq (window-buffer result) vt))))) + (kill-buffer vt)))) + +(ert-deftest test-vterm-toggle--displayed-window-skips-claude () + "Boundary: only a claude vterm is displayed -> nil (claude not F12-managed)." + (test-vterm-toggle--cleanup) + (let ((claude (test-vterm-toggle--make-vterm-buffer "claude [skip-test]"))) + (unwind-protect + (save-window-excursion + (delete-other-windows) + (let ((win (split-window-right))) + (set-window-buffer win claude) + (should-not (cj/--vterm-toggle-displayed-window)))) + (kill-buffer claude)))) + +(provide 'test-vterm-toggle--buffer-filter) +;;; test-vterm-toggle--buffer-filter.el ends here -- cgit v1.2.3