Bug 1434295 - Update CodeMirror to 5.34.0. r=bgrins draft
authorGabriel Luong <gabriel.luong@gmail.com>
Wed, 31 Jan 2018 18:46:39 -0500
changeset 749878 03f884eb782c2e0e43565f49e850450f8aef9cfe
parent 749877 e8b798a5205ad27d067b91210efb46df7ddab45d
push id97494
push userbmo:gl@mozilla.com
push dateWed, 31 Jan 2018 23:47:33 +0000
reviewersbgrins
bugs1434295
milestone60.0a1
Bug 1434295 - Update CodeMirror to 5.34.0. r=bgrins MozReview-Commit-ID: CATNhGJBKMs
devtools/client/sourceeditor/codemirror/README
devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
devtools/client/sourceeditor/codemirror/codemirror.bundle.js
devtools/client/sourceeditor/codemirror/keymap/sublime.js
devtools/client/sourceeditor/codemirror/lib/codemirror.js
devtools/client/sourceeditor/codemirror/mode/clike/clike.js
devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
--- a/devtools/client/sourceeditor/codemirror/README
+++ b/devtools/client/sourceeditor/codemirror/README
@@ -1,16 +1,16 @@
 This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
 is a JavaScript component that provides a code editor in the browser. When
 a mode is available for the language you are coding in, it will color your
 code, and optionally help with indentation.
 
 # Upgrade
 
-Currently used version is 5.33.0. To upgrade: download a new version of
+Currently used version is 5.34.0. To upgrade: download a new version of
 CodeMirror from the project's page [1] and replace all JavaScript and
 CSS files inside the codemirror directory [2].
 
 Then to recreate codemirror.bundle.js:
  > cd devtools/client/sourceeditor
  > npm install
  > webpack
 
--- a/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
+++ b/devtools/client/sourceeditor/codemirror/addon/edit/closebrackets.js
@@ -124,18 +124,18 @@
       } else if ((identical || !opening) && next == ch) {
         if (identical && stringStartsAfter(cm, cur))
           curType = "both";
         else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
           curType = "skipThree";
         else
           curType = "skip";
       } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
-                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
-                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
         curType = "addFour";
       } else if (identical) {
         var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
         if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
         else return CodeMirror.Pass;
       } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
                              isClosingBracket(next, pairs) ||
                              /\s/.test(next))) {
--- a/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
+++ b/devtools/client/sourceeditor/codemirror/addon/edit/matchbrackets.js
@@ -97,28 +97,33 @@
           for (var i = 0; i < marks.length; i++) marks[i].clear();
         });
       };
       if (autoclear) setTimeout(clear, 800);
       else return clear;
     }
   }
 
-  var currentlyHighlighted = null;
   function doMatchBrackets(cm) {
     cm.operation(function() {
-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
-      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+      if (cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
+      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
     });
   }
 
   CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
     if (old && old != CodeMirror.Init) {
       cm.off("cursorActivity", doMatchBrackets);
-      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
     }
     if (val) {
       cm.state.matchBrackets = typeof val == "object" ? val : {};
       cm.on("cursorActivity", doMatchBrackets);
     }
   });
 
   CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
--- a/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
+++ b/devtools/client/sourceeditor/codemirror/addon/fold/xml-fold.js
@@ -133,17 +133,17 @@
       }
     }
   }
 
   CodeMirror.registerHelper("fold", "xml", function(cm, start) {
     var iter = new Iter(cm, start.line, 0);
     for (;;) {
       var openTag = toNextTag(iter), end;
-      if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
+      if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
       if (!openTag[1] && end != "selfClose") {
         var startPos = Pos(iter.line, iter.ch);
         var endPos = findMatchingClose(iter, openTag[2]);
         return endPos && {from: startPos, to: endPos.from};
       }
     }
   });
   CodeMirror.findMatchingTag = function(cm, pos, range) {
--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
@@ -6810,21 +6810,21 @@ var CodeMirror =
 	    { return }
 	  // Might be a text scaling operation, clear size caches.
 	  d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
 	  d.scrollbarsClipped = false
 	  cm.setSize()
 	}
 
 	var keyNames = {
-	  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+	  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
 	  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
 	  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
 	  46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-	  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
+	  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
 	  173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
 	  221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
 	  63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
 	}
 
 	// Number keys
 	for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
 	// Alphabetic keys
