From 5b45c269cda09fe46e110adb6f6767040f4ade59 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Fri, 12 Jul 2019 19:27:53 +0100 Subject: [PATCH] New jit-lock-antiblink-grace feature * lisp/jit-lock.el (jit-lock-antiblink-grace): New defcustom. (jit-lock--antiblink-line-beginning-position) (jit-lock--antiblink-string-or-comment): New variables (jit-lock--antiblink-post-command): New helper. (jit-lock-mode): Tweak post-command-hook and jit-lock-context-timer. * etc/NEWS: Mention jit-lock-antiblink-grace --- etc/NEWS | 7 +++++ lisp/jit-lock.el | 75 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 8a72c31bf9b..3d17b8f979c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -626,6 +626,13 @@ If the region is active, the command joins all the lines in the region. When there's no active region, the command works on the current and the previous or the next line, as before. +--- +** New customizable variable 'jit-lock-antiblink-grace'. +When typing strings, this helps avoid "blinking", an oscillation +between string and non-string fontification. The variable holds a +number of seconds (default is 2) before a potentially unwanted +fontification starts. Set to nil to get back the old behavior. + * Changes in Specialized Modes and Packages in Emacs 27.1 diff --git a/lisp/jit-lock.el b/lisp/jit-lock.el index 48998a81fe7..a17224e4bd0 100644 --- a/lisp/jit-lock.el +++ b/lisp/jit-lock.el @@ -123,6 +123,20 @@ The value of this variable is used when JIT Lock mode is turned on." :type '(number :tag "seconds") :group 'jit-lock) +(defcustom jit-lock-antiblink-grace 2 + "Grace period after which to refontify due to unterminated strings. +If nil, no grace period is given. Otherwise, a newly created +unterminated string is fontified only to the end of the current +line, after which the system waits this many seconds of idle time +before deciding the string is multi-line and fontifying the +remaining lines. When typing strings, this helps avoid +\"blinking\", an unwanted oscillation between string and +non-string fontification." + :type '(choice (const :tag "never" nil) + (number :tag "seconds")) + :group 'jit-lock + :version "27.1") + (defcustom jit-lock-defer-time nil ;; 0.25 "Idle time after which deferred fontification should take place. If nil, fontification is not deferred. @@ -157,6 +171,13 @@ If nil, contextual fontification is disabled.") "List of buffers with pending deferred fontification.") (defvar jit-lock-stealth-buffers nil "List of buffers that are being fontified stealthily.") + +(defvar jit-lock--antiblink-grace-timer nil + "Idle timer for fontifying unterminated string or comment, or nil.") +(defvar jit-lock--antiblink-line-beginning-position (make-marker) + "Last line beginning position after last command (a marker).") +(defvar jit-lock--antiblink-string-or-comment nil + "Non-nil if in string or comment after last command (a boolean).") ;;; JIT lock mode @@ -232,7 +253,10 @@ If you need to debug code run from jit-lock, see `jit-lock-debug-mode'." (unless jit-lock-context-timer (setq jit-lock-context-timer (run-with-idle-timer jit-lock-context-time t - 'jit-lock-context-fontify))) + (lambda () + (unless jit-lock--antiblink-grace-timer + (jit-lock-context-fontify)))))) + (add-hook 'post-command-hook 'jit-lock--antiblink-post-command nil t) (setq jit-lock-context-unfontify-pos (or jit-lock-context-unfontify-pos (point-max)))) @@ -669,6 +693,55 @@ will take place when text is fontified stealthily." ;; buffer, only jit-lock-context-* will re-fontify it. (min jit-lock-context-unfontify-pos jit-lock-start)))))) +(defun jit-lock--antiblink-post-command () + (let* ((new-l-b-p (copy-marker (line-beginning-position))) + (l-b-p-2 (line-beginning-position 2)) + (same-line + (and jit-lock-antiblink-grace + (not (= new-l-b-p l-b-p-2)) + (eq (marker-buffer jit-lock--antiblink-line-beginning-position) + (current-buffer)) + (= new-l-b-p jit-lock--antiblink-line-beginning-position))) + (new-s-o-c + (and same-line + (nth 8 (save-excursion (syntax-ppss l-b-p-2)))))) + (cond (;; Opened a new multiline string... + (and same-line + (null jit-lock--antiblink-string-or-comment) new-s-o-c) + (setq jit-lock--antiblink-grace-timer + (run-with-idle-timer jit-lock-antiblink-grace nil + (lambda () + (jit-lock-context-fontify) + (setq jit-lock--antiblink-grace-timer + nil))))) + (;; Closed an unterminated multiline string. + (and same-line + (null new-s-o-c) jit-lock--antiblink-string-or-comment) + ;; Kill the grace timer, might already have run and died. + ;; Don't refontify immediately: it adds an unreasonable + ;; delay to a well-behaved operation. Leave it for the + ;; `jit-lock-context-timer' as usual. + (when jit-lock--antiblink-grace-timer + (cancel-timer jit-lock--antiblink-grace-timer) + (setq jit-lock--antiblink-grace-timer nil))) + (same-line + ;; In same line, but no state change, leave everything as it was + ) + (t + ;; Left the line somehow or customized feature away, etc + ;; kill timer if running, resume normal operation. + (when jit-lock--antiblink-grace-timer + ;; Do refontify immediately, adding a small delay. This + ;; makes sense because it remark somehow that we are + ;; leaving the unstable state. + (jit-lock-context-fontify) + (cancel-timer jit-lock--antiblink-grace-timer) + (setq jit-lock--antiblink-grace-timer nil)))) + ;; Update variables (and release the marker) + (set-marker jit-lock--antiblink-line-beginning-position nil) + (setq jit-lock--antiblink-line-beginning-position new-l-b-p + jit-lock--antiblink-string-or-comment new-s-o-c))) + (provide 'jit-lock) ;;; jit-lock.el ends here -- 2.39.2