branch: master commit 2cdd91ef433aff628d63bb6f7f7e280fea78616a Author: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com> Commit: Jackson Ray Hamilton <jack...@jacksonrayhamilton.com>
Write new scopifier with comment detection. --- context-coloring.el | 12 ++++- package.json | 2 + scopifier-esprima.js | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ test/fixtures/monad.js | 2 + 4 files changed, 136 insertions(+), 3 deletions(-) diff --git a/context-coloring.el b/context-coloring.el index d98eb9b..98edb8b 100644 --- a/context-coloring.el +++ b/context-coloring.el @@ -41,6 +41,12 @@ ;;; Faces +(defface context-coloring-depth--1-face + '((((background light)) (:foreground "#999999")) + (((background dark)) (:foreground "#999999"))) + "Nested blocks face, depth -1; comments." + :group 'context-coloring-faces) + (defface context-coloring-depth-0-face '((((background light)) (:foreground "#ffffff")) (((background dark)) (:foreground "#ffffff"))) @@ -121,7 +127,7 @@ For example: \"context-coloring-depth-1-face\"." "This file's directory.") (defconst context-coloring-scopifier-path - (expand-file-name "./scopifier.js" context-coloring-path) + (expand-file-name "./scopifier-esprima.js" context-coloring-path) "Path to the external scopifier executable.") (defconst context-coloring-delay 0.25 @@ -187,7 +193,7 @@ calling FUNCTION with the parsed list of tokens." (with-current-buffer buffer (context-coloring-apply-tokens tokens)) (setq context-coloring-scopifier-process nil) - ;; (message "Colorized (after %f seconds)." (- (float-time) start-time)) + (message "Colorized (after %f seconds)." (- (float-time) start-time)) ))))) ;; Give the process its input. @@ -200,7 +206,7 @@ calling FUNCTION with the parsed list of tokens." (defun context-coloring-colorize () (interactive) (setq context-coloring-colorize-start-time (float-time)) - ;; (message "%s" "Colorizing.") + (message "%s" "Colorizing.") (context-coloring-scopify)) (defun context-coloring-change-function (start end length) diff --git a/package.json b/package.json index c5cd241..09d3be2 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "mocha": "^2.0.1" }, "dependencies": { + "escope": "^1.0.1", + "esprima": "^1.2.2", "uglify-js": "^2.4.15" } } diff --git a/scopifier-esprima.js b/scopifier-esprima.js new file mode 100644 index 0000000..29bdc85 --- /dev/null +++ b/scopifier-esprima.js @@ -0,0 +1,123 @@ +/*jslint node: true */ + +'use strict'; + +var escope = require('escope'), + esprima = require('esprima'), + whole = ''; + +process.stdin.setEncoding('utf8'); + +process.stdin.on('readable', function () { + var chunk = process.stdin.read(); + if (chunk !== null) { + whole += chunk; + } +}); + +process.stdin.on('end', function () { + var ast, + analyzedScopes, + scopes = [], + symbols = [], + comments = [], + emacsified; + + // Gracefully handle parse errors by doing nothing. + try { + ast = esprima.parse(whole, { + comment: true, + range: true + }); + analyzedScopes = escope.analyze(ast).scopes; + } catch (error) { + process.exit(1); + } + + analyzedScopes.forEach(function (scope) { + if (scope.level === undefined) { + if (scope.upper) { + if (scope.upper.functionExpressionScope) { + // Pretend function expression scope doesn't exist. + scope.level = scope.upper.level; + scope.variables = scope.upper.variables.concat(scope.variables); + } else { + scope.level = scope.upper.level + 1; + } + } else { + scope.level = 0; + } + if (scope.functionExpressionScope) { + // We've only given the scope a level for posterity's sake. + return; + } + scopes.push([ + scope.level, + scope.block.range[0], + scope.block.range[1] + ]); + scope.variables.forEach(function (variable) { + var definitions = [], + references = []; + variable.defs.forEach(function (definition) { + var range = definition.name.range; + definitions.push([ + scope.level, + range[0], + range[1] + ]); + }); + variable.references.forEach(function (reference) { + var range = reference.identifier.range, + isDefined = definitions.some(function (definition) { + // Check for identical definitions. + return definition[1] === range[0] && + definition[2] === range[1]; + }); + if (isDefined) { + return; + } + references.push([ + scope.level, + range[0], + range[1] + ]); + }); + Array.prototype.push.apply(symbols, definitions); + Array.prototype.push.apply(symbols, references); + }); + scope.references.forEach(function (reference) { + var range; + if (reference.resolved) { + return; + } + // Handle global references. + range = reference.identifier.range; + symbols.push([ + 0, + range[0], + range[1] + ]); + }); + } + }); + + ast.comments.forEach(function (comment) { + var range = comment.range; + comments.push([ + -1, + range[0], + range[1] + ]); + }); + + emacsified = scopes.concat(symbols.concat(comments)); + + emacsified.forEach(function (instruction) { + // Emacs starts counting from 1. + instruction[1] += 1; + instruction[2] += 1; + }); + + console.log(JSON.stringify(emacsified)); +}); diff --git a/test/fixtures/monad.js b/test/fixtures/monad.js index 4a2e19c..7a90405 100644 --- a/test/fixtures/monad.js +++ b/test/fixtures/monad.js @@ -1,5 +1,7 @@ +/* A monad. */ function MONAD() { return function unit(value) { + // Some details. var monad = Object.create(null); monad.bind = function (func) { return func(value);