@@ -6961,16 +6961,19 @@ var CodeMirror =
 	  return name
 	}
 
 	// Look up the name of a key as indicated by an event object.
 	function keyName(event, noShift) {
 	  if (presto && event.keyCode == 34 && event["char"]) { return false }
 	  var name = keyNames[event.keyCode]
 	  if (name == null || event.altGraphKey) { return false }
+	  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+	  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+	  if (event.keyCode == 3 && event.code) { name = event.code }
 	  return addModifierNames(name, event, noShift)
 	}
 
 	function getKeyMap(val) {
 	  return typeof val == "string" ? keyMap[val] : val
 	}
 
 	// Helper for deleting text near the selection(s), used to implement
@@ -8250,17 +8253,17 @@ var CodeMirror =
 
 	function applyTextInput(cm, inserted, deleted, sel, origin) {
 	  var doc = cm.doc
 	  cm.display.shift = false
 	  if (!sel) { sel = doc.sel }
 
 	  var paste = cm.state.pasteIncoming || origin == "paste"
 	  var textLines = splitLinesAuto(inserted), multiPaste = null
-	  // When pasing N lines into N selections, insert one line per selection
+	  // When pasting N lines into N selections, insert one line per selection
 	  if (paste && sel.ranges.length > 1) {
 	    if (lastCopied && lastCopied.text.join("\n") == inserted) {
 	      if (sel.ranges.length % lastCopied.text.length == 0) {
 	        multiPaste = []
 	        for (var i = 0; i < lastCopied.text.length; i++)
 	          { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
 	      }
 	    } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
@@ -9884,17 +9887,17 @@ var CodeMirror =
 	CodeMirror.defineDocExtension = function (name, func) {
 	  Doc.prototype[name] = func
 	}
 
 	CodeMirror.fromTextArea = fromTextArea
 
 	addLegacyProps(CodeMirror)
 
-	CodeMirror.version = "5.33.0"
+	CodeMirror.version = "5.34.0"
 
 	return CodeMirror;
 
 	})));
 
 /***/ }),
 /* 3 */
 /***/ (function(module, exports, __webpack_require__) {
@@ -10566,28 +10569,33 @@ var CodeMirror =
 	          for (var i = 0; i < marks.length; i++) marks[i].clear();
 	        });
 	      };
 	      if (autoclear) setTimeout(clear, 800);
 	      else return clear;
 	    }
 	  }
 
-	  var currentlyHighlighted = null;
 	  function doMatchBrackets(cm) {
 	    cm.operation(function() {
-	      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
-	      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+	      if (cm.state.matchBrackets.currentlyHighlighted) {
+	        cm.state.matchBrackets.currentlyHighlighted();
+	        cm.state.matchBrackets.currentlyHighlighted = null;
+	      }
+	      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
 	    });
 	  }
 
 	  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
 	    if (old && old != CodeMirror.Init) {
 	      cm.off("cursorActivity", doMatchBrackets);
-	      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+	      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+	        cm.state.matchBrackets.currentlyHighlighted();
+	        cm.state.matchBrackets.currentlyHighlighted = null;
+	      }
 	    }
 	    if (val) {
 	      cm.state.matchBrackets = typeof val == "object" ? val : {};
 	      cm.on("cursorActivity", doMatchBrackets);
 	    }
 	  });
 
 	  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
