diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js
index 2c265420457a966bda236e97e784b3cd0aa126e7..586b7370dc46b50cab92abbb07e85a91a1992c75 100644
--- a/addon/dialog/dialog.js
+++ b/addon/dialog/dialog.js
@@ -46,6 +46,7 @@
       CodeMirror.on(inp, "keydown", function(e) {
         if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
         if (e.keyCode == 13 || e.keyCode == 27) {
+          inp.blur();
           CodeMirror.e_stop(e);
           close();
           me.focus();
diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js
index 1570664c4fda9b7c7f79c71801ba36b6cc7900d6..3ef979b5240770f931a1d571126697ba735ce98a 100644
--- a/addon/hint/anyword-hint.js
+++ b/addon/hint/anyword-hint.js
@@ -22,8 +22,8 @@
     var list = [], seen = {};
     var re = new RegExp(word.source, "g");
     for (var dir = -1; dir <= 1; dir += 2) {
-      var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
-      for (; line != end; line += dir) {
+      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
+      for (; line != endLine; line += dir) {
         var text = editor.getLine(line), m;
         while (m = re.exec(text)) {
           if (line == cur.line && m[0] === curWord) continue;
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index 6a4d74e3603a0eee19efab0b7bf82b7f925d32ff..084b34750526c528cd65c02fa6498b5f9330345f 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -200,8 +200,24 @@
     var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
     var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
     (options.container || document.body).appendChild(hints);
-    var box = hints.getBoundingClientRect();
-    var overlapX = box.right - winW, overlapY = box.bottom - winH;
+    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+    if (overlapY > 0) {
+      var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top);
+      if (curTop - height > 0) { // Fits above cursor
+        hints.style.top = (top = curTop - height) + "px";
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        hints.style.top = (top = pos.bottom - box.top) + "px";
+        var cursor = cm.getCursor();
+        if (data.from.ch != cursor.ch) {
+          pos = cm.cursorCoords(cursor);
+          hints.style.left = (left = pos.left) + "px";
+          box = hints.getBoundingClientRect();
+        }
+      }
+    }
+    var overlapX = box.left - winW;
     if (overlapX > 0) {
       if (box.right - box.left > winW) {
         hints.style.width = (winW - 5) + "px";
@@ -209,17 +225,6 @@
       }
       hints.style.left = (left = pos.left - overlapX) + "px";
     }
-    if (overlapY > 0) {
-      var height = box.bottom - box.top;
-      if (box.top - (pos.bottom - pos.top) - height > 0) {
-        overlapY = height + (pos.bottom - pos.top);
-        below = false;
-      } else if (height > winH) {
-        hints.style.height = (winH - 5) + "px";
-        overlapY -= height - winH;
-      }
-      hints.style.top = (top = pos.bottom - overlapY) + "px";
-    }
 
     cm.addKeyMap(this.keyMap = buildKeyMap(options, {
       moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
@@ -227,7 +232,8 @@
       menuSize: function() { return widget.screenAmount(); },
       length: completions.length,
       close: function() { completion.close(); },
-      pick: function() { widget.pick(); }
+      pick: function() { widget.pick(); },
+      data: data
     }));
 
     if (options.closeOnUnfocus !== false) {
diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js
index c7cc8486575eb048e5d8d3766262c15c75c68754..3bfd11a93e5882211f81dd276bb7e40f3d16fd4c 100644
--- a/addon/runmode/runmode.node.js
+++ b/addon/runmode/runmode.node.js
@@ -94,7 +94,7 @@ exports.resolveMode = function(spec) {
   else return spec || {name: "null"};
 };
 exports.getMode = function(options, spec) {
-  spec = exports.resolveMode(mimeModes[spec]);
+  spec = exports.resolveMode(spec);
   var mfactory = modes[spec.name];
   if (!mfactory) throw new Error("Unknown mode: " + spec);
   return mfactory(options, spec);
diff --git a/bin/source-highlight b/bin/source-highlight
index 7596ed776ca04d432d05b52867cdfbd99ce62cc2..1373f6626f6ac74b78f6f04f94704a97f7b8694c 100755
--- a/bin/source-highlight
+++ b/bin/source-highlight
@@ -40,7 +40,7 @@ function ensureMode(name) {
 ensureMode(modeName);
 
 function esc(str) {
-  return str.replace(/[<&]/, function(ch) { return ch == "&" ? "&amp;" : "&lt;"; });
+  return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&amp;" : "&lt;"; });
 }
 
 var code = fs.readFileSync("/dev/stdin", "utf8");
diff --git a/demo/variableheight.html b/demo/variableheight.html
index ab241c6d28ae13fcd8dd8d3f1ab2903ba4f21435..98f2fd29d054eb0c6617a54ff3fe05ca2d52f332 100644
--- a/demo/variableheight.html
+++ b/demo/variableheight.html
@@ -11,12 +11,12 @@
 <style type="text/css">
       .CodeMirror {border: 1px solid silver; border-width: 1px 2px; }
       .cm-header { font-family: arial; }
-      .cm-header1 { font-size: 150%; }
-      .cm-header2 { font-size: 130%; }
-      .cm-header3 { font-size: 120%; }
-      .cm-header4 { font-size: 110%; }
-      .cm-header5 { font-size: 100%; }
-      .cm-header6 { font-size: 90%; }
+      .cm-header-1 { font-size: 150%; }
+      .cm-header-2 { font-size: 130%; }
+      .cm-header-3 { font-size: 120%; }
+      .cm-header-4 { font-size: 110%; }
+      .cm-header-5 { font-size: 100%; }
+      .cm-header-6 { font-size: 90%; }
       .cm-strong { font-size: 140%; }
     </style>
 <div id=nav>
diff --git a/doc/manual.html b/doc/manual.html
index 25db83a1403d709801fa4a5bc1003bf3bccdbc51..7ec8dc657af1a0e9ab87cf82c704e2a9bdf1b46d 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -2162,7 +2162,11 @@
         has <code>moveFocus(n)</code>, <code>setFocus(n)</code>, <code>pick()</code>,
         and <code>close()</code> methods (see the source for details),
         that can be used to change the focused element, pick the
-        current element or close the menu.</dd>
+        current element or close the menu. Additionnaly <code>menuSize()</code>
+        can give you access to the size of the current dropdown menu,
+        <code>length</code> give you the number of availlable completions, and
+        <code>data</code> give you full access to the completion returned by the
+        hinting function.</dd>
         <dt><code><strong>extraKeys</strong>: keymap</code></dt>
         <dd>Like <code>customKeys</code> above, but the bindings will
         be added to the set of default bindings, instead of replacing
diff --git a/doc/realworld.html b/doc/realworld.html
index b9e35f2dfc3653126c745354ac0df23cc625358a..96d4df19564a9a0c530b68ef8a7378f4febea52c 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -74,6 +74,7 @@
       <li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li>
       <li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
       <li><a href="http://icecoder.net">ICEcoder</a> (web IDE)</li>
+      <li><a href="http://i-mos.org/imos/">i-MOS</a> (modeling and simulation platform)</li>
       <li><a href="http://www.janvas.com/">Janvas</a> (vector graphics editor)</li>
       <li><a href="http://extensions.joomla.org/extensions/edition/editors/8723">Joomla plugin</a></li>
       <li><a href="http://jqfundamentals.com/">jQuery fundamentals</a> (interactive tutorial)</li>
diff --git a/keymap/vim.js b/keymap/vim.js
index 1c41f8f671a80f393afb880b20a6bb2b4f8472d5..74d711cda80bf6c3f0ab92150c99de8ba96d7c38 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -81,7 +81,7 @@
     { keys: ['<S-BS>'], type: 'keyToKey', toKeys: ['b'] },
     { keys: ['<C-n>'], type: 'keyToKey', toKeys: ['j'] },
     { keys: ['<C-p>'], type: 'keyToKey', toKeys: ['k'] },
-    { keys: ['C-['], type: 'keyToKey', toKeys: ['<Esc>'] },
+    { keys: ['<C-[>'], type: 'keyToKey', toKeys: ['<Esc>'] },
     { keys: ['<C-c>'], type: 'keyToKey', toKeys: ['<Esc>'] },
     { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' },
     { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'},
@@ -1542,22 +1542,44 @@
                    findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
       },
       textObjectManipulation: function(cm, motionArgs) {
+        // TODO: lots of possible exceptions that can be thrown here. Try da(
+        //     outside of a () block.
+
+        // TODO: adding <> >< to this map doesn't work, presumably because
+        // they're operators
+        var mirroredPairs = {'(': ')', ')': '(',
+                             '{': '}', '}': '{',
+                             '[': ']', ']': '['};
+        var selfPaired = {'\'': true, '"': true};
+
         var character = motionArgs.selectedCharacter;
+
         // Inclusive is the difference between a and i
         // TODO: Instead of using the additional text object map to perform text
         //     object operations, merge the map into the defaultKeyMap and use
         //     motionArgs to define behavior. Define separate entries for 'aw',
         //     'iw', 'a[', 'i[', etc.
         var inclusive = !motionArgs.textObjectInner;
-        if (!textObjects[character]) {
+
+        var tmp;
+        if (mirroredPairs[character]) {
+          tmp = selectCompanionObject(cm, mirroredPairs[character], inclusive);
+        } else if (selfPaired[character]) {
+          tmp = findBeginningAndEnd(cm, character, inclusive);
+        } else if (character === 'W') {
+          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
+                                                     true /** bigWord */);
+        } else if (character === 'w') {
+          tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
+                                                     false /** bigWord */);
+        } else {
           // No text object defined for this, don't move.
           return null;
         }
-        var tmp = textObjects[character](cm, inclusive);
-        var start = tmp.start;
-        var end = tmp.end;
-        return [start, end];
+
+        return [tmp.start, tmp.end];
       },
+
       repeatLastCharacterSearch: function(cm, motionArgs) {
         var lastSearch = vimGlobalState.lastChararacterSearch;
         var repeat = motionArgs.repeat;
@@ -2025,36 +2047,6 @@
       }
     };
 
-    var textObjects = {
-      // TODO: lots of possible exceptions that can be thrown here. Try da(
-      //     outside of a () block.
-      // TODO: implement text objects for the reverse like }. Should just be
-      //     an additional mapping after moving to the defaultKeyMap.
-      'w': function(cm, inclusive) {
-        return expandWordUnderCursor(cm, inclusive, true /** forward */,
-            false /** bigWord */);
-      },
-      'W': function(cm, inclusive) {
-        return expandWordUnderCursor(cm, inclusive,
-            true /** forward */, true /** bigWord */);
-      },
-      '{': function(cm, inclusive) {
-        return selectCompanionObject(cm, '}', inclusive);
-      },
-      '(': function(cm, inclusive) {
-        return selectCompanionObject(cm, ')', inclusive);
-      },
-      '[': function(cm, inclusive) {
-        return selectCompanionObject(cm, ']', inclusive);
-      },
-      '\'': function(cm, inclusive) {
-        return findBeginningAndEnd(cm, "'", inclusive);
-      },
-      '"': function(cm, inclusive) {
-        return findBeginningAndEnd(cm, '"', inclusive);
-      }
-    };
-
     /*
      * Below are miscellaneous utility functions used by vim.js
      */
@@ -2644,13 +2636,25 @@
       return cur;
     }
 
+    // TODO: perhaps this finagling of start and end positions belonds
+    // in codmirror/replaceRange?
     function selectCompanionObject(cm, revSymb, inclusive) {
       var cur = cm.getCursor();
-
       var end = findMatchedSymbol(cm, cur, revSymb);
       var start = findMatchedSymbol(cm, end);
-      start.ch += inclusive ? 1 : 0;
-      end.ch += inclusive ? 0 : 1;
+
+      if((start.line == end.line && start.ch > end.ch)
+          || (start.line > end.line)) {
+        var tmp = start;
+        start = end;
+        end = tmp;
+      }
+
+      if(inclusive) {
+        end.ch += 1;
+      } else {
+        start.ch += 1;
+      }
 
       return { start: start, end: end };
     }
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 110693bed9ea549bd5d2b9e068ff506e5ad3b5fd..fcc023b753c51ea957da071df416e21636a7bb96 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -36,6 +36,8 @@
   min-width: 20px;
   text-align: right;
   color: #999;
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
 }
 
 /* CURSOR */
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 6b6a0e9ca3f8746cd7b82e4736132363e1e85de1..497f68e89aeef2e06403b53fded5460ce63011a4 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -2409,6 +2409,7 @@
     }
     if (clickInGutter(cm, e)) return;
     var start = posFromMouse(cm, e);
+    window.focus();
 
     switch (e_button(e)) {
     case 1:
diff --git a/mode/julia/julia.js b/mode/julia/julia.js
index b60371304066c19159c9155d77775d54c980018f..d37ab503ed658c9694106befbaea79ad6f42e899 100644
--- a/mode/julia/julia.js
+++ b/mode/julia/julia.js
@@ -15,21 +15,22 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     return new RegExp("^((" + words.join(")|(") + "))\\b");
   }
 
-  var operators = parserConf.operators || /^(?:\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|<:|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b|\.{3})/;
+  var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/;
   var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
   var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*!*/;
-  var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch"];
+  var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
   var blockClosers = ["end", "else", "elseif", "catch", "finally"];
   var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall'];
-  var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];
+  var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];
 
   //var stringPrefixes = new RegExp("^[br]?('|\")")
-  var stringPrefixes = /^[br]?('|"{3}|")/;
+  var stringPrefixes = /^(`|'|"{3}|([br]?"))/;
   var keywords = wordRegexp(keywordList);
   var builtins = wordRegexp(builtinList);
   var openers = wordRegexp(blockOpeners);
   var closers = wordRegexp(blockClosers);
-  var macro = /@[_A-Za-z][_A-Za-z0-9]*!*/;
+  var macro = /^@[_A-Za-z][_A-Za-z0-9]*/;
+  var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/;
   var indentInfo = null;
 
   function in_array(state) {
@@ -53,14 +54,19 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
   function tokenBase(stream, state) {
     // Handle scope changes
     var leaving_expr = state.leaving_expr;
+    if(stream.sol()) {
+      leaving_expr = false;
+    }
     state.leaving_expr = false;
     if(leaving_expr) {
       if(stream.match(/^'+/)) {
         return 'operator';
       }
-      if(stream.match("...")) {
-        return 'operator';
-      }
+
+    }
+
+    if(stream.match(/^\.{2,3}/)) {
+      return 'operator';
     }
 
     if (stream.eatSpace()) {
@@ -93,8 +99,12 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       state.leaving_expr=true;
     }
 
+    if(ch===')') {
+      state.leaving_expr = true;
+    }
+
     var match;
-    if(match=stream.match(openers, false)) {
+    if(!in_array(state) && (match=stream.match(openers, false))) {
       state.scopes.push(match);
     }
 
@@ -103,25 +113,29 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     }
 
     if(in_array(state)) {
-      if(stream.match("end")) {
+      if(stream.match(/^end/)) {
         return 'number';
       }
 
     }
-    if(stream.match("=>")) {
+
+    if(stream.match(/^=>/)) {
       return 'operator';
     }
+
+
     // Handle Number Literals
     if (stream.match(/^[0-9\.]/, false)) {
       var imMatcher = RegExp(/^im\b/);
       var floatLiteral = false;
       // Floats
-      if (stream.match(/^\d*\.\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
-      if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
+      if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
+      if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
       if (stream.match(/^\.\d+/)) { floatLiteral = true; }
       if (floatLiteral) {
           // Float literals may be "imaginary"
           stream.match(imMatcher);
+          state.leaving_expr = true;
           return 'number';
       }
       // Integers
@@ -134,9 +148,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
       // Decimal
       if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
-          // Decimal literals may be "imaginary"
-          stream.eat(/J/i);
-          // TODO - Can you have imaginary longs?
           intLiteral = true;
       }
       // Zero by itself with no other piece of number.
@@ -144,21 +155,37 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       if (intLiteral) {
           // Integer literals may be "long"
           stream.match(imMatcher);
+          state.leaving_expr = true;
           return 'number';
       }
     }
 
+    if(stream.match(/^(::)|(<:)/)) {
+      return 'operator';
+    }
+
+    // Handle symbols
+    if(!leaving_expr && stream.match(symbol)) {
+      return 'string';
+    }
+
+    // Handle operators and Delimiters
+    if (stream.match(operators)) {
+      return 'operator';
+    }
+
+
     // Handle Strings
     if (stream.match(stringPrefixes)) {
       state.tokenize = tokenStringFactory(stream.current());
       return state.tokenize(stream, state);
     }
 
-    // Handle operators and Delimiters
-    if (stream.match(operators)) {
-      return 'operator';
+    if (stream.match(macro)) {
+      return 'meta';
     }
 
+
     if (stream.match(delimiters)) {
       return null;
     }
@@ -171,9 +198,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       return 'builtin';
     }
 
-    if (stream.match(macro)) {
-      return 'meta';
-    }
 
     if (stream.match(identifiers)) {
       state.leaving_expr=true;
@@ -258,7 +282,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") {
         delta = -1;
       }
-      return (state.scopes.length + delta) * 2;
+      return (state.scopes.length + delta) * 4;
     },
 
     lineComment: "#",
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index cfa56b911d2e0b86dd653a71b0e84069ce7b29e1..cc831ee0d88154a7caeb0850840d9783b1d4fc6c 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -249,7 +249,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         styles.push(formatting + "-" + state.formatting[i]);
 
         if (state.formatting[i] === "header") {
-          styles.push(formatting + "-" + state.formatting[i] + state.header);
+          styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
         }
 
         // Add `formatting-quote` and `formatting-quote-#` for blockquotes
@@ -285,7 +285,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
 
     if (state.code) { styles.push(code); }
 
-    if (state.header) { styles.push(header); styles.push(header + state.header); }
+    if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
 
     if (state.quote) {
       styles.push(quote);
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index 30e82ca6541fcdb9b97515050633975c990db19e..c3016d3a6a476950d5b8c8fc13b5687168b6cb9b 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -23,11 +23,11 @@
      "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]");
 
   FT("formatting_atxHeader",
-     "[header&header1&formatting&formatting-header&formatting-header1 #][header&header1  foo # bar ][header&header1&formatting&formatting-header&formatting-header1 #]");
+     "[header&header-1&formatting&formatting-header&formatting-header-1 #][header&header-1  foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]");
 
   FT("formatting_setextHeader",
      "foo",
-     "[header&header1&formatting&formatting-header&formatting-header1 =]");
+     "[header&header-1&formatting&formatting-header&formatting-header-1 =]");
 
   FT("formatting_blockquote",
      "[quote&quote-1&formatting&formatting-quote&formatting-quote-1 > ][quote&quote-1 foo]");
@@ -138,31 +138,31 @@
   // http://daringfireball.net/projects/markdown/syntax#header
 
   MT("atxH1",
-     "[header&header1 # foo]");
+     "[header&header-1 # foo]");
 
   MT("atxH2",
-     "[header&header2 ## foo]");
+     "[header&header-2 ## foo]");
 
   MT("atxH3",
-     "[header&header3 ### foo]");
+     "[header&header-3 ### foo]");
 
   MT("atxH4",
-     "[header&header4 #### foo]");
+     "[header&header-4 #### foo]");
 
   MT("atxH5",
-     "[header&header5 ##### foo]");
+     "[header&header-5 ##### foo]");
 
   MT("atxH6",
-     "[header&header6 ###### foo]");
+     "[header&header-6 ###### foo]");
 
   // H6 - 7x '#' should still be H6, per Dingus
   // http://daringfireball.net/projects/markdown/dingus
   MT("atxH6NotH7",
-     "[header&header6 ####### foo]");
+     "[header&header-6 ####### foo]");
 
   // Inline styles should be parsed inside headers
   MT("atxH1inline",
-     "[header&header1 # foo ][header&header1&em *bar*]");
+     "[header&header-1 # foo ][header&header-1&em *bar*]");
 
   // Setext headers - H1, H2
   // Per documentation, "Any number of underlining =’s or -’s will work."
