]> code.delx.au - gnu-emacs-elpa/blob - scopifier-microoptimized.js
0cc4f77be7432e1e825ecb5d3847c07996afc26a
[gnu-emacs-elpa] / scopifier-microoptimized.js
1 'use strict';
2
3 var escope = require('escope'),
4 esprima = require('esprima'),
5
6 normal = 0,
7 bold = 1,
8 italic = 2;
9
10 // Given code, returns an array of `[start, end, level, style]' tokens for
11 // context-coloring.
12 module.exports = function (code) {
13 var analyzedScopes,
14 ast,
15 comment,
16 definition,
17 definitionsCount,
18 definitionsIndex,
19 i,
20 isDefined,
21 j,
22 k,
23 range,
24 reference,
25 scope,
26 scopes = [],
27 tokens = [],
28 variable;
29
30 // Gracefully handle parse errors by doing nothing.
31 try {
32 ast = esprima.parse(code, {
33 comment: true,
34 range: true
35 });
36 analyzedScopes = escope.analyze(ast).scopes;
37 } catch (error) {
38 process.exit(1);
39 }
40
41 for (i = 0; i < analyzedScopes.length; i += 1) {
42 scope = analyzedScopes[i];
43 // Having its level set implies it was already annotated.
44 if (scope.level === undefined) {
45 if (scope.upper) {
46 if (scope.upper.functionExpressionScope) {
47 // Pretend function expression scope doesn't exist.
48 scope.level = scope.upper.level;
49 scope.variables = scope.upper.variables.concat(scope.variables);
50 } else {
51 scope.level = scope.upper.level + 1;
52 }
53 } else {
54 // Base case.
55 scope.level = 0;
56 }
57 // We've only given the scope a level for posterity's sake. We're
58 // done now.
59 if (!scope.functionExpressionScope) {
60 range = scope.block.range;
61 scopes.push([
62 range[0] + 1,
63 range[1] + 1,
64 scope.level,
65 normal
66 ]);
67 definitionsIndex = tokens.length;
68 definitionsCount = 0;
69 for (j = 0; j < scope.variables.length; j += 1) {
70 variable = scope.variables[j];
71 definitionsCount += variable.defs.length;
72 for (k = 0; k < variable.defs.length; k += 1) {
73 definition = variable.defs[k];
74 range = definition.name.range;
75 tokens.push([
76 range[0] + 1,
77 range[1] + 1,
78 scope.level,
79 bold
80 ]);
81 }
82 }
83 for (j = 0; j < scope.references.length; j += 1) {
84 reference = scope.references[j];
85 range = reference.identifier.range;
86 isDefined = false;
87 // Determine if a definition already exists for the
88 // range. (escope detects variables twice if they are
89 // declared and initialized simultaneously; this filters
90 // them.)
91 for (k = 0; k < definitionsCount; k += 1) {
92 definition = tokens[definitionsIndex + k];
93 if (definition[0] === range[0] + 1 &&
94 definition[1] === range[1] + 1) {
95 isDefined = true;
96 break;
97 }
98 }
99 if (!isDefined) {
100 tokens.push([
101 // Handle global references too.
102 range[0] + 1,
103 range[1] + 1,
104 reference.resolved ? reference.resolved.scope.level : 0,
105 reference.__maybeImplicitGlobal ? bold : normal
106 ]);
107 }
108 }
109 }
110 }
111 }
112
113 for (i = 0; i < ast.comments.length; i += 1) {
114 comment = ast.comments[i];
115 range = comment.range;
116 tokens.push([
117 range[0] + 1,
118 range[1] + 1,
119 -1,
120 italic
121 ]);
122 }
123
124 return scopes.concat(tokens);
125 };