From: Jackson Hamilton Date: Sun, 18 Oct 2015 11:20:32 +0000 (-0700) Subject: Merge commit '0c3a48f05d17f486a0c522f0f9ec316dce24d86d' from context-coloring X-Git-Url: https://code.delx.au/gnu-emacs-elpa/commitdiff_plain/69347627ae1f60a116b3ef04084093df0fe89456?hp=-c Merge commit '0c3a48f05d17f486a0c522f0f9ec316dce24d86d' from context-coloring --- 69347627ae1f60a116b3ef04084093df0fe89456 diff --combined packages/context-coloring/README.md index ff92dd962,21f1eb5cf..21f1eb5cf --- a/packages/context-coloring/README.md +++ b/packages/context-coloring/README.md @@@ -16,6 -16,7 +16,7 @@@ By default, comments and strings are st - Light and dark customizable color schemes. - JavaScript support: - Script, function and block scopes (and even `catch` block scopes). + - Node.js "file-level" scope detection. - Emacs Lisp support: - `defun`, `lambda`, `let`, `let*`, `cond`, `condition-case`, `defadvice`, `dolist`, `quote`, `backquote` and backquote splicing. @@@ -38,32 -39,18 +39,18 @@@ then add the following to your init fil (add-hook 'emacs-lisp-mode-hook #'context-coloring-mode) ;; eval-expression: - (add-hook 'minibuffer-setup-hook #'context-coloring-mode) + (add-hook 'eval-expression-minibuffer-setup-hook #'context-coloring-mode) ; 24.4+ + (add-hook 'minibuffer-setup-hook #'context-coloring-mode) ; 24.3 ``` ## Color Schemes + The [Zenburn](https://github.com/bbatsov/zenburn-emacs) theme, featured in the + screenshot above, now supports context coloring. + You can define your own colors by customizing faces like `context-coloring-level-N-face`, where N is a number starting from 0. - These are the colors used in the screenshot above: - - ```lisp - (custom-theme-set-faces - 'zenburn - '(context-coloring-level-0-face ((t :foreground "#dcdccc"))) - '(context-coloring-level-1-face ((t :foreground "#93e0e3"))) - '(context-coloring-level-2-face ((t :foreground "#bfebbf"))) - '(context-coloring-level-3-face ((t :foreground "#f0dfaf"))) - '(context-coloring-level-4-face ((t :foreground "#dfaf8f"))) - '(context-coloring-level-5-face ((t :foreground "#cc9393"))) - '(context-coloring-level-6-face ((t :foreground "#dc8cc3"))) - '(context-coloring-level-7-face ((t :foreground "#94bff3"))) - '(context-coloring-level-8-face ((t :foreground "#9fc59f"))) - '(context-coloring-level-9-face ((t :foreground "#d0bf8f"))) - '(context-coloring-level-10-face ((t :foreground "#dca3a3")))) - ``` - [See here](https://gist.github.com/jacksonrayhamilton/6b89ca3b85182c490816) for some color schemes for popular custom themes. @@@ -75,3 -62,5 +62,5 @@@ strings using `font-lock`. - `context-coloring-javascript-block-scopes` (default: `nil`): If non-nil, also color block scopes in the scope hierarchy in JavaScript. + - `context-coloring-javascript-detect-top-level-scope` (default: `t`): If + non-nil, detect when to use file-level scope. diff --combined packages/context-coloring/context-coloring.el index 2b1d7afe4,8d6cacc0c..8d6cacc0c --- a/packages/context-coloring/context-coloring.el +++ b/packages/context-coloring/context-coloring.el @@@ -3,9 -3,9 +3,9 @@@ ;; Copyright (C) 2014-2015 Free Software Foundation, Inc. ;; Author: Jackson Ray Hamilton - ;; Version: 7.0.0 + ;; Version: 7.1.0 ;; Keywords: convenience faces tools - ;; Package-Requires: ((emacs "24.3") (js2-mode "20150126")) + ;; Package-Requires: ((emacs "24.3") (js2-mode "20150713")) ;; URL: https://github.com/jacksonrayhamilton/context-coloring ;; This file is part of GNU Emacs. @@@ -184,6 -184,7 +184,7 @@@ START, END and LENGTH are recorded for Increase this if your machine is high-performing. Decrease it if it ain't." + :type 'float :group 'context-coloring) (make-obsolete-variable @@@ -248,10 -249,12 +249,12 @@@ (defcustom context-coloring-syntactic-comments t "If non-nil, also color comments using `font-lock'." + :type 'boolean :group 'context-coloring) (defcustom context-coloring-syntactic-strings t "If non-nil, also color strings using `font-lock'." + :type 'boolean :group 'context-coloring) (defun context-coloring-font-lock-syntactic-comment-function (state) @@@ -285,8 -288,17 +288,17 @@@ MIN defaults to beginning of buffer. M (when (eq major-mode 'emacs-lisp-mode) (font-lock-fontify-keywords-region min max)))))) + (defcustom context-coloring-initial-level 0 + "Scope level at which to start coloring. + + If top-level variables and functions do not become global, but + are scoped to a file (as in Node.js), set this to `1'." + :type 'integer + :safe #'integerp + :group 'context-coloring) - ;;; js2-mode colorization + + ;;; JavaScript colorization (defvar-local context-coloring-js2-scope-level-hash-table nil "Associate `js2-scope' structures and with their scope @@@ -297,6 -309,8 +309,8 @@@ The block-scoped `let' and `const' are introduced in ES6. Enable this for ES6 code; disable it elsewhere." + :type 'boolean + :safe #'booleanp :group 'context-coloring) (make-obsolete-variable @@@ -304,11 -318,11 +318,11 @@@ 'context-coloring-javascript-block-scopes "7.0.0") - (defsubst context-coloring-js2-scope-level (scope) - "Return the level of SCOPE." + (defsubst context-coloring-js2-scope-level (scope initial) + "Return the level of SCOPE, starting from INITIAL." (cond ((gethash scope context-coloring-js2-scope-level-hash-table)) (t - (let ((level 0) + (let ((level initial) (current-scope scope) enclosing-scope) (while (and current-scope @@@ -351,7 -365,7 +365,7 @@@ context-coloring-point-max) level))) - (defun context-coloring-js2-colorize () + (defun context-coloring-js2-colorize-ast () "Color the buffer using the `js2-mode' abstract syntax tree." ;; Reset the hash table; the old one could be obsolete. (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test #'eq)) @@@ -365,7 -379,7 +379,7 @@@ ((js2-scope-p node) (context-coloring-js2-colorize-node node - (context-coloring-js2-scope-level node))) + (context-coloring-js2-scope-level node context-coloring-initial-level))) ((context-coloring-js2-local-name-node-p node) (let* ((enclosing-scope (js2-node-get-enclosing-scope node)) (defining-scope (js2-get-defining-scope @@@ -378,11 -392,104 +392,104 @@@ (when (not (eq defining-scope enclosing-scope)) (context-coloring-js2-colorize-node node - (context-coloring-js2-scope-level defining-scope)))))) + ;; Use `0' as an initial level so global variables are always at + ;; the highest level (even if `context-coloring-initial-level' + ;; specifies an initial level for the rest of the code). + (context-coloring-js2-scope-level defining-scope 0)))))) ;; The `t' indicates to search children. t))) (context-coloring-colorize-comments-and-strings))) + (defconst context-coloring-node-comment-regexp + (concat + ;; Ensure the "//" or "/*" comment starts with the directive. + "\\(//[[:space:]]*\\|/\\*[[:space:]]*\\)" + ;; Support multiple directive formats. + "\\(" + ;; JSLint and JSHint support a JSON-like format. + "\\(jslint\\|jshint\\)[[:space:]].*?node:[[:space:]]*true" + "\\|" + ;; ESLint just specifies the option name. + "eslint-env[[:space:]].*?node" + "\\)") + "Match a comment body hinting at a Node.js program.") + + ;; TODO: Add ES6 module detection. + (defun context-coloring-js2-top-level-local-p () + "Guess whether top-level variables are local. + For instance, the current file could be a Node.js program." + (or + ;; A shebang is a pretty obvious giveaway. + (string-equal + "node" + (save-excursion + (goto-char (point-min)) + (when (looking-at auto-mode-interpreter-regexp) + (match-string 2)))) + ;; Otherwise, perform static analysis. + (progn + (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test #'eq)) + (catch 'node-program-p + (js2-visit-ast + js2-mode-ast + (lambda (node end-p) + (when (null end-p) + (when + (cond + ;; Infer based on inline linter configuration. + ((js2-comment-node-p node) + (string-match-p + context-coloring-node-comment-regexp + (js2-node-string node))) + ;; Infer based on the prescence of certain variables. + ((and (js2-name-node-p node) + (let ((parent (js2-node-parent node))) + (not (and (js2-object-prop-node-p parent) + (eq node (js2-object-prop-node-left parent)))))) + (let ((name (js2-name-node-name node)) + (parent (js2-node-parent node))) + (and + (cond + ;; Check whether this is "exports.something" or + ;; "module.exports". + ((js2-prop-get-node-p parent) + (and + (eq node (js2-prop-get-node-left parent)) + (or (string-equal name "exports") + (let* ((property (js2-prop-get-node-right parent)) + (property-name (js2-name-node-name property))) + (and (string-equal name "module") + (string-equal property-name "exports")))))) + ;; Check whether it's a "require('module')" call. + ((js2-call-node-p parent) + (or (string-equal name "require")))) + (let* ((enclosing-scope (js2-node-get-enclosing-scope node)) + (defining-scope (js2-get-defining-scope + enclosing-scope name))) + ;; The variable also must be global. + (null defining-scope)))))) + (throw 'node-program-p t)) + ;; The `t' indicates to search children. + t))) + ;; Default to returning nil from the catch body. + nil)))) + + (defcustom context-coloring-javascript-detect-top-level-scope t + "If non-nil, detect when to use file-level scope." + :type 'boolean + :group 'context-coloring) + + (defun context-coloring-js2-colorize () + "Color the buffer using the `js2-mode'." + (cond + ;; Increase the initial level if we should. + ((and context-coloring-javascript-detect-top-level-scope + (context-coloring-js2-top-level-local-p)) + (let ((context-coloring-initial-level 1)) + (context-coloring-js2-colorize-ast))) + (t + (context-coloring-js2-colorize-ast)))) + ;;; Emacs Lisp colorization diff --combined packages/context-coloring/test/context-coloring-test.el index f5633b86e,f643e914c..f643e914c --- a/packages/context-coloring/test/context-coloring-test.el +++ b/packages/context-coloring/test/context-coloring-test.el @@@ -365,21 -365,21 +365,21 @@@ signaled. ;;; Coloring tests + (defun context-coloring-test-face-to-level (face) + "Convert FACE symbol to its corresponding level, or nil." + (when face + (let* ((face-string (symbol-name face)) + (matches (string-match + context-coloring-level-face-regexp + face-string))) + (when matches + (string-to-number (match-string 1 face-string)))))) + (defun context-coloring-test-assert-position-level (position level) "Assert that POSITION has LEVEL." - (let ((face (get-text-property position 'face)) - actual-level) - (when (not (and face - (let* ((face-string (symbol-name face)) - (matches (string-match - context-coloring-level-face-regexp - face-string))) - (when matches - (setq actual-level (string-to-number - (substring face-string - (match-beginning 1) - (match-end 1)))) - (= level actual-level))))) + (let* ((face (get-text-property position 'face)) + (actual-level (context-coloring-test-face-to-level face))) + (when (not (= level actual-level)) (ert-fail (format (concat "Expected level at position %s, " "which is \"%s\", to be %s; " "but it was %s") @@@ -493,7 -493,7 +493,7 @@@ other non-letters are guaranteed to alw (lambda () (context-coloring-test-assert-coloring " (xxxxxxxx () { - 111 1 1 00000001xxx11 + 111 1 1 0000001xxx11 }());"))) (context-coloring-test-deftest-javascript block-scopes @@@ -503,6 -503,16 +503,16 @@@ 11 111 2 222 12 222 22 + 22222 12 + 2 + }()); + + (xxxxxxxx () { + 'xxx xxxxxx'; + 11 111 2 + 222 12 + 222 22 + 22222 22 2 }());")) :before (lambda () @@@ -592,6 -602,59 +602,59 @@@ ssssssssssss0") ;; As long as `add-text-properties' doesn't signal an error, this test passes. (lambda ())) + (defun context-coloring-test-assert-javascript-elevated-level () + "Assert that the \"initial-level.js\" file has elevated scope." + (context-coloring-test-assert-coloring " + + 111 1 1 0000001xxx11")) + + (defun context-coloring-test-assert-javascript-global-level () + "Assert that the \"initial-level.js\" file has global scope." + (context-coloring-test-assert-coloring " + + 000 0 0 0000000xxx00")) + + (context-coloring-test-deftest-javascript initial-level + (lambda () + (context-coloring-test-assert-javascript-elevated-level)) + :fixture "initial-level.js" + :before (lambda () + (setq context-coloring-initial-level 1)) + :after (lambda () + (setq context-coloring-initial-level 0))) + + (defun context-coloring-test-setup-top-level-scope (string) + "Make STRING the first line and colorize again." + (goto-char (point-min)) + (kill-whole-line 0) + (insert string) + ;; Reparsing triggers recoloring. + (js2-reparse)) + + (context-coloring-test-deftest-javascript top-level-scope + (lambda () + (let ((positive-indicators + (list "#!/usr/bin/env node" + "/*jslint node: true */" + "// jshint node: true" + "/*eslint-env node */" + "module.exports" + "module.exports.a" + "exports.a" + "require('a')")) + (negative-indicators + (list "// Blah blah jshint blah." + "module" + "exports" + "var require; require('a')"))) + (dolist (indicator positive-indicators) + (context-coloring-test-setup-top-level-scope indicator) + (context-coloring-test-assert-javascript-elevated-level)) + (dolist (indicator negative-indicators) + (context-coloring-test-setup-top-level-scope indicator) + (context-coloring-test-assert-javascript-global-level)))) + :fixture "initial-level.js") + (context-coloring-test-deftest-emacs-lisp defun (lambda () (context-coloring-test-assert-coloring " @@@ -699,7 -762,8 +762,8 @@@ 22 02 22 222 2222 1 1 2 2 2 000022 - 1111 1 1 1 0 0 000011"))) + 1111 1 1 1 0 0 000011")) + :fixture "let-star.el") (context-coloring-test-deftest-emacs-lisp cond (lambda () diff --combined packages/context-coloring/test/fixtures/block-scopes.js index 735ca6f11,86e4a1368..86e4a1368 --- a/packages/context-coloring/test/fixtures/block-scopes.js +++ b/packages/context-coloring/test/fixtures/block-scopes.js @@@ -2,5 -2,15 +2,15 @@@ if (1) { var a; let b; + const c; + } + }()); + + (function () { + 'use strict'; + if (1) { + var a; + let b; + const c; } }()); diff --combined packages/context-coloring/test/fixtures/global.js index a35619d4a,3de21470d..3de21470d --- a/packages/context-coloring/test/fixtures/global.js +++ b/packages/context-coloring/test/fixtures/global.js @@@ -1,3 -1,3 +1,3 @@@ (function () { - var a = require('a'); + var a = global('a'); }()); diff --combined packages/context-coloring/test/fixtures/initial-level.js index 000000000,24a4b7173..24a4b7173 mode 000000,100644..100644 --- a/packages/context-coloring/test/fixtures/initial-level.js +++ b/packages/context-coloring/test/fixtures/initial-level.js @@@ -1,0 -1,2 +1,2 @@@ + + var a = global('a');