]> code.delx.au - gnu-emacs-elpa/blob - scopifier.js
Remove indentation level.
[gnu-emacs-elpa] / scopifier.js
1 /*jslint node: true */
2
3 'use strict';
4
5 var escope = require('escope'),
6 esprima = require('esprima'),
7
8 // Given an array of definitions, determines if a definition already exists
9 // for a given range. (escope detects variables twice if they are declared
10 // and initialized simultaneously; this filters them.)
11 isDefined = function (definitions, range) {
12 return definitions.some(function (definition) {
13 // Check for identical definitions.
14 return definition[1] === range[0] &&
15 definition[2] === range[1];
16 });
17 };
18
19 // Given code, returns an array of `[level, start, end]' tokens for
20 // context-coloring.
21 module.exports = function (code) {
22 var ast,
23 analyzedScopes,
24 scopes = [],
25 symbols = [],
26 comments,
27 emacsified;
28
29 // Gracefully handle parse errors by doing nothing.
30 try {
31 ast = esprima.parse(code, {
32 comment: true,
33 range: true
34 });
35 analyzedScopes = escope.analyze(ast).scopes;
36 } catch (error) {
37 process.exit(1);
38 }
39
40 analyzedScopes.forEach(function (scope) {
41 var scopeDefinitions;
42 if (scope.level !== undefined) {
43 return;
44 }
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 scope.level = 0;
55 }
56 if (scope.functionExpressionScope) {
57 // We've only given the scope a level for posterity's sake.
58 return;
59 }
60 scopes.push([
61 scope.level,
62 scope.block.range[0],
63 scope.block.range[1]
64 ]);
65 scopeDefinitions = [];
66 scope.variables.forEach(function (variable) {
67 var definitions = [],
68 references = [];
69 variable.defs.forEach(function (definition) {
70 var range = definition.name.range;
71 definitions.push([
72 scope.level,
73 range[0],
74 range[1]
75 ]);
76 });
77 variable.references.forEach(function (reference) {
78 var range = reference.identifier.range;
79 if (isDefined(definitions, range)) {
80 return;
81 }
82 references.push([
83 scope.level,
84 range[0],
85 range[1]
86 ]);
87 });
88 Array.prototype.push.apply(scopeDefinitions, definitions);
89 Array.prototype.push.apply(symbols, definitions);
90 Array.prototype.push.apply(symbols, references);
91 });
92 scope.references.forEach(function (reference) {
93 var range = reference.identifier.range;
94 if (reference.resolved || isDefined(scopeDefinitions, range)) {
95 return;
96 }
97 // Handle global references.
98 symbols.push([
99 0,
100 range[0],
101 range[1]
102 ]);
103 });
104 });
105
106 comments = ast.comments
107 .map(function (comment) {
108 var range = comment.range;
109 return [
110 -1,
111 range[0],
112 range[1]
113 ];
114 });
115
116 emacsified = scopes
117 .concat(symbols)
118 .concat(comments)
119 .map(function (token) {
120 // Emacs starts counting from 1.
121 return [
122 token[0],
123 token[1] + 1,
124 token[2] + 1
125 ];
126 });
127
128 return emacsified;
129 };