@@ -10739,18 +10747,18 @@ var CodeMirror =
 	      } else if ((identical || !opening) && next == ch) {
 	        if (identical && stringStartsAfter(cm, cur))
 	          curType = "both";
 	        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
 	          curType = "skipThree";
 	        else
 	          curType = "skip";
 	      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
-	                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
-	                 (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+	                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+	        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
 	        curType = "addFour";
 	      } else if (identical) {
 	        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
 	        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
 	        else return CodeMirror.Pass;
 	      } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
 	                             isClosingBracket(next, pairs) ||
 	                             /\s/.test(next))) {
@@ -11370,25 +11378,24 @@ var CodeMirror =
 	      if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
 	        cx.state.cc.pop()();
 	      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
 	    }
 	    if (type == "function") return cont(functiondef);
 	    if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
 	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
 	    if (type == "variable") {
-	      if (isTS && value == "type") {
-	        cx.marked = "keyword"
-	        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
-	      } else if (isTS && value == "declare") {
+	      if (isTS && value == "declare") {
 	        cx.marked = "keyword"
 	        return cont(statement)
-	      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
+	      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
 	        cx.marked = "keyword"
-	        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+	        if (value == "enum") return cont(enumdef);
+	        else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+	        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
 	      } else if (isTS && value == "namespace") {
 	        cx.marked = "keyword"
 	        return cont(pushlex("form"), expression, block, poplex)
 	      } else {
 	        return cont(pushlex("stat"), maybelabel);
 	      }
 	    }
 	    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
@@ -11633,17 +11640,18 @@ var CodeMirror =
 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
 	  }
 	  function typeparam() {
 	    return pass(typeexpr, maybeTypeDefault)
 	  }
 	  function maybeTypeDefault(_, value) {
 	    if (value == "=") return cont(typeexpr)
 	  }
