-;;; context-coloring.el --- JavaScript syntax highlighting for grown-ups. -*- lexical-binding: t; -*-
+;;; context-coloring.el --- JavaScript syntax highlighting, except not for syntax. -*- lexical-binding: t; -*-
;; Copyright (C) 2014 Jackson Ray Hamilton
;;; Commentary:
-;; Highlights JavaScript code according to function context. Code in the global
-;; scope is white, code in functions within the global scope is yellow, code
-;; within such functions is green, etc.
+;; Highlights JavaScript code according to function context.
;;
;; Usage:
;;
-;; Install Node.js 0.10.
-;;
-;; Put context-coloring.el on your load path. Then, in your ~/.emacs:
+;; Install Node.js 0.10+.
+;; In your ~/.emacs:
;;
;; (require 'context-coloring)
;; (add-hook 'js-mode-hook 'context-coloring-mode)
;;; Faces
(defface context-coloring-depth--1-face
- '((((background light)) (:foreground "#7f7f7f"))
+ '((((type tty)) (:foreground "white"))
+ (((background light)) (:foreground "#7f7f7f"))
(((background dark)) (:foreground "#7f7f7f")))
"Context coloring face, depth -1; comments."
:group 'context-coloring-faces)
(defface context-coloring-depth-0-face
- '((((background light)) (:foreground "#000000"))
+ '((((type tty)) (:foreground "white"))
+ (((background light)) (:foreground "#000000"))
(((background dark)) (:foreground "#ffffff")))
"Context coloring face, depth 0; global scope."
:group 'context-coloring-faces)
(defface context-coloring-depth-1-face
- '((((background light)) (:foreground "#2D6994"))
+ '((((type tty)) (:foreground "yellow"))
+ (((background light)) (:foreground "#2D6994"))
(((background dark)) (:foreground "#ffff80")))
"Context coloring face, depth 1."
:group 'context-coloring-faces)
(defface context-coloring-depth-2-face
- '((((background light)) (:foreground "#592D94"))
+ '((((type tty)) (:foreground "green"))
+ (((background light)) (:foreground "#592D94"))
(((background dark)) (:foreground "#cdfacd")))
"Context coloring face, depth 2."
:group 'context-coloring-faces)
(defface context-coloring-depth-3-face
- '((((background light)) (:foreground "#A13143"))
+ '((((type tty)) (:foreground "cyan"))
+ (((background light)) (:foreground "#A13143"))
(((background dark)) (:foreground "#d8d8ff")))
"Context coloring face, depth 3."
:group 'context-coloring-faces)
(defface context-coloring-depth-4-face
- '((((background light)) (:foreground "#AC7135"))
+ '((((type tty)) (:foreground "blue"))
+ (((background light)) (:foreground "#AC7135"))
(((background dark)) (:foreground "#e7c7ff")))
"Context coloring face, depth 4."
:group 'context-coloring-faces)
(defface context-coloring-depth-5-face
- '((((background light)) (:foreground "#ACA135"))
+ '((((type tty)) (:foreground "magenta"))
+ (((background light)) (:foreground "#ACA135"))
(((background dark)) (:foreground "#ffcdcd")))
"Context coloring face, depth 5."
:group 'context-coloring-faces)
(defface context-coloring-depth-6-face
- '((((background light)) (:foreground "#539A2F"))
+ '((((type tty)) (:foreground "red"))
+ (((background light)) (:foreground "#539A2F"))
(((background dark)) (:foreground "#ffe390")))
"Context coloring face, depth 6."
:group 'context-coloring-faces)
"Number of faces defined for highlighting delimiter levels.
Determines depth at which to cycle through faces again.")
-(defface context-coloring-depth--1-italic-face
- '((default (:inherit context-coloring-depth--1-face :slant italic)))
- "Context coloring face, depth -1; italic; comments."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-0-bold-face
- '((default (:inherit context-coloring-depth-0-face :weight bold)))
- "Context coloring face, depth 0; bold; global scope."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-1-bold-face
- '((default (:inherit context-coloring-depth-1-face :weight bold)))
- "Context coloring face, depth 1; bold."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-2-bold-face
- '((default (:inherit context-coloring-depth-2-face :weight bold)))
- "Context coloring face, depth 2; bold."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-3-bold-face
- '((default (:inherit context-coloring-depth-3-face :weight bold)))
- "Context coloring face, depth 3; bold."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-4-bold-face
- '((default (:inherit context-coloring-depth-4-face :weight bold)))
- "Context coloring face, depth 4; bold."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-5-bold-face
- '((default (:inherit context-coloring-depth-5-face :weight bold)))
- "Context coloring face, depth 5; bold."
- :group 'context-coloring-faces)
-
-(defface context-coloring-depth-6-bold-face
- '((default (:inherit context-coloring-depth-6-face :weight bold)))
- "Context coloring face, depth 6; bold."
- :group 'context-coloring-faces)
-
;;; Face functions
-(defun context-coloring-level-face (depth style)
- "Return face-name for DEPTH and STYLE as a string \"context-coloring-depth-DEPTH-face\".
+(defsubst context-coloring-level-face (depth)
+ "Return face-name for DEPTH as a string \"context-coloring-depth-DEPTH-face\".
For example: \"context-coloring-depth-1-face\"."
(intern-soft
(concat "context-coloring-depth-"
(+ 1
(mod (- depth 1)
(- context-coloring-face-count 1)))))
- (cond ((= 1 style) "-bold")
- ((= 2 style) "-italic")
- (t ""))
"-face")))
(defcustom context-coloring-delay 0.25
"Delay between a buffer update and colorization.
-If your performance is poor, you might want to increase this.")
+Increase this if your machine is high-performing. Decrease it if it ain't."
+ :group 'context-coloring)
+
+(defcustom context-coloring-benchmark-colorization nil
+ "If non-nil, display how long each colorization took."
+ :group 'context-coloring)
;;; Local variables
imply that it should be colorized again.")
(make-variable-buffer-local 'context-coloring-changed)
-(defvar context-coloring-colorize-start-time nil
- "Used for dirty benchmarking of async colorization time.")
-(make-variable-buffer-local 'context-coloring-colorize-start-time)
+(defvar context-coloring-start-time nil
+ "Used to benchmark colorization time.")
+(make-variable-buffer-local 'context-coloring-start-time)
;;; Scopification
(defun context-coloring-apply-tokens (tokens)
"Processes TOKENS to apply context-based coloring to the
-current buffer. Tokens are vectors consisting of 4 integers:
-start, end, level, and style."
+current buffer. Tokens are 3 integers: start, end, level. The
+array is flat, with a new token occurring after every 3rd
+number."
(with-silent-modifications
;; Reset in case there should be uncolored areas.
(remove-text-properties (point-min) (point-max) `(face nil rear-nonsticky nil))
(let ((i 0)
(len (length tokens)))
(while (< i len)
- (let ((start (elt tokens i))
- (end (elt tokens (+ i 1)))
- (face (context-coloring-level-face (elt tokens (+ i 2))
- (elt tokens (+ i 3)))))
- (add-text-properties start end `(face ,face rear-nonsticky t)))
- (setq i (+ i 4))))))
-
-(defun context-coloring-kill-scopifier ()
+ (add-text-properties
+ (elt tokens i)
+ (elt tokens (+ i 1))
+ `(face ,(context-coloring-level-face (elt tokens (+ i 2))) rear-nonsticky t))
+ (setq i (+ i 3))))))
+
+(defsubst context-coloring-kill-scopifier ()
"Kills the currently-running scopifier process for this
buffer."
(when (not (null context-coloring-scopifier-process))
(setq context-coloring-scopifier-process nil)))
(defun context-coloring-parse-array (input)
- "Specialized alternative JSON parser."
- (apply 'vector (mapcar 'string-to-number (split-string (substring input 1 -1) ","))))
+ "Specialized JSON parser for a flat array of numbers."
+ (vconcat (mapcar 'string-to-number (split-string (substring input 1 -1) ","))))
(defun context-coloring-scopify ()
"Invokes the external scopifier with the current buffer's
(let ((output "")
(buffer context-coloring-buffer)
- (start-time context-coloring-colorize-start-time))
+ (start-time context-coloring-start-time))
;; The process may produce output in multiple chunks. This filter
;; accumulates the chunks into a message.
(with-current-buffer buffer
(context-coloring-apply-tokens tokens))
(setq context-coloring-scopifier-process nil)
- (message "Colorized (after %f seconds)." (- (float-time) start-time))
- )))))
+ (when context-coloring-benchmark-colorization
+ (message "Colorized (after %f seconds)." (- (float-time) start-time))))))))
- ;; Give the process its input.
+ ;; Give the process its input so it can begin.
(process-send-region context-coloring-scopifier-process (point-min) (point-max))
(process-send-eof context-coloring-scopifier-process))
(defun context-coloring-colorize ()
"Colors the current buffer by function context."
(interactive)
- (setq context-coloring-colorize-start-time (float-time))
- (message "%s" "Colorizing.")
- (context-coloring-scopify))
+ (when (executable-find "node")
+ (when context-coloring-benchmark-colorization
+ (setq context-coloring-start-time (float-time))
+ (message "%s" "Colorizing..."))
+ (context-coloring-scopify)))
(defun context-coloring-change-function (start end length)
"Registers a change so that a context-colored buffer can be
(font-lock-mode)
(jit-lock-mode t))
+ ;; Remember this buffer. This value should not be dynamically-bound.
(setq context-coloring-buffer (current-buffer))
+ ;; Alert the user that the mode is not going to work.
+ (if (null (executable-find "node"))
+ (message "context-coloring-mode requires Node.js 0.10+ to be installed"))
+
;; Colorize once initially.
(context-coloring-colorize)
- ;; Font lock is not compatible with this mode; the converse is also true.
+ ;; Font lock is incompatible with this mode; the converse is also true.
(font-lock-mode 0)
(jit-lock-mode nil)
(setq context-coloring-colorize-idle-timer
(run-with-idle-timer context-coloring-delay t 'context-coloring-maybe-colorize))))
-;;;###autoload
-(defun context-coloring-mode-enable ()
- (context-coloring-mode 1))
-
-;;;###autoload
-(defun context-coloring-mode-disable ()
- (context-coloring-mode 0))
-
-;;;###autoload
-(define-globalized-minor-mode global-context-coloring-mode
- context-coloring-mode context-coloring-mode-enable)
-
(provide 'context-coloring)
;;; context-coloring.el ends here