;; Author: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
;; Keywords: context coloring syntax highlighting
-;; Version: 1.0.0
-;; Package-Requires: ((emacs "24"))
+;; Version: 2.1.0
+;; Package-Requires: ((emacs "24") (js2-mode "20141228"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;;; Commentary:
-;; Colors code by scope, rather than by syntax.
-
-;; A range of characters encompassing a scope is colored according to its level;
-;; the global scope is white, scopes within the global scope are yellow, scopes
-;; within scopes within the global scope are green, etc. Variables defined in a
-;; parent scope which are referenced from child scopes retain the same color as
-;; the scope in which they are defined; a variable defined in the global scope
-;; will be the same color when referenced from nested scopes.
+;; Highlights code according to function context.
;; To use, add the following to your ~/.emacs:
;; (require 'context-coloring)
-;; (add-hook 'js-mode-hook 'context-coloring-mode) ; Requires Node.js 0.10+.
+;; (add-hook 'js-mode-hook 'context-coloring-mode)
;;; Code:
+(require 'js2-mode)
+
;;; Constants
(defcustom context-coloring-delay 0.25
"Delay between a buffer update and colorization.
-Increase this if your machine is high-performing. Decrease it if it ain't."
+Increase this if your machine is high-performing. Decrease it if
+it ain't.
+
+Supported modes: `js-mode', `js3-mode'"
:group 'context-coloring)
-(defcustom context-coloring-block-scopes nil
- "If non-nil, add block scopes to the scope hierarchy.
+(defcustom context-coloring-comments-and-strings t
+ "If non-nil, also color comments and strings using `font-lock'."
+ :group 'context-coloring)
-The block-scope-inducing `let' and `const' are introduced in
-ES6. If you are writing ES6 code, then turn this on; otherwise,
-confusion will ensue."
+(defcustom context-coloring-js-block-scopes nil
+ "If non-nil, also color block scopes in the scope hierarchy in JavaScript.
+
+The block-scope-inducing `let' and `const' are introduced in ES6.
+If you are writing ES6 code, enable this; otherwise, don't.
+
+Supported modes: `js2-mode'"
+ :group 'context-coloring)
+
+(defcustom context-coloring-benchmark-colorization nil
+ "If non-nil, track how long colorization takes and print
+messages with the colorization duration."
:group 'context-coloring)
"Reference to this buffer (for timers).")
(defvar-local context-coloring-scopifier-process nil
- "Only allow a single scopifier process to run at a time. This
-is a reference to that one process.")
+ "Reference to the single scopifier process that can be
+ running.")
(defvar-local context-coloring-colorize-idle-timer nil
- "Reference to currently-running idle timer.")
+ "Reference to the currently-running idle timer.")
(defvar-local context-coloring-changed nil
"Indication that the buffer has changed recently, which would
-imply that it should be colorized again.")
+imply that it should be colorized again by
+`context-coloring-colorize-idle-timer' if that timer is being
+used.")
;;; Faces
-(defface context-coloring-level--1-face
- '((((type tty)) (:foreground "white"))
- (t (:foreground "#7f7f7f")))
- "Context coloring face, level -1; comments."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-0-face
- '((((type tty)) (:foreground "white"))
- (((background light)) (:foreground "#000000"))
- (((background dark)) (:foreground "#ffffff")))
- "Context coloring face, level 0; global scope."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-1-face
- '((((type tty)) (:foreground "yellow"))
- (((background light)) (:foreground "#007f80"))
- (((background dark)) (:foreground "#ffff80")))
- "Context coloring face, level 1."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-2-face
- '((((type tty)) (:foreground "green"))
- (((background light)) (:foreground "#001580"))
- (((background dark)) (:foreground "#cdfacd")))
- "Context coloring face, level 2."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-3-face
- '((((type tty)) (:foreground "cyan"))
- (((background light)) (:foreground "#550080"))
- (((background dark)) (:foreground "#d8d8ff")))
- "Context coloring face, level 3."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-4-face
- '((((type tty)) (:foreground "blue"))
- (((background light)) (:foreground "#802b00"))
- (((background dark)) (:foreground "#e7c7ff")))
- "Context coloring face, level 4."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-5-face
- '((((type tty)) (:foreground "magenta"))
- (((background light)) (:foreground "#6a8000"))
- (((background dark)) (:foreground "#ffcdcd")))
- "Context coloring face, level 5."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-6-face
- '((((type tty)) (:foreground "red"))
- (((background light)) (:foreground "#008000"))
- (((background dark)) (:foreground "#ffe390")))
- "Context coloring face, level 6."
- :group 'context-coloring-faces)
-
-;;; Additional 6 faces for insane levels of nesting
-
-(defface context-coloring-level-7-face
- '((t (:inherit context-coloring-level-1-face)))
- "Context coloring face, level 7."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-8-face
- '((t (:inherit context-coloring-level-2-face)))
- "Context coloring face, level 8."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-9-face
- '((t (:inherit context-coloring-level-3-face)))
- "Context coloring face, level 9."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-10-face
- '((t (:inherit context-coloring-level-4-face)))
- "Context coloring face, level 10."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-11-face
- '((t (:inherit context-coloring-level-5-face)))
- "Context coloring face, level 11."
- :group 'context-coloring-faces)
-
-(defface context-coloring-level-12-face
- '((t (:inherit context-coloring-level-6-face)))
- "Context coloring face, level 12."
- :group 'context-coloring-faces)
-
-(defcustom context-coloring-face-count 7
- "Number of faces defined for highlighting delimiter levels.
-Determines level at which to cycle through faces again.")
+(defun context-coloring-defface (level tty light dark)
+ (let ((face (intern (format "context-coloring-level-%s-face" level)))
+ (doc (format "Context coloring face, level %s." level)))
+ (eval (macroexpand `(defface ,face
+ '((((type tty)) (:foreground ,tty))
+ (((background light)) (:foreground ,light))
+ (((background dark)) (:foreground ,dark)))
+ ,doc
+ :group 'context-coloring)))))
+
+(defvar context-coloring-face-count nil
+ "Number of faces available for context coloring.")
+
+(defun context-coloring-defface-default (level)
+ (context-coloring-defface level "white" "#3f3f3f" "#cdcdcd"))
+
+(defun context-coloring-set-colors-default ()
+ (context-coloring-defface 0 "white" "#000000" "#ffffff")
+ (context-coloring-defface 1 "yellow" "#007f80" "#ffff80")
+ (context-coloring-defface 2 "green" "#001580" "#cdfacd")
+ (context-coloring-defface 3 "cyan" "#550080" "#d8d8ff")
+ (context-coloring-defface 4 "blue" "#802b00" "#e7c7ff")
+ (context-coloring-defface 5 "magenta" "#6a8000" "#ffcdcd")
+ (context-coloring-defface 6 "red" "#008000" "#ffe390")
+ (context-coloring-defface-default 7)
+ (setq context-coloring-face-count 8))
+
+(context-coloring-set-colors-default)
;;; Face functions
+(defsubst context-coloring-face-symbol (level)
+ "Returns a symbol for a face with LEVEL."
+ ;; `concat' is faster than `format' here.
+ (intern-soft (concat "context-coloring-level-"
+ (number-to-string level)
+ "-face")))
+
+(defun context-coloring-set-colors (&rest colors)
+ "Set context coloring's levels' coloring to COLORS, where the
+Nth element of COLORS is level N's color."
+ (setq context-coloring-face-count (length colors))
+ (let ((level 0))
+ (dolist (color colors)
+ ;; Ensure there are available faces to contain new colors.
+ (when (not (context-coloring-face-symbol level))
+ (context-coloring-defface-default level))
+ (set-face-foreground (context-coloring-face-symbol level) color)
+ (setq level (+ level 1)))))
+
(defsubst context-coloring-level-face (level)
- "Return face-name for LEVEL as a string \"context-coloring-level-LEVEL-face\".
-For example: \"context-coloring-level-1-face\"."
- (intern-soft
- (concat "context-coloring-level-"
- (number-to-string
- (or
- ;; Has a face directly mapping to it.
- (and (< level context-coloring-face-count)
- level)
- ;; After the number of available faces are used up, pretend the 0th
- ;; face doesn't exist.
- (+ 1
- (mod (- level 1)
- (- context-coloring-face-count 1)))))
- "-face")))
+ "Returns the face name for LEVEL."
+ (context-coloring-face-symbol (min level context-coloring-face-count)))
;;; Colorization utilities
-(defun context-coloring-uncolorize-buffer ()
- "Clears all coloring in the current buffer."
- (remove-text-properties (point-min) (point-max) `(face nil rear-nonsticky nil)))
-
(defsubst context-coloring-colorize-region (start end level)
- "Colorizes characters from 1-indexed START (inclusive) to END
-\(exclusive) with the face corresponding to LEVEL."
+ "Colorizes characters from the 1-indexed START (inclusive) to
+END (exclusive) with the face corresponding to LEVEL."
(add-text-properties
start
end
- `(face ,(context-coloring-level-face level) rear-nonsticky t)))
+ `(face ,(context-coloring-level-face level))))
+
+(defsubst context-coloring-maybe-colorize-comments-and-strings ()
+ "Colorizes the current buffer's comments and strings if
+`context-coloring-comments-and-strings' is non-nil."
+ (when context-coloring-comments-and-strings
+ (save-excursion
+ (font-lock-fontify-syntactically-region (point-min) (point-max)))))
;;; js2-mode colorization
+(defvar-local context-coloring-js2-scope-level-hash-table nil
+ "Associates `js2-scope' structures and with their scope
+ levels.")
+
(defsubst context-coloring-js2-scope-level (scope)
"Gets the level of SCOPE."
- (let ((level 0)
- enclosing-scope)
- (while (and (not (null scope))
- (not (null (js2-node-parent scope)))
- (not (null (setq enclosing-scope (js2-node-get-enclosing-scope scope)))))
- (when (or context-coloring-block-scopes
- (let ((type (js2-scope-type scope)))
- (or (= type js2-SCRIPT)
- (= type js2-FUNCTION)
- (= type js2-CATCH)
- (= type js2-WITH))))
- (setq level (+ level 1)))
- (setq scope enclosing-scope))
- level))
-
-;; Adapted from js2-refactor.el/js2r-vars.el
+ (cond ((gethash scope context-coloring-js2-scope-level-hash-table))
+ (t
+ (let ((level 0)
+ (current-scope scope)
+ enclosing-scope)
+ (while (and current-scope
+ (js2-node-parent current-scope)
+ (setq enclosing-scope
+ (js2-node-get-enclosing-scope current-scope)))
+ (when (or context-coloring-js-block-scopes
+ (let ((type (js2-scope-type current-scope)))
+ (or (= type js2-SCRIPT)
+ (= type js2-FUNCTION)
+ (= type js2-CATCH))))
+ (setq level (+ level 1)))
+ (setq current-scope enclosing-scope))
+ (puthash scope level context-coloring-js2-scope-level-hash-table)))))
+
(defsubst context-coloring-js2-local-name-node-p (node)
+ "Determines if NODE is a js2-name-node representing a local
+variable."
(and (js2-name-node-p node)
- (let ((start (js2-node-abs-pos node)))
- (and
- ;; (save-excursion ; not key in object literal { key: value }
- ;; (goto-char (+ (js2-node-abs-pos node) (js2-node-len node)))
- ;; (looking-at "[\n\t ]*:"))
- (let ((end (+ start (js2-node-len node))))
- (not (string-match "[\n\t ]*:" (buffer-substring-no-properties
- end
- (+ end 1)))))
- ;; (save-excursion ; not property lookup on object
- ;; (goto-char (js2-node-abs-pos node))
- ;; (looking-back "\\.[\n\t ]*"))
- (not (string-match "\\.[\n\t ]*" (buffer-substring-no-properties
- (max 1 (- start 1)) ; 0 throws an
- ; error. "" will
- ; fail the test.
- start)))))))
+ (let ((parent (js2-node-parent node)))
+ (not (or (and (js2-object-prop-node-p parent)
+ (eq node (js2-object-prop-node-left parent)))
+ (and (js2-prop-get-node-p parent)
+ ;; For nested property lookup, the node on the left is a
+ ;; `js2-prop-get-node', so this always works.
+ (eq node (js2-prop-get-node-right parent))))))))
+
+(defsubst context-coloring-js2-colorize-node (node level)
+ "Colors NODE with the color for LEVEL."
+ (let ((start (js2-node-abs-pos node)))
+ (context-coloring-colorize-region
+ start
+ (+ start (js2-node-len node)) ; End
+ level)))
(defun context-coloring-js2-colorize ()
+ "Colorizes the current buffer using the abstract syntax tree
+generated by js2-mode."
+ ;; Reset the hash table; the old one could be obsolete.
+ (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test 'eq))
(with-silent-modifications
- (context-coloring-uncolorize-buffer)
(js2-visit-ast
js2-mode-ast
(lambda (node end-p)
(when (null end-p)
(cond
((js2-scope-p node)
- (let ((start (js2-node-abs-pos node)))
- (context-coloring-colorize-region
- start
- (+ start (js2-scope-len node)) ; End
- (context-coloring-js2-scope-level node) ; Level
- )))
+ (context-coloring-js2-colorize-node
+ node
+ (context-coloring-js2-scope-level node)))
((context-coloring-js2-local-name-node-p node)
- (let ((start (js2-node-abs-pos node)))
- (context-coloring-colorize-region
- start
- (+ start (js2-name-node-len node)) ; End
- (context-coloring-js2-scope-level ; Level
- (js2-get-defining-scope
- (js2-node-get-enclosing-scope node)
- (js2-name-node-name node)))))))
+ (let* ((enclosing-scope (js2-node-get-enclosing-scope node))
+ (defining-scope (js2-get-defining-scope
+ enclosing-scope
+ (js2-name-node-name node))))
+ ;; The tree seems to be walked lexically, so an entire scope will
+ ;; be colored, including its name nodes, before they are
+ ;; reached. Coloring the nodes defined in that scope would be
+ ;; redundant, so don't do it.
+ (when (not (eq defining-scope enclosing-scope))
+ (context-coloring-js2-colorize-node
+ node
+ (context-coloring-js2-scope-level defining-scope))))))
;; The `t' indicates to search children.
- t)))))
+ t)))
+ (context-coloring-maybe-colorize-comments-and-strings)))
-;;; Shell command copification / colorization
+;;; Shell command scopification / colorization
(defun context-coloring-apply-tokens (tokens)
"Processes a vector of TOKENS to apply context-based coloring
-to the current buffer. Tokens are 3 integers: start, end,
-level. The vector is flat, with a new token occurring after every
-3rd element."
+to the current buffer. Tokens are 3 integers: start, end, level.
+The vector is flat, with a new token occurring after every 3rd
+element."
(with-silent-modifications
- (context-coloring-uncolorize-buffer)
(let ((i 0)
(len (length tokens)))
(while (< i len)
(elt tokens i)
(elt tokens (+ i 1))
(elt tokens (+ i 2)))
- (setq i (+ i 3))))))
+ (setq i (+ i 3))))
+ (context-coloring-maybe-colorize-comments-and-strings)))
(defun context-coloring-parse-array (input)
"Specialized JSON parser for a flat array of numbers."
(delete-process context-coloring-scopifier-process)
(setq context-coloring-scopifier-process nil)))
-(defun context-coloring-scopify-shell-command (command)
+(defun context-coloring-scopify-shell-command (command &optional callback)
"Invokes a scopifier with the current buffer's contents,
reading the scopifier's response asynchronously and applying a
-parsed list of tokens to `context-coloring-apply-tokens'."
+parsed list of tokens to `context-coloring-apply-tokens'.
+
+Invokes CALLBACK when complete."
;; Prior running tokenization is implicitly obsolete if this function is
;; called.
;; accumulates the chunks into a message.
(set-process-filter
context-coloring-scopifier-process
- (lambda (process chunk)
+ (lambda (_process chunk)
(setq output (concat output chunk))))
;; When the process's message is complete, this sentinel parses it as JSON
;; and applies the tokens to the buffer.
(set-process-sentinel
context-coloring-scopifier-process
- (lambda (process event)
+ (lambda (_process event)
(when (equal "finished\n" event)
(let ((tokens (context-coloring-parse-array output)))
(with-current-buffer buffer
(context-coloring-apply-tokens tokens))
- (setq context-coloring-scopifier-process nil))))))
+ (setq context-coloring-scopifier-process nil)
+ (if callback (funcall callback)))))))
;; Give the process its input so it can begin.
(process-send-region context-coloring-scopifier-process (point-min) (point-max))
;;; Dispatch
-(defvar context-coloring-javascript-scopifier
- `(:type shell-command
- :executable "node"
- :command ,(expand-file-name
- "./languages/javascript/bin/scopifier"
- context-coloring-path)))
-
-(defvar context-coloring-js2-colorizer
- `(:type elisp
- :colorizer context-coloring-js2-colorize))
-
-(defcustom context-coloring-dispatch-plist
- `(js-mode ,context-coloring-javascript-scopifier
- js2-mode ,context-coloring-js2-colorizer
- js3-mode ,context-coloring-javascript-scopifier)
- "Property list mapping major modes to scopification programs."
- :group 'context-coloring)
-
-(defun context-coloring-dispatch ()
+(defvar context-coloring-dispatch-hash-table (make-hash-table :test 'eq)
+ "Mapping of dispatch strategy names to their corresponding
+ property lists, which contain details about the strategies.")
+
+(defvar context-coloring-mode-hash-table (make-hash-table :test 'eq)
+ "Mapping of major mode names to dispatch property lists.")
+
+(defun context-coloring-select-dispatch (mode dispatch)
+ "Use DISPATCH for MODE."
+ (puthash
+ mode
+ (gethash
+ dispatch
+ context-coloring-dispatch-hash-table)
+ context-coloring-mode-hash-table))
+
+(defun context-coloring-define-dispatch (symbol &rest properties)
+ "Define a new dispatch named SYMBOL with PROPERTIES.
+
+A \"dispatch\" is a property list describing a strategy for
+coloring a buffer. There are three possible strategies: Parse and
+color in a single function (`:colorizer'), parse in a function
+that returns scope data (`:scopifier'), or parse with a shell
+command that returns scope data (`:command'). In the latter two
+cases, the scope data will be used to automatically color the
+buffer.
+
+PROPERTIES must include `:modes' and one of `:colorizer',
+`:scopifier' or `:command'.
+
+`:modes' - List of major modes this dispatch is valid for.
+
+`:colorizer' - Symbol referring to a function that parses and
+colors the buffer.
+
+`:scopifier' - Symbol referring to a function that parses the
+buffer a returns a flat vector of start, end and level data.
+
+`:executable' - Optional name of an executable required by
+`:command'.
+
+`:command' - Shell command to execute with the current buffer
+sent via stdin, and with a flat JSON array of start, end and
+level data returned via stdout."
+ (let ((modes (plist-get properties :modes))
+ (colorizer (plist-get properties :colorizer))
+ (scopifier (plist-get properties :scopifier))
+ (command (plist-get properties :command)))
+ (when (null modes)
+ (error "No mode defined for dispatch"))
+ (when (not (or colorizer
+ scopifier
+ command))
+ (error "No colorizer, scopifier or command defined for dispatch"))
+ (puthash symbol properties context-coloring-dispatch-hash-table)
+ (dolist (mode modes)
+ (when (null (gethash mode context-coloring-mode-hash-table))
+ (puthash mode properties context-coloring-mode-hash-table)))))
+
+(context-coloring-define-dispatch 'javascript-node
+ :modes '(js-mode js3-mode)
+ :executable "node"
+ :command (expand-file-name
+ "./languages/javascript/binaries/scopifier"
+ context-coloring-path))
+
+(context-coloring-define-dispatch 'javascript-js2
+ :modes '(js2-mode)
+ :colorizer 'context-coloring-js2-colorize)
+
+(defun context-coloring-dispatch (&optional callback)
"Determines the optimal track for scopification / colorization
-of the current buffer, then does it."
- (let ((dispatch (plist-get context-coloring-dispatch-plist major-mode)))
+of the current buffer, then executes it.
+
+Invokes CALLBACK when complete. It is invoked synchronously for
+elisp tracks, and asynchronously for shell command tracks."
+ (let ((dispatch (gethash major-mode context-coloring-mode-hash-table)))
(if (null dispatch)
(message "%s" "Context coloring is not available for this major mode"))
- (let ((type (plist-get dispatch :type)))
+ (let (colorizer
+ scopifier
+ command
+ executable)
(cond
- ((eq type 'elisp)
- (let ((colorizer (plist-get dispatch :colorizer))
- (scopifier (plist-get dispatch :scopifier)))
- (cond
- ((not (null colorizer))
- (funcall colorizer))
- ((not (null scopifier))
- (context-coloring-apply-tokens (funcall scopifier)))
- (t
- (error "No `:colorizer' nor `:scopifier' specified for dispatch of `:type' elisp")))))
- ((eq type 'shell-command)
- (let ((executable (plist-get dispatch :executable))
- (command (plist-get dispatch :command)))
- (if (null command)
- (error "No `:command' specified for dispatch of `:type' shell-command"))
- (if (and (not (null executable))
- (null (executable-find executable)))
- (message "Executable \"%s\" not found" executable))
- (context-coloring-scopify-shell-command command)))))))
+ ((setq colorizer (plist-get dispatch :colorizer))
+ (funcall colorizer)
+ (if callback (funcall callback)))
+ ((setq scopifier (plist-get dispatch :scopifier))
+ (context-coloring-apply-tokens (funcall scopifier))
+ (if callback (funcall callback)))
+ ((setq command (plist-get dispatch :command))
+ (setq executable (plist-get dispatch :executable))
+ (if (and (not (null executable))
+ (null (executable-find executable)))
+ (message "Executable \"%s\" not found" executable)
+ (context-coloring-scopify-shell-command command callback)))))))
;;; Colorization
-(defun context-coloring-colorize ()
- "Colors the current buffer by function context."
- (interactive)
- (context-coloring-dispatch))
+(defun context-coloring-colorize (&optional callback)
+ "Colors the current buffer by function context.
-(defun context-coloring-change-function (start end length)
- "Registers a change so that a context-colored buffer can be
-colorized soon."
+Invokes CALLBACK when complete; see `context-coloring-dispatch'."
+ (interactive)
+ (let ((start-time (float-time)))
+ (context-coloring-dispatch
+ (lambda ()
+ (when context-coloring-benchmark-colorization
+ (message "Colorization took %.3f seconds" (- (float-time) start-time)))
+ (if callback (funcall callback))))))
+
+(defun context-coloring-change-function (_start _end _length)
+ "Registers a change so that a buffer can be colorized soon."
;; Tokenization is obsolete if there was a change.
(context-coloring-kill-scopifier)
(setq context-coloring-changed t))
(defun context-coloring-maybe-colorize ()
- "Colorize unders certain conditions. This will run as an idle
-timer, so firstly the buffer must not be some other
-buffer. Additionally, the buffer must have changed, otherwise
-colorizing would be redundant."
+ "Colorize unders certain conditions. This will run as an idle
+timer, so firstly the buffer must not be some other buffer.
+Additionally, the buffer must have changed, otherwise colorizing
+would be redundant."
(when (and (eq context-coloring-buffer (window-buffer (selected-window)))
context-coloring-changed)
(setq context-coloring-changed nil)
(if (not context-coloring-mode)
(progn
(context-coloring-kill-scopifier)
- (when (not (null 'context-coloring-colorize-idle-timer))
+ (when context-coloring-colorize-idle-timer
(cancel-timer context-coloring-colorize-idle-timer))
+ (remove-hook 'js2-post-parse-callbacks 'context-coloring-colorize t)
(remove-hook 'after-change-functions 'context-coloring-change-function t)
(font-lock-mode)
(jit-lock-mode t))
(jit-lock-mode nil)
;; Colorize once initially.
- ;; (let ((start-time (float-time)))
- (context-coloring-colorize)
- ;; (message "Elapsed time: %f" (- (float-time) start-time)))
-
- ;; Only recolor on change.
- (add-hook 'after-change-functions 'context-coloring-change-function nil t)
-
- ;; Only recolor idly.
- (setq context-coloring-colorize-idle-timer
- (run-with-idle-timer context-coloring-delay t 'context-coloring-maybe-colorize))))
+ (context-coloring-colorize)
+
+ (cond
+ ((equal major-mode 'js2-mode)
+ ;; Only recolor on reparse.
+ (add-hook 'js2-post-parse-callbacks 'context-coloring-colorize nil t))
+ (t
+ ;; Only recolor on change.
+ (add-hook 'after-change-functions 'context-coloring-change-function nil t)))
+
+ (when (not (equal major-mode 'js2-mode))
+ ;; Only recolor idly.
+ (setq context-coloring-colorize-idle-timer
+ (run-with-idle-timer
+ context-coloring-delay
+ t
+ 'context-coloring-maybe-colorize)))))
(provide 'context-coloring)
+;; Local Variables:
+;; eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))
+;; End:
+
;;; context-coloring.el ends here