[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 6ac3b43 12/16: Merge branch 'detect-node'
From: |
Jackson Ray Hamilton |
Subject: |
[elpa] master 6ac3b43 12/16: Merge branch 'detect-node' |
Date: |
Sun, 18 Oct 2015 11:21:49 +0000 |
branch: master
commit 6ac3b4371e507209ea7e96562bd97ad6273da869
Merge: 614de4c 6531b86
Author: Jackson Hamilton <address@hidden>
Commit: Jackson Hamilton <address@hidden>
Merge branch 'detect-node'
---
context-coloring.el | 94 +++++++++++++++++++++++++++++++++++++++-
test/context-coloring-test.el | 75 +++++++++++++++++++++++++-------
test/fixtures/global.js | 2 +-
test/fixtures/initial-level.js | 3 +-
4 files changed, 154 insertions(+), 20 deletions(-)
diff --git a/context-coloring.el b/context-coloring.el
index 902a9db..ef908fd 100644
--- a/context-coloring.el
+++ b/context-coloring.el
@@ -298,7 +298,7 @@ are scoped to a file (as in Node.js), set this to `1'."
: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
@@ -365,7 +365,7 @@ this for ES6 code; disable it elsewhere."
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))
@@ -400,6 +400,96 @@ this for ES6 code; disable it elsewhere."
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 --git a/test/context-coloring-test.el b/test/context-coloring-test.el
index e267941..f643e91 100644
--- a/test/context-coloring-test.el
+++ b/test/context-coloring-test.el
@@ -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 @@ other non-letters are guaranteed to always be discarded."
(lambda ()
(context-coloring-test-assert-coloring "
(xxxxxxxx () {
- 111 1 1 00000001xxx11
+ 111 1 1 0000001xxx11
}());")))
(context-coloring-test-deftest-javascript block-scopes
@@ -602,16 +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-coloring "
-111 1 1 00000001xxx11"))
+ (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 "
diff --git a/test/fixtures/global.js b/test/fixtures/global.js
index a35619d..3de2147 100644
--- a/test/fixtures/global.js
+++ b/test/fixtures/global.js
@@ -1,3 +1,3 @@
(function () {
- var a = require('a');
+ var a = global('a');
}());
diff --git a/test/fixtures/initial-level.js b/test/fixtures/initial-level.js
index 119773e..24a4b71 100644
--- a/test/fixtures/initial-level.js
+++ b/test/fixtures/initial-level.js
@@ -1 +1,2 @@
-var a = require('a');
+
+var a = global('a');
- [elpa] master 85397da 02/16: Add more sophisticated block scope test., (continued)
- [elpa] master 85397da 02/16: Add more sophisticated block scope test., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 683a522 04/16: Suggest eval-expression-minibuffer-setup-hook., Jackson Ray Hamilton, 2015/10/18
- [elpa] master df914b8 03/16: Bump minimum js2-mode version., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 7c16f46 05/16: Add initial level setting., Jackson Ray Hamilton, 2015/10/18
- [elpa] master fa99117 06/16: Add defcustom types., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 614de4c 07/16: Don't make the initial level buffer-local., Jackson Ray Hamilton, 2015/10/18
- [elpa] master a193f4e 08/16: Refactor position level assertion., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 6fe9e58 09/16: Add Node.js detection., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 6531b86 11/16: Use more agnostic names., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 2c62db1 10/16: Ensure Node.js variable is global., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 6ac3b43 12/16: Merge branch 'detect-node',
Jackson Ray Hamilton <=
- [elpa] master ff9643c 14/16: Update Zenburn paragraph., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 765d4c4 13/16: Document new scope detection., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 0c3a48f 15/16: Version 7.1.0., Jackson Ray Hamilton, 2015/10/18
- [elpa] master 6934762 16/16: Merge commit '0c3a48f05d17f486a0c522f0f9ec316dce24d86d' from context-coloring, Jackson Ray Hamilton, 2015/10/18