Skip to content

Instantly share code, notes, and snippets.

@michaelficarra
Last active December 17, 2015 00:49
Show Gist options
  • Save michaelficarra/5523730 to your computer and use it in GitHub Desktop.
Save michaelficarra/5523730 to your computer and use it in GitHub Desktop.
identifier-only scope colouring levels
fs = require 'fs'
esprima = require 'esprima'
escope = require 'escope'
eslevels = require 'eslevels'
js = (fs.readFileSync './input.js').toString()
ast = esprima.parse js, {range: yes}
scopes = (escope.analyze ast).scopes
scopeLevel = (scope) ->
return 0 unless scope?
level = 0
while scope = scope.upper
++level unless scope.functionExpressionScope
level
levels = []
for scope in scopes
level = scopeLevel scope
for variable in scope.variables
# parameters and NFE names and catch variables and uninitialised variable declarations
if variable.identifiers[0] and (not variable.defs[0].node.init or variable.defs[0].type in ['Parameter', 'CatchClause', 'FunctionName'])
levels.push [level, variable.identifiers[0].range[0], variable.identifiers[0].range[1] - 1]
# references to the variables declared in this scope
for reference in variable.references when reference.identifier
levels.push [level, reference.identifier.range[0], reference.identifier.range[1] - 1]
switch scope.type
when 'global'
# unresolved globals
for reference in scope.through
levels.push [0, reference.identifier.range[0], reference.identifier.range[1] - 1]
when 'function'
# `function` keyword
unless scope.functionExpressionScope
levels.push [level, scope.block.range[0], scope.block.range[0] + 8 - 1]
when 'with'
# `with` keyword
levels.push [level, scope.block.range[0], scope.block.range[0] + 4 - 1]
when 'catch'
# `catch` keyword
levels.push [level, scope.block.range[0], scope.block.range[0] + 5 - 1]
levels.sort (a, b) -> if a[1] < b[1] then -1 else 1
console.dir levels
@michaelficarra
Copy link
Author

TODO: the position of the a in the following program is not handled properly because it is both an unreferenced variable declaration and a reference to the catch variable.

(function(){
  try {} catch(a) { var a = 0; }
}());

I guess JavaScript can have overlapping scope levels. In other words, a single character can be simultaneously affecting more than one scope. /cc @mazurov and @Constellation since they'd probably be interested.

I'm thinking maybe unreferenced variable declarations should not be highlighted.

edit: fixed by changing the rule from "declarations of unreferenced variables" to "uninitialised variable declarations". Now the reference to the catch variable takes precedence 😄. That character should still technically be highlighted with both colours.

@mazurov
Copy link

mazurov commented May 9, 2013

You are theoretician 😄 In practice, how many people use redeclaration of exception variable? (I think it also a bad coding style).

(function () {
  var a="OUTSIDE";
  try {throw new Error();} catch(a) { var a = "IN"; console.log(a);}
  console.log(a);
}())

This code will output "IN" and "OUTSIDE", and I see that the reference to the catch variable takes precedence, so the current escope library and new escope-demo page handled it in the right way.

(function () {
  var a="OUTSIDE";
  try {throw new Error();} catch(a) { var a; console.log(a);}
  console.log(a);
}())

will output "Error{}" and "OUTSIDE". And "var a" in "Highlight only important constructions" mode is not highlighted on escope-demo page. I think it is a correct visualization.

Or I miss something?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment