1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
| | // Copyright (C) 2014-2015 Free Software Foundation, Inc.
// This file is part of GNU Emacs.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
'use strict';
var escope = require('./libraries/escope'),
esprima = require('./libraries/esprima');
// Given code, returns an array of tokens for context-coloring.
function scopifier(code) {
var analyzedScopes,
ast,
definition,
definitionsCount,
definitionsIndex,
i,
isDefined,
j,
k,
pointer,
range,
reference,
scope,
scopes,
tokens,
variable;
// Strip BOM.
code = code.replace(/^\ufeff/g, '');
// Gracefully handle parse errors by doing nothing.
try {
ast = esprima.parse(code, {
range: true
});
analyzedScopes = escope.analyze(ast).scopes;
} catch (error) {
process.exit(1);
}
scopes = [];
tokens = [];
for (i = 0; i < analyzedScopes.length; i += 1) {
scope = analyzedScopes[i];
// Having its level set implies it was already annotated.
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 {
// Base case.
scope.level = 0;
}
// We've only given the scope a level for posterity's sake. We're
// done now.
if (!scope.functionExpressionScope) {
range = scope.block.range;
scopes.push(
range[0] + 1,
range[1] + 1,
scope.level
);
definitionsIndex = tokens.length;
definitionsCount = 0;
for (j = 0; j < scope.variables.length; j += 1) {
variable = scope.variables[j];
definitionsCount += variable.defs.length;
for (k = 0; k < variable.defs.length; k += 1) {
definition = variable.defs[k];
range = definition.name.range;
tokens.push(
range[0] + 1,
range[1] + 1,
scope.level
);
}
}
for (j = 0; j < scope.references.length; j += 1) {
reference = scope.references[j];
range = reference.identifier.range;
isDefined = false;
// Determine if a definition already exists for the range.
// (escope detects variables twice if they are declared and
// initialized simultaneously; this filters them.)
for (k = 0; k < definitionsCount; k += 1) {
pointer = definitionsIndex + (k * 3);
if (tokens[pointer] === range[0] + 1 &&
tokens[pointer + 1] === range[1] + 1) {
isDefined = true;
break;
}
}
if (!isDefined) {
tokens.push(
// Handle global references too.
range[0] + 1,
range[1] + 1,
reference.resolved ? reference.resolved.scope.level : 0
);
}
}
}
}
}
return scopes.concat(tokens);
}
module.exports = scopifier;
|