diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-02 00:00:41 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-02 00:00:41 -0400 |
| commit | 37d92510afbaea8609e8aa3612c6e9d27edba12d (patch) | |
| tree | 78820bdb59e4f8334528dcc490b63151e619d0ac /tests | |
| parent | 60560d1eb346c76de355a524e78ae389e8e07807 (diff) | |
| download | dotemacs-37d92510afbaea8609e8aa3612c6e9d27edba12d.tar.gz dotemacs-37d92510afbaea8609e8aa3612c6e9d27edba12d.zip | |
feat(modeline): mode icons, status segments, and a repair command
I rebuilt the custom modeline as pure segment helpers with thin :eval wiring:
- The nerd-icons mode icon replaces the mode-name text (cached per buffer, plain name on terminal frames), with the full mode name in the help-echo.
- New left-side segments: modified dot / read-only lock (file buffers only), remote @host tag, Narrow tag that widens on click, point-based percentage, region selection info, and a MACRO tag while a keyboard macro records.
- New right-side segments: mode-line-process (eat and compilation state was invisible) and flycheck per-severity counts with click-through to the error list, replacing the stock status text. Glyphs are nerd-icons private-use codepoints so emojify can't rewrite them, with text fallbacks when icons are unavailable.
- cj/modeline-reset kills a hijacked buffer-local mode-line-format (two-column mode, ediff, calc).
- Optional taller bar via cj/modeline-height-factor, a display height property on the padding space so the theme's mode-line faces stay untouched.
- Housekeeping: the dead user-constants require and stale commentary are gone, cj/modeline-vc-faces left the risky-local-variable list, and the cache-key defun is cj/--modeline-vc-cache-key so it no longer shadows the same-named defvar.
Percent signs from :eval strings go through the mode-line %-construct pass, so the position segment emits %% for a literal percent.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-modeline-config-buffer-status.el | 73 | ||||
| -rw-r--r-- | tests/test-modeline-config-flycheck-render.el | 51 | ||||
| -rw-r--r-- | tests/test-modeline-config-flycheck-segment.el | 6 | ||||
| -rw-r--r-- | tests/test-modeline-config-reset.el | 37 | ||||
| -rw-r--r-- | tests/test-modeline-config-segments.el | 122 | ||||
| -rw-r--r-- | tests/test-modeline-config-vc-cache-key.el | 10 |
6 files changed, 291 insertions, 8 deletions
diff --git a/tests/test-modeline-config-buffer-status.el b/tests/test-modeline-config-buffer-status.el new file mode 100644 index 00000000..123f1062 --- /dev/null +++ b/tests/test-modeline-config-buffer-status.el @@ -0,0 +1,73 @@ +;;; test-modeline-config-buffer-status.el --- buffer-status segment -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for `cj/--modeline-buffer-status': the modified / read-only +;; indicator. Read-only wins over modified; the modified dot shows only +;; for file-visiting buffers (special buffers are perpetually modified +;; and would be noise); clean file buffers show nothing. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +(require 'modeline-config) + +(ert-deftest test-modeline-config-buffer-status-modified-file-shows-dot () + "Normal: a modified file-visiting buffer returns the warning-faced dot." + (let ((file (make-temp-file "modeline-status-" nil ".txt"))) + (unwind-protect + (with-current-buffer (find-file-noselect file) + (insert "x") + (let ((status (cj/--modeline-buffer-status))) + (should (stringp status)) + (should (string-match-p "●" status)) + (should (eq (get-text-property + (string-match "●" status) 'face status) + 'warning))) + (set-buffer-modified-p nil) + (kill-buffer)) + (delete-file file)))) + +(ert-deftest test-modeline-config-buffer-status-clean-file-nil () + "Normal: an unmodified file-visiting buffer returns nil." + (let ((file (make-temp-file "modeline-status-" nil ".txt"))) + (unwind-protect + (with-current-buffer (find-file-noselect file) + (should-not (cj/--modeline-buffer-status)) + (kill-buffer)) + (delete-file file)))) + +(ert-deftest test-modeline-config-buffer-status-read-only-shows-lock () + "Normal: a read-only buffer returns the read-only indicator." + (with-temp-buffer + (setq buffer-read-only t) + (let ((status (cj/--modeline-buffer-status))) + (should (stringp status)) + (should (> (length status) 0))))) + +(ert-deftest test-modeline-config-buffer-status-read-only-wins-over-modified () + "Boundary: read-only + modified shows the read-only indicator, not the dot." + (let ((file (make-temp-file "modeline-status-" nil ".txt"))) + (unwind-protect + (with-current-buffer (find-file-noselect file) + (insert "x") + (setq buffer-read-only t) + (let ((status (cj/--modeline-buffer-status))) + (should (stringp status)) + (should-not (string-match-p "●" status))) + (setq buffer-read-only nil) + (set-buffer-modified-p nil) + (kill-buffer)) + (delete-file file)))) + +(ert-deftest test-modeline-config-buffer-status-modified-non-file-nil () + "Boundary: a modified non-file buffer (scratch-like) returns nil." + (with-temp-buffer + (insert "x") + (should (buffer-modified-p)) + (should-not (cj/--modeline-buffer-status)))) + +(provide 'test-modeline-config-buffer-status) +;;; test-modeline-config-buffer-status.el ends here diff --git a/tests/test-modeline-config-flycheck-render.el b/tests/test-modeline-config-flycheck-render.el new file mode 100644 index 00000000..46d0cdef --- /dev/null +++ b/tests/test-modeline-config-flycheck-render.el @@ -0,0 +1,51 @@ +;;; test-modeline-config-flycheck-render.el --- flycheck counts rendering -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for `cj/--modeline-flycheck-render', the pure formatter that +;; turns a flycheck counts alist ((error . N) (warning . M) ...) into a +;; propertized modeline string. Errors carry the error face, warnings +;; the warning face; zero-count severities are omitted; an all-clean +;; alist (or nil) renders nothing. Glyphs come from nerd-icons when +;; available (private-use codepoints, safe from emojify) with plain-text +;; fallbacks in batch mode. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +(require 'modeline-config) + +(ert-deftest test-modeline-config-flycheck-render-errors-and-warnings () + "Normal: both counts render, error face on errors, warning on warnings." + (let ((s (cj/--modeline-flycheck-render '((error . 2) (warning . 5))))) + (should (stringp s)) + (should (string-match-p "2" s)) + (should (string-match-p "5" s)) + (let ((epos (string-match "2" s)) + (wpos (string-match "5" s))) + (should (eq (get-text-property epos 'face s) 'error)) + (should (eq (get-text-property wpos 'face s) 'warning))))) + +(ert-deftest test-modeline-config-flycheck-render-errors-only () + "Normal: warnings absent when their count is zero or missing." + (let ((s (cj/--modeline-flycheck-render '((error . 3))))) + (should (stringp s)) + (should (string-match-p "3" s)) + (should-not (text-property-any 0 (length s) 'face 'warning s)))) + +(ert-deftest test-modeline-config-flycheck-render-clean-nil () + "Boundary: zero counts render nothing." + (should-not (cj/--modeline-flycheck-render '((error . 0) (warning . 0))))) + +(ert-deftest test-modeline-config-flycheck-render-nil-input () + "Boundary: nil counts alist renders nothing." + (should-not (cj/--modeline-flycheck-render nil))) + +(ert-deftest test-modeline-config-flycheck-render-info-ignored () + "Boundary: info-level counts alone render nothing (errors/warnings only)." + (should-not (cj/--modeline-flycheck-render '((info . 4))))) + +(provide 'test-modeline-config-flycheck-render) +;;; test-modeline-config-flycheck-render.el ends here diff --git a/tests/test-modeline-config-flycheck-segment.el b/tests/test-modeline-config-flycheck-segment.el index 2ae2f5de..ed4d0601 100644 --- a/tests/test-modeline-config-flycheck-segment.el +++ b/tests/test-modeline-config-flycheck-segment.el @@ -2,7 +2,7 @@ ;;; Commentary: ;; Smoke test that the custom modeline's `mode-line-format' includes -;; a guarded reference to `flycheck-mode-line-status-text', and that +;; a guarded reference to `cj/--modeline-flycheck-status', and that ;; the guard requires both `mode-line-window-selected-p' and ;; `bound-and-true-p flycheck-mode'. See ;; docs/specs/flycheck-modeline-customization-spec-implemented.org for the design. @@ -16,9 +16,9 @@ (require 'modeline-config) (ert-deftest test-modeline-config-flycheck-segment-present () - "`mode-line-format' contains an :eval form invoking flycheck-mode-line-status-text." + "`mode-line-format' contains an :eval form invoking cj/--modeline-flycheck-status." (let ((printed (format "%S" (default-value 'mode-line-format)))) - (should (string-match-p "flycheck-mode-line-status-text" printed)))) + (should (string-match-p "cj/--modeline-flycheck-status" printed)))) (ert-deftest test-modeline-config-flycheck-segment-guarded-by-active-window () "Flycheck segment gates on `mode-line-window-selected-p'." diff --git a/tests/test-modeline-config-reset.el b/tests/test-modeline-config-reset.el new file mode 100644 index 00000000..54b7bb2f --- /dev/null +++ b/tests/test-modeline-config-reset.el @@ -0,0 +1,37 @@ +;;; test-modeline-config-reset.el --- cj/modeline-reset -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for `cj/modeline-reset', the repair command for buffers whose +;; `mode-line-format' was hijacked buffer-locally (two-column mode, ediff, +;; calc). It kills the buffer-local value so the default format returns. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +(require 'modeline-config) + +(ert-deftest test-modeline-config-reset-kills-local-format () + "Normal: a hijacked buffer-local mode-line-format is removed." + (with-temp-buffer + (setq-local mode-line-format '("hijacked")) + (should (local-variable-p 'mode-line-format)) + (cj/modeline-reset) + (should-not (local-variable-p 'mode-line-format)) + (should (eq mode-line-format (default-value 'mode-line-format))))) + +(ert-deftest test-modeline-config-reset-noop-without-local () + "Boundary: harmless when the buffer has no local mode-line-format." + (with-temp-buffer + (should-not (local-variable-p 'mode-line-format)) + (cj/modeline-reset) + (should-not (local-variable-p 'mode-line-format)))) + +(ert-deftest test-modeline-config-reset-is-a-command () + "Normal: cj/modeline-reset is interactive." + (should (commandp #'cj/modeline-reset))) + +(provide 'test-modeline-config-reset) +;;; test-modeline-config-reset.el ends here diff --git a/tests/test-modeline-config-segments.el b/tests/test-modeline-config-segments.el new file mode 100644 index 00000000..580a7711 --- /dev/null +++ b/tests/test-modeline-config-segments.el @@ -0,0 +1,122 @@ +;;; test-modeline-config-segments.el --- small modeline segments -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for the small pure segment helpers added in the 2026-07-01 +;; modeline overhaul: macro indicator, remote-host tag, narrowing +;; indicator, position/region info, and the padding space. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +(require 'modeline-config) + +;; ---------------------------- Macro Indicator -------------------------------- + +(ert-deftest test-modeline-config-macro-indicator-shows-when-defining () + "Normal: MACRO text with the error face while a macro is recording." + (let ((defining-kbd-macro t)) + (let ((s (cj/--modeline-macro-indicator))) + (should (stringp s)) + (should (string-match-p "MACRO" s)) + (should (eq (get-text-property (string-match "MACRO" s) 'face s) + 'error))))) + +(ert-deftest test-modeline-config-macro-indicator-nil-when-idle () + "Boundary: nil when no macro is being defined." + (let ((defining-kbd-macro nil)) + (should-not (cj/--modeline-macro-indicator)))) + +;; ---------------------------- Remote Host Tag -------------------------------- + +(ert-deftest test-modeline-config-remote-host-shows-host () + "Normal: remote default-directory yields an @host tag." + (with-temp-buffer + (setq default-directory "/ssh:velox:/home/cjennings/") + (let ((s (cj/--modeline-remote-host))) + (should (stringp s)) + (should (string-match-p "@velox" s))))) + +(ert-deftest test-modeline-config-remote-host-nil-when-local () + "Boundary: nil for a local directory." + (with-temp-buffer + (setq default-directory "/tmp/") + (should-not (cj/--modeline-remote-host)))) + +;; --------------------------- Narrowing Indicator ----------------------------- + +(ert-deftest test-modeline-config-narrow-indicator-shows-when-narrowed () + "Normal: narrowed buffer yields the Narrow tag." + (with-temp-buffer + (insert "line one\nline two\nline three\n") + (narrow-to-region 1 9) + (let ((s (cj/--modeline-narrow-indicator))) + (should (stringp s)) + (should (string-match-p "Narrow" s))))) + +(ert-deftest test-modeline-config-narrow-indicator-nil-when-widened () + "Boundary: nil when the buffer is not narrowed." + (with-temp-buffer + (insert "text") + (should-not (cj/--modeline-narrow-indicator)))) + +;; --------------------------- Position / Region Info --------------------------- + +(ert-deftest test-modeline-config-position-info-line-column-percent () + "Normal: no region yields L: C: plus a percentage-through-buffer." + (with-temp-buffer + (insert (make-string 200 ?x)) + (goto-char (point-min)) + (deactivate-mark) + (let ((s (cj/--modeline-position-info))) + (should (stringp s)) + (should (string-match-p "L:" s)) + (should (string-match-p "C:" s)) + ;; point-based percent, %%-escaped for the mode-line construct pass + (should (string-match-p "%" s))))) + +(ert-deftest test-modeline-config-position-info-region-lines-chars () + "Normal: an active region yields selection info instead of position." + (with-temp-buffer + (insert "one\ntwo\nthree\n") + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char 9) ; through "one\ntwo\n" + (let ((transient-mark-mode t)) + (let ((s (cj/--modeline-position-info))) + (should (stringp s)) + (should (string-match-p "2 lines" s)) + (should (string-match-p "8 chars" s)))))) + +(ert-deftest test-modeline-config-position-info-single-char-region () + "Boundary: a one-char region reports 1 line, 1 char." + (with-temp-buffer + (insert "abc") + (goto-char 1) + (push-mark (point) t t) + (goto-char 2) + (let ((transient-mark-mode t)) + (let ((s (cj/--modeline-position-info))) + (should (string-match-p "1 line" s)) + (should (string-match-p "1 char" s)))))) + +;; ------------------------------- Padding -------------------------------------- + +(ert-deftest test-modeline-config-padding-carries-height-display () + "Normal: padding space carries a display height property." + (let ((cj/modeline-height-factor 1.2)) + (let ((s (cj/--modeline-padding))) + (should (stringp s)) + (should (get-text-property 0 'display s))))) + +(ert-deftest test-modeline-config-padding-plain-at-factor-one () + "Boundary: factor 1.0 (or nil) yields a plain space, no display prop." + (let ((cj/modeline-height-factor 1.0)) + (let ((s (cj/--modeline-padding))) + (should (stringp s)) + (should-not (get-text-property 0 'display s))))) + +(provide 'test-modeline-config-segments) +;;; test-modeline-config-segments.el ends here diff --git a/tests/test-modeline-config-vc-cache-key.el b/tests/test-modeline-config-vc-cache-key.el index 6ba7985c..38052949 100644 --- a/tests/test-modeline-config-vc-cache-key.el +++ b/tests/test-modeline-config-vc-cache-key.el @@ -17,20 +17,20 @@ (ert-deftest test-modeline-vc-cache-key-is-file-and-show-remote () "Normal: the key is (FILE SHOW-REMOTE), with no per-render file-truename stat." (let ((cj/modeline-vc-show-remote nil)) - (should (equal (cj/modeline-vc-cache-key "/x/y.el") '("/x/y.el" nil))))) + (should (equal (cj/--modeline-vc-cache-key "/x/y.el") '("/x/y.el" nil))))) (ert-deftest test-modeline-vc-cache-key-tracks-show-remote () "Boundary: toggling show-remote yields a different key (separate cache entry)." (should-not (equal (let ((cj/modeline-vc-show-remote nil)) - (cj/modeline-vc-cache-key "/x/y.el")) + (cj/--modeline-vc-cache-key "/x/y.el")) (let ((cj/modeline-vc-show-remote t)) - (cj/modeline-vc-cache-key "/x/y.el"))))) + (cj/--modeline-vc-cache-key "/x/y.el"))))) (ert-deftest test-modeline-vc-cache-key-stable-for-same-file () "Boundary: the key is stable across calls for an unchanged file + show-remote." (let ((cj/modeline-vc-show-remote nil)) - (should (equal (cj/modeline-vc-cache-key "/x/y.el") - (cj/modeline-vc-cache-key "/x/y.el"))))) + (should (equal (cj/--modeline-vc-cache-key "/x/y.el") + (cj/--modeline-vc-cache-key "/x/y.el"))))) (provide 'test-modeline-config-vc-cache-key) ;;; test-modeline-config-vc-cache-key.el ends here |
