Skip to content

Commit 2e99e14

Browse files
committed
[javascript mode] Simplify handling of block scopes
Issue #5296
1 parent 40027e2 commit 2e99e14

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

mode/javascript/javascript.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -265,22 +265,42 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
265265
pass.apply(null, arguments);
266266
return true;
267267
}
268+
function inList(name, list) {
269+
for (var v = list; v; v = v.next) if (v.name == name) return true
270+
return false;
271+
}
268272
function register(varname) {
269-
function inList(list, block) {
270-
for (var v = list; v; v = v.next)
271-
if (v.name == varname && (block === undefined || v.block === block)) return true;
272-
return false;
273-
}
274273
var state = cx.state;
275274
cx.marked = "def";
276275
if (state.context) {
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};
276+
if (state.lexical.info == "var" && state.context && state.context.block) {
277+
// FIXME function decls are also not block scoped
278+
var newContext = registerVarScoped(varname, state.context)
279+
if (newContext != null) {
280+
state.context = newContext
281+
return
282+
}
283+
} else if (!inList(varname, state.localVars)) {
284+
state.localVars = new Var(varname, state.localVars)
285+
return
286+
}
287+
}
288+
// Fall through means this is global
289+
if (parserConfig.globalVars && !inList(varname, state.globalVars))
290+
state.globalVars = new Var(varname, state.globalVars)
291+
}
292+
function registerVarScoped(varname, context) {
293+
if (!context) {
294+
return null
295+
} else if (context.block) {
296+
var inner = registerVarScoped(varname, context.prev)
297+
if (!inner) return null
298+
if (inner == context.prev) return context
299+
return new Context(inner, context.vars, true)
300+
} else if (inList(varname, context.vars)) {
301+
return context
280302
} else {
281-
if (inList(state.globalVars)) return;
282-
if (parserConfig.globalVars)
283-
state.globalVars = {name: varname, next: state.globalVars};
303+
return new Context(context.prev, new Var(varname, context.vars), false)
284304
}
285305
}
286306

@@ -290,32 +310,23 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
290310

291311
// Combinators
292312

293-
var defaultVars = {name: "this", next: {name: "arguments"}};
313+
function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
314+
function Var(name, next) { this.name = name; this.next = next }
315+
316+
var defaultVars = new Var("this", new Var("arguments", null))
294317
function pushcontext() {
295-
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
296-
cx.state.localVars = defaultVars;
318+
cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
319+
cx.state.localVars = defaultVars
297320
}
298321
function pushblockcontext() {
299-
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars, block: true};
300-
cx.state.localVars = defaultVars;
322+
cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
323+
cx.state.localVars = null
301324
}
302325
function popcontext() {
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-
}
317-
cx.state.context = cx.state.context.prev;
326+
cx.state.localVars = cx.state.context.vars
327+
cx.state.context = cx.state.context.prev
318328
}
329+
popcontext.lex = true
319330
function pushlex(type, info) {
320331
var result = function() {
321332
var state = cx.state, indent = state.indented;
@@ -347,7 +358,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
347358
}
348359

349360
function statement(type, value) {
350-
if (type == "var") return cont(pushlex("vardef", { scope: value, length: value.length }), vardef, expect(";"), poplex);
361+
if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
351362
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
352363
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
353364
if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
@@ -381,7 +392,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
381392
return cont(pushlex("stat"), maybelabel);
382393
}
383394
}
384-
if (type == "switch") return cont(pushblockcontext, pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
395+
if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
385396
block, poplex, poplex, popcontext);
386397
if (type == "case") return cont(expression, expect(":"));
387398
if (type == "default") return cont(expect(":"));
@@ -803,7 +814,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
803814
cc: [],
804815
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
805816
localVars: parserConfig.localVars,
806-
context: parserConfig.localVars && {vars: parserConfig.localVars},
817+
context: parserConfig.localVars && new Context(null, null, false),
807818
indented: basecolumn || 0
808819
};
809820
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")

mode/javascript/test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@
101101
"}",
102102
"[variable j];")
103103

104+
MT("leaving_scope",
105+
"[keyword function] [def a]() {",
106+
" {",
107+
" [keyword const] [def x] [operator =] [number 1]",
108+
" [keyword if] ([atom true]) {",
109+
" [keyword let] [def y] [operator =] [number 2]",
110+
" [keyword var] [def z] [operator =] [number 3]",
111+
" [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])",
112+
" }",
113+
" [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])",
114+
" }",
115+
" [variable console].[property log]([variable x], [variable y], [variable-2 z])",
116+
"}")
117+
104118
MT("quotedStringAddition",
105119
"[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
106120

0 commit comments

Comments
 (0)