Bug 1428170 - Update Codemirror to 5.33.0. r=bgrins draft
authorGabriel Luong <gabriel.luong@gmail.com>
Sat, 27 Jan 2018 17:07:09 -0500
changeset 748069 a0bc40715d2bdea6c2fb826962742f772452de69
parent 748068 c6c4331c0c3c3223fd885e5da3532c3039217fe3
push id97063
push userbmo:gl@mozilla.com
push dateSat, 27 Jan 2018 22:08:47 +0000
reviewersbgrins
bugs1428170
milestone60.0a1
Bug 1428170 - Update Codemirror to 5.33.0. r=bgrins MozReview-Commit-ID: CykkmZBv9bx
devtools/client/sourceeditor/codemirror/README
devtools/client/sourceeditor/codemirror/addon/edit/closetag.js
devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
devtools/client/sourceeditor/codemirror/addon/tern/tern.js
devtools/client/sourceeditor/codemirror/codemirror.bundle.js
devtools/client/sourceeditor/codemirror/keymap/sublime.js
devtools/client/sourceeditor/codemirror/keymap/vim.js
devtools/client/sourceeditor/codemirror/lib/codemirror.css
devtools/client/sourceeditor/codemirror/lib/codemirror.js
devtools/client/sourceeditor/codemirror/mode/clike/clike.js
devtools/client/sourceeditor/codemirror/mode/css/css.js
devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
devtools/client/sourceeditor/codemirror/mode/xml/xml.js
devtools/client/sourceeditor/test/codemirror/mode_test.js
devtools/client/sourceeditor/test/codemirror/sublime_test.js
devtools/client/sourceeditor/test/codemirror/vim_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.32.0. To upgrade: download a new version of
+Currently used version is 5.33.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/closetag.js
+++ b/devtools/client/sourceeditor/codemirror/addon/edit/closetag.js
@@ -48,23 +48,24 @@
   var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
                        "source", "track", "wbr"];
   var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
                     "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
 
   function autoCloseGT(cm) {
     if (cm.getOption("disableInput")) return CodeMirror.Pass;
     var ranges = cm.listSelections(), replacements = [];
+    var opt = cm.getOption("autoCloseTags");
     for (var i = 0; i < ranges.length; i++) {
       if (!ranges[i].empty()) return CodeMirror.Pass;
       var pos = ranges[i].head, tok = cm.getTokenAt(pos);
       var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
       if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass;
 
-      var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
+      var html = inner.mode.configuration == "html";
       var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
       var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
 
       var tagName = state.tagName;
       if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
       var lowerTagName = tagName.toLowerCase();
       // Don't process the '>' at the end of an end-tag or self-closing tag
       if (!tagName ||
@@ -76,32 +77,35 @@
         return CodeMirror.Pass;
 
       var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
       replacements[i] = {indent: indent,
                          text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
                          newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
     }
 
+    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
     for (var i = ranges.length - 1; i >= 0; i--) {
       var info = replacements[i];
       cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
       var sel = cm.listSelections().slice(0);
       sel[i] = {head: info.newPos, anchor: info.newPos};
       cm.setSelections(sel);
-      if (info.indent) {
+      if (!dontIndentOnAutoClose && info.indent) {
         cm.indentLine(info.newPos.line, null, true);
         cm.indentLine(info.newPos.line + 1, null, true);
       }
     }
   }
 
   function autoCloseCurrent(cm, typingSlash) {
     var ranges = cm.listSelections(), replacements = [];
     var head = typingSlash ? "/" : "</";
+    var opt = cm.getOption("autoCloseTags");
+    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
     for (var i = 0; i < ranges.length; i++) {
       if (!ranges[i].empty()) return CodeMirror.Pass;
       var pos = ranges[i].head, tok = cm.getTokenAt(pos);
       var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
       if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
                           tok.start != pos.ch - 1))
         return CodeMirror.Pass;
       // Kludge to get around the fact that we are not in XML mode
@@ -122,19 +126,21 @@
           return CodeMirror.Pass;
         replacement = head + state.context.tagName;
       }
       if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
       replacements[i] = replacement;
     }
     cm.replaceSelections(replacements);
     ranges = cm.listSelections();
-    for (var i = 0; i < ranges.length; i++)
-      if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
-        cm.indentLine(ranges[i].head.line);
+    if (!dontIndentOnAutoClose) {
+        for (var i = 0; i < ranges.length; i++)
+            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+                cm.indentLine(ranges[i].head.line);
+    }
   }
 
   function autoCloseSlash(cm) {
     if (cm.getOption("disableInput")) return CodeMirror.Pass;
     return autoCloseCurrent(cm, true);
   }
 
   CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
--- a/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
+++ b/devtools/client/sourceeditor/codemirror/addon/edit/continuelist.js
@@ -34,23 +34,21 @@
         if (!/>\s*$/.test(line)) cm.replaceRange("", {
           line: pos.line, ch: 0
         }, {
           line: pos.line, ch: pos.ch + 1
         });
         replacements[i] = "\n";
       } else {
         var indent = match[1], after = match[5];
-        var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
-          ? match[2].replace("x", " ")
-          : (parseInt(match[3], 10) + 1) + match[4];
-
+        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
+        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
         replacements[i] = "\n" + indent + bullet + after;
 
-        incrementRemainingMarkdownListNumbers(cm, pos);
+        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
       }
     }
 
     cm.replaceSelections(replacements);
   };
 
   // Auto-updating Markdown list numbers when a new item is added to the
   // middle of a list
--- a/devtools/client/sourceeditor/codemirror/addon/tern/tern.js
+++ b/devtools/client/sourceeditor/codemirror/addon/tern/tern.js
@@ -609,17 +609,18 @@
     function clear() {
       cm.state.ternTooltip = null;
       if (tip.parentNode) fadeOut(tip)
       clearActivity()
     }
     var mouseOnTip = false, old = false;
     CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
     CodeMirror.on(tip, "mouseout", function(e) {
-      if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) {
+      var related = e.relatedTarget || e.toElement
+      if (!related || !CodeMirror.contains(tip, related)) {
         if (old) clear();
         else mouseOnTip = false;
       }
     });
     setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
     var clearActivity = onEditorActivity(cm, clear)
   }
 
--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
@@ -7843,16 +7843,17 @@ var CodeMirror =
 	  option("indentUnit", 2, loadMode, true)
 	  option("indentWithTabs", false)
 	  option("smartIndent", true)
 	  option("tabSize", 4, function (cm) {
 	    resetModeState(cm)
 	    clearCaches(cm)
 	    regChange(cm)
 	  }, true)
