emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 6fe9e58 09/16: Add Node.js detection.


From: Jackson Ray Hamilton
Subject: [elpa] master 6fe9e58 09/16: Add Node.js detection.
Date: Sun, 18 Oct 2015 11:21:46 +0000

branch: master
commit 6fe9e585b033426d2abceb78d7afaec1efe67dd3
Author: Jackson Ray Hamilton <address@hidden>
Commit: Jackson Ray Hamilton <address@hidden>

    Add Node.js detection.
---
 context-coloring.el            |   87 +++++++++++++++++++++++++++++++++++++++-
 test/context-coloring-test.el  |   48 ++++++++++++++++++++-
 test/fixtures/global.js        |    2 +-
 test/fixtures/initial-level.js |    3 +-
 4 files changed, 133 insertions(+), 7 deletions(-)

diff --git a/context-coloring.el b/context-coloring.el
index 902a9db..a2cd88f 100644
--- a/context-coloring.el
+++ b/context-coloring.el
@@ -298,7 +298,79 @@ are scoped to a file (as in Node.js), set this to `1'."
   :group 'context-coloring)
 
 
-;;; js2-mode colorization
+;;; Node.js colorization
+
+(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.")
+
+(defun context-coloring-node-program-p ()
+  "Guess whether the current file is 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.
+   (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)))
+                  (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)))
+                           (or (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")))))))
+            (throw 'node-program-p t))
+          ;; The `t' indicates to search children.
+          t)))
+     ;; Default to returning nil from the catch body.
+     nil)))
+
+(defcustom context-coloring-detect-node t
+  "If non-nil, use file-level scope for variables in Node.js."
+  :type 'boolean
+  :group 'context-coloring)
+
+
+;;; JavaScript colorization
 
 (defvar-local context-coloring-js2-scope-level-hash-table nil
   "Associate `js2-scope' structures and with their scope
@@ -365,7 +437,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 +472,17 @@ this for ES6 code; disable it elsewhere."
          t)))
     (context-coloring-colorize-comments-and-strings)))
 
+(defun context-coloring-js2-colorize ()
+  "Color the buffer using the `js2-mode'."
+  (cond
+   ;; Increase the initial level if we can detect Node.js.
+   ((and context-coloring-detect-node
+         (context-coloring-node-program-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 c8574da..abd4df1 100644
--- a/test/context-coloring-test.el
+++ b/test/context-coloring-test.el
@@ -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,58 @@ 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-detect-node (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 detect-node
+  (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")))
+      (dolist (indicator positive-indicators)
+        (context-coloring-test-setup-detect-node indicator)
+        (context-coloring-test-assert-javascript-elevated-level))
+      (dolist (indicator negative-indicators)
+        (context-coloring-test-setup-detect-node 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');



reply via email to

[Prev in Thread] Current Thread [Next in Thread]