]> code.delx.au - gnu-emacs-elpa/blobdiff - context-coloring.el
Remove styling from faces.
[gnu-emacs-elpa] / context-coloring.el
index 98edb8b28d6deeaffd22b8aeea754efe027b2210..9f21dfc213f8576f66ff178ddb23f28757e73e34 100644 (file)
@@ -1,4 +1,4 @@
-;;; 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)
 
 ;;; Code:
 
-(require 'json)
-
 ;;; Faces
 
 (defface context-coloring-depth--1-face
-  '((((background light)) (:foreground "#999999"))
-    (((background dark)) (:foreground "#999999")))
-  "Nested blocks face, depth -1; comments."
+  '((((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 "#ffffff"))
+  '((((type tty)) (:foreground "white"))
+    (((background light)) (:foreground "#000000"))
     (((background dark)) (:foreground "#ffffff")))
-  "Nested blocks face, depth 0; global scope."
+  "Context coloring face, depth 0; global scope."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-1-face
-  '((((background light)) (:foreground "#ffff80"))
+  '((((type tty)) (:foreground "yellow"))
+    (((background light)) (:foreground "#2D6994"))
     (((background dark)) (:foreground "#ffff80")))
-  "Nested blocks face, depth 1."
+  "Context coloring face, depth 1."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-2-face
-  '((((background light)) (:foreground "#cdfacd"))
+  '((((type tty)) (:foreground "green"))
+    (((background light)) (:foreground "#592D94"))
     (((background dark)) (:foreground "#cdfacd")))
-  "Nested blocks face, depth 2."
+  "Context coloring face, depth 2."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-3-face
-  '((((background light)) (:foreground "#d8d8ff"))
+  '((((type tty)) (:foreground "cyan"))
+    (((background light)) (:foreground "#A13143"))
     (((background dark)) (:foreground "#d8d8ff")))
-  "Nested blocks face, depth 3."
+  "Context coloring face, depth 3."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-4-face
-  '((((background light)) (:foreground "#e7c7ff"))
+  '((((type tty)) (:foreground "blue"))
+    (((background light)) (:foreground "#AC7135"))
     (((background dark)) (:foreground "#e7c7ff")))
-  "Nested blocks face, depth 4."
+  "Context coloring face, depth 4."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-5-face
-  '((((background light)) (:foreground "#ffcdcd"))
+  '((((type tty)) (:foreground "magenta"))
+    (((background light)) (:foreground "#ACA135"))
     (((background dark)) (:foreground "#ffcdcd")))
-  "Nested blocks face, depth 5."
+  "Context coloring face, depth 5."
   :group 'context-coloring-faces)
 
 (defface context-coloring-depth-6-face
-  '((((background light)) (:foreground "#ffe390"))
+  '((((type tty)) (:foreground "red"))
+    (((background light)) (:foreground "#539A2F"))
     (((background dark)) (:foreground "#ffe390")))
-  "Nested blocks face, depth 6."
-  :group 'context-coloring-faces)
-
-(defface context-coloring-depth-7-face
-  '((((background light)) (:foreground "#cdcdcd"))
-    (((background dark)) (:foreground "#cdcdcd")))
-  "Nested blocks face, depth 7."
+  "Context coloring face, depth 6."
   :group 'context-coloring-faces)
 