+
 	  option("lineSeparator", null, function (cm, val) {
 	    cm.doc.lineSep = val
 	    if (!val) { return }
 	    var newBreaks = [], lineNo = cm.doc.first
 	    cm.doc.iter(function (line) {
 	      for (var pos = 0;;) {
 	        var found = line.text.indexOf(val, pos)
 	        if (found == -1) { break }
@@ -9883,17 +9884,17 @@ var CodeMirror =
 	CodeMirror.defineDocExtension = function (name, func) {
 	  Doc.prototype[name] = func
 	}
 
 	CodeMirror.fromTextArea = fromTextArea
 
 	addLegacyProps(CodeMirror)
 
-	CodeMirror.version = "5.32.0"
+	CodeMirror.version = "5.33.0"
 
 	return CodeMirror;
 
 	})));
 
 /***/ }),
 /* 3 */
 /***/ (function(module, exports, __webpack_require__) {
@@ -11050,55 +11051,28 @@ var CodeMirror =
 
 	  // Tokenizer
 
 	  var keywords = function(){
 	    function kw(type) {return {type: type, style: "keyword"};}
 	    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
 	    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
 
-	    var jsKeywords = {
+	    return {
 	      "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
 	      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
 	      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
 	      "function": kw("function"), "catch": kw("catch"),
 	      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
 	      "in": operator, "typeof": operator, "instanceof": operator,
 	      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
 	      "this": kw("this"), "class": kw("class"), "super": kw("atom"),
 	      "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
 	      "await": C
 	    };
-
-	    // Extend the 'normal' keywords with the TypeScript language extensions
-	    if (isTS) {
-	      var type = {type: "variable", style: "type"};
-	      var tsKeywords = {
-	        // object-like things
-	        "interface": kw("class"),
-	        "implements": C,
-	        "namespace": C,
-
-	        // scope modifiers
-	        "public": kw("modifier"),
-	        "private": kw("modifier"),
-	        "protected": kw("modifier"),
-	        "abstract": kw("modifier"),
-	        "readonly": kw("modifier"),
-
-	        // types
-	        "string": type, "number": type, "boolean": type, "any": type
-	      };
-
-	      for (var attr in tsKeywords) {
-	        jsKeywords[attr] = tsKeywords[attr];
-	      }
-	    }
-
-	    return jsKeywords;
 	  }();
 
 	  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
 	  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
 
 	  function readRegexp(stream) {
 	    var escaped = false, next, inSet = false;
 	    while ((next = stream.next()) != null) {
@@ -11334,16 +11308,20 @@ var CodeMirror =
 	      state.localVars = {name: varname, next: state.localVars};
 	    } else {
 	      if (inList(state.globalVars)) return;
 	      if (parserConfig.globalVars)
 	        state.globalVars = {name: varname, next: state.globalVars};
 	    }
 	  }
 
+	  function isModifier(name) {
+	    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
+	  }
+
 	  // Combinators
 
 	  var defaultVars = {name: "this", next: {name: "arguments"}};
 	  function pushcontext() {
 	    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
 	    cx.state.localVars = defaultVars;
 	  }
 	  function popcontext() {
@@ -11390,64 +11368,67 @@ var CodeMirror =
 	    if (type == ";") return cont();
 	    if (type == "if") {
 	      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") {
 	        cx.marked = "keyword"
 	        return cont(statement)
 	      } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
 	        cx.marked = "keyword"
 	        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"),
 	                                      block, poplex, poplex);
 	    if (type == "case") return cont(expression, expect(":"));
 	    if (type == "default") return cont(expect(":"));
 	    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
 	                                     statement, poplex, popcontext);
-	    if (type == "class") return cont(pushlex("form"), className, poplex);
 	    if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
 	    if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
 	    if (type == "async") return cont(statement)
 	    if (value == "@") return cont(expression, statement)
 	    return pass(pushlex("stat"), expression, expect(";"), poplex);
 	  }
-	  function expression(type) {
-	    return expressionInner(type, false);
-	  }
-	  function expressionNoComma(type) {
-	    return expressionInner(type, true);
+	  function expression(type, value) {
+	    return expressionInner(type, value, false);
+	  }
+	  function expressionNoComma(type, value) {
+	    return expressionInner(type, value, true);
 	  }
 	  function parenExpr(type) {
 	    if (type != "(") return pass()
 	    return cont(pushlex(")"), expression, expect(")"), poplex)
 	  }
-	  function expressionInner(type, noComma) {
+	  function expressionInner(type, value, noComma) {
 	    if (cx.state.fatArrowAt == cx.stream.start) {
 	      var body = noComma ? arrowBodyNoComma : arrowBody;
 	      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
 	      else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
 	    }
 
 	    var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
 	    if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
 	    if (type == "function") return cont(functiondef, maybeop);
-	    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
+	    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
 	    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
 	    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
 	    if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
 	    if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
 	    if (type == "{") return contCommasep(objprop, "}", null, maybeop);
 	    if (type == "quasi") return pass(quasi, maybeop);
 	    if (type == "new") return cont(maybeTarget(noComma));
 	    return cont();
@@ -11535,20 +11516,21 @@ var CodeMirror =
 	      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
 	        cx.state.fatArrowAt = cx.stream.pos + m[0].length
 	      return cont(afterprop);
 	    } else if (type == "number" || type == "string") {
 	      cx.marked = jsonldMode ? "property" : (cx.style + " property");
 	      return cont(afterprop);
 	    } else if (type == "jsonld-keyword") {
 	      return cont(afterprop);
-	    } else if (type == "modifier") {
+	    } else if (isTS && isModifier(value)) {
+	      cx.marked = "keyword"
 	      return cont(objprop)
 	    } else if (type == "[") {
-	      return cont(expression, expect("]"), afterprop);
+	      return cont(expression, maybetype, expect("]"), afterprop);
 	    } else if (type == "spread") {
 	      return cont(expressionNoComma, afterprop);
 	    } else if (value == "*") {
 	      cx.marked = "keyword";
 	      return cont(objprop);
 	    } else if (type == ":") {
 	      return pass(afterprop)
 	    }
@@ -11640,32 +11622,32 @@ var CodeMirror =
 	  function typearg(type) {
 	    if (type == "variable") return cont(typearg)
 	    else if (type == ":") return cont(typeexpr)
 	  }
 	  function afterType(type, value) {
 	    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
 	    if (value == "|" || type == ".") return cont(typeexpr)
 	    if (type == "[") return cont(expect("]"), afterType)
-	    if (value == "extends") return cont(typeexpr)
+	    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
 	  }
 	  function maybeTypeArgs(_, value) {
 	    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() {
 	    return pass(pattern, maybetype, maybeAssign, vardefCont);
 	  }
 	  function pattern(type, value) {
-	    if (type == "modifier") return cont(pattern)
+	    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, "}");
 	  }
 	  function proppattern(type, value) {
 	    if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
 	      register(value);
@@ -11709,17 +11691,18 @@ var CodeMirror =
 	  function functiondef(type, value) {
 	    if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
 	    if (type == "variable") {register(value); return cont(functiondef);}
 	    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
 	    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
 	  }
 	  function funarg(type, value) {
 	    if (value == "@") cont(expression, funarg)
-	    if (type == "spread" || type == "modifier") return cont(funarg);
+	    if (type == "spread") return cont(funarg);
+	    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
 	    return pass(pattern, maybetype, maybeAssign);
 	  }
 	  function classExpression(type, value) {
 	    // Class expressions may have an optional name.
 	    if (type == "variable") return className(type, value);
 	    return classNameAfter(type, value);
 	  }
 	  function className(type, value) {
@@ -11727,29 +11710,29 @@ var CodeMirror =
 	  }
 	  function classNameAfter(type, value) {
 	    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
 	    if (value == "extends" || value == "implements" || (isTS && type == ","))
 	      return cont(isTS ? typeexpr : expression, classNameAfter);
 	    if (type == "{") return cont(pushlex("}"), classBody, poplex);
 	  }
 	  function classBody(type, value) {
-	    if (type == "modifier" || type == "async" ||
+	    if (type == "async" ||
 	        (type == "variable" &&
-	         (value == "static" || value == "get" || value == "set") &&
+	         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
 	         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
 	      cx.marked = "keyword";
 	      return cont(classBody);
 	    }
 	    if (type == "variable" || cx.style == "keyword") {
 	      cx.marked = "property";
 	      return cont(isTS ? classfield : functiondef, classBody);
 	    }
 	    if (type == "[")
-	      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
+	      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
 	    if (value == "*") {
 	      cx.marked = "keyword";
 	      return cont(classBody);
 	    }
 	    if (type == ";") return cont(classBody);
 	    if (type == "}") return cont();
 	    if (value == "@") return cont(expression, classBody)
 	  }
@@ -11957,16 +11940,17 @@ var CodeMirror =
 
 	var xmlConfig = {
 	  autoSelfClosers: {},
 	  implicitlyClosed: {},
 	  contextGrabbers: {},
 	  doNotIndent: {},
 	  allowUnquoted: false,
 	  allowMissing: false,
+	  allowMissingTagName: false,
 	  caseFold: false
 	}
 
 	CodeMirror.defineMode("xml", function(editorConf, config_) {
 	  var indentUnit = editorConf.indentUnit
 	  var config = {}
 	  var defaults = config_.htmlMode ? htmlConfig : xmlConfig
 	  for (var prop in defaults) config[prop] = defaults[prop]
@@ -12131,16 +12115,19 @@ var CodeMirror =
 	      return baseState;
 	    }
 	  }
 	  function tagNameState(type, stream, state) {
 	    if (type == "word") {
 	      state.tagName = stream.current();
 	      setStyle = "tag";
 	      return attrState;
+	    } else if (config.allowMissingTagName && type == "endTag") {
+	      setStyle = "tag bracket";
+	      return attrState(type, stream, state);
 	    } else {
 	      setStyle = "error";
 	      return tagNameState;
 	    }
 	  }
 	  function closeTagNameState(type, stream, state) {
 	    if (type == "word") {
 	      var tagName = stream.current();
@@ -12149,16 +12136,19 @@ var CodeMirror =
 	        popContext(state);
 	      if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
 	        setStyle = "tag";
 	        return closeState;
 	      } else {
 	        setStyle = "tag error";
 	        return closeStateErr;
 	      }
+	    } else if (config.allowMissingTagName && type == "endTag") {
+	      setStyle = "tag bracket";
+	      return closeState(type, stream, state);
 	    } else {
 	      setStyle = "error";
 	      return closeStateErr;
 	    }
 	  }
 
 	  function closeState(type, _stream, state) {
 	    if (type != "endTag") {
@@ -12382,19 +12372,19 @@ var CodeMirror =
 	        return ret("meta", "meta");
 	      }
 	    } else if (/[,+>*\/]/.test(ch)) {
 	      return ret(null, "select-op");
 	    } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
 	      return ret("qualifier", "qualifier");
 	    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
 	      return ret(null, ch);
-	    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
-	               (ch == "d" && stream.match("omain(")) ||
-	               (ch == "r" && stream.match("egexp("))) {
+	    } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
+	               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
+	               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
 	      stream.backUp(1);
 	      state.tokenize = tokenParenthesized;
 	      return ret("property", "word");
 	    } else if (/[\w\\\-]/.test(ch)) {
 	      stream.eatWhile(/[\w\\\-]/);
 	      return ret("property", "word");
 	    } else {
 	      return ret(null, null);
@@ -12467,26 +12457,26 @@ var CodeMirror =
 
 	  var states = {};
 
 	  states.top = function(type, stream, state) {
 	    if (type == "{") {
 	      return pushContext(state, stream, "block");
 	    } else if (type == "}" && state.context.prev) {
 	      return popContext(state);
-	    } else if (supportsAtComponent && /@component/.test(type)) {
+	    } else if (supportsAtComponent && /@component/i.test(type)) {
 	      return pushContext(state, stream, "atComponentBlock");
-	    } else if (/^@(-moz-)?document$/.test(type)) {
+	    } else if (/^@(-moz-)?document$/i.test(type)) {
 	      return pushContext(state, stream, "documentTypes");
-	    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
+	    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
 	      return pushContext(state, stream, "atBlock");
-	    } else if (/^@(font-face|counter-style)/.test(type)) {
+	    } else if (/^@(font-face|counter-style)/i.test(type)) {
 	      state.stateArg = type;
 	      return "restricted_atBlock_before";
-	    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+	    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
 	      return "keyframes";
 	    } else if (type && type.charAt(0) == "@") {
 	      return pushContext(state, stream, "at");
 	    } else if (type == "hash") {
 	      override = "builtin";
 	    } else if (type == "word") {
 	      override = "tag";
 	    } else if (type == "variable-definition") {
@@ -13098,17 +13088,17 @@ var CodeMirror =
 	          state.tokenize = tokenCComment;
 	          return tokenCComment(stream, state);
 	        } else {
 	          return ["operator", "operator"];
 	        }
 	      },
 	      "@": function(stream) {
 	        if (stream.eat("{")) return [null, "interpolation"];
-	        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+	        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
 	        stream.eatWhile(/[\w\\\-]/);
 	        if (stream.match(/^\s*:/, false))
 	          return ["variable-2", "variable-definition"];
 	        return ["variable-2", "variable"];
 	      },
 	      "&": function() {
 	        return ["atom", "atom"];
 	      }
@@ -13327,17 +13317,17 @@ var CodeMirror =
 	  function copyContext(context) {
 	    return new Context(CodeMirror.copyState(context.mode, context.state),
 	                       context.mode,
 	                       context.depth,
 	                       context.prev && copyContext(context.prev))
 	  }
 
 	  CodeMirror.defineMode("jsx", function(config, modeConfig) {
-	    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+	    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
 	    var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
 
 	    function flatXMLIndent(state) {
 	      var tagName = state.tagName
 	      state.tagName = null
 	      var result = xmlMode.indent(state, "")
 	      state.tagName = tagName
 	      return result
@@ -14520,16 +14510,37 @@ var CodeMirror =
 	        state.tokenize = null;
 	        break;
 	      }
 	      escaped = stream.next() == "\\" && !escaped;
 	    }
 	    return "string";
 	  }
 
+	  function tokenNestedComment(depth) {
+	    return function (stream, state) {
+	      var ch
+	      while (ch = stream.next()) {
+	        if (ch == "*" && stream.eat("/")) {
+	          if (depth == 1) {
+	            state.tokenize = null
+	            break
+	          } else {
+	            state.tokenize = tokenNestedComment(depth - 1)
+	            return state.tokenize(stream, state)
+	          }
+	        } else if (ch == "/" && stream.eat("*")) {
+	          state.tokenize = tokenNestedComment(depth + 1)
+	          return state.tokenize(stream, state)
+	        }
+	      }
+	      return "comment"
+	    }
+	  }
+
 	  def("text/x-scala", {
 	    name: "clike",
 	    keywords: words(
 
 	      /* scala */
 	      "abstract case catch class def do else extends final finally for forSome if " +
 	      "implicit import lazy match new null object override package private protected return " +
 	      "sealed super this throw trait try type val var while with yield _ " +
@@ -14575,16 +14586,22 @@ var CodeMirror =
 	      "=": function(stream, state) {
 	        var cx = state.context
 	        if (cx.type == "}" && cx.align && stream.eat(">")) {
 	          state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
 	          return "operator"
 	        } else {
 	          return false
 	        }
+	      },
+
+	      "/": function(stream, state) {
+	        if (!stream.eat("*")) return false
+	        state.tokenize = tokenNestedComment(1)
+	        return state.tokenize(stream, state)
 	      }
 	    },
 	    modeProps: {closeBrackets: {triples: '"'}}
 	  });
 
 	  function tokenKotlinString(tripleString){
 	    return function (stream, state) {
 	      var escaped = false, next, end = false;
@@ -14609,17 +14626,17 @@ var CodeMirror =
 	      "package as typealias class interface this super val " +
 	      "var fun for is in This throw return " +
 	      "break continue object if else while do try when !in !is as? " +
 
 	      /*soft keywords*/
 	      "file import where by get set abstract enum open inner override private public internal " +
 	      "protected catch finally out final vararg reified dynamic companion constructor init " +
 	      "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
-	      "external annotation crossinline const operator infix suspend"
+	      "external annotation crossinline const operator infix suspend actual expect"
 	    ),
 	    types: words(
 	      /* package java.lang */
 	      "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"
 	    ),
@@ -18206,35 +18223,42 @@ var CodeMirror =
 	          } else {
 	            cm.setCursor(offsetCursor(curEnd, 0, -1));
 	          }
 	        }
 	      },
 	      incrementNumberToken: function(cm, actionArgs) {
 	        var cur = cm.getCursor();
 	        var lineStr = cm.getLine(cur.line);
-	        var re = /-?\d+/g;
+	        var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
 	        var match;
 	        var start;
 	        var end;
 	        var numberStr;
-	        var token;
 	        while ((match = re.exec(lineStr)) !== null) {
-	          token = match[0];
 	          start = match.index;
-	          end = start + token.length;
+	          end = start + match[0].length;
 	          if (cur.ch < end)break;
 	        }
 	        if (!actionArgs.backtrack && (end <= cur.ch))return;
-	        if (token) {
+	        if (match) {
+	          var baseStr = match[2] || match[4]
+	          var digits = match[3] || match[5]
 	          var increment = actionArgs.increase ? 1 : -1;
-	          var number = parseInt(token) + (increment * actionArgs.repeat);
+	          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];
+	          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
+	          numberStr = number.toString(base);
+	          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''
+	          if (numberStr.charAt(0) === '-') {
+	            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
+	          } else {
+	            numberStr = baseStr + zeroPadding + numberStr;
+	          }
 	          var from = Pos(cur.line, start);
 	          var to = Pos(cur.line, end);
-	          numberStr = number.toString();
 	          cm.replaceRange(numberStr, from, to);
 	        } else {
 	          return;
 	        }
 	        cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
 	      },
 	      repeatLastEdit: function(cm, actionArgs, vim) {
 	        var lastEditInputState = vim.lastEditInputState;
@@ -20970,19 +20994,25 @@ var CodeMirror =
 	    var ranges = cm.listSelections(), newRanges = []
 	    for (var i = 0; i < ranges.length; i++) {
 	      var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
 	      if (!opening) return false;
 	      for (;;) {
 	        var closing = cm.scanForBracket(pos, 1);
 	        if (!closing) return false;
 	        if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
-	          newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1),
-	                          head: closing.pos});
-	          break;
+	          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
+	          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
+	              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
+	            opening = cm.scanForBracket(opening.pos, -1);
+	            if (!opening) return false;
+	          } else {
+	            newRanges.push({anchor: startPos, head: closing.pos});
+	            break;
+	          }
 	        }
 	        pos = Pos(closing.pos.line, closing.pos.ch + 1);
 	      }
 	    }
 	    cm.setSelections(newRanges);
 	    return true;
 	  }
 
