Skip to content

Commit 40027e2

Browse files
tmcwmarijnh
authored andcommitted
Implement binding types for var/let/const variables.
1 parent 10aa13e commit 40027e2

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

mode/javascript/javascript.js

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
266266
return true;
267267
}
268268
function register(varname) {
269-
function inList(list) {
269+
function inList(list, block) {
270270
for (var v = list; v; v = v.next)
271-
if (v.name == varname) return true;
271+
if (v.name == varname && (block === undefined || v.block === block)) return true;
272272
return false;
273273
}
274274
var state = cx.state;
275275
cx.marked = "def";
276276
if (state.context) {
277-
if (inList(state.localVars)) return;
278-
state.localVars = {name: varname, next: state.localVars};
277+
var block = !(cx.state.lexical.info && cx.state.lexical.info.scope === "var");
278+
if (inList(state.localVars, block)) return;
279+
state.localVars = {name: varname, block: block, next: state.localVars};
279280
} else {
280281
if (inList(state.globalVars)) return;
281282
if (parserConfig.globalVars)
@@ -294,8 +295,25 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
294295
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
295296
cx.state.localVars = defaultVars;
296297
}
298+
function pushblockcontext() {
299+
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars, block: true};
300+
cx.state.localVars = defaultVars;
301+
}
297302
function popcontext() {
298-
cx.state.localVars = cx.state.context.vars;
303+
if (cx.state.context.block) {
304+
var newVars = {name: "this", next: {name: "arguments"}};
305+
// drop block-scoped variables
306+
for (var v = cx.state.localVars; v; v = v.next) {
307+
if (!v.block) newVars = { name: v.name, scope: v.scope, next: newVars };
308+
}
309+
// inherit from previous scope
310+
for (v = cx.state.context.vars; v; v = v.next) {
311+
newVars = { name: v.name, scope: v.scope, next: newVars };
312+
}
313+
cx.state.localVars = newVars;
314+
} else {
315+
cx.state.localVars = cx.state.context.vars;
316+
}
299317
cx.state.context = cx.state.context.prev;
300318
}
301319
function pushlex(type, info) {
@@ -329,12 +347,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
329347
}
330348

331349
function statement(type, value) {
332-
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
350+
if (type == "var") return cont(pushlex("vardef", { scope: value, length: value.length }), vardef, expect(";"), poplex);
333351
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
334352
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
335353
if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
336354
if (type == "debugger") return cont(expect(";"));
337-
if (type == "{") return cont(pushlex("}"), block, poplex);
355+
if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
338356
if (type == ";") return cont();
339357
if (type == "if") {
340358
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
@@ -363,8 +381,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
363381
return cont(pushlex("stat"), maybelabel);
364382
}
365383
}
366-
if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
367-
block, poplex, poplex);
384+
if (type == "switch") return cont(pushblockcontext, pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
385+
block, poplex, poplex, popcontext);
368386
if (type == "case") return cont(expression, expect(":"));
369387
if (type == "default") return cont(expect(":"));
370388
if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
@@ -826,7 +844,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
826844
lexical = lexical.prev;
827845
var type = lexical.type, closing = firstChar == type;
828846

829-
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
847+
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
830848
else if (type == "form" && firstChar == "{") return lexical.indented;
831849
else if (type == "form") return lexical.indented + indentUnit;
832850
else if (type == "stat")

mode/javascript/test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,21 @@
8686
" [keyword yield] [variable-2 i];",
8787
"}");
8888

89+
MT("let_scoping",
90+
"[keyword function] [def scoped]([def n]) {",
91+
" { [keyword var] [def i]; } [variable-2 i];",
92+
" { [keyword let] [def j]; [variable-2 j]; } [variable j];",
93+
" [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];",
94+
"}");
95+
96+
MT("switch_scoping",
97+
"[keyword switch] ([variable x]) {",
98+
" [keyword default]:",
99+
" [keyword let] [def j];",
100+
" [keyword return] [variable-2 j]",
101+
"}",
102+
"[variable j];")
103+
89104
MT("quotedStringAddition",
90105
"[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
91106

0 commit comments

Comments
 (0)