emacs-evil / evil

The extensible vi layer for Emacs.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cursor color does not persist

pietroiusti opened this issue · comments

Issue type

  • Bug report

Environment

Emacs version: GNU Emacs 29.1.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.38, cairo version 1.17.8) of 2023-09-02
Operating System: Arch Linux
Evil version: Evil version evil-git-5fc1677
Evil installation type: I'm using make emacs in Evil's source directory
Graphical/Terminal: Graphical
Tested in a make emacs session (see CONTRIBUTING.md): Yes

Reproduction steps

  • Start Emacs
  • switch to the scratch buffer
  • switch to Emacs state using C-z
  • yank the following lines:
     (setq evil-disable-insert-state-bindings t)
     (setq evil-default-state 'emacs)
     (setq evil-normal-state-cursor '("#e80000" box)) ;; red
     (setq evil-insert-state-cursor '("#e80000" bar)) ;; red
     (setq evil-emacs-state-cursor '("#839496" box)) ;; grey
    
  • M-x eval-buffer
  • Press C-z. The cursor correctly becomes red.
  • Place the cursor on any characters of evil-emacs-state-cursor on the last line.

Expected behavior

Cursor's color should remain red.

Actual behavior

Cursor's color becomes grey.

Further notes

I suspect the color change is triggered in other contexts as well. This was the easiest way to replicate.

Here is the backtrace using debug-on-entry on set-cursor-color:

Debugger entered--entering a function:
* set-cursor-color("#839496")
  (if (equal (frame-parameter nil 'cursor-color) color) nil (set-cursor-color color))
  evil-set-cursor-color("#839496")
  (cond ((functionp spec) (condition-case nil (progn (funcall spec)) (error nil))) ((stringp spec) (evil-set-cursor-color spec)) (t (setq cursor-type spec)))
  (let ((spec (car tail))) (cond ((functionp spec) (condition-case nil (progn (funcall spec)) (error nil))) ((stringp spec) (evil-set-cursor-color spec)) (t (setq cursor-type spec))) (setq tail (cdr tail)))
  (while tail (let ((spec (car tail))) (cond ((functionp spec) (condition-case nil (progn (funcall spec)) (error nil))) ((stringp spec) (evil-set-cursor-color spec)) (t (setq cursor-type spec))) (setq tail (cdr tail))))
  (let ((tail specs)) (while tail (let ((spec (car tail))) (cond ((functionp spec) (condition-case nil (progn (funcall spec)) (error nil))) ((stringp spec) (evil-set-cursor-color spec)) (t (setq cursor-type spec))) (setq tail (cdr tail)))))
  evil-set-cursor(("#839496" box))
  (save-current-buffer (set-buffer (or buffer (current-buffer))) (evil-set-cursor (if (and color (listp default)) (cl-remove-if #'stringp default) default)) (evil-set-cursor cursor))
  (let* ((state (or state evil-force-cursor evil-state 'normal)) (default (or evil-default-cursor t)) (cursor (evil-state-property state :cursor t)) (color (or (and (stringp cursor) cursor) (and (listp cursor) (evil-member-if #'stringp cursor)) (frame-parameter nil 'cursor-color)))) (save-current-buffer (set-buffer (or buffer (current-buffer))) (evil-set-cursor (if (and color (listp default)) (cl-remove-if #'stringp default) default)) (evil-set-cursor cursor)))
  (progn (let* ((state (or state evil-force-cursor evil-state 'normal)) (default (or evil-default-cursor t)) (cursor (evil-state-property state :cursor t)) (color (or (and (stringp cursor) cursor) (and (listp cursor) (evil-member-if #'stringp cursor)) (frame-parameter nil 'cursor-color)))) (save-current-buffer (set-buffer (or buffer (current-buffer))) (evil-set-cursor (if (and color (listp default)) (cl-remove-if #'stringp default) default)) (evil-set-cursor cursor))))
  (if evil-local-mode (progn (let* ((state (or state evil-force-cursor evil-state 'normal)) (default (or evil-default-cursor t)) (cursor (evil-state-property state :cursor t)) (color (or (and (stringp cursor) cursor) (and (listp cursor) (evil-member-if ... cursor)) (frame-parameter nil 'cursor-color)))) (save-current-buffer (set-buffer (or buffer (current-buffer))) (evil-set-cursor (if (and color (listp default)) (cl-remove-if #'stringp default) default)) (evil-set-cursor cursor)))))
  evil-refresh-cursor(emacs)
  (if evil-no-display nil (evil-refresh-cursor 'emacs) (evil-refresh-mode-line 'emacs))
  (let ((evil-state 'emacs)) (evil-normalize-keymaps) (if 't (activate-input-method evil-input-method) (if deactivate-current-input-method-function (progn (deactivate-input-method)))) (if evil-no-display nil (evil-refresh-cursor 'emacs) (evil-refresh-mode-line 'emacs)) (run-hooks 'evil-emacs-state-entry-hook) (if (and evil-echo-state arg (not evil-no-display) evil-emacs-state-message) (progn (if (functionp evil-emacs-state-message) (funcall evil-emacs-state-message) (evil-echo "%s" evil-emacs-state-message)))))
  (let ((evil-next-state 'emacs) input-method-activate-hook input-method-deactivate-hook) (evil-change-state nil) (setq evil-state 'emacs) (progn (let* ((p (if (and #'equal (not ...)) (assoc 'emacs evil-previous-state-alist #'equal) (assq 'emacs evil-previous-state-alist))) (v evil-previous-state)) (progn (if p (setcdr p v) (setq evil-previous-state-alist (cons (setq p ...) evil-previous-state-alist))) v)) evil-previous-state-alist) (let ((evil-state 'emacs)) (evil-normalize-keymaps) (if 't (activate-input-method evil-input-method) (if deactivate-current-input-method-function (progn (deactivate-input-method)))) (if evil-no-display nil (evil-refresh-cursor 'emacs) (evil-refresh-mode-line 'emacs)) (run-hooks 'evil-emacs-state-entry-hook) (if (and evil-echo-state arg (not evil-no-display) evil-emacs-state-message) (progn (if (functionp evil-emacs-state-message) (funcall evil-emacs-state-message) (evil-echo "%s" evil-emacs-state-message))))))
  (cond ((and (numberp arg) (< arg 1)) (progn (setq evil-previous-state evil-state) (setq evil-state nil)) (let ((evil-state 'emacs)) (run-hooks 'evil-emacs-state-exit-hook) (setq evil-state nil) (evil-normalize-keymaps))) (t (if evil-local-mode nil (evil-local-mode)) (let ((evil-next-state 'emacs) input-method-activate-hook input-method-deactivate-hook) (evil-change-state nil) (setq evil-state 'emacs) (progn (let* ((p (if ... ... ...)) (v evil-previous-state)) (progn (if p (setcdr p v) (setq evil-previous-state-alist ...)) v)) evil-previous-state-alist) (let ((evil-state 'emacs)) (evil-normalize-keymaps) (if 't (activate-input-method evil-input-method) (if deactivate-current-input-method-function (progn (deactivate-input-method)))) (if evil-no-display nil (evil-refresh-cursor 'emacs) (evil-refresh-mode-line 'emacs)) (run-hooks 'evil-emacs-state-entry-hook) (if (and evil-echo-state arg (not evil-no-display) evil-emacs-state-message) (progn (if (functionp evil-emacs-state-message) (funcall evil-emacs-state-message) (evil-echo "%s" evil-emacs-state-message))))))))
  evil-emacs-state(nil)
  funcall(evil-emacs-state nil)
  (progn (funcall func (if state (and message 1) -1)))
  (if (and (functionp func) (or message (not (eq state evil-state)))) (progn (funcall func (if state (and message 1) -1))))
  (let ((func (evil-state-property (or state evil-state) :toggle))) (if (and (functionp func) (or message (not (eq state evil-state)))) (progn (funcall func (if state (and message 1) -1)))))
  evil-change-state(emacs)
  evil-initialize-state()
  (progn (if (memql 'evil-mode-map-alist emulation-mode-map-alists) (with-no-warnings emulation-mode-map-alists) (setq emulation-mode-map-alists (cons 'evil-mode-map-alist emulation-mode-map-alists))) (evil-initialize-local-keymaps) (if (minibufferp) (progn (set (make-local-variable 'evil-default-state) 'insert) (set (make-local-variable 'evil-echo-state) nil))) (setq evil-input-method current-input-method) (evil-initialize-state) (add-hook 'input-method-activate-hook #'evil-activate-input-method t t) (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t t) (add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t) (add-hook 'pre-command-hook 'evil-repeat-pre-hook) (add-hook 'post-command-hook 'evil-repeat-post-hook))
  (if evil-local-mode (progn (if (memql 'evil-mode-map-alist emulation-mode-map-alists) (with-no-warnings emulation-mode-map-alists) (setq emulation-mode-map-alists (cons 'evil-mode-map-alist emulation-mode-map-alists))) (evil-initialize-local-keymaps) (if (minibufferp) (progn (set (make-local-variable 'evil-default-state) 'insert) (set (make-local-variable 'evil-echo-state) nil))) (setq evil-input-method current-input-method) (evil-initialize-state) (add-hook 'input-method-activate-hook #'evil-activate-input-method t t) (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t t) (add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t) (add-hook 'pre-command-hook 'evil-repeat-pre-hook) (add-hook 'post-command-hook 'evil-repeat-post-hook)) (evil-refresh-mode-line) (remove-hook 'activate-mark-hook 'evil-visual-activate-hook t) (remove-hook 'input-method-activate-hook #'evil-activate-input-method t) (remove-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t) (activate-input-method evil-input-method) (evil-change-state nil))
  (let ((last-message (current-message))) (setq evil-local-mode (cond ((eq arg 'toggle) (not evil-local-mode)) ((and (numberp arg) (< arg 1)) nil) (t t))) (if (boundp 'local-minor-modes) (progn (setq local-minor-modes (delq 'evil-local-mode local-minor-modes)) (if evil-local-mode (progn (setq local-minor-modes (cons 'evil-local-mode local-minor-modes)))))) (if evil-local-mode (progn (if (memql 'evil-mode-map-alist emulation-mode-map-alists) (with-no-warnings emulation-mode-map-alists) (setq emulation-mode-map-alists (cons 'evil-mode-map-alist emulation-mode-map-alists))) (evil-initialize-local-keymaps) (if (minibufferp) (progn (set (make-local-variable 'evil-default-state) 'insert) (set (make-local-variable 'evil-echo-state) nil))) (setq evil-input-method current-input-method) (evil-initialize-state) (add-hook 'input-method-activate-hook #'evil-activate-input-method t t) (add-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t t) (add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t) (add-hook 'pre-command-hook 'evil-repeat-pre-hook) (add-hook 'post-command-hook 'evil-repeat-post-hook)) (evil-refresh-mode-line) (remove-hook 'activate-mark-hook 'evil-visual-activate-hook t) (remove-hook 'input-method-activate-hook #'evil-activate-input-method t) (remove-hook 'input-method-deactivate-hook #'evil-deactivate-input-method t) (activate-input-method evil-input-method) (evil-change-state nil)) (run-hooks 'evil-local-mode-hook (if evil-local-mode 'evil-local-mode-on-hook 'evil-local-mode-off-hook)) (if (called-interactively-p 'any) (progn nil (if (and (current-message) (not (equal last-message (current-message)))) nil (let ((local " in current buffer")) (message "%s %sabled%s" "Evil-Local mode" (if evil-local-mode "en" "dis") local))))))
  evil-local-mode()
  (or (and (minibufferp) (not evil-want-minibuffer)) (evil-disabled-buffer-p) (evil-local-mode))
  (if evil-local-mode (evil-initialize-state) (or (and (minibufferp) (not evil-want-minibuffer)) (evil-disabled-buffer-p) (evil-local-mode)))
  evil-initialize()
  (progn (evil-local-mode -1) (evil-initialize))
  (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize))
  (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize)))
  (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize))))
  (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize)))) (setq evil-local-mode-major-mode major-mode))
  (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize)))) (setq evil-local-mode-major-mode major-mode)))
  (if (buffer-live-p buf) (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn (evil-local-mode -1) (evil-initialize)) (evil-initialize)))) (setq evil-local-mode-major-mode major-mode))))
  (let ((buf (car tail))) (if (buffer-live-p buf) (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode (progn ... ...) (evil-initialize)))) (setq evil-local-mode-major-mode major-mode)))) (setq tail (cdr tail)))
  (while tail (let ((buf (car tail))) (if (buffer-live-p buf) (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if (eq evil-local-mode-major-mode major-mode) nil (if evil-local-mode ... ...))) (setq evil-local-mode-major-mode major-mode)))) (setq tail (cdr tail))))
  (let ((tail buffers)) (while tail (let ((buf (car tail))) (if (buffer-live-p buf) (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil (if ... nil ...)) (setq evil-local-mode-major-mode major-mode)))) (setq tail (cdr tail)))))
  (let ((buffers evil-mode-buffers)) (setq evil-mode-buffers nil) (let ((tail buffers)) (while tail (let ((buf (car tail))) (if (buffer-live-p buf) (progn (save-current-buffer (set-buffer buf) (if evil-local-mode-set-explicitly nil ...) (setq evil-local-mode-major-mode major-mode)))) (setq tail (cdr tail))))))
  evil-mode-enable-in-buffers()
  run-hooks(after-change-major-mode-hook)
  run-mode-hooks(special-mode-hook)
  special-mode()
  eldoc--format-doc-buffer((("Cursor for Emacs state." :thing evil-emacs-state-cursor :face font-lock-variable-name-face)))
  eldoc-display-in-echo-area((("Cursor for Emacs state." :thing evil-emacs-state-cursor :face font-lock-variable-name-face)) nil)
  run-hook-with-args(eldoc-display-in-echo-area (("Cursor for Emacs state." :thing evil-emacs-state-cursor :face font-lock-variable-name-face)) nil)
  #f(compiled-function () #<bytecode 0x1d63293ec8df93a>)()
  #f(compiled-function (string &rest plist) #<bytecode 0x138508dae21649a6>)("Cursor for Emacs state." :thing evil-emacs-state-cursor :face font-lock-variable-name-face)
  elisp-eldoc-var-docstring(#f(compiled-function (string &rest plist) #<bytecode 0x138508dae21649a6>))
  eldoc-documentation-default()
  eldoc--invoke-strategy(nil)
  eldoc-print-current-symbol-info()
  #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_12>()
  apply(#<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_12> nil)
  timer-event-handler([t 0 0 500000 nil #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_12> nil idle 0 nil])

I'm temporarily fixing the issue, at least in emacs-lisp-mode buffers and typescript-ts-mode buffers (where I use eglot, which uses eldoc) with this:

(defun gp/is-buffer-to-ignore (orig-fun &rest args)
  (if (or (string-match "*eldoc" (buffer-name))
          (string-match " *temp*" (buffer-name))
          (string= " markdown-code-fontification:typescript-ts-mode" (buffer-name)))          
      nil
    (apply orig-fun args)))

(advice-add 'evil-initialize-state :around #'gp/is-buffer-to-ignore)

The issue seems to be that the cursor colour doesn't update after emacs visited a buffer without displaying it. You can replicate this by evaluating for example

(with-temp-buffer 
  (special-mode))

After evaluation your cursor colour will match whatever motion state the mode you invoked for the temp buffer defaults to.

I can confirm this issue persists with Emacs 29.2. Disabling eldoc-mode makes the issue go away. But the workaround from #1835 (comment) works for me while keeping eldoc-mode enabled. Thanks @pietroiusti!

Happy it works, @kommen. This, though, might be a better fix: (add-hook 'special-mode-hook (lambda () (turn-off-evil-mode)))

update:
Actually that doesn't take into account the ``text enrichment'' eglot does when you start a buffer of a certain mode for the first time. So I'm using this:

  (add-hook 'special-mode-hook (lambda () (turn-off-evil-mode)))
  (defun gp/is-buffer-to-ignore (orig-fun &rest args)
    (if (string-match " markdown-code-fontification" (buffer-name))
        nil
      (apply orig-fun args)))
  (advice-add 'evil-initialize-state :around #'gp/is-buffer-to-ignore)

@pietroiusti also (add-hook 'special-mode-hook (lambda () (turn-off-evil-mode))) breaks my evil keybindings for various other modes, most importantly from magit. So I'm back to an adapted version of your snippet from #1835 (comment)

Thanks for letting me know @kommen