@@ -21295,37 +21325,16 @@ var CodeMirror =
 	      cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
 	  };
 
 	  cmds.showInCenter = function(cm) {
 	    var pos = cm.cursorCoords(null, "local");
 	    cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
 	  };
 
-	  cmds.selectLinesUpward = function(cm) {
-	    cm.operation(function() {
-	      var ranges = cm.listSelections();
-	      for (var i = 0; i < ranges.length; i++) {
-	        var range = ranges[i];
-	        if (range.head.line > cm.firstLine())
-	          cm.addSelection(Pos(range.head.line - 1, range.head.ch));
-	      }
-	    });
-	  };
-	  cmds.selectLinesDownward = function(cm) {
-	    cm.operation(function() {
-	      var ranges = cm.listSelections();
-	      for (var i = 0; i < ranges.length; i++) {
-	        var range = ranges[i];
-	        if (range.head.line < cm.lastLine())
-	          cm.addSelection(Pos(range.head.line + 1, range.head.ch));
-	      }
-	    });
-	  };
-
 	  function getTarget(cm) {
 	    var from = cm.getCursor("from"), to = cm.getCursor("to");
 	    if (CodeMirror.cmpPos(from, to) == 0) {
 	      var word = wordAt(cm, from);
 	      if (!word.word) return;
 	      from = word.from;
 	      to = word.to;
 	    }
@@ -21377,18 +21386,16 @@ var CodeMirror =
 	    "Ctrl-Alt-Up": "scrollLineUp",
 	    "Ctrl-Alt-Down": "scrollLineDown",
 	    "Cmd-L": "selectLine",
 	    "Shift-Cmd-L": "splitSelectionByLine",
 	    "Esc": "singleSelectionTop",
 	    "Cmd-Enter": "insertLineAfter",
 	    "Shift-Cmd-Enter": "insertLineBefore",
 	    "Cmd-D": "selectNextOccurrence",
-	    "Shift-Cmd-Up": "addCursorToPrevLine",
-	    "Shift-Cmd-Down": "addCursorToNextLine",
 	    "Shift-Cmd-Space": "selectScope",
 	    "Shift-Cmd-M": "selectBetweenBrackets",
 	    "Cmd-M": "goToBracket",
 	    "Cmd-Ctrl-Up": "swapLineUp",
 	    "Cmd-Ctrl-Down": "swapLineDown",
 	    "Cmd-/": "toggleCommentIndented",
 	    "Cmd-J": "joinLines",
 	    "Shift-Cmd-D": "duplicateLine",
@@ -21408,18 +21415,18 @@ var CodeMirror =
 	    "Cmd-K Cmd-W": "deleteToSublimeMark",
 	    "Cmd-K Cmd-X": "swapWithSublimeMark",
 	    "Cmd-K Cmd-Y": "sublimeYank",
 	    "Cmd-K Cmd-C": "showInCenter",
 	    "Cmd-K Cmd-G": "clearBookmarks",
 	    "Cmd-K Cmd-Backspace": "delLineLeft",
 	    "Cmd-K Cmd-0": "unfoldAll",
 	    "Cmd-K Cmd-J": "unfoldAll",
-	    "Ctrl-Shift-Up": "selectLinesUpward",
-	    "Ctrl-Shift-Down": "selectLinesDownward",
+	    "Ctrl-Shift-Up": "addCursorToPrevLine",
+	    "Ctrl-Shift-Down": "addCursorToNextLine",
 	    "Cmd-F3": "findUnder",
 	    "Shift-Cmd-F3": "findUnderPrevious",
 	    "Alt-F3": "findAllUnder",
 	    "Shift-Cmd-[": "fold",
 	    "Shift-Cmd-]": "unfold",
 	    "Cmd-I": "findIncremental",
 	    "Shift-Cmd-I": "findIncrementalReverse",
 	    "Cmd-H": "replace",
@@ -21439,18 +21446,16 @@ var CodeMirror =
 	    "Ctrl-Up": "scrollLineUp",
 	    "Ctrl-Down": "scrollLineDown",
 	    "Ctrl-L": "selectLine",
 	    "Shift-Ctrl-L": "splitSelectionByLine",
 	    "Esc": "singleSelectionTop",
 	    "Ctrl-Enter": "insertLineAfter",
 	    "Shift-Ctrl-Enter": "insertLineBefore",
 	    "Ctrl-D": "selectNextOccurrence",
-	    "Alt-CtrlUp": "addCursorToPrevLine",
-	    "Alt-CtrlDown": "addCursorToNextLine",
 	    "Shift-Ctrl-Space": "selectScope",
 	    "Shift-Ctrl-M": "selectBetweenBrackets",
 	    "Ctrl-M": "goToBracket",
 	    "Shift-Ctrl-Up": "swapLineUp",
 	    "Shift-Ctrl-Down": "swapLineDown",
 	    "Ctrl-/": "toggleCommentIndented",
 	    "Ctrl-J": "joinLines",
 	    "Shift-Ctrl-D": "duplicateLine",
@@ -21470,18 +21475,18 @@ var CodeMirror =
 	    "Ctrl-K Ctrl-W": "deleteToSublimeMark",
 	    "Ctrl-K Ctrl-X": "swapWithSublimeMark",
 	    "Ctrl-K Ctrl-Y": "sublimeYank",
 	    "Ctrl-K Ctrl-C": "showInCenter",
 	    "Ctrl-K Ctrl-G": "clearBookmarks",
 	    "Ctrl-K Ctrl-Backspace": "delLineLeft",
 	    "Ctrl-K Ctrl-0": "unfoldAll",
 	    "Ctrl-K Ctrl-J": "unfoldAll",
-	    "Ctrl-Alt-Up": "selectLinesUpward",
-	    "Ctrl-Alt-Down": "selectLinesDownward",
+	    "Ctrl-Alt-Up": "addCursorToPrevLine",
+	    "Ctrl-Alt-Down": "addCursorToNextLine",
 	    "Ctrl-F3": "findUnder",
 	    "Shift-Ctrl-F3": "findUnderPrevious",
 	    "Alt-F3": "findAllUnder",
 	    "Shift-Ctrl-[": "fold",
 	    "Shift-Ctrl-]": "unfold",
 	    "Ctrl-I": "findIncremental",
 	    "Shift-Ctrl-I": "findIncrementalReverse",
 	    "Ctrl-H": "replace",
--- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js
+++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js
@@ -178,19 +178,25 @@
     var ranges = cm.listSelections(), newRanges = []
     for (var i = 0; i < ranges.length; i++) {
       var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
       if (!opening) return false;
       for (;;) {
         var closing = cm.scanForBracket(pos, 1);
         if (!closing) return false;
         if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
-          newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1),
-                          head: closing.pos});
-          break;
+          var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
+          if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
+              CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
+            opening = cm.scanForBracket(opening.pos, -1);
+            if (!opening) return false;
+          } else {
+            newRanges.push({anchor: startPos, head: closing.pos});
+            break;
+          }
         }
         pos = Pos(closing.pos.line, closing.pos.ch + 1);
       }
     }
     cm.setSelections(newRanges);
     return true;
   }
 
