]> code.delx.au - gnu-emacs-elpa/blob - scopifier.js
Cleanup old scopifier.
[gnu-emacs-elpa] / scopifier.js
1 /*jslint node: true */
2
3 'use strict';
4
5 var escope = require('escope'),
6 esprima = require('esprima'),
7 whole = '';
8
9 process.stdin.setEncoding('utf8');
10
11 process.stdin.on('readable', function () {
12 var chunk = process.stdin.read();
13 if (chunk !== null) {
14 whole += chunk;
15 }
16 });
17
18 process.stdin.on('end', function () {
19 var ast,
20 analyzedScopes,
21 scopes = [],
22 symbols = [],
23 comments = [],
24 emacsified;
25
26 // Gracefully handle parse errors by doing nothing.
27 try {
28 ast = esprima.parse(whole, {
29 comment: true,
30 range: true
31 });
32 analyzedScopes = escope.analyze(ast).scopes;
33 } catch (error) {
34 process.exit(1);
35 }
36
37 analyzedScopes.forEach(function (scope) {
38 if (scope.level === undefined) {
39 if (scope.upper) {
40 if (scope.upper.functionExpressionScope) {
41 // Pretend function expression scope doesn't exist.
42 scope.level = scope.upper.level;
43 scope.variables = scope.upper.variables.concat(scope.variables);
44 } else {
45 scope.level = scope.upper.level + 1;
46 }
47 } else {
48 scope.level = 0;
49 }
50 if (scope.functionExpressionScope) {
51 // We've only given the scope a level for posterity's sake.
52 return;
53 }
54 scopes.push([
55 scope.level,
56 scope.block.range[0],
57 scope.block.range[1]
58 ]);
59 scope.variables.forEach(function (variable) {
60 var definitions = [],
61 references = [];
62 variable.defs.forEach(function (definition) {
63 var range = definition.name.range;
64 definitions.push([
65 scope.level,
66 range[0],
67 range[1]
68 ]);
69 });
70 variable.references.forEach(function (reference) {
71 var range = reference.identifier.range,
72 isDefined = definitions.some(function (definition) {
73 // Check for identical definitions.
74 return definition[1] === range[0] &&
75 definition[2] === range[1];
76 });
77 if (isDefined) {
78 return;
79 }
80 references.push([
81 scope.level,
82 range[0],
83 range[1]
84 ]);
85 });
86 Array.prototype.push.apply(symbols, definitions);
87 Array.prototype.push.apply(symbols, references);
88 });
89 scope.references.forEach(function (reference) {
90 var range;
91 if (reference.resolved) {
92 return;
93 }
94 // Handle global references.
95 range = reference.identifier.range;
96 symbols.push([
97 0,
98 range[0],
99 range[1]
100 ]);
101 });
102 }
103 });
104
105 ast.comments.forEach(function (comment) {
106 var range = comment.range;
107 comments.push([
108 -1,
109 range[0],
110 range[1]
111 ]);
112 });
113
114 emacsified = scopes.concat(symbols.concat(comments));
115
116 emacsified.forEach(function (instruction) {
117 // Emacs starts counting from 1.
118 instruction[1] += 1;
119 instruction[2] += 1;
120 });
121
122 console.log(JSON.stringify(emacsified));
123 });