@@ -174,22 +174,22 @@
   // Check if single underlining = works
   MT("setextH1",
      "foo",
-     "[header&header1 =]");
+     "[header&header-1 =]");
 
   // Check if 3+ ='s work
   MT("setextH1",
      "foo",
-     "[header&header1 ===]");
+     "[header&header-1 ===]");
 
   // Check if single underlining - works
   MT("setextH2",
      "foo",
-     "[header&header2 -]");
+     "[header&header-2 -]");
 
   // Check if 3+ -'s work
   MT("setextH2",
      "foo",
-     "[header&header2 ---]");
+     "[header&header-2 ---]");
 
   // Single-line blockquote with trailing space
   MT("blockquoteSpace",
@@ -278,7 +278,7 @@
 
   // List after header
   MT("listAfterHeader",
-     "[header&header1 # foo]",
+     "[header&header-1 # foo]",
      "[variable-2 - bar]");
 
   // Formatting in lists (*)
diff --git a/mode/rst/index.html b/mode/rst/index.html
index 78030ebe94cb34566de3931703f332f66294ae35..cb8269751a22cc43377441a529eedc56778c9408 100644
--- a/mode/rst/index.html
+++ b/mode/rst/index.html
@@ -6,6 +6,7 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/mode/overlay.js"></script>
 <script src="rst.js"></script>
 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
diff --git a/mode/rst/rst.js b/mode/rst/rst.js
index 01c6a056505aae60f7c84d6ace5f2173a46b371f..0763d4b999eece835395eff3fedcc712690a8af5 100644
--- a/mode/rst/rst.js
+++ b/mode/rst/rst.js
@@ -8,557 +8,560 @@
 })(function(CodeMirror) {
 "use strict";
 
-CodeMirror.defineMode('rst-base', function (config) {
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
+CodeMirror.defineMode('rst', function (config, options) {
 
-    function format(string) {
-        var args = Array.prototype.slice.call(arguments, 1);
-        return string.replace(/{(\d+)}/g, function (match, n) {
-            return typeof args[n] != 'undefined' ? args[n] : match;
-        });
+  var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/;
+  var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/;
+  var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/;
+
+  var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/;
+  var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/;
+  var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/;
+
+  var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://";
+  var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})";
+  var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*";
+  var rx_uri = new RegExp("^" + rx_uri_protocol + rx_uri_domain + rx_uri_path);
+
+  var overlay = {
+    token: function (stream) {
+
+      if (stream.match(rx_strong) && stream.match (/\W+|$/, false))
+        return 'strong';
+      if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false))
+        return 'em';
+      if (stream.match(rx_literal) && stream.match (/\W+|$/, false))
+        return 'string-2';
+      if (stream.match(rx_number))
+        return 'number';
+      if (stream.match(rx_positive))
+        return 'positive';
+      if (stream.match(rx_negative))
+        return 'negative';
+      if (stream.match(rx_uri))
+        return 'link';
+
+      while (stream.next() != null) {
+        if (stream.match(rx_strong, false)) break;
+        if (stream.match(rx_emphasis, false)) break;
+        if (stream.match(rx_literal, false)) break;
+        if (stream.match(rx_number, false)) break;
+        if (stream.match(rx_positive, false)) break;
+        if (stream.match(rx_negative, false)) break;
+        if (stream.match(rx_uri, false)) break;
+      }
+
+      return null;
     }
+  };
 
-    function AssertException(message) {
-        this.message = message;
-    }
+  var mode = CodeMirror.getMode(
+    config, options.backdrop || 'rst-base'
+  );
 
-    AssertException.prototype.toString = function () {
-        return 'AssertException: ' + this.message;
-    };
+  return CodeMirror.overlayMode(mode, overlay, true); // combine
+}, 'python', 'stex');
 
-    function assert(expression, message) {
-        if (!expression) throw new AssertException(message);
-        return expression;
-    }
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    var mode_python = CodeMirror.getMode(config, 'python');
-    var mode_stex = CodeMirror.getMode(config, 'stex');
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    var SEPA = "\\s+";
-    var TAIL = "(?:\\s*|\\W|$)",
-        rx_TAIL = new RegExp(format('^{0}', TAIL));
-
-    var NAME =
-        "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)",
-        rx_NAME = new RegExp(format('^{0}', NAME));
-    var NAME_WWS =
-        "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)";
-    var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS);
-
-    var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)";
-    var TEXT2 = "(?:[^\\`]+)",
-        rx_TEXT2 = new RegExp(format('^{0}', TEXT2));
-
-    var rx_section = new RegExp(
-        "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$");
-    var rx_explicit = new RegExp(
-        format('^\\.\\.{0}', SEPA));
-    var rx_link = new RegExp(
-        format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL));
-    var rx_directive = new RegExp(
-        format('^{0}::{1}', REF_NAME, TAIL));
-    var rx_substitution = new RegExp(
-        format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL));
-    var rx_footnote = new RegExp(
-        format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL));
-    var rx_citation = new RegExp(
-        format('^\\[{0}\\]{1}', REF_NAME, TAIL));
-
-    var rx_substitution_ref = new RegExp(
-        format('^\\|{0}\\|', TEXT1));
-    var rx_footnote_ref = new RegExp(
-        format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME));
-    var rx_citation_ref = new RegExp(
-        format('^\\[{0}\\]_', REF_NAME));
-    var rx_link_ref1 = new RegExp(
-        format('^{0}__?', REF_NAME));
-    var rx_link_ref2 = new RegExp(
-        format('^`{0}`_', TEXT2));
-
-    var rx_role_pre = new RegExp(
-        format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL));
-    var rx_role_suf = new RegExp(
-        format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL));
-    var rx_role = new RegExp(
-        format('^:{0}:{1}', NAME, TAIL));
-
-    var rx_directive_name = new RegExp(format('^{0}', REF_NAME));
-    var rx_directive_tail = new RegExp(format('^::{0}', TAIL));
-    var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1));
-    var rx_substitution_sepa = new RegExp(format('^{0}', SEPA));
-    var rx_substitution_name = new RegExp(format('^{0}', REF_NAME));
-    var rx_substitution_tail = new RegExp(format('^::{0}', TAIL));
-    var rx_link_head = new RegExp("^_");
-    var rx_link_name = new RegExp(format('^{0}|_', REF_NAME));
-    var rx_link_tail = new RegExp(format('^:{0}', TAIL));
-
-    var rx_verbatim = new RegExp('^::\\s*$');
-    var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s');
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    function to_normal(stream, state) {
-        var token = null;
-
-        if (stream.sol() && stream.match(rx_examples, false)) {
-            change(state, to_mode, {
-                mode: mode_python, local: mode_python.startState()
-            });
-        } else if (stream.sol() && stream.match(rx_explicit)) {
-            change(state, to_explicit);
-            token = 'meta';
-        } else if (stream.sol() && stream.match(rx_section)) {
-            change(state, to_normal);
-            token = 'header';
-        } else if (phase(state) == rx_role_pre ||
-            stream.match(rx_role_pre, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_normal, context(rx_role_pre, 1));
-                    assert(stream.match(/^:/));
-                    token = 'meta';
-                    break;
-                case 1:
-                    change(state, to_normal, context(rx_role_pre, 2));
-                    assert(stream.match(rx_NAME));
-                    token = 'keyword';
-
-                    if (stream.current().match(/^(?:math|latex)/)) {
-                        state.tmp_stex = true;
-                    }
-                    break;
-                case 2:
-                    change(state, to_normal, context(rx_role_pre, 3));
-                    assert(stream.match(/^:`/));
-                    token = 'meta';
-                    break;
-                case 3:
-                    if (state.tmp_stex) {
-                        state.tmp_stex = undefined; state.tmp = {
-                            mode: mode_stex, local: mode_stex.startState()
-                        };
-                    }
-
-                    if (state.tmp) {
-                        if (stream.peek() == '`') {
-                            change(state, to_normal, context(rx_role_pre, 4));
-                            state.tmp = undefined;
-                            break;
-                        }
-
-                        token = state.tmp.mode.token(stream, state.tmp.local);
-                        break;
-                    }
-
-                    change(state, to_normal, context(rx_role_pre, 4));
-                    assert(stream.match(rx_TEXT2));
-                    token = 'string';
-                    break;
-                case 4:
-                    change(state, to_normal, context(rx_role_pre, 5));
-                    assert(stream.match(/^`/));
-                    token = 'meta';
-                    break;
-                case 5:
-                    change(state, to_normal, context(rx_role_pre, 6));
-                    assert(stream.match(rx_TAIL));
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (phase(state) == rx_role_suf ||
-            stream.match(rx_role_suf, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_normal, context(rx_role_suf, 1));
-                    assert(stream.match(/^`/));
-                    token = 'meta';
-                    break;
-                case 1:
-                    change(state, to_normal, context(rx_role_suf, 2));
-                    assert(stream.match(rx_TEXT2));
-                    token = 'string';
-                    break;
-                case 2:
-                    change(state, to_normal, context(rx_role_suf, 3));
-                    assert(stream.match(/^`:/));
-                    token = 'meta';
-                    break;
-                case 3:
-                    change(state, to_normal, context(rx_role_suf, 4));
-                    assert(stream.match(rx_NAME));
-                    token = 'keyword';
-                    break;
-                case 4:
-                    change(state, to_normal, context(rx_role_suf, 5));
-                    assert(stream.match(/^:/));
-                    token = 'meta';
-                    break;
-                case 5:
-                    change(state, to_normal, context(rx_role_suf, 6));
-                    assert(stream.match(rx_TAIL));
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (phase(state) == rx_role || stream.match(rx_role, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_normal, context(rx_role, 1));
-                    assert(stream.match(/^:/));
-                    token = 'meta';
-                    break;
-                case 1:
-                    change(state, to_normal, context(rx_role, 2));
-                    assert(stream.match(rx_NAME));
-                    token = 'keyword';
-                    break;
-                case 2:
-                    change(state, to_normal, context(rx_role, 3));
-                    assert(stream.match(/^:/));
-                    token = 'meta';
-                    break;
-                case 3:
-                    change(state, to_normal, context(rx_role, 4));
-                    assert(stream.match(rx_TAIL));
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (phase(state) == rx_substitution_ref ||
-            stream.match(rx_substitution_ref, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_normal, context(rx_substitution_ref, 1));
-                    assert(stream.match(rx_substitution_text));
-                    token = 'variable-2';
-                    break;
-                case 1:
-                    change(state, to_normal, context(rx_substitution_ref, 2));
-                    if (stream.match(/^_?_?/)) token = 'link';
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (stream.match(rx_footnote_ref)) {
-            change(state, to_normal);
-            token = 'quote';
-        } else if (stream.match(rx_citation_ref)) {
-            change(state, to_normal);
-            token = 'quote';
-        } else if (stream.match(rx_link_ref1)) {
-            change(state, to_normal);
-            if (!stream.peek() || stream.peek().match(/^\W$/)) {
-                token = 'link';
-            }
-        } else if (phase(state) == rx_link_ref2 ||
-            stream.match(rx_link_ref2, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    if (!stream.peek() || stream.peek().match(/^\W$/)) {
-                        change(state, to_normal, context(rx_link_ref2, 1));
-                    } else {
-                        stream.match(rx_link_ref2);
-                    }
-                    break;
-                case 1:
-                    change(state, to_normal, context(rx_link_ref2, 2));
-                    assert(stream.match(/^`/));
-                    token = 'link';
-                    break;
-                case 2:
-                    change(state, to_normal, context(rx_link_ref2, 3));
-                    assert(stream.match(rx_TEXT2));
-                    break;
-                case 3:
-                    change(state, to_normal, context(rx_link_ref2, 4));
-                    assert(stream.match(/^`_/));
-                    token = 'link';
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (stream.match(rx_verbatim)) {
-            change(state, to_verbatim);
-        }
+CodeMirror.defineMode('rst-base', function (config) {
 
-        else {
-            if (stream.next()) change(state, to_normal);
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  function format(string) {
+    var args = Array.prototype.slice.call(arguments, 1);
+    return string.replace(/{(\d+)}/g, function (match, n) {
+      return typeof args[n] != 'undefined' ? args[n] : match;
+    });
+  }
+
+  function AssertException(message) {
+    this.message = message;
+  }
+
+  AssertException.prototype.toString = function () {
+    return 'AssertException: ' + this.message;
+  };
+
+  function assert(expression, message) {
+    if (!expression) throw new AssertException(message);
+    return expression;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  var mode_python = CodeMirror.getMode(config, 'python');
+  var mode_stex = CodeMirror.getMode(config, 'stex');
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  var SEPA = "\\s+";
+  var TAIL = "(?:\\s*|\\W|$)",
+  rx_TAIL = new RegExp(format('^{0}', TAIL));
+
+  var NAME =
+    "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)",
+  rx_NAME = new RegExp(format('^{0}', NAME));
+  var NAME_WWS =
+    "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)";
+  var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS);
+
+  var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)";
+  var TEXT2 = "(?:[^\\`]+)",
+  rx_TEXT2 = new RegExp(format('^{0}', TEXT2));
+
+  var rx_section = new RegExp(
+    "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$");
+  var rx_explicit = new RegExp(
+    format('^\\.\\.{0}', SEPA));
+  var rx_link = new RegExp(
+    format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL));
+  var rx_directive = new RegExp(
+    format('^{0}::{1}', REF_NAME, TAIL));
+  var rx_substitution = new RegExp(
+    format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL));
+  var rx_footnote = new RegExp(
+    format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL));
+  var rx_citation = new RegExp(
+    format('^\\[{0}\\]{1}', REF_NAME, TAIL));
+
+  var rx_substitution_ref = new RegExp(
+    format('^\\|{0}\\|', TEXT1));
+  var rx_footnote_ref = new RegExp(
+    format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME));
+  var rx_citation_ref = new RegExp(
+    format('^\\[{0}\\]_', REF_NAME));
+  var rx_link_ref1 = new RegExp(
+    format('^{0}__?', REF_NAME));
+  var rx_link_ref2 = new RegExp(
+    format('^`{0}`_', TEXT2));
+
+  var rx_role_pre = new RegExp(
+    format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL));
+  var rx_role_suf = new RegExp(
+    format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL));
+  var rx_role = new RegExp(
+    format('^:{0}:{1}', NAME, TAIL));
+
+  var rx_directive_name = new RegExp(format('^{0}', REF_NAME));
+  var rx_directive_tail = new RegExp(format('^::{0}', TAIL));
+  var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1));
+  var rx_substitution_sepa = new RegExp(format('^{0}', SEPA));
+  var rx_substitution_name = new RegExp(format('^{0}', REF_NAME));
+  var rx_substitution_tail = new RegExp(format('^::{0}', TAIL));
+  var rx_link_head = new RegExp("^_");
+  var rx_link_name = new RegExp(format('^{0}|_', REF_NAME));
+  var rx_link_tail = new RegExp(format('^:{0}', TAIL));
+
+  var rx_verbatim = new RegExp('^::\\s*$');
+  var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s');
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  function to_normal(stream, state) {
+    var token = null;
+
+    if (stream.sol() && stream.match(rx_examples, false)) {
+      change(state, to_mode, {
+        mode: mode_python, local: CodeMirror.startState(mode_python)
+      });
+    } else if (stream.sol() && stream.match(rx_explicit)) {
+      change(state, to_explicit);
+      token = 'meta';
+    } else if (stream.sol() && stream.match(rx_section)) {
+      change(state, to_normal);
+      token = 'header';
+    } else if (phase(state) == rx_role_pre ||
+               stream.match(rx_role_pre, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_normal, context(rx_role_pre, 1));
+        assert(stream.match(/^:/));
+        token = 'meta';
+        break;
+      case 1:
+        change(state, to_normal, context(rx_role_pre, 2));
+        assert(stream.match(rx_NAME));
+        token = 'keyword';
+
+        if (stream.current().match(/^(?:math|latex)/)) {
+          state.tmp_stex = true;
+        }
+        break;
+      case 2:
+        change(state, to_normal, context(rx_role_pre, 3));
+        assert(stream.match(/^:`/));
+        token = 'meta';
+        break;
+      case 3:
+        if (state.tmp_stex) {
+          state.tmp_stex = undefined; state.tmp = {
+            mode: mode_stex, local: CodeMirror.startState(mode_stex)
+          };
         }
 
-        return token;
-    }
+        if (state.tmp) {
+          if (stream.peek() == '`') {
+            change(state, to_normal, context(rx_role_pre, 4));
+            state.tmp = undefined;
+            break;
+          }
 
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    function to_explicit(stream, state) {
-        var token = null;
-
-        if (phase(state) == rx_substitution ||
-            stream.match(rx_substitution, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_explicit, context(rx_substitution, 1));
-                    assert(stream.match(rx_substitution_text));
-                    token = 'variable-2';
-                    break;
-                case 1:
-                    change(state, to_explicit, context(rx_substitution, 2));
-                    assert(stream.match(rx_substitution_sepa));
-                    break;
-                case 2:
-                    change(state, to_explicit, context(rx_substitution, 3));
-                    assert(stream.match(rx_substitution_name));
-                    token = 'keyword';
-                    break;
-                case 3:
-                    change(state, to_explicit, context(rx_substitution, 4));
-                    assert(stream.match(rx_substitution_tail));
-                    token = 'meta';
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (phase(state) == rx_directive ||
-            stream.match(rx_directive, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_explicit, context(rx_directive, 1));
-                    assert(stream.match(rx_directive_name));
-                    token = 'keyword';
-
-                    if (stream.current().match(/^(?:math|latex)/))
-                        state.tmp_stex = true;
-                    else if (stream.current().match(/^python/))
-                        state.tmp_py = true;
-                    break;
-                case 1:
-                    change(state, to_explicit, context(rx_directive, 2));
-                    assert(stream.match(rx_directive_tail));
-                    token = 'meta';
-
-                    if (stream.match(/^latex\s*$/) || state.tmp_stex) {
-                        state.tmp_stex = undefined; change(state, to_mode, {
-                            mode: mode_stex, local: mode_stex.startState()
-                        });
-                    }
-                    break;
-                case 2:
-                    change(state, to_explicit, context(rx_directive, 3));
-                    if (stream.match(/^python\s*$/) || state.tmp_py) {
-                        state.tmp_py = undefined; change(state, to_mode, {
-                            mode: mode_python, local: mode_python.startState()
-                        });
-                    }
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (phase(state) == rx_link || stream.match(rx_link, false)) {
-
-            switch (stage(state)) {
-                case 0:
-                    change(state, to_explicit, context(rx_link, 1));
-                    assert(stream.match(rx_link_head));
-                    assert(stream.match(rx_link_name));
-                    token = 'link';
-                    break;
-                case 1:
-                    change(state, to_explicit, context(rx_link, 2));
-                    assert(stream.match(rx_link_tail));
-                    token = 'meta';
-                    break;
-                default:
-                    change(state, to_normal);
-                    assert(stream.current() == '');
-            }
-        } else if (stream.match(rx_footnote)) {
-            change(state, to_normal);
-            token = 'quote';
-        } else if (stream.match(rx_citation)) {
-            change(state, to_normal);
-            token = 'quote';
+          token = state.tmp.mode.token(stream, state.tmp.local);
+          break;
         }
 
-        else {
-            stream.eatSpace();
-            if (stream.eol()) {
-                change(state, to_normal);
-            } else {
-                stream.skipToEnd();
-                change(state, to_comment);
-                token = 'comment';
-            }
+        change(state, to_normal, context(rx_role_pre, 4));
+        assert(stream.match(rx_TEXT2));
+        token = 'string';
+        break;
+      case 4:
+        change(state, to_normal, context(rx_role_pre, 5));
+        assert(stream.match(/^`/));
+        token = 'meta';
+        break;
+      case 5:
+        change(state, to_normal, context(rx_role_pre, 6));
+        assert(stream.match(rx_TAIL));
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (phase(state) == rx_role_suf ||
+               stream.match(rx_role_suf, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_normal, context(rx_role_suf, 1));
+        assert(stream.match(/^`/));
+        token = 'meta';
+        break;
+      case 1:
+        change(state, to_normal, context(rx_role_suf, 2));
+        assert(stream.match(rx_TEXT2));
+        token = 'string';
+        break;
+      case 2:
+        change(state, to_normal, context(rx_role_suf, 3));
+        assert(stream.match(/^`:/));
+        token = 'meta';
+        break;
+      case 3:
+        change(state, to_normal, context(rx_role_suf, 4));
+        assert(stream.match(rx_NAME));
+        token = 'keyword';
+        break;
+      case 4:
+        change(state, to_normal, context(rx_role_suf, 5));
+        assert(stream.match(/^:/));
+        token = 'meta';
+        break;
+      case 5:
+        change(state, to_normal, context(rx_role_suf, 6));
+        assert(stream.match(rx_TAIL));
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (phase(state) == rx_role || stream.match(rx_role, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_normal, context(rx_role, 1));
+        assert(stream.match(/^:/));
+        token = 'meta';
+        break;
+      case 1:
+        change(state, to_normal, context(rx_role, 2));
+        assert(stream.match(rx_NAME));
+        token = 'keyword';
+        break;
+      case 2:
+        change(state, to_normal, context(rx_role, 3));
+        assert(stream.match(/^:/));
+        token = 'meta';
+        break;
+      case 3:
+        change(state, to_normal, context(rx_role, 4));
+        assert(stream.match(rx_TAIL));
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (phase(state) == rx_substitution_ref ||
+               stream.match(rx_substitution_ref, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_normal, context(rx_substitution_ref, 1));
+        assert(stream.match(rx_substitution_text));
+        token = 'variable-2';
+        break;
+      case 1:
+        change(state, to_normal, context(rx_substitution_ref, 2));
+        if (stream.match(/^_?_?/)) token = 'link';
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (stream.match(rx_footnote_ref)) {
+      change(state, to_normal);
+      token = 'quote';
+    } else if (stream.match(rx_citation_ref)) {
+      change(state, to_normal);
+      token = 'quote';
+    } else if (stream.match(rx_link_ref1)) {
+      change(state, to_normal);
+      if (!stream.peek() || stream.peek().match(/^\W$/)) {
+        token = 'link';
+      }
+    } else if (phase(state) == rx_link_ref2 ||
+               stream.match(rx_link_ref2, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        if (!stream.peek() || stream.peek().match(/^\W$/)) {
+          change(state, to_normal, context(rx_link_ref2, 1));
+        } else {
+          stream.match(rx_link_ref2);
         }
-
-        return token;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    function to_comment(stream, state) {
-        return as_block(stream, state, 'comment');
+        break;
+      case 1:
+        change(state, to_normal, context(rx_link_ref2, 2));
+        assert(stream.match(/^`/));
+        token = 'link';
+        break;
+      case 2:
+        change(state, to_normal, context(rx_link_ref2, 3));
+        assert(stream.match(rx_TEXT2));
+        break;
+      case 3:
+        change(state, to_normal, context(rx_link_ref2, 4));
+        assert(stream.match(/^`_/));
+        token = 'link';
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (stream.match(rx_verbatim)) {
+      change(state, to_verbatim);
     }
 
-    function to_verbatim(stream, state) {
-        return as_block(stream, state, 'meta');
+    else {
+      if (stream.next()) change(state, to_normal);
     }
 
-    function as_block(stream, state, token) {
-        if (stream.eol() || stream.eatSpace()) {
-            stream.skipToEnd();
-            return token;
-        } else {
-            change(state, to_normal);
-            return null;
+    return token;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  function to_explicit(stream, state) {
+    var token = null;
+
+    if (phase(state) == rx_substitution ||
+        stream.match(rx_substitution, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_explicit, context(rx_substitution, 1));
+        assert(stream.match(rx_substitution_text));
+        token = 'variable-2';
+        break;
+      case 1:
+        change(state, to_explicit, context(rx_substitution, 2));
+        assert(stream.match(rx_substitution_sepa));
+        break;
+      case 2:
+        change(state, to_explicit, context(rx_substitution, 3));
+        assert(stream.match(rx_substitution_name));
+        token = 'keyword';
+        break;
+      case 3:
+        change(state, to_explicit, context(rx_substitution, 4));
+        assert(stream.match(rx_substitution_tail));
+        token = 'meta';
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (phase(state) == rx_directive ||
+               stream.match(rx_directive, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_explicit, context(rx_directive, 1));
+        assert(stream.match(rx_directive_name));
+        token = 'keyword';
+
+        if (stream.current().match(/^(?:math|latex)/))
+          state.tmp_stex = true;
+        else if (stream.current().match(/^python/))
+          state.tmp_py = true;
+        break;
+      case 1:
+        change(state, to_explicit, context(rx_directive, 2));
+        assert(stream.match(rx_directive_tail));
+        token = 'meta';
+
+        if (stream.match(/^latex\s*$/) || state.tmp_stex) {
+          state.tmp_stex = undefined; change(state, to_mode, {
+            mode: mode_stex, local: CodeMirror.startState(mode_stex)
+          });
         }
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    function to_mode(stream, state) {
-
-        if (state.ctx.mode && state.ctx.local) {
-
-            if (stream.sol()) {
-                if (!stream.eatSpace()) change(state, to_normal);
-                return null;
-            }
-
-            return state.ctx.mode.token(stream, state.ctx.local);
+        break;
+      case 2:
+        change(state, to_explicit, context(rx_directive, 3));
+        if (stream.match(/^python\s*$/) || state.tmp_py) {
+          state.tmp_py = undefined; change(state, to_mode, {
+            mode: mode_python, local: CodeMirror.startState(mode_python)
+          });
         }
-
+        break;
+      default:
         change(state, to_normal);
-        return null;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
-
-    function context(phase, stage, mode, local) {
-        return {phase: phase, stage: stage, mode: mode, local: local};
-    }
-
-    function change(state, tok, ctx) {
-        state.tok = tok;
-        state.ctx = ctx || {};
+        assert(stream.current() == '');
+      }
+    } else if (phase(state) == rx_link || stream.match(rx_link, false)) {
+
+      switch (stage(state)) {
+      case 0:
+        change(state, to_explicit, context(rx_link, 1));
+        assert(stream.match(rx_link_head));
+        assert(stream.match(rx_link_name));
+        token = 'link';
+        break;
+      case 1:
+        change(state, to_explicit, context(rx_link, 2));
+        assert(stream.match(rx_link_tail));
+        token = 'meta';
+        break;
+      default:
+        change(state, to_normal);
+        assert(stream.current() == '');
+      }
+    } else if (stream.match(rx_footnote)) {
+      change(state, to_normal);
+      token = 'quote';
+    } else if (stream.match(rx_citation)) {
+      change(state, to_normal);
+      token = 'quote';
     }
 
-    function stage(state) {
-        return state.ctx.stage || 0;
+    else {
+      stream.eatSpace();
+      if (stream.eol()) {
+        change(state, to_normal);
+      } else {
+        stream.skipToEnd();
+        change(state, to_comment);
+        token = 'comment';
+      }
     }
 
-    function phase(state) {
-        return state.ctx.phase;
-    }
+    return token;
+  }
 
-    ///////////////////////////////////////////////////////////////////////////
-    ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
 
-    return {
-        startState: function () {
-            return {tok: to_normal, ctx: context(undefined, 0)};
-        },
+  function to_comment(stream, state) {
+    return as_block(stream, state, 'comment');
+  }
 
-        copyState: function (state) {
-            return {tok: state.tok, ctx: state.ctx};
-        },
+  function to_verbatim(stream, state) {
+    return as_block(stream, state, 'meta');
+  }
 
-        innerMode: function (state) {
-            return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode}
-                 : state.ctx ? {state: state.ctx.local, mode: state.ctx.mode}
-                             : null;
-        },
+  function as_block(stream, state, token) {
+    if (stream.eol() || stream.eatSpace()) {
+      stream.skipToEnd();
+      return token;
+    } else {
+      change(state, to_normal);
+      return null;
+    }
+  }
 
-        token: function (stream, state) {
-            return state.tok(stream, state);
-        }
-    };
-}, 'python', 'stex');
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
 
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
+  function to_mode(stream, state) {
 
-CodeMirror.defineMode('rst', function (config, options) {
+    if (state.ctx.mode && state.ctx.local) {
 
-    var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/;
-    var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/;
-    var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/;
-
-    var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/;
-    var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/;
-    var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/;
-
-    var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://";
-    var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})";
-    var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*";
-    var rx_uri = new RegExp("^" +
-        rx_uri_protocol + rx_uri_domain + rx_uri_path
-    );
-
-    var overlay = {
-        token: function (stream) {
-
-            if (stream.match(rx_strong) && stream.match (/\W+|$/, false))
-                return 'strong';
-            if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false))
-                return 'em';
-            if (stream.match(rx_literal) && stream.match (/\W+|$/, false))
-                return 'string-2';
-            if (stream.match(rx_number))
-                return 'number';
-            if (stream.match(rx_positive))
-                return 'positive';
-            if (stream.match(rx_negative))
-                return 'negative';
-            if (stream.match(rx_uri))
-                return 'link';
-
-            while (stream.next() != null) {
-                if (stream.match(rx_strong, false)) break;
-                if (stream.match(rx_emphasis, false)) break;
-                if (stream.match(rx_literal, false)) break;
-                if (stream.match(rx_number, false)) break;
-                if (stream.match(rx_positive, false)) break;
-                if (stream.match(rx_negative, false)) break;
-                if (stream.match(rx_uri, false)) break;
-            }
-
-            return null;
-        }
-    };
+      if (stream.sol()) {
+        if (!stream.eatSpace()) change(state, to_normal);
+        return null;
+      }
 
-    var mode = CodeMirror.getMode(
-        config, options.backdrop || 'rst-base'
-    );
+      return state.ctx.mode.token(stream, state.ctx.local);
+    }
 
-    return CodeMirror.overlayMode(mode, overlay, true); // combine
+    change(state, to_normal);
+    return null;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  function context(phase, stage, mode, local) {
+    return {phase: phase, stage: stage, mode: mode, local: local};
+  }
+
+  function change(state, tok, ctx) {
+    state.tok = tok;
+    state.ctx = ctx || {};
+  }
+
+  function stage(state) {
+    return state.ctx.stage || 0;
+  }
+
+  function phase(state) {
+    return state.ctx.phase;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////
+
+  return {
+    startState: function () {
+      return {tok: to_normal, ctx: context(undefined, 0)};
+    },
+
+    copyState: function (state) {
+      var ctx = state.ctx, tmp = state.tmp;
+      if (ctx.local)
+        ctx = {mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local)};
+      if (tmp)
+        tmp = {mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local)};
+      return {tok: state.tok, ctx: ctx, tmp: tmp};
+    },
+
+    innerMode: function (state) {
+      return state.tmp      ? {state: state.tmp.local, mode: state.tmp.mode}
+      : state.ctx.mode ? {state: state.ctx.local, mode: state.ctx.mode}
+      : null;
+    },
+
+    token: function (stream, state) {
+      return state.tok(stream, state);
+    }
+  };
 }, 'python', 'stex');
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/test/vim_test.js b/test/vim_test.js
index ee02dc09417ac60078b52c481bc86e051a924f69..8008f499fb8ce5347e21fda31ea56f78d5e626b4 100644
--- a/test/vim_test.js
+++ b/test/vim_test.js
@@ -997,6 +997,40 @@ testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t');
 testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo');
 testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t');
 testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo');
+// Deleting text objects
+//    Open and close on same line
+testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz');
+testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz');
+testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo  baz');
+testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo  baz');
+
+testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz');
+testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz');
+testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo  baz');
+testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo  baz');
+
+testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz');
+testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz');
+testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo  baz');
+testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo  baz');
+
+//  Open and close on different lines, equally indented
+testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b');
+testEdit('di}_middle_spc', 'a{\n\tbar\n}b', /r/, 'di}', 'a{}b');
+testEdit('da{_middle_spc', 'a{\n\tbar\n}b', /r/, 'da{', 'ab');
+testEdit('da}_middle_spc', 'a{\n\tbar\n}b', /r/, 'da}', 'ab');
+
+// open and close on diff lines, open indented less than close
+testEdit('di{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di{', 'a{}b');
+testEdit('di}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di}', 'a{}b');
+testEdit('da{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da{', 'ab');
+testEdit('da}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da}', 'ab');
+
+// open and close on diff lines, open indented more than close
+testEdit('di[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di[', 'a\t[]b');
+testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b');
+testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb');
+testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb');
 
 // Operator-motion tests
 testVim('D', function(cm, vim, helpers) {