branch: master commit 233abf8df3d0750692f5edea2a0f1fb4b7746a29 Merge: 3f035ad 901db77 Author: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com> Commit: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com>
Merge commit '901db7732bf61d9809712e8adfad9f84adc2eb56' from context-coloring --- packages/context-coloring/context-coloring.el | 207 ++++++++++++++------ packages/context-coloring/test/binaries/outta-date | 5 + .../context-coloring/test/context-coloring-test.el | 44 ++++- 3 files changed, 195 insertions(+), 61 deletions(-) diff --git a/packages/context-coloring/context-coloring.el b/packages/context-coloring/context-coloring.el index 02537c6..cd1b97a 100644 --- a/packages/context-coloring/context-coloring.el +++ b/packages/context-coloring/context-coloring.el @@ -5,7 +5,7 @@ ;; Author: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com> ;; URL: https://github.com/jacksonrayhamilton/context-coloring ;; Keywords: context coloring syntax highlighting -;; Version: 6.1.0 +;; Version: 6.2.0 ;; Package-Requires: ((emacs "24") (js2-mode "20150126")) ;; This file is part of GNU Emacs. @@ -62,6 +62,13 @@ "Reference to this buffer (for timers).") +;;; Utilities + +(defun context-coloring-join (strings delimiter) + "Join a list of STRINGS with the string DELIMITER." + (mapconcat 'identity strings delimiter)) + + ;;; Faces (defun context-coloring-defface (level tty light dark) @@ -303,12 +310,9 @@ element." (delete-process context-coloring-scopifier-process) (setq context-coloring-scopifier-process nil))) -(defun context-coloring-scopify-shell-command (command &optional callback) - "Invoke a scopifier via COMMAND with the current buffer's contents, -read the scopifier's response asynchronously and apply a parsed -list of tokens to `context-coloring-apply-tokens'. - -Invoke CALLBACK when complete." +(defun context-coloring-scopify-shell-command (command callback) + "Invoke a scopifier via COMMAND, read its response +asynchronously and invoke CALLBACK with its output." ;; Prior running tokenization is implicitly obsolete if this function is ;; called. @@ -318,8 +322,7 @@ Invoke CALLBACK when complete." (setq context-coloring-scopifier-process (start-process-shell-command "scopifier" nil command)) - (let ((output "") - (buffer context-coloring-buffer)) + (let ((output "")) ;; The process may produce output in multiple chunks. This filter ;; accumulates the chunks into a message. @@ -334,19 +337,34 @@ Invoke CALLBACK when complete." context-coloring-scopifier-process (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) - (when callback (funcall callback))))))) + (funcall callback output)))))) - ;; Give the process its input so it can begin. +(defun context-coloring-send-buffer-to-scopifier () + "Give the scopifier process its input so it can begin +scopifying." (process-send-region context-coloring-scopifier-process (point-min) (point-max)) (process-send-eof context-coloring-scopifier-process)) +(defun context-coloring-scopify-and-colorize (command &optional callback) + "Invoke a scopifier via COMMAND with the current buffer's contents, +read the scopifier's response asynchronously and apply a parsed +list of tokens to `context-coloring-apply-tokens'. + +Invoke CALLBACK when complete." + (let ((buffer context-coloring-buffer)) + (context-coloring-scopify-shell-command + command + (lambda (output) + (let ((tokens (context-coloring-parse-array output))) + (with-current-buffer buffer + (context-coloring-apply-tokens tokens)) + (setq context-coloring-scopifier-process nil) + (when callback (funcall callback)))))) + (context-coloring-send-buffer-to-scopifier)) + ;;; Dispatch @@ -395,6 +413,11 @@ buffer a returns a flat vector of start, end and level data. sent via stdin, and with a flat JSON array of start, end and level data returned via stdout. +`:version' - Minimum required version that should be printed when +executing `:command' with a \"--version\" flag. The version +should be numeric, e.g. \"2\", \"19700101\", \"1.2.3\", +\"v1.2.3\" etc. + `:setup' - Arbitrary code to set up this dispatch when `context-coloring-mode' is enabled. @@ -419,7 +442,8 @@ level data returned via stdout. 'javascript-node :modes '(js-mode js3-mode) :executable "scopifier" - :command "scopifier") + :command "scopifier" + :version "v1.1.1") (context-coloring-define-dispatch 'javascript-js2 @@ -438,27 +462,19 @@ the current buffer, then execute it. Invoke 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))) - (when (null dispatch) - (message "%s" "Context coloring is not available for this major mode")) - (let (colorizer - scopifier - command - executable) - (cond - ((setq colorizer (plist-get dispatch :colorizer)) - (funcall colorizer) - (when callback (funcall callback))) - ((setq scopifier (plist-get dispatch :scopifier)) - (context-coloring-apply-tokens (funcall scopifier)) - (when callback (funcall callback))) - ((setq command (plist-get dispatch :command)) - (setq executable (plist-get dispatch :executable)) - (if (and executable - (null (executable-find executable))) - (progn - (message "Executable \"%s\" not found" executable)) - (context-coloring-scopify-shell-command command callback))))))) + (let ((dispatch (gethash major-mode context-coloring-mode-hash-table)) + colorizer + scopifier + command) + (cond + ((setq colorizer (plist-get dispatch :colorizer)) + (funcall colorizer) + (when callback (funcall callback))) + ((setq scopifier (plist-get dispatch :scopifier)) + (context-coloring-apply-tokens (funcall scopifier)) + (when callback (funcall callback))) + ((setq command (plist-get dispatch :command)) + (context-coloring-scopify-and-colorize command callback))))) ;;; Colorization @@ -468,9 +484,7 @@ elisp tracks, and asynchronously for shell command tracks." Invoke CALLBACK when complete; see `context-coloring-dispatch'." (interactive) - (context-coloring-dispatch - (lambda () - (when callback (funcall callback))))) + (context-coloring-dispatch callback)) (defvar-local context-coloring-changed nil "Indication that the buffer has changed recently, which implies @@ -492,6 +506,59 @@ used.") (context-coloring-colorize))) +;;; Versioning + +(defun context-coloring-parse-version (string) + "Extract segments of a version STRING into a list. \"v1.0.0\" +produces (1 0 0), \"19700101\" produces (19700101), etc." + (let (version) + (while (string-match "[0-9]+" string) + (setq version (append version + (list (string-to-number (match-string 0 string))))) + (setq string (substring string (match-end 0)))) + version)) + +(defun context-coloring-check-version (expected actual) + "Check that version EXPECTED is less than or equal to ACTUAL." + (let ((expected (context-coloring-parse-version expected)) + (actual (context-coloring-parse-version actual)) + (continue t) + (acceptable t)) + (while (and continue expected) + (let ((an-expected (car expected)) + (an-actual (car actual))) + (cond + ((> an-actual an-expected) + (setq acceptable t) + (setq continue nil)) + ((< an-actual an-expected) + (setq acceptable nil) + (setq continue nil)))) + (setq expected (cdr expected)) + (setq actual (cdr actual))) + acceptable)) + +(defvar context-coloring-check-scopifier-version-hook nil + "Hooks to run after checking the scopifier version.") + +(defun context-coloring-check-scopifier-version (&optional callback) + "Asynchronously invoke CALLBACK with a predicate indicating +whether the current scopifier version satisfies the minimum +version number required for the current major mode." + (let ((dispatch (gethash major-mode context-coloring-mode-hash-table))) + (when dispatch + (let ((version (plist-get dispatch :version)) + (command (plist-get dispatch :command))) + (context-coloring-scopify-shell-command + (context-coloring-join (list command "--version") " ") + (lambda (output) + (if (context-coloring-check-version version output) + (progn + (when callback (funcall callback t))) + (when callback (funcall callback nil))) + (run-hooks 'context-coloring-check-scopifier-version-hook))))))) + + ;;; Themes (defvar context-coloring-theme-hash-table (make-hash-table :test 'eq) @@ -846,6 +913,16 @@ it ain't. Supported modes: `js-mode', `js3-mode'" :group 'context-coloring) +(defun context-coloring-setup-idle-change-detection () + "Setup idle change detection." + (add-hook + 'after-change-functions 'context-coloring-change-function nil t) + (setq context-coloring-colorize-idle-timer + (run-with-idle-timer + context-coloring-delay + t + 'context-coloring-maybe-colorize))) + ;;;###autoload (define-minor-mode context-coloring-mode "Context-based code coloring, inspired by Douglas Crockford." @@ -878,23 +955,39 @@ Supported modes: `js-mode', `js3-mode'" (make-local-variable 'font-lock-syntactic-face-function) (let ((dispatch (gethash major-mode context-coloring-mode-hash-table))) - (when dispatch - (let ((command (plist-get dispatch :command)) - (setup (plist-get dispatch :setup))) - (when command - ;; Shell commands recolor on change, idly. - (add-hook - 'after-change-functions 'context-coloring-change-function nil t) - (setq context-coloring-colorize-idle-timer - (run-with-idle-timer - context-coloring-delay - t - 'context-coloring-maybe-colorize))) - (when setup - (funcall setup))))) - - ;; Colorize once initially. - (context-coloring-colorize))) + (if dispatch + (progn + (let ((command (plist-get dispatch :command)) + (version (plist-get dispatch :version)) + (executable (plist-get dispatch :executable)) + (setup (plist-get dispatch :setup)) + (colorize-initially-p t)) + (when command + ;; Shell commands recolor on change, idly. + (cond + ((and executable + (null (executable-find executable))) + (message "Executable \"%s\" not found" executable) + (setq colorize-initially-p nil)) + (version + (context-coloring-check-scopifier-version + (lambda (sufficient-p) + (if sufficient-p + (progn + (context-coloring-setup-idle-change-detection) + (context-coloring-colorize)) + (message "Update to the minimum version of \"%s\" (%s)" + executable version)))) + (setq colorize-initially-p nil)) + (t + (context-coloring-setup-idle-change-detection)))) + (when setup + (funcall setup)) + ;; Colorize once initially. + (when colorize-initially-p + (context-coloring-colorize)))) + (when (null dispatch) + (message "Context coloring is not available for this major mode")))))) (provide 'context-coloring) diff --git a/packages/context-coloring/test/binaries/outta-date b/packages/context-coloring/test/binaries/outta-date new file mode 100755 index 0000000..3ac2dd5 --- /dev/null +++ b/packages/context-coloring/test/binaries/outta-date @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +'use strict'; + +console.log('v2.0.4'); diff --git a/packages/context-coloring/test/context-coloring-test.el b/packages/context-coloring/test/context-coloring-test.el index 903da68..cbd2002 100644 --- a/packages/context-coloring/test/context-coloring-test.el +++ b/packages/context-coloring/test/context-coloring-test.el @@ -54,7 +54,8 @@ (setq context-coloring-comments-and-strings t) (setq context-coloring-syntactic-comments nil) (setq context-coloring-syntactic-strings nil) - (setq context-coloring-js-block-scopes nil)) + (setq context-coloring-js-block-scopes nil) + (setq context-coloring-check-scopifier-version-hook nil)) (defmacro context-coloring-test-with-fixture (fixture &rest body) "With the relative FIXTURE, evaluate BODY in a temporary @@ -136,7 +137,8 @@ in the typical format." (function-name (intern-soft (format "context-coloring-test-js-%s" name))) (setup-function-name (intern-soft - (format "context-coloring-test-js-%s-setup" name)))) + (format + "context-coloring-test-js-%s-setup" name)))) `(ert-deftest-async ,test-name (done) (context-coloring-test-js-mode ,fixture @@ -156,7 +158,8 @@ format." (function-name (intern-soft (format "context-coloring-test-js-%s" name))) (setup-function-name (intern-soft - (format "context-coloring-test-js-%s-setup" name)))) + (format + "context-coloring-test-js-%s-setup" name)))) `(ert-deftest ,test-name () (context-coloring-test-js2-mode ,fixture @@ -287,7 +290,8 @@ is FOREGROUND, or the inverse if NEGATE is non-nil." "but it %s.") level (if negate "not " "") foreground - (if negate "did" (format "was `%s'" actual-foreground))))))) + (if negate + "did" (format "was `%s'" actual-foreground))))))) (defun context-coloring-test-assert-not-face (&rest arguments) "Assert that LEVEL does not have a face with `:foreground' @@ -307,6 +311,38 @@ FOREGROUND. Apply ARGUMENTS to "Context coloring is not available for this major mode" "*Messages*"))) +(define-derived-mode + context-coloring-test-unsupported-version-mode + fundamental-mode + "Testing" + "Prevent `context-coloring-test-unsupported-version' from + having any unintentional side-effects on mode support.") + +(ert-deftest-async context-coloring-test-unsupported-version (done) + (context-coloring-define-dispatch + 'outta-date + :modes '(context-coloring-test-unsupported-version-mode) + :executable "node" + :command "node test/binaries/outta-date" + :version "v2.1.3") + (context-coloring-test-with-fixture-async + "./fixtures/function-scopes.js" + (lambda (teardown) + (context-coloring-test-unsupported-version-mode) + (add-hook + 'context-coloring-check-scopifier-version-hook + (lambda () + (unwind-protect + (progn + ;; Normally the executable would be something like "outta-date" + ;; rather than "node". + (context-coloring-test-assert-message + "Update to the minimum version of \"node\" (v2.1.3)" + "*Messages*")) + (funcall teardown)) + (funcall done))) + (context-coloring-mode)))) + (defvar context-coloring-test-theme-index 0 "Unique index for unique theme names.")