diff options
| -rw-r--r-- | modules/modeline-config.el | 13 | ||||
| -rw-r--r-- | modules/nerd-icons-config.el | 12 | ||||
| -rw-r--r-- | modules/popper-config.el | 4 | ||||
| -rw-r--r-- | modules/ui-config.el | 41 | ||||
| -rw-r--r-- | tests/test-ui-config--buffer-cursor-state.el | 7 | ||||
| -rw-r--r-- | tests/test-ui-cursor-color-integration.el | 11 |
6 files changed, 68 insertions, 20 deletions
diff --git a/modules/modeline-config.el b/modules/modeline-config.el index 771d960f0..61ab0f9df 100644 --- a/modules/modeline-config.el +++ b/modules/modeline-config.el @@ -141,15 +141,20 @@ Uses built-in cached values for performance.") (defun cj/modeline-vc-fetch (file) "Fetch modeline VC data for FILE. -Return a plist with `:branch' and `:state', or nil when FILE has no VC data." +Return a plist with `:branch' and `:state', or nil when FILE has no VC data. +Uses `vc-git--symbolic-ref' for branch names when available (it returns the +symbolic ref like \"main\" instead of a SHA when HEAD is on a branch), but +falls back to `vc-working-revision' if the internal accessor is missing -- +the symbol is internal and can be renamed or removed between Emacs versions." (unless (and (file-remote-p file) (not cj/modeline-vc-show-remote)) (when-let* ((backend (vc-backend file)) (branch (vc-working-revision file backend))) (when (eq backend 'Git) (unless (fboundp 'vc-git--symbolic-ref) - (require 'vc-git)) - (when-let* ((symbolic (vc-git--symbolic-ref file))) - (setq branch symbolic))) + (require 'vc-git nil 'noerror)) + (when (fboundp 'vc-git--symbolic-ref) + (when-let* ((symbolic (ignore-errors (vc-git--symbolic-ref file)))) + (setq branch symbolic)))) (list :branch branch :state (vc-state file backend))))) diff --git a/modules/nerd-icons-config.el b/modules/nerd-icons-config.el index 4a8ce194d..52a4627dc 100644 --- a/modules/nerd-icons-config.el +++ b/modules/nerd-icons-config.el @@ -70,11 +70,21 @@ every call. The `memq' check skips when the face is already present." ;; ------------------------------- Packages ------------------------------------ (use-package nerd-icons - :demand t + :defer t :config (advice-add 'nerd-icons-icon-for-dir :filter-return #'cj/--nerd-icons-color-dir) (cj/nerd-icons-apply-tint)) +;; If nerd-icons is already loaded (e.g. when this module is re-evaluated +;; after a session in which a feature module already required it), the +;; `:config' block above won't fire again -- fall through to install the +;; advice and tint immediately. +(with-eval-after-load 'nerd-icons + (unless (advice-member-p #'cj/--nerd-icons-color-dir 'nerd-icons-icon-for-dir) + (advice-add 'nerd-icons-icon-for-dir + :filter-return #'cj/--nerd-icons-color-dir)) + (cj/nerd-icons-apply-tint)) + (use-package nerd-icons-completion :demand t :after (nerd-icons marginalia) diff --git a/modules/popper-config.el b/modules/popper-config.el index 359e789c4..35780eb23 100644 --- a/modules/popper-config.el +++ b/modules/popper-config.el @@ -37,6 +37,10 @@ (side . bottom) (slot . 0) (window-height . 0.5))) ; Half the frame height + ;; Mode activation moves to :config so `:disabled t' actually + ;; disables it (`:init' runs even when `:disabled t', `:config' + ;; does not). + :config (popper-mode +1) (popper-echo-mode +1)) diff --git a/modules/ui-config.el b/modules/ui-config.el index 5d3a66431..39b861823 100644 --- a/modules/ui-config.el +++ b/modules/ui-config.el @@ -120,21 +120,36 @@ through to `read-only' and keeps the orange cursor." (defun cj/set-cursor-color-according-to-mode () "Change cursor color according to buffer state (modified, read-only, overwrite). -Only updates for real user buffers, not internal/temporary buffers." - ;; Only update cursor for real buffers (not internal ones like *temp*, *Echo Area*, etc.) - (unless (string-prefix-p " " (buffer-name)) ; Internal buffers start with space - (let ((color (alist-get (cj/--buffer-cursor-state) cj/buffer-status-colors))) - ;; Only skip if BOTH color AND buffer are the same (optimization) - ;; This allows color to update when buffer state changes - (unless (and (string= color cj/-cursor-last-color) - (string= (buffer-name) cj/-cursor-last-buffer)) - (set-cursor-color color) - (setq cj/-cursor-last-color color - cj/-cursor-last-buffer (buffer-name)))))) +Only updates for real user buffers, not internal/temporary buffers. +A no-op on non-graphical frames -- TTY/batch sessions have no cursor color +to set." + (when (display-graphic-p) + ;; Only update cursor for real buffers (not internal ones like *temp*, *Echo Area*, etc.) + (unless (string-prefix-p " " (buffer-name)) ; Internal buffers start with space + (let ((color (alist-get (cj/--buffer-cursor-state) cj/buffer-status-colors))) + ;; Only skip if BOTH color AND buffer are the same (optimization) + ;; This allows color to update when buffer state changes + (unless (and (string= color cj/-cursor-last-color) + (string= (buffer-name) cj/-cursor-last-buffer)) + (set-cursor-color color) + (setq cj/-cursor-last-color color + cj/-cursor-last-buffer (buffer-name))))))) ;; Use post-command-hook to update cursor color after every command -;; This ensures cursor color always matches the current buffer's state -(add-hook 'post-command-hook #'cj/set-cursor-color-according-to-mode) +;; This ensures cursor color always matches the current buffer's state. +;; The hook only registers under a graphical session so batch / TTY runs +;; don't pay per-command overhead for a no-op. +(when (display-graphic-p) + (add-hook 'post-command-hook #'cj/set-cursor-color-according-to-mode)) +;; Daemon mode: the first frame may be created after this module loads. +;; Re-attempt the hook install once a GUI frame appears. +(add-hook 'server-after-make-frame-hook + (lambda () + (when (and (display-graphic-p) + (not (memq #'cj/set-cursor-color-according-to-mode + post-command-hook))) + (add-hook 'post-command-hook + #'cj/set-cursor-color-according-to-mode)))) ;; Don’t show a cursor in non-selected windows: (setq cursor-in-non-selected-windows nil) diff --git a/tests/test-ui-config--buffer-cursor-state.el b/tests/test-ui-config--buffer-cursor-state.el index add0d030b..ead057413 100644 --- a/tests/test-ui-config--buffer-cursor-state.el +++ b/tests/test-ui-config--buffer-cursor-state.el @@ -72,7 +72,9 @@ buffer the user navigates, so `read-only' (orange) is kept." (ert-deftest test-ui-config-set-cursor-color-live-vterm-not-orange () "Normal: in a live vterm the cursor-color hook picks a writeable color, -not the read-only orange -- even though the vterm buffer is read-only." +not the read-only orange -- even though the vterm buffer is read-only. +`display-graphic-p' is stubbed t so the function reaches its work body +in batch mode (the live function no-ops on TTY frames by design)." (let ((buf (cj/test--make-fake-vterm-buffer "*test-vterm-cursor-color*")) (applied 'unset)) (unwind-protect @@ -81,7 +83,8 @@ not the read-only orange -- even though the vterm buffer is read-only." (setq-local vterm-copy-mode nil) (let ((cj/-cursor-last-color nil) (cj/-cursor-last-buffer nil)) - (cl-letf (((symbol-function 'set-cursor-color) + (cl-letf (((symbol-function 'display-graphic-p) (lambda () t)) + ((symbol-function 'set-cursor-color) (lambda (c) (setq applied c)))) (cj/set-cursor-color-according-to-mode))) (should (stringp applied)) diff --git a/tests/test-ui-cursor-color-integration.el b/tests/test-ui-cursor-color-integration.el index 00b7f57b5..c28bde923 100644 --- a/tests/test-ui-cursor-color-integration.el +++ b/tests/test-ui-cursor-color-integration.el @@ -9,6 +9,17 @@ (require 'ert) (require 'user-constants) + +;; `cj/set-cursor-color-according-to-mode' and the `post-command-hook' +;; install both gate on `display-graphic-p' -- a TTY / batch run is a +;; no-op for cursor coloring by design. These integration tests +;; exercise the work body, so we pretend we're in a graphical session +;; for the whole file. Stubbing the symbol BEFORE loading ui-config +;; matters because the hook install reads `display-graphic-p' at load +;; time. +(advice-add 'display-graphic-p :around + (lambda (orig &rest args) (or (apply orig args) t))) + (require 'ui-config) ;;; Hook Integration Tests |