-(defconst context-coloring-face-count 8
+(defconst context-coloring-face-count 7
   "Number of faces defined for highlighting delimiter levels.
 Determines depth at which to cycle through faces again.")
 
 
-;;; Face utility functions
+;;; Face functions
 
-(defun context-coloring-level-face (depth)
+(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
@@ -120,50 +117,87 @@ For example: \"context-coloring-depth-1-face\"."
            "-face")))
 
 
-;;; Constants
+;;; Customizable variables
+
+(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."
+  :group 'context-coloring)
+
+(defcustom context-coloring-benchmark-colorization nil
+  "If non-nil, display how long each colorization took."
+  :group 'context-coloring)
+
+
+;;; Local variables
+
+(defvar context-coloring-buffer nil
+  "Reference to this buffer (for timers).")
+(make-variable-buffer-local 'context-coloring-buffer)
+
+(defvar context-coloring-scopifier-process nil
+  "Only allow a single scopifier process to run at a time. This
+is a reference to that one process.")
+(make-variable-buffer-local 'context-coloring-scopifier-process)
+
+(defvar context-coloring-colorize-idle-timer nil
+  "Reference to currently-running idle timer.")
+(make-variable-buffer-local 'context-coloring-colorize-idle-timer)
+
+(defvar context-coloring-changed nil
+  "Indication that the buffer has changed recently, which would
+imply that it should be colorized again.")
+(make-variable-buffer-local 'context-coloring-changed)
+
+(defvar context-coloring-start-time nil
+  "Used to benchmark colorization time.")
+(make-variable-buffer-local 'context-coloring-start-time)
+
+
+;;; Scopification
 
 (defconst context-coloring-path
   (file-name-directory (or load-file-name buffer-file-name))
   "This file's directory.")
 
 (defconst context-coloring-scopifier-path
-  (expand-file-name "./scopifier-esprima.js" context-coloring-path)
+  (expand-file-name "./bin/scopifier" context-coloring-path)
   "Path to the external scopifier executable.")
 
-(defconst context-coloring-delay 0.25
-  "Time between colorization.")
-
-
-;;; Tokenization functions
-
-;; Tokens are vectors with the following form:
-;; 0: Level
-;; 1: Start
-;; 2: End
 (defun context-coloring-apply-tokens (tokens)
-  "Processes TOKENS to apply context-based coloring to the current buffer."
+  "Processes TOKENS to apply context-based coloring to the
+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 ((token (elt tokens i)))
-          (let ((face (context-coloring-level-face (elt token 0)))
-                (start (elt token 1))
-                (end (elt token 2)))
-            (add-text-properties start end `(face ,face rear-nonsticky t))))
-        (setq i (+ i 1))))))
-
-(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))
     (delete-process context-coloring-scopifier-process)
     (setq context-coloring-scopifier-process nil)))
 
+(defun context-coloring-parse-array (input)
+  "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
 contents, reading the scopifier's response asynchronously and
-calling FUNCTION with the parsed list of tokens."
+applying a parsed list of tokens to
+`context-coloring-apply-tokens'."
 
   ;; Prior running tokenization is implicitly obsolete if this function is
   ;; called.
@@ -171,15 +205,14 @@ calling FUNCTION with the parsed list of tokens."
 
   ;; Start the process.
   (setq context-coloring-scopifier-process
-        (start-process-shell-command "scopifier" nil
-                                     (concat "node " context-coloring-scopifier-path)))
+        (start-process-shell-command "scopifier" nil context-coloring-scopifier-path))
 
   (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.
+    ;; The process may produce output in multiple chunks. This filter
+    ;; accumulates the chunks into a message.
     (set-process-filter context-coloring-scopifier-process
                         (lambda (process chunk)
                           (setq output (concat output chunk))))
@@ -189,33 +222,38 @@ calling FUNCTION with the parsed list of tokens."
     (set-process-sentinel context-coloring-scopifier-process
                           (lambda (process event)
                             (when (equal "finished\n" event)
-                              (let ((tokens (json-read-from-string output)))
+                              (let ((tokens (context-coloring-parse-array output)))
                                 (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))
 
 
-;;; Colorization functions
+;;; Colorization
 
 (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
+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 under certain conditions. This will run as an idle
+  "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."
@@ -225,31 +263,6 @@ colorizing would be redundant."
     (context-coloring-colorize)))
 
 
-;;; Local variables
-
-(defvar context-coloring-buffer nil
-  "Reference to this buffer for timers.")
-(make-variable-buffer-local 'context-coloring-buffer)
-
-(defvar context-coloring-scopifier-process nil
-  "Only allow a single scopifier process to run at a time. This
-is a reference to that one process.")
-(make-variable-buffer-local 'context-coloring-scopifier-process)
-
-(defvar context-coloring-colorize-idle-timer nil
-  "Reference to currently-running idle timer.")
-(make-variable-buffer-local 'context-coloring-colorize-idle-timer)
-
-(defvar context-coloring-changed nil
-  "Indication that the buffer has changed recently, which would
-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)
-
-
 ;;; Minor mode
 
 ;;;###autoload
@@ -265,32 +278,27 @@ imply that it should be colorized again.")
         (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)
 
-    ;; Only recolor on change. So watch for changes.
+    ;; Font lock is incompatible with this mode; the converse is also true.
     (font-lock-mode 0)
     (jit-lock-mode nil)
+
+    ;; 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))))
 
-;;;###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