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