@@ -503,37 +509,16 @@
       cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
   };
 
   cmds.showInCenter = function(cm) {
     var pos = cm.cursorCoords(null, "local");
     cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
   };
 
-  cmds.selectLinesUpward = function(cm) {
-    cm.operation(function() {
-      var ranges = cm.listSelections();
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        if (range.head.line > cm.firstLine())
-          cm.addSelection(Pos(range.head.line - 1, range.head.ch));
-      }
-    });
-  };
-  cmds.selectLinesDownward = function(cm) {
-    cm.operation(function() {
-      var ranges = cm.listSelections();
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        if (range.head.line < cm.lastLine())
-          cm.addSelection(Pos(range.head.line + 1, range.head.ch));
-      }
-    });
-  };
-
   function getTarget(cm) {
     var from = cm.getCursor("from"), to = cm.getCursor("to");
     if (CodeMirror.cmpPos(from, to) == 0) {
       var word = wordAt(cm, from);
       if (!word.word) return;
       from = word.from;
       to = word.to;
     }
@@ -585,18 +570,16 @@
     "Ctrl-Alt-Up": "scrollLineUp",
     "Ctrl-Alt-Down": "scrollLineDown",
     "Cmd-L": "selectLine",
     "Shift-Cmd-L": "splitSelectionByLine",
     "Esc": "singleSelectionTop",
     "Cmd-Enter": "insertLineAfter",
     "Shift-Cmd-Enter": "insertLineBefore",
     "Cmd-D": "selectNextOccurrence",
-    "Shift-Cmd-Up": "addCursorToPrevLine",
-    "Shift-Cmd-Down": "addCursorToNextLine",
     "Shift-Cmd-Space": "selectScope",
     "Shift-Cmd-M": "selectBetweenBrackets",
     "Cmd-M": "goToBracket",
     "Cmd-Ctrl-Up": "swapLineUp",
     "Cmd-Ctrl-Down": "swapLineDown",
     "Cmd-/": "toggleCommentIndented",
     "Cmd-J": "joinLines",
     "Shift-Cmd-D": "duplicateLine",
@@ -616,18 +599,18 @@
     "Cmd-K Cmd-W": "deleteToSublimeMark",
     "Cmd-K Cmd-X": "swapWithSublimeMark",
     "Cmd-K Cmd-Y": "sublimeYank",
     "Cmd-K Cmd-C": "showInCenter",
     "Cmd-K Cmd-G": "clearBookmarks",
     "Cmd-K Cmd-Backspace": "delLineLeft",
     "Cmd-K Cmd-0": "unfoldAll",
     "Cmd-K Cmd-J": "unfoldAll",
-    "Ctrl-Shift-Up": "selectLinesUpward",
-    "Ctrl-Shift-Down": "selectLinesDownward",
+    "Ctrl-Shift-Up": "addCursorToPrevLine",
+    "Ctrl-Shift-Down": "addCursorToNextLine",
     "Cmd-F3": "findUnder",
     "Shift-Cmd-F3": "findUnderPrevious",
     "Alt-F3": "findAllUnder",
     "Shift-Cmd-[": "fold",
     "Shift-Cmd-]": "unfold",
     "Cmd-I": "findIncremental",
     "Shift-Cmd-I": "findIncrementalReverse",
     "Cmd-H": "replace",
@@ -647,18 +630,16 @@
     "Ctrl-Up": "scrollLineUp",
     "Ctrl-Down": "scrollLineDown",
     "Ctrl-L": "selectLine",
     "Shift-Ctrl-L": "splitSelectionByLine",
     "Esc": "singleSelectionTop",
     "Ctrl-Enter": "insertLineAfter",
     "Shift-Ctrl-Enter": "insertLineBefore",
     "Ctrl-D": "selectNextOccurrence",
-    "Alt-CtrlUp": "addCursorToPrevLine",
-    "Alt-CtrlDown": "addCursorToNextLine",
     "Shift-Ctrl-Space": "selectScope",
     "Shift-Ctrl-M": "selectBetweenBrackets",
     "Ctrl-M": "goToBracket",
     "Shift-Ctrl-Up": "swapLineUp",
     "Shift-Ctrl-Down": "swapLineDown",
     "Ctrl-/": "toggleCommentIndented",
     "Ctrl-J": "joinLines",
     "Shift-Ctrl-D": "duplicateLine",
@@ -678,18 +659,18 @@
     "Ctrl-K Ctrl-W": "deleteToSublimeMark",
     "Ctrl-K Ctrl-X": "swapWithSublimeMark",
     "Ctrl-K Ctrl-Y": "sublimeYank",
     "Ctrl-K Ctrl-C": "showInCenter",
     "Ctrl-K Ctrl-G": "clearBookmarks",
     "Ctrl-K Ctrl-Backspace": "delLineLeft",
     "Ctrl-K Ctrl-0": "unfoldAll",
     "Ctrl-K Ctrl-J": "unfoldAll",
-    "Ctrl-Alt-Up": "selectLinesUpward",
-    "Ctrl-Alt-Down": "selectLinesDownward",
+    "Ctrl-Alt-Up": "addCursorToPrevLine",
+    "Ctrl-Alt-Down": "addCursorToNextLine",
     "Ctrl-F3": "findUnder",
     "Shift-Ctrl-F3": "findUnderPrevious",
     "Alt-F3": "findAllUnder",
     "Shift-Ctrl-[": "fold",
     "Shift-Ctrl-]": "unfold",
     "Ctrl-I": "findIncremental",
     "Shift-Ctrl-I": "findIncrementalReverse",
     "Ctrl-H": "replace",
--- a/devtools/client/sourceeditor/codemirror/keymap/vim.js
+++ b/devtools/client/sourceeditor/codemirror/keymap/vim.js
@@ -2632,35 +2632,42 @@
           } else {
             cm.setCursor(offsetCursor(curEnd, 0, -1));
           }
         }
       },
       incrementNumberToken: function(cm, actionArgs) {
         var cur = cm.getCursor();
         var lineStr = cm.getLine(cur.line);
-        var re = /-?\d+/g;
+        var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
         var match;
         var start;
         var end;
         var numberStr;
-        var token;
         while ((match = re.exec(lineStr)) !== null) {
-          token = match[0];
           start = match.index;
-          end = start + token.length;
+          end = start + match[0].length;
           if (cur.ch < end)break;
         }
         if (!actionArgs.backtrack && (end <= cur.ch))return;
-        if (token) {
+        if (match) {
+          var baseStr = match[2] || match[4]
+          var digits = match[3] || match[5]
           var increment = actionArgs.increase ? 1 : -1;
-          var number = parseInt(token) + (increment * actionArgs.repeat);
+          var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];
+          var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
+          numberStr = number.toString(base);
+          var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''
+          if (numberStr.charAt(0) === '-') {
+            numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
+          } else {
+            numberStr = baseStr + zeroPadding + numberStr;
+          }
           var from = Pos(cur.line, start);
           var to = Pos(cur.line, end);
-          numberStr = number.toString();
           cm.replaceRange(numberStr, from, to);
         } else {
           return;
         }
         cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
       },
       repeatLastEdit: function(cm, actionArgs, vim) {
         var lastEditInputState = vim.lastEditInputState;
--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.css
+++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.css
@@ -265,17 +265,17 @@ div.CodeMirror span.CodeMirror-nonmatchi
   position: absolute;
   left: 0; right: 0; top: 0; bottom: 0;
   z-index: 0;
 }
 
 .CodeMirror-linewidget {
   position: relative;
   z-index: 2;
-  overflow: auto;
+  padding: 0.1px; /* Force widget margins to stay inside of the container */
 }
 
 .CodeMirror-widget {}
 
 .CodeMirror-rtl pre { direction: rtl; }
 
 .CodeMirror-code {
   outline: none;
--- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js
+++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js
@@ -7601,16 +7601,17 @@ function defineOptions(CodeMirror) {
   option("indentUnit", 2, loadMode, true)
   option("indentWithTabs", false)
   option("smartIndent", true)
   option("tabSize", 4, function (cm) {
     resetModeState(cm)
     clearCaches(cm)
     regChange(cm)
   }, true)
+
   option("lineSeparator", null, function (cm, val) {
     cm.doc.lineSep = val
     if (!val) { return }
     var newBreaks = [], lineNo = cm.doc.first
     cm.doc.iter(function (line) {
       for (var pos = 0;;) {
         var found = line.text.indexOf(val, pos)
         if (found == -1) { break }
@@ -9641,13 +9642,13 @@ CodeMirror.defineExtension = function (n
 CodeMirror.defineDocExtension = function (name, func) {
   Doc.prototype[name] = func
 }
 
 CodeMirror.fromTextArea = fromTextArea
 
 addLegacyProps(CodeMirror)
 
-CodeMirror.version = "5.32.0"
+CodeMirror.version = "5.33.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
@@ -484,16 +484,37 @@ CodeMirror.defineMode("clike", function(
         state.tokenize = null;
         break;
       }
       escaped = stream.next() == "\\" && !escaped;
     }
     return "string";
   }
 
+  function tokenNestedComment(depth) {
+    return function (stream, state) {
+      var ch
+      while (ch = stream.next()) {
+        if (ch == "*" && stream.eat("/")) {
+          if (depth == 1) {
+            state.tokenize = null
+            break
+          } else {
+            state.tokenize = tokenNestedComment(depth - 1)
+            return state.tokenize(stream, state)
+          }
+        } else if (ch == "/" && stream.eat("*")) {
+          state.tokenize = tokenNestedComment(depth + 1)
+          return state.tokenize(stream, state)
+        }
+      }
+      return "comment"
+    }
+  }
+
   def("text/x-scala", {
     name: "clike",
     keywords: words(
 
       /* scala */
       "abstract case catch class def do else extends final finally for forSome if " +
       "implicit import lazy match new null object override package private protected return " +
       "sealed super this throw trait try type val var while with yield _ " +
@@ -539,16 +560,22 @@ CodeMirror.defineMode("clike", function(
       "=": function(stream, state) {
         var cx = state.context
         if (cx.type == "}" && cx.align && stream.eat(">")) {
           state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
           return "operator"
         } else {
           return false
         }
+      },
+
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false
+        state.tokenize = tokenNestedComment(1)
+        return state.tokenize(stream, state)
       }
     },
     modeProps: {closeBrackets: {triples: '"'}}
   });
 
   function tokenKotlinString(tripleString){
     return function (stream, state) {
       var escaped = false, next, end = false;
@@ -573,17 +600,17 @@ CodeMirror.defineMode("clike", function(
       "package as typealias class interface this super val " +
       "var fun for is in This throw return " +
       "break continue object if else while do try when !in !is as? " +
 
       /*soft keywords*/
       "file import where by get set abstract enum open inner override private public internal " +
       "protected catch finally out final vararg reified dynamic companion constructor init " +
       "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
-      "external annotation crossinline const operator infix suspend"
+      "external annotation crossinline const operator infix suspend actual expect"
     ),
     types: words(
       /* package java.lang */
       "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"
     ),
--- a/devtools/client/sourceeditor/codemirror/mode/css/css.js
+++ b/devtools/client/sourceeditor/codemirror/mode/css/css.js
@@ -72,19 +72,19 @@ CodeMirror.defineMode("css", function(co
         return ret("meta", "meta");
       }
     } else if (/[,+>*\/]/.test(ch)) {
       return ret(null, "select-op");
     } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
       return ret("qualifier", "qualifier");
     } else if (/[:;{}\[\]\(\)]/.test(ch)) {
       return ret(null, ch);
-    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
-               (ch == "d" && stream.match("omain(")) ||
-               (ch == "r" && stream.match("egexp("))) {
+    } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) ||
+               ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) ||
+               ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) {
       stream.backUp(1);
       state.tokenize = tokenParenthesized;
       return ret("property", "word");
     } else if (/[\w\\\-]/.test(ch)) {
       stream.eatWhile(/[\w\\\-]/);
       return ret("property", "word");
     } else {
       return ret(null, null);
@@ -157,26 +157,26 @@ CodeMirror.defineMode("css", function(co
 
   var states = {};
 
   states.top = function(type, stream, state) {
     if (type == "{") {
       return pushContext(state, stream, "block");
     } else if (type == "}" && state.context.prev) {
       return popContext(state);
-    } else if (supportsAtComponent && /@component/.test(type)) {
+    } else if (supportsAtComponent && /@component/i.test(type)) {
       return pushContext(state, stream, "atComponentBlock");
-    } else if (/^@(-moz-)?document$/.test(type)) {
+    } else if (/^@(-moz-)?document$/i.test(type)) {
       return pushContext(state, stream, "documentTypes");
-    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
+    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
       return pushContext(state, stream, "atBlock");
-    } else if (/^@(font-face|counter-style)/.test(type)) {
+    } else if (/^@(font-face|counter-style)/i.test(type)) {
       state.stateArg = type;
       return "restricted_atBlock_before";
-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
       return "keyframes";
     } else if (type && type.charAt(0) == "@") {
       return pushContext(state, stream, "at");
     } else if (type == "hash") {
       override = "builtin";
     } else if (type == "word") {
       override = "tag";
     } else if (type == "variable-definition") {
@@ -788,17 +788,17 @@ CodeMirror.defineMode("css", function(co
           state.tokenize = tokenCComment;
           return tokenCComment(stream, state);
         } else {
           return ["operator", "operator"];
         }
       },
       "@": function(stream) {
         if (stream.eat("{")) return [null, "interpolation"];
-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
         stream.eatWhile(/[\w\\\-]/);
         if (stream.match(/^\s*:/, false))
           return ["variable-2", "variable-definition"];
         return ["variable-2", "variable"];
       },
       "&": function() {
         return ["atom", "atom"];
       }
--- a/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
+++ b/devtools/client/sourceeditor/codemirror/mode/javascript/javascript.js
@@ -21,55 +21,28 @@ CodeMirror.defineMode("javascript", func
 
   // Tokenizer
 
   var keywords = function(){
     function kw(type) {return {type: type, style: "keyword"};}
     var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
     var operator = kw("operator"), atom = {type: "atom", style: "atom"};
 
-    var jsKeywords = {
+    return {
       "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
       "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
       "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
       "function": kw("function"), "catch": kw("catch"),
       "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
       "in": operator, "typeof": operator, "instanceof": operator,
       "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
       "this": kw("this"), "class": kw("class"), "super": kw("atom"),
       "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
       "await": C
     };
-
-    // Extend the 'normal' keywords with the TypeScript language extensions
-    if (isTS) {
-      var type = {type: "variable", style: "type"};
-      var tsKeywords = {
-        // object-like things
-        "interface": kw("class"),
-        "implements": C,
-        "namespace": C,
-
-        // scope modifiers
-        "public": kw("modifier"),
-        "private": kw("modifier"),
-        "protected": kw("modifier"),
-        "abstract": kw("modifier"),
-        "readonly": kw("modifier"),
-
-        // types
-        "string": type, "number": type, "boolean": type, "any": type
-      };
-
-      for (var attr in tsKeywords) {
-        jsKeywords[attr] = tsKeywords[attr];
-      }
-    }
-
-    return jsKeywords;
   }();
 
   var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
   var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
 
   function readRegexp(stream) {
     var escaped = false, next, inSet = false;
     while ((next = stream.next()) != null) {
@@ -305,16 +278,20 @@ CodeMirror.defineMode("javascript", func
       state.localVars = {name: varname, next: state.localVars};
     } else {
       if (inList(state.globalVars)) return;
       if (parserConfig.globalVars)
         state.globalVars = {name: varname, next: state.globalVars};
     }
   }
 
+  function isModifier(name) {
+    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
+  }
+
   // Combinators
 
   var defaultVars = {name: "this", next: {name: "arguments"}};
   function pushcontext() {
     cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
     cx.state.localVars = defaultVars;
   }
   function popcontext() {
@@ -361,64 +338,67 @@ CodeMirror.defineMode("javascript", func
     if (type == ";") return cont();
     if (type == "if") {
       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") {
         cx.marked = "keyword"
         return cont(statement)
       } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) {
         cx.marked = "keyword"
         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"),
                                       block, poplex, poplex);
     if (type == "case") return cont(expression, expect(":"));
     if (type == "default") return cont(expect(":"));
     if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
                                      statement, poplex, popcontext);
-    if (type == "class") return cont(pushlex("form"), className, poplex);
     if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
     if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
     if (type == "async") return cont(statement)
     if (value == "@") return cont(expression, statement)
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
-  function expression(type) {
-    return expressionInner(type, false);
+  function expression(type, value) {
+    return expressionInner(type, value, false);
   }
-  function expressionNoComma(type) {
-    return expressionInner(type, true);
+  function expressionNoComma(type, value) {
+    return expressionInner(type, value, true);
   }
   function parenExpr(type) {
     if (type != "(") return pass()
     return cont(pushlex(")"), expression, expect(")"), poplex)
   }
-  function expressionInner(type, noComma) {
+  function expressionInner(type, value, noComma) {
     if (cx.state.fatArrowAt == cx.stream.start) {
       var body = noComma ? arrowBodyNoComma : arrowBody;
       if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
       else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
     }
 
     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef, maybeop);
-    if (type == "class") return cont(pushlex("form"), classExpression, poplex);
+    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
     if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
     if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
     if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
     if (type == "{") return contCommasep(objprop, "}", null, maybeop);
     if (type == "quasi") return pass(quasi, maybeop);
     if (type == "new") return cont(maybeTarget(noComma));
     return cont();
@@ -506,20 +486,21 @@ CodeMirror.defineMode("javascript", func
       if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
         cx.state.fatArrowAt = cx.stream.pos + m[0].length
       return cont(afterprop);
     } else if (type == "number" || type == "string") {
       cx.marked = jsonldMode ? "property" : (cx.style + " property");
       return cont(afterprop);
     } else if (type == "jsonld-keyword") {
       return cont(afterprop);
-    } else if (type == "modifier") {
+    } else if (isTS && isModifier(value)) {
+      cx.marked = "keyword"
       return cont(objprop)
     } else if (type == "[") {
-      return cont(expression, expect("]"), afterprop);
+      return cont(expression, maybetype, expect("]"), afterprop);
     } else if (type == "spread") {
       return cont(expressionNoComma, afterprop);
     } else if (value == "*") {
       cx.marked = "keyword";
       return cont(objprop);
     } else if (type == ":") {
       return pass(afterprop)
     }
@@ -611,32 +592,32 @@ CodeMirror.defineMode("javascript", func
   function typearg(type) {
     if (type == "variable") return cont(typearg)
     else if (type == ":") return cont(typeexpr)
   }
   function afterType(type, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
     if (value == "|" || type == ".") return cont(typeexpr)
     if (type == "[") return cont(expect("]"), afterType)
-    if (value == "extends") return cont(typeexpr)
+    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
   }
   function maybeTypeArgs(_, value) {
     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() {
     return pass(pattern, maybetype, maybeAssign, vardefCont);
   }
   function pattern(type, value) {
-    if (type == "modifier") return cont(pattern)
+    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, "}");
   }
   function proppattern(type, value) {
     if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
       register(value);
@@ -680,17 +661,18 @@ CodeMirror.defineMode("javascript", func
   function functiondef(type, value) {
     if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
     if (type == "variable") {register(value); return cont(functiondef);}
     if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
     if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
   }
   function funarg(type, value) {
     if (value == "@") cont(expression, funarg)
-    if (type == "spread" || type == "modifier") return cont(funarg);
+    if (type == "spread") return cont(funarg);
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
     return pass(pattern, maybetype, maybeAssign);
   }
   function classExpression(type, value) {
     // Class expressions may have an optional name.
     if (type == "variable") return className(type, value);
     return classNameAfter(type, value);
   }
   function className(type, value) {
@@ -698,29 +680,29 @@ CodeMirror.defineMode("javascript", func
   }
   function classNameAfter(type, value) {
     if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
     if (value == "extends" || value == "implements" || (isTS && type == ","))
       return cont(isTS ? typeexpr : expression, classNameAfter);
     if (type == "{") return cont(pushlex("}"), classBody, poplex);
   }
   function classBody(type, value) {
-    if (type == "modifier" || type == "async" ||
+    if (type == "async" ||
         (type == "variable" &&
-         (value == "static" || value == "get" || value == "set") &&
+         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
          cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
       cx.marked = "keyword";
       return cont(classBody);
     }
     if (type == "variable" || cx.style == "keyword") {
       cx.marked = "property";
       return cont(isTS ? classfield : functiondef, classBody);
     }
     if (type == "[")
-      return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
+      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
     if (value == "*") {
       cx.marked = "keyword";
       return cont(classBody);
     }
     if (type == ";") return cont(classBody);
     if (type == "}") return cont();
     if (value == "@") return cont(expression, classBody)
   }
--- a/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
+++ b/devtools/client/sourceeditor/codemirror/mode/jsx/jsx.js
@@ -21,17 +21,17 @@
   function copyContext(context) {
     return new Context(CodeMirror.copyState(context.mode, context.state),
                        context.mode,
                        context.depth,
                        context.prev && copyContext(context.prev))
   }
 
   CodeMirror.defineMode("jsx", function(config, modeConfig) {
-    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
     var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
 
     function flatXMLIndent(state) {
       var tagName = state.tagName
       state.tagName = null
       var result = xmlMode.indent(state, "")
       state.tagName = tagName
       return result
--- a/devtools/client/sourceeditor/codemirror/mode/xml/xml.js
+++ b/devtools/client/sourceeditor/codemirror/mode/xml/xml.js
@@ -47,16 +47,17 @@ var htmlConfig = {
 
 var xmlConfig = {
   autoSelfClosers: {},
   implicitlyClosed: {},
   contextGrabbers: {},
   doNotIndent: {},
   allowUnquoted: false,
   allowMissing: false,
+  allowMissingTagName: false,
   caseFold: false
 }
 
 CodeMirror.defineMode("xml", function(editorConf, config_) {
   var indentUnit = editorConf.indentUnit
   var config = {}
   var defaults = config_.htmlMode ? htmlConfig : xmlConfig
   for (var prop in defaults) config[prop] = defaults[prop]
@@ -221,16 +222,19 @@ CodeMirror.defineMode("xml", function(ed
       return baseState;
     }
   }
   function tagNameState(type, stream, state) {
     if (type == "word") {
       state.tagName = stream.current();
       setStyle = "tag";
       return attrState;
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return attrState(type, stream, state);
     } else {
       setStyle = "error";
       return tagNameState;
     }
   }
   function closeTagNameState(type, stream, state) {
     if (type == "word") {
       var tagName = stream.current();
@@ -239,16 +243,19 @@ CodeMirror.defineMode("xml", function(ed
         popContext(state);
       if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
         setStyle = "tag";
         return closeState;
       } else {
         setStyle = "tag error";
         return closeStateErr;
       }
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return closeState(type, stream, state);
     } else {
       setStyle = "error";
       return closeStateErr;
     }
   }
 
   function closeState(type, _stream, state) {
     if (type != "endTag") {
--- a/devtools/client/sourceeditor/test/codemirror/mode_test.js
+++ b/devtools/client/sourceeditor/test/codemirror/mode_test.js
@@ -62,17 +62,17 @@
   test.mode = function(name, mode, tokens, modeName) {
     var data = parseTokens(tokens);
     return test((modeName || mode.name) + "_" + name, function() {
       return compare(data.plain, data.tokens, mode);
     });
   };
 
   function esc(str) {
-    return str.replace('&', '&amp;').replace('<', '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
+    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
   }
 
   function compare(text, expected, mode) {
 
     var expectedOutput = [];
     for (var i = 0; i < expected.length; ++i) {
       var sty = expected[i].style;
       if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
--- a/devtools/client/sourceeditor/test/codemirror/sublime_test.js
+++ b/devtools/client/sourceeditor/test/codemirror/sublime_test.js
@@ -147,17 +147,19 @@
   stTest("selectScope", "foo(a) {\n  bar[1, 2];\n}",
          "selectScope", hasSel(0, 0, 2, 1),
          Pos(0, 4), "selectScope", hasSel(0, 4, 0, 5),
          Pos(0, 5), "selectScope", hasSel(0, 4, 0, 5),
          Pos(0, 6), "selectScope", hasSel(0, 0, 2, 1),
          Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0),
          Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0),
          Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10),
-         Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10));
+         Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10),
+         "selectScope", hasSel(0, 8, 2, 0),
+         "selectScope", hasSel(0, 0, 2, 1));
 
   stTest("goToBracket", "foo(a) {\n  bar[1, 2];\n}",
          Pos(0, 0), "goToBracket", at(0, 0),
          Pos(0, 4), "goToBracket", at(0, 5), "goToBracket", at(0, 4),
          Pos(0, 8), "goToBracket", at(2, 0), "goToBracket", at(0, 8),
          Pos(1, 2), "goToBracket", at(2, 0),
          Pos(1, 7), "goToBracket", at(1, 10), "goToBracket", at(1, 6));
 
@@ -214,41 +216,16 @@
          setSel(0, 1, 0, 1,
                 0, 2, 0, 4,
                 0, 5, 0, 5),
          "duplicateLine",
          val("abcdef\nabcdcdef\nabcdcdef"), hasSel(2, 1, 2, 1,
                                                    2, 4, 2, 6,
                                                    2, 7, 2, 7));
 
-  stTest("selectLinesUpward", "123\n345\n789\n012",
-         setSel(0, 1, 0, 1,
-                1, 1, 1, 3,
-                2, 0, 2, 0,
-                3, 0, 3, 0),
-         "selectLinesUpward",
-         hasSel(0, 1, 0, 1,
-                0, 3, 0, 3,
-                1, 0, 1, 0,
-                1, 1, 1, 3,
-                2, 0, 2, 0,
-                3, 0, 3, 0));
-
-  stTest("selectLinesDownward", "123\n345\n789\n012",
-         setSel(0, 1, 0, 1,
-                1, 1, 1, 3,
-                2, 0, 2, 0,
-                3, 0, 3, 0),
-         "selectLinesDownward",
-         hasSel(0, 1, 0, 1,
-                1, 1, 1, 3,
-                2, 0, 2, 0,
-                2, 3, 2, 3,
-                3, 0, 3, 0));
-
   stTest("sortLines", "c\nb\na\nC\nB\nA",
          "sortLines", val("A\nB\nC\na\nb\nc"),
          "undo",
          setSel(0, 0, 2, 0,
                 3, 0, 5, 0),
          "sortLines", val("b\nc\na\nB\nC\nA"),
          hasSel(0, 0, 2, 0,
                 3, 0, 5, 0),
--- a/devtools/client/sourceeditor/test/codemirror/vim_test.js
+++ b/devtools/client/sourceeditor/test/codemirror/vim_test.js
@@ -4230,9 +4230,255 @@ testVim('ex_map_key2key_from_colon', fun
 }, { value: 'abc' });
 
 // Test event handlers
 testVim('beforeSelectionChange', function(cm, vim, helpers) {
   cm.setCursor(0, 100);
   eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor'));
 }, { value: 'abc' });
 
+testVim('increment_binary', function(cm, vim, helpers) {
+  cm.setCursor(0, 4);
+  helpers.doKeys('<C-a>');
+  eq('0b001', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0b010', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0b001', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0b000', cm.getValue());
+  cm.setCursor(0, 0);
+  helpers.doKeys('<C-a>');
+  eq('0b001', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0b010', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0b001', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0b000', cm.getValue());
+}, { value: '0b000' });
 
+testVim('increment_octal', function(cm, vim, helpers) {
+  cm.setCursor(0, 2);
+  helpers.doKeys('<C-a>');
+  eq('001', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('002', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('003', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('004', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('005', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('006', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('007', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('010', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('007', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('006', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('005', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('004', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('003', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('002', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('001', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('000', cm.getValue());
+  cm.setCursor(0, 0);
+  helpers.doKeys('<C-a>');
+  eq('001', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('002', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('001', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('000', cm.getValue());
+}, { value: '000' });
+
+testVim('increment_decimal', function(cm, vim, helpers) {
+  cm.setCursor(0, 2);
+  helpers.doKeys('<C-a>');
+  eq('101', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('102', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('103', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('104', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('105', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('106', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('107', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('108', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('109', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('110', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('109', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('108', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('107', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('106', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('105', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('104', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('103', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('102', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('101', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('100', cm.getValue());
+  cm.setCursor(0, 0);
+  helpers.doKeys('<C-a>');
+  eq('101', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('102', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('101', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('100', cm.getValue());
+}, { value: '100' });
+
+testVim('increment_decimal_single_zero', function(cm, vim, helpers) {
+  helpers.doKeys('<C-a>');
+  eq('1', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('2', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('3', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('4', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('5', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('6', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('7', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('8', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('9', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('10', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('9', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('8', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('7', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('6', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('5', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('4', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('3', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('2', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('1', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0', cm.getValue());
+  cm.setCursor(0, 0);
+  helpers.doKeys('<C-a>');
+  eq('1', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('2', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('1', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0', cm.getValue());
+}, { value: '0' });
+
+testVim('increment_hexadecimal', function(cm, vim, helpers) {
+  cm.setCursor(0, 2);
+  helpers.doKeys('<C-a>');
+  eq('0x1', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x2', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x3', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x4', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x5', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x6', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x7', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x8', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x9', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xa', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xb', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xc', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xd', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xe', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0xf', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x10', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0f', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0e', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0d', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0c', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0b', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x0a', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x09', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x08', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x07', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x06', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x05', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x04', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x03', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x02', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x01', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x00', cm.getValue());
+  cm.setCursor(0, 0);
+  helpers.doKeys('<C-a>');
+  eq('0x01', cm.getValue());
+  helpers.doKeys('<C-a>');
+  eq('0x02', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x01', cm.getValue());
+  helpers.doKeys('<C-x>');
+  eq('0x00', cm.getValue());
+}, { value: '0x0' });