-	  function vardef() {
+	  function vardef(_, value) {
+	    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
 	    return pass(pattern, maybetype, maybeAssign, vardefCont);
 	  }
 	  function pattern(type, value) {
 	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
 	    if (type == "variable") { register(value); return cont(); }
 	    if (type == "spread") return cont(pattern);
 	    if (type == "[") return contCommasep(pattern, "]");
 	    if (type == "{") return contCommasep(proppattern, "}");
@@ -11705,18 +11713,20 @@ var CodeMirror =
 	    if (type == "variable") return className(type, value);
 	    return classNameAfter(type, value);
 	  }
 	  function className(type, value) {
 	    if (type == "variable") {register(value); return cont(classNameAfter);}
 	  }
 	  function classNameAfter(type, value) {
 	    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
-	    if (value == "extends" || value == "implements" || (isTS && type == ","))
+	    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+	      if (value == "implements") cx.marked = "keyword";
 	      return cont(isTS ? typeexpr : expression, classNameAfter);
+	    }
 	    if (type == "{") return cont(pushlex("}"), classBody, poplex);
 	  }
 	  function classBody(type, value) {
 	    if (type == "async" ||
 	        (type == "variable" &&
 	         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
 	         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
 	      cx.marked = "keyword";
@@ -11770,16 +11780,22 @@ var CodeMirror =
 	  }
 	  function maybeFrom(_type, value) {
 	    if (value == "from") { cx.marked = "keyword"; return cont(expression); }
 	  }
 	  function arrayLiteral(type) {
 	    if (type == "]") return cont();
 	    return pass(commasep(expressionNoComma, "]"));
 	  }
+	  function enumdef() {
+	    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+	  }
+	  function enummember() {
+	    return pass(pattern, maybeAssign);
+	  }
 
 	  function isContinuedStatement(state, textAfter) {
 	    return state.lastType == "operator" || state.lastType == "," ||
 	      isOperatorChar.test(textAfter.charAt(0)) ||
 	      /[,.]/.test(textAfter.charAt(0));
 	  }
 
 	  function expressionAllowed(stream, state, backUp) {
@@ -14638,17 +14654,17 @@ var CodeMirror =
 	      "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
 	      "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
 	      "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
 	      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
 	    ),
 	    intendSwitch: false,
 	    indentStatements: false,
 	    multiLineStrings: true,
-	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+	    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
 	    blockKeywords: words("catch class do else finally for if where try while enum"),
 	    defKeywords: words("class val var object interface fun"),
 	    atoms: words("true false null this"),
 	    hooks: {
 	      '"': function(stream, state) {
 	        state.tokenize = tokenKotlinString(stream.match('""'));
 	        return state.tokenize(stream, state);
 	      }
@@ -21193,17 +21209,17 @@ var CodeMirror =
 	    }
 	  };
 
 	  cmds.toggleBookmark = function(cm) {
 	    var ranges = cm.listSelections();
 	    var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
 	    for (var i = 0; i < ranges.length; i++) {
 	      var from = ranges[i].from(), to = ranges[i].to();
-	      var found = cm.findMarks(from, to);
+	      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
 	      for (var j = 0; j < found.length; j++) {
 	        if (found[j].sublimeBookmark) {
 	          found[j].clear();
 	          for (var k = 0; k < marks.length; k++)
 	            if (marks[k] == found[j])
 	              marks.splice(k--, 1);
 	          break;
 	        }
@@ -21974,17 +21990,17 @@ var CodeMirror =
 	      }
 	    }
 	  }
 
 	  CodeMirror.registerHelper("fold", "xml", function(cm, start) {
 	    var iter = new Iter(cm, start.line, 0);
 	    for (;;) {
 	      var openTag = toNextTag(iter), end;
-	      if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
+	      if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return;
 	      if (!openTag[1] && end != "selfClose") {
 	        var startPos = Pos(iter.line, iter.ch);
 	        var endPos = findMatchingClose(iter, openTag[2]);
 	        return endPos && {from: startPos, to: endPos.from};
 	      }
 	    }
 	  });
 	  CodeMirror.findMatchingTag = function(cm, pos, range) {
--- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
@@ -377,17 +377,17 @@
     }
   };
 
   cmds.toggleBookmark = function(cm) {
     var ranges = cm.listSelections();
     var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
     for (var i = 0; i < ranges.length; i++) {
       var from = ranges[i].from(), to = ranges[i].to();
-      var found = cm.findMarks(from, to);
+      var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
       for (var j = 0; j < found.length; j++) {
         if (found[j].sublimeBookmark) {
           found[j].clear();
           for (var k = 0; k < marks.length; k++)
             if (marks[k] == found[j])
               marks.splice(k--, 1);
           break;
         }
--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
@@ -6568,21 +6568,21 @@ function onResize(cm) {
     { return }
   // Might be a text scaling operation, clear size caches.
   d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null
   d.scrollbarsClipped = false
   cm.setSize()
 }
 
 var keyNames = {
-  3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+  3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
   19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
   36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
   46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
-  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete",
+  106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock",
   173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
   221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
   63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
 }
 
 // Number keys
 for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i) }
 // Alphabetic keys
@@ -6719,16 +6719,19 @@ function addModifierNames(name, event, n
   return name
 }
 
 // Look up the name of a key as indicated by an event object.
 function keyName(event, noShift) {
   if (presto && event.keyCode == 34 && event["char"]) { return false }
   var name = keyNames[event.keyCode]
   if (name == null || event.altGraphKey) { return false }
+  // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+  // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+  if (event.keyCode == 3 && event.code) { name = event.code }
   return addModifierNames(name, event, noShift)
 }
 
 function getKeyMap(val) {
   return typeof val == "string" ? keyMap[val] : val
 }
 
 // Helper for deleting text near the selection(s), used to implement
@@ -8008,17 +8011,17 @@ function setLastCopied(newLastCopied) {
 
 function applyTextInput(cm, inserted, deleted, sel, origin) {
   var doc = cm.doc
   cm.display.shift = false
   if (!sel) { sel = doc.sel }
 
   var paste = cm.state.pasteIncoming || origin == "paste"
   var textLines = splitLinesAuto(inserted), multiPaste = null
-  // When pasing N lines into N selections, insert one line per selection
+  // When pasting N lines into N selections, insert one line per selection
   if (paste && sel.ranges.length > 1) {
     if (lastCopied && lastCopied.text.join("\n") == inserted) {
       if (sel.ranges.length % lastCopied.text.length == 0) {
         multiPaste = []
         for (var i = 0; i < lastCopied.text.length; i++)
           { multiPaste.push(doc.splitLines(lastCopied.text[i])) }
       }
     } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
@@ -9642,13 +9645,13 @@ CodeMirror.defineExtension = function (n
 CodeMirror.defineDocExtension = function (name, func) {
   Doc.prototype[name] = func
 }
 
 CodeMirror.fromTextArea = fromTextArea
 
 addLegacyProps(CodeMirror)
 
-CodeMirror.version = "5.33.0"
+CodeMirror.version = "5.34.0"
 
 return CodeMirror;
 
 })));
\ No newline at end of file
--- a/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
+++ b/devtools/client/sourceeditor/codemirror/mode/clike/clike.js
@@ -612,17 +612,17 @@ CodeMirror.defineMode("clike", function(
       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
       "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
     ),
     intendSwitch: false,
     indentStatements: false,
     multiLineStrings: true,
-    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
     blockKeywords: words("catch class do else finally for if where try while enum"),
     defKeywords: words("class val var object interface fun"),
     atoms: words("true false null this"),
     hooks: {
       '"': function(stream, state) {
         state.tokenize = tokenKotlinString(stream.match('""'));
         return state.tokenize(stream, state);
       }
--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
@@ -340,25 +340,24 @@ CodeMirror.defineMode("javascript", func
       if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
         cx.state.cc.pop()();
       return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
     }
     if (type == "function") return cont(functiondef);
     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
     if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); }
     if (type == "variable") {
-      if (isTS && value == "type") {
-        cx.marked = "keyword"
-        return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
-      } else if (isTS && value == "declare") {
+      if (isTS && value == "declare") {
         cx.marked = "keyword"
         return cont(statement)
-      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
+      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
         cx.marked = "keyword"
-        return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+        if (value == "enum") return cont(enumdef);
+        else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
+        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
       } else if (isTS && value == "namespace") {
         cx.marked = "keyword"
         return cont(pushlex("form"), expression, block, poplex)
       } else {
         return cont(pushlex("stat"), maybelabel);
       }
     }
     if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
@@ -603,17 +602,18 @@ CodeMirror.defineMode("javascript", func
     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
   }
   function typeparam() {
     return pass(typeexpr, maybeTypeDefault)
   }
   function maybeTypeDefault(_, value) {
     if (value == "=") return cont(typeexpr)
   }
-  function vardef() {
+  function vardef(_, value) {
+    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
     return pass(pattern, maybetype, maybeAssign, vardefCont);
   }
   function pattern(type, value) {
     if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
     if (type == "variable") { register(value); return cont(); }
     if (type == "spread") return cont(pattern);
     if (type == "[") return contCommasep(pattern, "]");
     if (type == "{") return contCommasep(proppattern, "}");
@@ -675,18 +675,20 @@ CodeMirror.defineMode("javascript", func
     if (type == "variable") return className(type, value);
     return classNameAfter(type, value);
   }
   function className(type, value) {
     if (type == "variable") {register(value); return cont(classNameAfter);}
   }
   function classNameAfter(type, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
-    if (value == "extends" || value == "implements" || (isTS && type == ","))
+    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+      if (value == "implements") cx.marked = "keyword";
       return cont(isTS ? typeexpr : expression, classNameAfter);
+    }
     if (type == "{") return cont(pushlex("}"), classBody, poplex);
   }
   function classBody(type, value) {
     if (type == "async" ||
         (type == "variable" &&
          (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
       cx.marked = "keyword";
@@ -740,16 +742,22 @@ CodeMirror.defineMode("javascript", func
   }
   function maybeFrom(_type, value) {
     if (value == "from") { cx.marked = "keyword"; return cont(expression); }
   }
   function arrayLiteral(type) {
     if (type == "]") return cont();
     return pass(commasep(expressionNoComma, "]"));
   }
+  function enumdef() {
+    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+  }
+  function enummember() {
+    return pass(pattern, maybeAssign);
+  }
 
   function isContinuedStatement(state, textAfter) {
     return state.lastType == "operator" || state.lastType == "," ||
       isOperatorChar.test(textAfter.charAt(0)) ||
       /[,.]/.test(textAfter.charAt(0));
   }
 
   function expressionAllowed(stream, state, backUp) {
--- a/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
+++ b/devtools/client/sourceeditor/test/codemirror/mode/javascript/test.js
@@ -378,16 +378,26 @@
   TS("type guard",
      "[keyword class] [def Appler] {",
      "  [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
      "    [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
      "      [keyword throw] [keyword new] [variable Error]();",
      "  }",
      "}")
 
+  TS("type as variable",
+     "[variable type] [operator =] [variable x] [keyword as] [type Bar];");
+
+  TS("enum body",
+     "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {",
+     "  [def ERROR] [operator =] [string 'problem_type_error'],",
+     "  [def WARNING] [operator =] [string 'problem_type_warning'],",
+     "  [def META],",
+     "}")
+
   var jsonld_mode = CodeMirror.getMode(
     {indentUnit: 2},
     {name: "javascript", jsonld: true}
   );
   function LD(name) {
     test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
   }