<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[167294] trunk/Source/WebInspectorUI</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/167294">167294</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2014-04-14 21:31:41 -0700 (Mon, 14 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Update to CodeMirror 4.0.
https://bugs.webkit.org/show_bug.cgi?id=130019

The CodeMirror 4.0 library adds multiple selections and the ability to
undo/redo selections. Selections are made by holding the Command (Cmd) key
and clicking into an editor, or by holding Option (Alt) and making a block
selection followed by entering a character or moving the cursor.

Patch by Jono Wells &lt;jonowells@apple.com&gt; on 2014-04-14
Reviewed by Timothy Hatcher.

* Scripts/update-codemirror-resources.rb:
* Scripts/update-pretty-printer.rb:
Update scripts to reflect updated locations for CodeMirror files.
No longer copies LESS mode file as this has been integrated into CSS mode
in CodeMirror 4.0.

* Tools/PrettyPrinting/codemirror.css:
* Tools/PrettyPrinting/codemirror.js:
* Tools/PrettyPrinting/css.js:
* Tools/PrettyPrinting/javascript.js:
* UserInterface/External/CodeMirror/clojure.js:
* UserInterface/External/CodeMirror/closebrackets.js:
* UserInterface/External/CodeMirror/codemirror.css:
* UserInterface/External/CodeMirror/codemirror.js:
* UserInterface/External/CodeMirror/coffeescript.js:
* UserInterface/External/CodeMirror/comment.js:
* UserInterface/External/CodeMirror/css.js:
* UserInterface/External/CodeMirror/htmlmixed.js:
* UserInterface/External/CodeMirror/javascript.js:
* UserInterface/External/CodeMirror/livescript.js:
* UserInterface/External/CodeMirror/matchbrackets.js:
* UserInterface/External/CodeMirror/overlay.js:
* UserInterface/External/CodeMirror/placeholder.js:
* UserInterface/External/CodeMirror/runmode.js:
* UserInterface/External/CodeMirror/sass.js:
* UserInterface/External/CodeMirror/searchcursor.js:
* UserInterface/External/CodeMirror/sql.js:
* UserInterface/External/CodeMirror/xml.js:
Update to CodeMirror 4.0.

* UserInterface/Main.html: Remove less.js which is now part of css.js.

* UserInterface/Views/CSSStyleDeclarationTextEditor.css:
* UserInterface/Views/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor.prototype.):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createColorSwatches):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._updateJumpToSymbolTrackingMode):
* UserInterface/Views/SyntaxHighlightingDefaultTheme.css:
Update styles to match CodeMirror changes. Update CSSStyleDeclarationTextEditor.js
to match CodeMirror API updates (doc.removeLine() has been removed, so
replaceRange() is used instead). Also the tokenTrackingController is now enabled in the
CSSStyleDeclarationTextEditor and SourceCodeTextEditor when the Option (Alt) key
is pressed instead of the Command (Cmd) key so as not to conflict with multiple
cursor placement in the CodeMirror update.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIScriptsupdatecodemirrorresourcesrb">trunk/Source/WebInspectorUI/Scripts/update-codemirror-resources.rb</a></li>
<li><a href="#trunkSourceWebInspectorUIScriptsupdateprettyprinterrb">trunk/Source/WebInspectorUI/Scripts/update-pretty-printer.rb</a></li>
<li><a href="#trunkSourceWebInspectorUIToolsPrettyPrintingcodemirrorcss">trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.css</a></li>
<li><a href="#trunkSourceWebInspectorUIToolsPrettyPrintingcodemirrorjs">trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.js</a></li>
<li><a href="#trunkSourceWebInspectorUIToolsPrettyPrintingcssjs">trunk/Source/WebInspectorUI/Tools/PrettyPrinting/css.js</a></li>
<li><a href="#trunkSourceWebInspectorUIToolsPrettyPrintingjavascriptjs">trunk/Source/WebInspectorUI/Tools/PrettyPrinting/javascript.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorclojurejs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/clojure.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorclosebracketsjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/closebrackets.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcodemirrorcss">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcodemirrorjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcoffeescriptjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/coffeescript.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcommentjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/comment.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcssjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorhtmlmixedjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/htmlmixed.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorjavascriptjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/javascript.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorlivescriptjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/livescript.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrormatchbracketsjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/matchbrackets.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirroroverlayjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/overlay.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorplaceholderjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/placeholder.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorrunmodejs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/runmode.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsassjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sass.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsearchcursorjs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/searchcursor.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsqljs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sql.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorxmljs">trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/xml.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceMainhtml">trunk/Source/WebInspectorUI/UserInterface/Main.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorcss">trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs">trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSyntaxHighlightingDefaultThemecss">trunk/Source/WebInspectorUI/UserInterface/Views/SyntaxHighlightingDefaultTheme.css</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/ChangeLog        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,3 +1,60 @@
</span><ins>+2014-04-14  Jono Wells  &lt;jonowells@apple.com&gt;
+
+        Update to CodeMirror 4.0.
+        https://bugs.webkit.org/show_bug.cgi?id=130019
+
+        The CodeMirror 4.0 library adds multiple selections and the ability to
+        undo/redo selections. Selections are made by holding the Command (Cmd) key
+        and clicking into an editor, or by holding Option (Alt) and making a block
+        selection followed by entering a character or moving the cursor.
+
+        Reviewed by Timothy Hatcher.
+
+        * Scripts/update-codemirror-resources.rb:
+        * Scripts/update-pretty-printer.rb:
+        Update scripts to reflect updated locations for CodeMirror files.
+        No longer copies LESS mode file as this has been integrated into CSS mode
+        in CodeMirror 4.0.
+
+        * Tools/PrettyPrinting/codemirror.css:
+        * Tools/PrettyPrinting/codemirror.js:
+        * Tools/PrettyPrinting/css.js:
+        * Tools/PrettyPrinting/javascript.js:
+        * UserInterface/External/CodeMirror/clojure.js:
+        * UserInterface/External/CodeMirror/closebrackets.js:
+        * UserInterface/External/CodeMirror/codemirror.css:
+        * UserInterface/External/CodeMirror/codemirror.js:
+        * UserInterface/External/CodeMirror/coffeescript.js:
+        * UserInterface/External/CodeMirror/comment.js:
+        * UserInterface/External/CodeMirror/css.js:
+        * UserInterface/External/CodeMirror/htmlmixed.js:
+        * UserInterface/External/CodeMirror/javascript.js:
+        * UserInterface/External/CodeMirror/livescript.js:
+        * UserInterface/External/CodeMirror/matchbrackets.js:
+        * UserInterface/External/CodeMirror/overlay.js:
+        * UserInterface/External/CodeMirror/placeholder.js:
+        * UserInterface/External/CodeMirror/runmode.js:
+        * UserInterface/External/CodeMirror/sass.js:
+        * UserInterface/External/CodeMirror/searchcursor.js:
+        * UserInterface/External/CodeMirror/sql.js:
+        * UserInterface/External/CodeMirror/xml.js:
+        Update to CodeMirror 4.0.
+
+        * UserInterface/Main.html: Remove less.js which is now part of css.js.
+
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.css:
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype.):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createColorSwatches):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._updateJumpToSymbolTrackingMode):
+        * UserInterface/Views/SyntaxHighlightingDefaultTheme.css:
+        Update styles to match CodeMirror changes. Update CSSStyleDeclarationTextEditor.js
+        to match CodeMirror API updates (doc.removeLine() has been removed, so
+        replaceRange() is used instead). Also the tokenTrackingController is now enabled in the
+        CSSStyleDeclarationTextEditor and SourceCodeTextEditor when the Option (Alt) key
+        is pressed instead of the Command (Cmd) key so as not to conflict with multiple
+        cursor placement in the CodeMirror update.
+
</ins><span class="cx"> 2014-04-08  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Windows] Generate Optimized WebInspectorUI in Release Build
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIScriptsupdatecodemirrorresourcesrb"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Scripts/update-codemirror-resources.rb (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Scripts/update-codemirror-resources.rb        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Scripts/update-codemirror-resources.rb        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -43,7 +43,6 @@
</span><span class="cx">   mode/css/css.js
</span><span class="cx">   mode/htmlmixed/htmlmixed.js
</span><span class="cx">   mode/javascript/javascript.js
</span><del>-  mode/less/less.js
</del><span class="cx">   mode/livescript/livescript.js
</span><span class="cx">   mode/sass/sass.js
</span><span class="cx">   mode/sql/sql.js
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIScriptsupdateprettyprinterrb"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Scripts/update-pretty-printer.rb (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Scripts/update-pretty-printer.rb        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Scripts/update-pretty-printer.rb        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -23,21 +23,21 @@
</span><span class="cx"> 
</span><span class="cx"> # Copy the formatter and CodeMirror files from UserInterface to Tools.
</span><span class="cx"> USER_INTERFACE_TO_TOOLS_MAP = {
</span><del>-  &quot;UserInterface/Controllers/CodeMirrorFormatters.js&quot;    =&gt; &quot;Tools/PrettyPrinting/CodeMirrorFormatters.js&quot;,
</del><ins>+  &quot;UserInterface/Views/CodeMirrorFormatters.js&quot;          =&gt; &quot;Tools/PrettyPrinting/CodeMirrorFormatters.js&quot;,
</ins><span class="cx">   &quot;UserInterface/Controllers/Formatter.js&quot;               =&gt; &quot;Tools/PrettyPrinting/Formatter.js&quot;,
</span><span class="cx">   &quot;UserInterface/Controllers/FormatterContentBuilder.js&quot; =&gt; &quot;Tools/PrettyPrinting/FormatterContentBuilder.js&quot;,
</span><span class="cx"> 
</span><del>-  &quot;UserInterface/External/CodeMirror/codemirror.css&quot; =&gt; &quot;Tools/PrettyPrinting/codemirror.css&quot;,
-  &quot;UserInterface/External/CodeMirror/codemirror.js&quot;  =&gt; &quot;Tools/PrettyPrinting/codemirror.js&quot;,
-  &quot;UserInterface/External/CodeMirror/javascript.js&quot;  =&gt; &quot;Tools/PrettyPrinting/javascript.js&quot;,
-  &quot;UserInterface/External/CodeMirror/css.js&quot;         =&gt; &quot;Tools/PrettyPrinting/css.js&quot;,
</del><ins>+  &quot;UserInterface/External/CodeMirror/codemirror.css&quot;     =&gt; &quot;Tools/PrettyPrinting/codemirror.css&quot;,
+  &quot;UserInterface/External/CodeMirror/codemirror.js&quot;      =&gt; &quot;Tools/PrettyPrinting/codemirror.js&quot;,
+  &quot;UserInterface/External/CodeMirror/javascript.js&quot;      =&gt; &quot;Tools/PrettyPrinting/javascript.js&quot;,
+  &quot;UserInterface/External/CodeMirror/css.js&quot;             =&gt; &quot;Tools/PrettyPrinting/css.js&quot;,
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> # Copy only the formatter files from Tools to UserInterface.
</span><span class="cx"> TOOLS_TO_USER_INTERFACE_MAP = {
</span><del>-  &quot;Tools/PrettyPrinting/CodeMirrorFormatters.js&quot;    =&gt; &quot;UserInterface/Controllers/CodeMirrorFormatters.js&quot;,
-  &quot;Tools/PrettyPrinting/Formatter.js&quot;               =&gt; &quot;UserInterface/Controllers/Formatter.js&quot;,
-  &quot;Tools/PrettyPrinting/FormatterContentBuilder.js&quot; =&gt; &quot;UserInterface/Controllers/FormatterContentBuilder.js&quot;
</del><ins>+  &quot;Tools/PrettyPrinting/CodeMirrorFormatters.js&quot;         =&gt; &quot;UserInterface/Views/CodeMirrorFormatters.js&quot;,
+  &quot;Tools/PrettyPrinting/Formatter.js&quot;                    =&gt; &quot;UserInterface/Controllers/Formatter.js&quot;,
+  &quot;Tools/PrettyPrinting/FormatterContentBuilder.js&quot;      =&gt; &quot;UserInterface/Controllers/FormatterContentBuilder.js&quot;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> web_inspector_path = File.expand_path File.join(File.dirname(__FILE__), &quot;..&quot;)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIToolsPrettyPrintingcodemirrorcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.css (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.css        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.css        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -36,13 +36,14 @@
</span><span class="cx">   min-width: 20px;
</span><span class="cx">   text-align: right;
</span><span class="cx">   color: #999;
</span><ins>+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /* CURSOR */
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror div.CodeMirror-cursor {
</span><span class="cx">   border-left: 1px solid black;
</span><del>-  z-index: 3;
</del><span class="cx"> }
</span><span class="cx"> /* Shown when moving in bi-directional text */
</span><span class="cx"> .CodeMirror div.CodeMirror-secondarycursor {
</span><span class="lines">@@ -52,24 +53,29 @@
</span><span class="cx">   width: auto;
</span><span class="cx">   border: 0;
</span><span class="cx">   background: #7e7;
</span><del>-  z-index: 1;
</del><span class="cx"> }
</span><span class="cx"> /* Can style cursor different in overwrite (non-insert) mode */
</span><del>-.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
</del><ins>+div.CodeMirror-overwrite div.CodeMirror-cursor {}
</ins><span class="cx"> 
</span><span class="cx"> .cm-tab { display: inline-block; }
</span><span class="cx"> 
</span><ins>+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  position: absolute;
+}
+
</ins><span class="cx"> /* DEFAULT THEME */
</span><span class="cx"> 
</span><span class="cx"> .cm-s-default .cm-keyword {color: #708;}
</span><span class="cx"> .cm-s-default .cm-atom {color: #219;}
</span><span class="cx"> .cm-s-default .cm-number {color: #164;}
</span><span class="cx"> .cm-s-default .cm-def {color: #00f;}
</span><del>-.cm-s-default .cm-variable {color: black;}
</del><ins>+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
</ins><span class="cx"> .cm-s-default .cm-variable-2 {color: #05a;}
</span><span class="cx"> .cm-s-default .cm-variable-3 {color: #085;}
</span><del>-.cm-s-default .cm-property {color: black;}
-.cm-s-default .cm-operator {color: black;}
</del><span class="cx"> .cm-s-default .cm-comment {color: #a50;}
</span><span class="cx"> .cm-s-default .cm-string {color: #a11;}
</span><span class="cx"> .cm-s-default .cm-string-2 {color: #f50;}
</span><span class="lines">@@ -114,7 +120,7 @@
</span><span class="cx">   /* 30px is the magic margin used to hide the element's real scrollbars */
</span><span class="cx">   /* See overflow: hidden in .CodeMirror */
</span><span class="cx">   margin-bottom: -30px; margin-right: -30px;
</span><del>-  padding-bottom: 30px; padding-right: 30px;
</del><ins>+  padding-bottom: 30px;
</ins><span class="cx">   height: 100%;
</span><span class="cx">   outline: none; /* Prevent dragging from highlighting the element */
</span><span class="cx">   position: relative;
</span><span class="lines">@@ -123,6 +129,9 @@
</span><span class="cx"> }
</span><span class="cx"> .CodeMirror-sizer {
</span><span class="cx">   position: relative;
</span><ins>+  border-right: 30px solid transparent;
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /* The fake, visible scrollbars. Used to force redraw during scrolling
</span><span class="lines">@@ -197,16 +206,7 @@
</span><span class="cx">   white-space: pre-wrap;
</span><span class="cx">   word-break: normal;
</span><span class="cx"> }
</span><del>-.CodeMirror-code pre {
-  border-right: 30px solid transparent;
-  width: -webkit-fit-content;
-  width: -moz-fit-content;
-  width: fit-content;
-}
-.CodeMirror-wrap .CodeMirror-code pre {
-  border-right: none;
-  width: auto;
-}
</del><ins>+
</ins><span class="cx"> .CodeMirror-linebackground {
</span><span class="cx">   position: absolute;
</span><span class="cx">   left: 0; right: 0; top: 0; bottom: 0;
</span><span class="lines">@@ -236,16 +236,22 @@
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror div.CodeMirror-cursor {
</span><span class="cx">   position: absolute;
</span><del>-  visibility: hidden;
</del><span class="cx">   border-right: none;
</span><span class="cx">   width: 0;
</span><span class="cx"> }
</span><del>-.CodeMirror-focused div.CodeMirror-cursor {
</del><ins>+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 1;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
</ins><span class="cx">   visibility: visible;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror-selected { background: #d9d9d9; }
</span><span class="cx"> .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
</span><ins>+.CodeMirror-crosshair { cursor: crosshair; }
</ins><span class="cx"> 
</span><span class="cx"> .cm-searching {
</span><span class="cx">   background: #ffa;
</span><span class="lines">@@ -255,9 +261,12 @@
</span><span class="cx"> /* IE7 hack to prevent it from returning funny offsetTops on the spans */
</span><span class="cx"> .CodeMirror span { *vertical-align: text-bottom; }
</span><span class="cx"> 
</span><ins>+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
</ins><span class="cx"> @media print {
</span><span class="cx">   /* Hide the cursor when printing */
</span><del>-  .CodeMirror div.CodeMirror-cursor {
</del><ins>+  .CodeMirror div.CodeMirror-cursors {
</ins><span class="cx">     visibility: hidden;
</span><span class="cx">   }
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIToolsPrettyPrintingcodemirrorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/codemirror.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,23 +1,36 @@
</span><del>-// CodeMirror is the only global var we claim
-window.CodeMirror = (function() {
</del><ins>+// This is CodeMirror (http://codemirror.net), a code editor
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
+
+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    module.exports = mod();
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    return define([], mod);
+  else // Plain browser env
+    this.CodeMirror = mod();
+})(function() {
</ins><span class="cx">   &quot;use strict&quot;;
</span><span class="cx"> 
</span><span class="cx">   // BROWSER SNIFFING
</span><span class="cx"> 
</span><del>-  // Crude, but necessary to handle a number of hard-to-feature-detect
-  // bugs and behavior differences.
</del><ins>+  // Kludges for bugs and behavior differences that can't be feature
+  // detected are enabled based on userAgent etc sniffing.
+
</ins><span class="cx">   var gecko = /gecko\/\d/i.test(navigator.userAgent);
</span><del>-  // IE11 currently doesn't count as 'ie', since it has almost none of
-  // the same bugs as earlier versions. Use ie_gt10 to handle
-  // incompatibilities in that version.
-  var ie = /MSIE \d/.test(navigator.userAgent);
-  var ie_lt8 = ie &amp;&amp; (document.documentMode == null || document.documentMode &lt; 8);
-  var ie_lt9 = ie &amp;&amp; (document.documentMode == null || document.documentMode &lt; 9);
-  var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
</del><ins>+  // ie_uptoN means Internet Explorer version N or lower
+  var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
+  var ie_upto7 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 8);
+  var ie_upto8 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 9);
+  var ie_upto9 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 10);
+  var ie_11up = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
+  var ie = ie_upto10 || ie_11up;
</ins><span class="cx">   var webkit = /WebKit\//.test(navigator.userAgent);
</span><span class="cx">   var qtwebkit = webkit &amp;&amp; /Qt\/\d+\.\d+/.test(navigator.userAgent);
</span><span class="cx">   var chrome = /Chrome\//.test(navigator.userAgent);
</span><del>-  var opera = /Opera\//.test(navigator.userAgent);
</del><ins>+  var presto = /Opera\//.test(navigator.userAgent);
</ins><span class="cx">   var safari = /Apple Computer/.test(navigator.vendor);
</span><span class="cx">   var khtml = /KHTML\//.test(navigator.userAgent);
</span><span class="cx">   var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
</span><span class="lines">@@ -30,151 +43,181 @@
</span><span class="cx">   var mac = ios || /Mac/.test(navigator.platform);
</span><span class="cx">   var windows = /win/i.test(navigator.platform);
</span><span class="cx"> 
</span><del>-  var opera_version = opera &amp;&amp; navigator.userAgent.match(/Version\/(\d*\.\d*)/);
-  if (opera_version) opera_version = Number(opera_version[1]);
-  if (opera_version &amp;&amp; opera_version &gt;= 15) { opera = false; webkit = true; }
</del><ins>+  var presto_version = presto &amp;&amp; navigator.userAgent.match(/Version\/(\d*\.\d*)/);
+  if (presto_version) presto_version = Number(presto_version[1]);
+  if (presto_version &amp;&amp; presto_version &gt;= 15) { presto = false; webkit = true; }
</ins><span class="cx">   // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
</span><del>-  var flipCtrlCmd = mac &amp;&amp; (qtwebkit || opera &amp;&amp; (opera_version == null || opera_version &lt; 12.11));
-  var captureMiddleClick = gecko || (ie &amp;&amp; !ie_lt9);
</del><ins>+  var flipCtrlCmd = mac &amp;&amp; (qtwebkit || presto &amp;&amp; (presto_version == null || presto_version &lt; 12.11));
+  var captureRightClick = gecko || (ie &amp;&amp; !ie_upto8);
</ins><span class="cx"> 
</span><del>-  // Optimize some code when these features are not used
</del><ins>+  // Optimize some code when these features are not used.
</ins><span class="cx">   var sawReadOnlySpans = false, sawCollapsedSpans = false;
</span><span class="cx"> 
</span><del>-  // CONSTRUCTOR
</del><ins>+  // EDITOR CONSTRUCTOR
</ins><span class="cx"> 
</span><ins>+  // A CodeMirror instance represents an editor. This is the object
+  // that user code is usually dealing with.
+
</ins><span class="cx">   function CodeMirror(place, options) {
</span><span class="cx">     if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
</span><span class="cx"> 
</span><span class="cx">     this.options = options = options || {};
</span><span class="cx">     // Determine effective options based on given values and defaults.
</span><del>-    for (var opt in defaults) if (!options.hasOwnProperty(opt) &amp;&amp; defaults.hasOwnProperty(opt))
-      options[opt] = defaults[opt];
</del><ins>+    copyObj(defaults, options, false);
</ins><span class="cx">     setGuttersForLineNumbers(options);
</span><span class="cx"> 
</span><del>-    var docStart = typeof options.value == &quot;string&quot; ? 0 : options.value.first;
-    var display = this.display = makeDisplay(place, docStart);
</del><ins>+    var doc = options.value;
+    if (typeof doc == &quot;string&quot;) doc = new Doc(doc, options.mode);
+    this.doc = doc;
+
+    var display = this.display = new Display(place, doc);
</ins><span class="cx">     display.wrapper.CodeMirror = this;
</span><span class="cx">     updateGutters(this);
</span><del>-    if (options.autofocus &amp;&amp; !mobile) focusInput(this);
-
-    this.state = {keyMaps: [],
-                  overlays: [],
-                  modeGen: 0,
-                  overwrite: false, focused: false,
-                  suppressEdits: false, pasteIncoming: false,
-                  draggingText: false,
-                  highlight: new Delayed()};
-
</del><span class="cx">     themeChanged(this);
</span><span class="cx">     if (options.lineWrapping)
</span><span class="cx">       this.display.wrapper.className += &quot; CodeMirror-wrap&quot;;
</span><ins>+    if (options.autofocus &amp;&amp; !mobile) focusInput(this);
</ins><span class="cx"> 
</span><del>-    var doc = options.value;
-    if (typeof doc == &quot;string&quot;) doc = new Doc(options.value, options.mode);
-    operation(this, attachDoc)(this, doc);
</del><ins>+    this.state = {
+      keyMaps: [],  // stores maps added by addKeyMap
+      overlays: [], // highlighting overlays, as added by addOverlay
+      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
+      overwrite: false, focused: false,
+      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
+      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
+      draggingText: false,
+      highlight: new Delayed() // stores highlight worker timeout
+    };
</ins><span class="cx"> 
</span><span class="cx">     // Override magic textarea content restore that IE sometimes does
</span><span class="cx">     // on our hidden textarea on reload
</span><del>-    if (ie) setTimeout(bind(resetInput, this, true), 20);
</del><ins>+    if (ie_upto10) setTimeout(bind(resetInput, this, true), 20);
</ins><span class="cx"> 
</span><span class="cx">     registerEventHandlers(this);
</span><del>-    // IE throws unspecified error in certain cases, when
-    // trying to access activeElement before onload
-    var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
-    if (hasFocus || (options.autofocus &amp;&amp; !mobile)) setTimeout(bind(onFocus, this), 20);
-    else onBlur(this);
</del><span class="cx"> 
</span><del>-    operation(this, function() {
-      for (var opt in optionHandlers)
-        if (optionHandlers.propertyIsEnumerable(opt))
-          optionHandlers[opt](this, options[opt], Init);
-      for (var i = 0; i &lt; initHooks.length; ++i) initHooks[i](this);
-    })();
</del><ins>+    var cm = this;
+    runInOp(this, function() {
+      cm.curOp.forceUpdate = true;
+      attachDoc(cm, doc);
+
+      if ((options.autofocus &amp;&amp; !mobile) || activeElt() == display.input)
+        setTimeout(bind(onFocus, cm), 20);
+      else
+        onBlur(cm);
+
+      for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
+        optionHandlers[opt](cm, options[opt], Init);
+      for (var i = 0; i &lt; initHooks.length; ++i) initHooks[i](cm);
+    });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DISPLAY CONSTRUCTOR
</span><span class="cx"> 
</span><del>-  function makeDisplay(place, docStart) {
-    var d = {};
</del><ins>+  // The display handles the DOM integration, both for input reading
+  // and content drawing. It holds references to DOM nodes and
+  // display-related state.
</ins><span class="cx"> 
</span><del>-    var input = d.input = elt(&quot;textarea&quot;, null, null, &quot;position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;&quot;);
</del><ins>+  function Display(place, doc) {
+    var d = this;
+
+    // The semihidden textarea that is focused when the editor is
+    // focused, and receives input.
+    var input = d.input = elt(&quot;textarea&quot;, null, null, &quot;position: absolute; padding: 0; width: 1px; height: 1em; outline: none&quot;);
+    // The textarea is kept positioned near the cursor to prevent the
+    // fact that it'll be scrolled into view on input from scrolling
+    // our fake cursor out of view. On webkit, when wrap=off, paste is
+    // very slow. So make the area wide instead.
</ins><span class="cx">     if (webkit) input.style.width = &quot;1000px&quot;;
</span><span class="cx">     else input.setAttribute(&quot;wrap&quot;, &quot;off&quot;);
</span><del>-    // if border: 0; -- iOS fails to open keyboard (issue #1287)
</del><ins>+    // If border: 0; -- iOS fails to open keyboard (issue #1287)
</ins><span class="cx">     if (ios) input.style.border = &quot;1px solid black&quot;;
</span><span class="cx">     input.setAttribute(&quot;autocorrect&quot;, &quot;off&quot;); input.setAttribute(&quot;autocapitalize&quot;, &quot;off&quot;); input.setAttribute(&quot;spellcheck&quot;, &quot;false&quot;);
</span><span class="cx"> 
</span><span class="cx">     // Wraps and hides input textarea
</span><span class="cx">     d.inputDiv = elt(&quot;div&quot;, [input], null, &quot;overflow: hidden; position: relative; width: 3px; height: 0px;&quot;);
</span><del>-    // The actual fake scrollbars.
-    d.scrollbarH = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;height: 1px&quot;)], &quot;CodeMirror-hscrollbar&quot;);
-    d.scrollbarV = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;width: 1px&quot;)], &quot;CodeMirror-vscrollbar&quot;);
</del><ins>+    // The fake scrollbar elements.
+    d.scrollbarH = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;height: 100%; min-height: 1px&quot;)], &quot;CodeMirror-hscrollbar&quot;);
+    d.scrollbarV = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;min-width: 1px&quot;)], &quot;CodeMirror-vscrollbar&quot;);
+    // Covers bottom-right square when both scrollbars are present.
</ins><span class="cx">     d.scrollbarFiller = elt(&quot;div&quot;, null, &quot;CodeMirror-scrollbar-filler&quot;);
</span><ins>+    // Covers bottom of gutter when coverGutterNextToScrollbar is on
+    // and h scrollbar is present.
</ins><span class="cx">     d.gutterFiller = elt(&quot;div&quot;, null, &quot;CodeMirror-gutter-filler&quot;);
</span><del>-    // DIVs containing the selection and the actual code
</del><ins>+    // Will contain the actual code, positioned to cover the viewport.
</ins><span class="cx">     d.lineDiv = elt(&quot;div&quot;, null, &quot;CodeMirror-code&quot;);
</span><ins>+    // Elements are added to these to represent selection and cursors.
</ins><span class="cx">     d.selectionDiv = elt(&quot;div&quot;, null, null, &quot;position: relative; z-index: 1&quot;);
</span><del>-    // Blinky cursor, and element used to ensure cursor fits at the end of a line
-    d.cursor = elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor&quot;);
-    // Secondary cursor, shown when on a 'jump' in bi-directional text
-    d.otherCursor = elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor CodeMirror-secondarycursor&quot;);
-    // Used to measure text size
</del><ins>+    d.cursorDiv = elt(&quot;div&quot;, null, &quot;CodeMirror-cursors&quot;);
+    // A visibility: hidden element used to find the size of things.
</ins><span class="cx">     d.measure = elt(&quot;div&quot;, null, &quot;CodeMirror-measure&quot;);
</span><ins>+    // When lines outside of the viewport are measured, they are drawn in this.
+    d.lineMeasure = elt(&quot;div&quot;, null, &quot;CodeMirror-measure&quot;);
</ins><span class="cx">     // Wraps everything that needs to exist inside the vertically-padded coordinate system
</span><del>-    d.lineSpace = elt(&quot;div&quot;, [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
-                         null, &quot;position: relative; outline: none&quot;);
-    // Moved around its parent to cover visible view
</del><ins>+    d.lineSpace = elt(&quot;div&quot;, [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+                      null, &quot;position: relative; outline: none&quot;);
+    // Moved around its parent to cover visible view.
</ins><span class="cx">     d.mover = elt(&quot;div&quot;, [elt(&quot;div&quot;, [d.lineSpace], &quot;CodeMirror-lines&quot;)], null, &quot;position: relative&quot;);
</span><del>-    // Set to the height of the text, causes scrolling
</del><ins>+    // Set to the height of the document, allowing scrolling.
</ins><span class="cx">     d.sizer = elt(&quot;div&quot;, [d.mover], &quot;CodeMirror-sizer&quot;);
</span><del>-    // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
</del><ins>+    // Behavior of elts with overflow: auto and padding is
+    // inconsistent across browsers. This is used to ensure the
+    // scrollable area is big enough.
</ins><span class="cx">     d.heightForcer = elt(&quot;div&quot;, null, null, &quot;position: absolute; height: &quot; + scrollerCutOff + &quot;px; width: 1px;&quot;);
</span><del>-    // Will contain the gutters, if any
</del><ins>+    // Will contain the gutters, if any.
</ins><span class="cx">     d.gutters = elt(&quot;div&quot;, null, &quot;CodeMirror-gutters&quot;);
</span><span class="cx">     d.lineGutter = null;
</span><del>-    // Provides scrolling
</del><ins>+    // Actual scrollable element.
</ins><span class="cx">     d.scroller = elt(&quot;div&quot;, [d.sizer, d.heightForcer, d.gutters], &quot;CodeMirror-scroll&quot;);
</span><span class="cx">     d.scroller.setAttribute(&quot;tabIndex&quot;, &quot;-1&quot;);
</span><span class="cx">     // The element in which the editor lives.
</span><span class="cx">     d.wrapper = elt(&quot;div&quot;, [d.inputDiv, d.scrollbarH, d.scrollbarV,
</span><span class="cx">                             d.scrollbarFiller, d.gutterFiller, d.scroller], &quot;CodeMirror&quot;);
</span><del>-    // Work around IE7 z-index bug
-    if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
</del><span class="cx"> 
</span><ins>+    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
+    if (ie_upto7) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
</ins><span class="cx">     // Needed to hide big blue blinking cursor on Mobile Safari
</span><span class="cx">     if (ios) input.style.width = &quot;0px&quot;;
</span><span class="cx">     if (!webkit) d.scroller.draggable = true;
</span><span class="cx">     // Needed to handle Tab key in KHTML
</span><span class="cx">     if (khtml) { d.inputDiv.style.height = &quot;1px&quot;; d.inputDiv.style.position = &quot;absolute&quot;; }
</span><span class="cx">     // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
</span><del>-    else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = &quot;18px&quot;;
</del><ins>+    if (ie_upto7) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = &quot;18px&quot;;
</ins><span class="cx"> 
</span><del>-    // Current visible range (may be bigger than the view window).
-    d.viewOffset = d.lastSizeC = 0;
-    d.showingFrom = d.showingTo = docStart;
</del><ins>+    if (place.appendChild) place.appendChild(d.wrapper);
+    else place(d.wrapper);
</ins><span class="cx"> 
</span><ins>+    // Current rendered range (may be bigger than the view window).
+    d.viewFrom = d.viewTo = doc.first;
+    // Information about the rendered lines.
+    d.view = [];
+    // Holds info about a single rendered line when it was rendered
+    // for measurement, while not in view.
+    d.externalMeasured = null;
+    // Empty space (in pixels) above the view
+    d.viewOffset = 0;
+    d.lastSizeC = 0;
+    d.updateLineNumbers = null;
+
</ins><span class="cx">     // Used to only resize the line number gutter when necessary (when
</span><span class="cx">     // the amount of lines crosses a boundary that makes its width change)
</span><span class="cx">     d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
</span><span class="cx">     // See readInput and resetInput
</span><span class="cx">     d.prevInput = &quot;&quot;;
</span><del>-    // Set to true when a non-horizontal-scrolling widget is added. As
-    // an optimization, widget aligning is skipped when d is false.
</del><ins>+    // Set to true when a non-horizontal-scrolling line widget is
+    // added. As an optimization, line widget aligning is skipped when
+    // this is false.
</ins><span class="cx">     d.alignWidgets = false;
</span><del>-    // Flag that indicates whether we currently expect input to appear
-    // (after some event like 'keypress' or 'input') and are polling
-    // intensively.
</del><ins>+    // Flag that indicates whether we expect input to appear real soon
+    // now (after some event like 'keypress' or 'input') and are
+    // polling intensively.
</ins><span class="cx">     d.pollingFast = false;
</span><span class="cx">     // Self-resetting timeout for the poller
</span><span class="cx">     d.poll = new Delayed();
</span><span class="cx"> 
</span><del>-    d.cachedCharWidth = d.cachedTextHeight = null;
-    d.measureLineCache = [];
-    d.measureLineCachePos = 0;
</del><ins>+    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
</ins><span class="cx"> 
</span><span class="cx">     // Tracks when resetInput has punted to just putting a short
</span><del>-    // string instead of the (large) selection.
</del><ins>+    // string into the textarea instead of the full selection.
</ins><span class="cx">     d.inaccurateSelection = false;
</span><span class="cx"> 
</span><span class="cx">     // Tracks the maximum line length so that the horizontal scrollbar
</span><span class="lines">@@ -186,7 +229,8 @@
</span><span class="cx">     // Used for measuring wheel scrolling granularity
</span><span class="cx">     d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
</span><span class="cx"> 
</span><del>-    return d;
</del><ins>+    // True when shift is held down.
+    d.shift = false;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // STATE UPDATES
</span><span class="lines">@@ -195,6 +239,10 @@
</span><span class="cx"> 
</span><span class="cx">   function loadMode(cm) {
</span><span class="cx">     cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
</span><ins>+    resetModeState(cm);
+  }
+
+  function resetModeState(cm) {
</ins><span class="cx">     cm.doc.iter(function(line) {
</span><span class="cx">       if (line.stateAfter) line.stateAfter = null;
</span><span class="cx">       if (line.styles) line.styles = null;
</span><span class="lines">@@ -207,11 +255,11 @@
</span><span class="cx"> 
</span><span class="cx">   function wrappingChanged(cm) {
</span><span class="cx">     if (cm.options.lineWrapping) {
</span><del>-      cm.display.wrapper.className += &quot; CodeMirror-wrap&quot;;
</del><ins>+      addClass(cm.display.wrapper, &quot;CodeMirror-wrap&quot;);
</ins><span class="cx">       cm.display.sizer.style.minWidth = &quot;&quot;;
</span><span class="cx">     } else {
</span><del>-      cm.display.wrapper.className = cm.display.wrapper.className.replace(&quot; CodeMirror-wrap&quot;, &quot;&quot;);
-      computeMaxLength(cm);
</del><ins>+      rmClass(cm.display.wrapper, &quot;CodeMirror-wrap&quot;);
+      findMaxLine(cm);
</ins><span class="cx">     }
</span><span class="cx">     estimateLineHeights(cm);
</span><span class="cx">     regChange(cm);
</span><span class="lines">@@ -219,16 +267,24 @@
</span><span class="cx">     setTimeout(function(){updateScrollbars(cm);}, 100);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Returns a function that estimates the height of a line, to use as
+  // first approximation until the line becomes visible (and is thus
+  // properly measurable).
</ins><span class="cx">   function estimateHeight(cm) {
</span><span class="cx">     var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
</span><span class="cx">     var perLine = wrapping &amp;&amp; Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
</span><span class="cx">     return function(line) {
</span><del>-      if (lineIsHidden(cm.doc, line))
-        return 0;
-      else if (wrapping)
-        return (Math.ceil(line.text.length / perLine) || 1) * th;
</del><ins>+      if (lineIsHidden(cm.doc, line)) return 0;
+
+      var widgetsHeight = 0;
+      if (line.widgets) for (var i = 0; i &lt; line.widgets.length; i++) {
+        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
+      }
+
+      if (wrapping)
+        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
</ins><span class="cx">       else
</span><del>-        return th;
</del><ins>+        return widgetsHeight + th;
</ins><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -244,7 +300,6 @@
</span><span class="cx">     var map = keyMap[cm.options.keyMap], style = map.style;
</span><span class="cx">     cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, &quot;&quot;) +
</span><span class="cx">       (style ? &quot; cm-keymap-&quot; + style : &quot;&quot;);
</span><del>-    cm.state.disableInput = map.disableInput;
</del><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function themeChanged(cm) {
</span><span class="lines">@@ -259,6 +314,8 @@
</span><span class="cx">     setTimeout(function(){alignHorizontally(cm);}, 20);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Rebuild the gutter elements, ensure the margin to the left of the
+  // code matches their width.
</ins><span class="cx">   function updateGutters(cm) {
</span><span class="cx">     var gutters = cm.display.gutters, specs = cm.options.gutters;
</span><span class="cx">     removeChildren(gutters);
</span><span class="lines">@@ -271,33 +328,44 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     gutters.style.display = i ? &quot;&quot; : &quot;none&quot;;
</span><ins>+    updateGutterSpace(cm);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function lineLength(doc, line) {
</del><ins>+  function updateGutterSpace(cm) {
+    var width = cm.display.gutters.offsetWidth;
+    cm.display.sizer.style.marginLeft = width + &quot;px&quot;;
+    cm.display.scrollbarH.style.left = cm.options.fixedGutter ? width + &quot;px&quot; : 0;
+  }
+
+  // Compute the character length of a line, taking into account
+  // collapsed ranges (see markText) that might hide parts, and join
+  // other lines onto it.
+  function lineLength(line) {
</ins><span class="cx">     if (line.height == 0) return 0;
</span><span class="cx">     var len = line.text.length, merged, cur = line;
</span><span class="cx">     while (merged = collapsedSpanAtStart(cur)) {
</span><del>-      var found = merged.find();
-      cur = getLine(doc, found.from.line);
</del><ins>+      var found = merged.find(0, true);
+      cur = found.from.line;
</ins><span class="cx">       len += found.from.ch - found.to.ch;
</span><span class="cx">     }
</span><span class="cx">     cur = line;
</span><span class="cx">     while (merged = collapsedSpanAtEnd(cur)) {
</span><del>-      var found = merged.find();
</del><ins>+      var found = merged.find(0, true);
</ins><span class="cx">       len -= cur.text.length - found.from.ch;
</span><del>-      cur = getLine(doc, found.to.line);
</del><ins>+      cur = found.to.line;
</ins><span class="cx">       len += cur.text.length - found.to.ch;
</span><span class="cx">     }
</span><span class="cx">     return len;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function computeMaxLength(cm) {
</del><ins>+  // Find the longest line in the document.
+  function findMaxLine(cm) {
</ins><span class="cx">     var d = cm.display, doc = cm.doc;
</span><span class="cx">     d.maxLine = getLine(doc, doc.first);
</span><del>-    d.maxLineLength = lineLength(doc, d.maxLine);
</del><ins>+    d.maxLineLength = lineLength(d.maxLine);
</ins><span class="cx">     d.maxLineChanged = true;
</span><span class="cx">     doc.iter(function(line) {
</span><del>-      var len = lineLength(doc, line);
</del><ins>+      var len = lineLength(line);
</ins><span class="cx">       if (len &gt; d.maxLineLength) {
</span><span class="cx">         d.maxLineLength = len;
</span><span class="cx">         d.maxLine = line;
</span><span class="lines">@@ -319,21 +387,33 @@
</span><span class="cx"> 
</span><span class="cx">   // SCROLLBARS
</span><span class="cx"> 
</span><ins>+  // Prepare DOM reads needed to update the scrollbars. Done in one
+  // shot to minimize update/measure roundtrips.
+  function measureForScrollbars(cm) {
+    var scroll = cm.display.scroller;
+    return {
+      clientHeight: scroll.clientHeight,
+      barHeight: cm.display.scrollbarV.clientHeight,
+      scrollWidth: scroll.scrollWidth, clientWidth: scroll.clientWidth,
+      barWidth: cm.display.scrollbarH.clientWidth,
+      docHeight: Math.round(cm.doc.height + paddingVert(cm.display))
+    };
+  }
+
</ins><span class="cx">   // Re-synchronize the fake scrollbars with the actual size of the
</span><del>-  // content. Optionally force a scrollTop.
-  function updateScrollbars(cm) {
-    var d = cm.display, docHeight = cm.doc.height;
-    var totalHeight = docHeight + paddingVert(d);
-    d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + &quot;px&quot;;
-    d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + &quot;px&quot;;
-    var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
-    var needsH = d.scroller.scrollWidth &gt; (d.scroller.clientWidth + 1);
-    var needsV = scrollHeight &gt; (d.scroller.clientHeight + 1);
</del><ins>+  // content.
+  function updateScrollbars(cm, measure) {
+    if (!measure) measure = measureForScrollbars(cm);
+    var d = cm.display;
+    var scrollHeight = measure.docHeight + scrollerCutOff;
+    var needsH = measure.scrollWidth &gt; measure.clientWidth;
+    var needsV = scrollHeight &gt; measure.clientHeight;
</ins><span class="cx">     if (needsV) {
</span><span class="cx">       d.scrollbarV.style.display = &quot;block&quot;;
</span><span class="cx">       d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + &quot;px&quot; : &quot;0&quot;;
</span><ins>+      // A bug in IE8 can cause this value to be negative, so guard it.
</ins><span class="cx">       d.scrollbarV.firstChild.style.height =
</span><del>-        (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + &quot;px&quot;;
</del><ins>+        Math.max(0, scrollHeight - measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + &quot;px&quot;;
</ins><span class="cx">     } else {
</span><span class="cx">       d.scrollbarV.style.display = &quot;&quot;;
</span><span class="cx">       d.scrollbarV.firstChild.style.height = &quot;0&quot;;
</span><span class="lines">@@ -342,7 +422,7 @@
</span><span class="cx">       d.scrollbarH.style.display = &quot;block&quot;;
</span><span class="cx">       d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + &quot;px&quot; : &quot;0&quot;;
</span><span class="cx">       d.scrollbarH.firstChild.style.width =
</span><del>-        (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + &quot;px&quot;;
</del><ins>+        (measure.scrollWidth - measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + &quot;px&quot;;
</ins><span class="cx">     } else {
</span><span class="cx">       d.scrollbarH.style.display = &quot;&quot;;
</span><span class="cx">       d.scrollbarH.firstChild.style.width = &quot;0&quot;;
</span><span class="lines">@@ -359,33 +439,61 @@
</span><span class="cx"> 
</span><span class="cx">     if (mac_geLion &amp;&amp; scrollbarWidth(d.measure) === 0) {
</span><span class="cx">       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? &quot;18px&quot; : &quot;12px&quot;;
</span><del>-      d.scrollbarV.style.pointerEvents = d.scrollbarH.style.pointerEvents = &quot;none&quot;;
</del><ins>+      var barMouseDown = function(e) {
+        if (e_target(e) != d.scrollbarV &amp;&amp; e_target(e) != d.scrollbarH)
+          operation(cm, onMouseDown)(e);
+      };
+      on(d.scrollbarV, &quot;mousedown&quot;, barMouseDown);
+      on(d.scrollbarH, &quot;mousedown&quot;, barMouseDown);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute the lines that are visible in a given viewport (defaults
+  // the the current scroll position). viewPort may contain top,
+  // height, and ensure (see op.scrollToPos) properties.
</ins><span class="cx">   function visibleLines(display, doc, viewPort) {
</span><del>-    var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
-    if (typeof viewPort == &quot;number&quot;) top = viewPort;
-    else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
</del><ins>+    var top = viewPort &amp;&amp; viewPort.top != null ? viewPort.top : display.scroller.scrollTop;
</ins><span class="cx">     top = Math.floor(top - paddingTop(display));
</span><del>-    var bottom = Math.ceil(top + height);
-    return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
</del><ins>+    var bottom = viewPort &amp;&amp; viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight;
+
+    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
+    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+    // forces those lines into the viewport (if possible).
+    if (viewPort &amp;&amp; viewPort.ensure) {
+      var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line;
+      if (ensureFrom &lt; from)
+        return {from: ensureFrom,
+                to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
+      if (Math.min(ensureTo, doc.lastLine()) &gt;= to)
+        return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight),
+                to: ensureTo};
+    }
+    return {from: from, to: to};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // LINE NUMBERS
</span><span class="cx"> 
</span><ins>+  // Re-align line numbers and gutter marks to compensate for
+  // horizontal scrolling.
</ins><span class="cx">   function alignHorizontally(cm) {
</span><del>-    var display = cm.display;
</del><ins>+    var display = cm.display, view = display.view;
</ins><span class="cx">     if (!display.alignWidgets &amp;&amp; (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
</span><span class="cx">     var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
</span><del>-    var gutterW = display.gutters.offsetWidth, l = comp + &quot;px&quot;;
-    for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
-      for (var i = 0, a = n.alignable; i &lt; a.length; ++i) a[i].style.left = l;
</del><ins>+    var gutterW = display.gutters.offsetWidth, left = comp + &quot;px&quot;;
+    for (var i = 0; i &lt; view.length; i++) if (!view[i].hidden) {
+      if (cm.options.fixedGutter &amp;&amp; view[i].gutter)
+        view[i].gutter.style.left = left;
+      var align = view[i].alignable;
+      if (align) for (var j = 0; j &lt; align.length; j++)
+        align[j].style.left = left;
</ins><span class="cx">     }
</span><span class="cx">     if (cm.options.fixedGutter)
</span><span class="cx">       display.gutters.style.left = (comp + gutterW) + &quot;px&quot;;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to ensure that the line number gutter is still the right
+  // size for the current document size. Returns true when an update
+  // is needed.
</ins><span class="cx">   function maybeUpdateLineNumberWidth(cm) {
</span><span class="cx">     if (!cm.options.lineNumbers) return false;
</span><span class="cx">     var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
</span><span class="lines">@@ -398,6 +506,7 @@
</span><span class="cx">       display.lineNumWidth = display.lineNumInnerWidth + padding;
</span><span class="cx">       display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
</span><span class="cx">       display.lineGutter.style.width = display.lineNumWidth + &quot;px&quot;;
</span><ins>+      updateGutterSpace(cm);
</ins><span class="cx">       return true;
</span><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="lines">@@ -406,191 +515,193 @@
</span><span class="cx">   function lineNumberFor(options, i) {
</span><span class="cx">     return String(options.lineNumberFormatter(i + options.firstLineNumber));
</span><span class="cx">   }
</span><ins>+
+  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
+  // but using getBoundingClientRect to get a sub-pixel-accurate
+  // result.
</ins><span class="cx">   function compensateForHScroll(display) {
</span><del>-    return getRect(display.scroller).left - getRect(display.sizer).left;
</del><ins>+    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DISPLAY DRAWING
</span><span class="cx"> 
</span><del>-  function updateDisplay(cm, changes, viewPort, forced) {
-    var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
</del><ins>+  // Updates the display, selection, and scrollbars, using the
+  // information in display.view to find out which nodes are no longer
+  // up-to-date. Tries to bail out early when no changes are needed,
+  // unless forced is true.
+  // Returns true if an actual update happened, false otherwise.
+  function updateDisplay(cm, viewPort, forced) {
+    var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated;
</ins><span class="cx">     var visible = visibleLines(cm.display, cm.doc, viewPort);
</span><span class="cx">     for (var first = true;; first = false) {
</span><span class="cx">       var oldWidth = cm.display.scroller.clientWidth;
</span><del>-      if (!updateDisplayInner(cm, changes, visible, forced)) break;
</del><ins>+      if (!updateDisplayInner(cm, visible, forced)) break;
</ins><span class="cx">       updated = true;
</span><del>-      changes = [];
</del><ins>+
+      // If the max line changed since it was last measured, measure it,
+      // and ensure the document's width matches it.
+      if (cm.display.maxLineChanged &amp;&amp; !cm.options.lineWrapping)
+        adjustContentWidth(cm);
+
+      var barMeasure = measureForScrollbars(cm);
</ins><span class="cx">       updateSelection(cm);
</span><del>-      updateScrollbars(cm);
</del><ins>+      setDocumentHeight(cm, barMeasure);
+      updateScrollbars(cm, barMeasure);
+      if (webkit &amp;&amp; cm.options.lineWrapping)
+        checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420)
</ins><span class="cx">       if (first &amp;&amp; cm.options.lineWrapping &amp;&amp; oldWidth != cm.display.scroller.clientWidth) {
</span><span class="cx">         forced = true;
</span><span class="cx">         continue;
</span><span class="cx">       }
</span><span class="cx">       forced = false;
</span><span class="cx"> 
</span><del>-      // Clip forced viewport to actual scrollable area
-      if (viewPort)
-        viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
-                            typeof viewPort == &quot;number&quot; ? viewPort : viewPort.top);
</del><ins>+      // Clip forced viewport to actual scrollable area.
+      if (viewPort &amp;&amp; viewPort.top != null)
+        viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)};
+      // Updated line heights might result in the drawn area not
+      // actually covering the viewport. Keep looping until it does.
</ins><span class="cx">       visible = visibleLines(cm.display, cm.doc, viewPort);
</span><del>-      if (visible.from &gt;= cm.display.showingFrom &amp;&amp; visible.to &lt;= cm.display.showingTo)
</del><ins>+      if (visible.from &gt;= cm.display.viewFrom &amp;&amp; visible.to &lt;= cm.display.viewTo)
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    cm.display.updateLineNumbers = null;
</ins><span class="cx">     if (updated) {
</span><span class="cx">       signalLater(cm, &quot;update&quot;, cm);
</span><del>-      if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
-        signalLater(cm, &quot;viewportChange&quot;, cm, cm.display.showingFrom, cm.display.showingTo);
</del><ins>+      if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
+        signalLater(cm, &quot;viewportChange&quot;, cm, cm.display.viewFrom, cm.display.viewTo);
</ins><span class="cx">     }
</span><span class="cx">     return updated;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Uses a set of changes plus the current scroll position to
-  // determine which DOM updates have to be made, and makes the
-  // updates.
-  function updateDisplayInner(cm, changes, visible, forced) {
</del><ins>+  // Does the actual updating of the line display. Bails out
+  // (returning false) when there is nothing to be done and forced is
+  // false.
+  function updateDisplayInner(cm, visible, forced) {
</ins><span class="cx">     var display = cm.display, doc = cm.doc;
</span><del>-    if (!display.wrapper.clientWidth) {
-      display.showingFrom = display.showingTo = doc.first;
-      display.viewOffset = 0;
</del><ins>+    if (!display.wrapper.offsetWidth) {
+      resetView(cm);
</ins><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Bail out if the visible area is already rendered and nothing changed.
</span><del>-    if (!forced &amp;&amp; changes.length == 0 &amp;&amp;
-        visible.from &gt; display.showingFrom &amp;&amp; visible.to &lt; display.showingTo)
</del><ins>+    if (!forced &amp;&amp; visible.from &gt;= display.viewFrom &amp;&amp; visible.to &lt;= display.viewTo &amp;&amp;
+        countDirtyView(cm) == 0)
</ins><span class="cx">       return;
</span><span class="cx"> 
</span><span class="cx">     if (maybeUpdateLineNumberWidth(cm))
</span><del>-      changes = [{from: doc.first, to: doc.first + doc.size}];
-    var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + &quot;px&quot;;
-    display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : &quot;0&quot;;
</del><ins>+      resetView(cm);
+    var dims = getDimensions(cm);
</ins><span class="cx"> 
</span><del>-    // Used to determine which lines need their line numbers updated
-    var positionsChangedFrom = Infinity;
-    if (cm.options.lineNumbers)
-      for (var i = 0; i &lt; changes.length; ++i)
-        if (changes[i].diff &amp;&amp; changes[i].from &lt; positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
-
</del><ins>+    // Compute a suitable new viewport (from &amp; to)
</ins><span class="cx">     var end = doc.first + doc.size;
</span><span class="cx">     var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
</span><span class="cx">     var to = Math.min(end, visible.to + cm.options.viewportMargin);
</span><del>-    if (display.showingFrom &lt; from &amp;&amp; from - display.showingFrom &lt; 20) from = Math.max(doc.first, display.showingFrom);
-    if (display.showingTo &gt; to &amp;&amp; display.showingTo - to &lt; 20) to = Math.min(end, display.showingTo);
</del><ins>+    if (display.viewFrom &lt; from &amp;&amp; from - display.viewFrom &lt; 20) from = Math.max(doc.first, display.viewFrom);
+    if (display.viewTo &gt; to &amp;&amp; display.viewTo - to &lt; 20) to = Math.min(end, display.viewTo);
</ins><span class="cx">     if (sawCollapsedSpans) {
</span><del>-      from = lineNo(visualLine(doc, getLine(doc, from)));
-      while (to &lt; end &amp;&amp; lineIsHidden(doc, getLine(doc, to))) ++to;
</del><ins>+      from = visualLineNo(cm.doc, from);
+      to = visualLineEndNo(cm.doc, to);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Create a range of theoretically intact lines, and punch holes
-    // in that using the change info.
-    var intact = [{from: Math.max(display.showingFrom, doc.first),
-                   to: Math.min(display.showingTo, end)}];
-    if (intact[0].from &gt;= intact[0].to) intact = [];
-    else intact = computeIntact(intact, changes);
-    // When merged lines are present, we might have to reduce the
-    // intact ranges because changes in continued fragments of the
-    // intact lines do require the lines to be redrawn.
-    if (sawCollapsedSpans)
-      for (var i = 0; i &lt; intact.length; ++i) {
-        var range = intact[i], merged;
-        while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
-          var newTo = merged.find().from.line;
-          if (newTo &gt; range.from) range.to = newTo;
-          else { intact.splice(i--, 1); break; }
-        }
-      }
</del><ins>+    var different = from != display.viewFrom || to != display.viewTo ||
+      display.lastSizeC != display.wrapper.clientHeight;
+    adjustView(cm, from, to);
</ins><span class="cx"> 
</span><del>-    // Clip off the parts that won't be visible
-    var intactLines = 0;
-    for (var i = 0; i &lt; intact.length; ++i) {
-      var range = intact[i];
-      if (range.from &lt; from) range.from = from;
-      if (range.to &gt; to) range.to = to;
-      if (range.from &gt;= range.to) intact.splice(i--, 1);
-      else intactLines += range.to - range.from;
-    }
-    if (!forced &amp;&amp; intactLines == to - from &amp;&amp; from == display.showingFrom &amp;&amp; to == display.showingTo) {
-      updateViewOffset(cm);
-      return;
-    }
-    intact.sort(function(a, b) {return a.from - b.from;});
</del><ins>+    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
+    // Position the mover div to align with the current scroll position
+    cm.display.mover.style.top = display.viewOffset + &quot;px&quot;;
</ins><span class="cx"> 
</span><del>-    // Avoid crashing on IE's &quot;unspecified error&quot; when in iframes
-    try {
-      var focused = document.activeElement;
-    } catch(e) {}
-    if (intactLines &lt; (to - from) * .7) display.lineDiv.style.display = &quot;none&quot;;
-    patchDisplay(cm, from, to, intact, positionsChangedFrom);
-    display.lineDiv.style.display = &quot;&quot;;
-    if (focused &amp;&amp; document.activeElement != focused &amp;&amp; focused.offsetHeight) focused.focus();
</del><ins>+    var toUpdate = countDirtyView(cm);
+    if (!different &amp;&amp; toUpdate == 0 &amp;&amp; !forced) return;
</ins><span class="cx"> 
</span><del>-    var different = from != display.showingFrom || to != display.showingTo ||
-      display.lastSizeC != display.wrapper.clientHeight;
-    // This is just a bogus formula that detects when the editor is
-    // resized or the font size changes.
</del><ins>+    // For big changes, we hide the enclosing element during the
+    // update, since that speeds up the operations on most browsers.
+    var focused = activeElt();
+    if (toUpdate &gt; 4) display.lineDiv.style.display = &quot;none&quot;;
+    patchDisplay(cm, display.updateLineNumbers, dims);
+    if (toUpdate &gt; 4) display.lineDiv.style.display = &quot;&quot;;
+    // There might have been a widget with a focused element that got
+    // hidden or updated, if so re-focus it.
+    if (focused &amp;&amp; activeElt() != focused &amp;&amp; focused.offsetHeight) focused.focus();
+
+    // Prevent selection and cursors from interfering with the scroll
+    // width.
+    removeChildren(display.cursorDiv);
+    removeChildren(display.selectionDiv);
+
</ins><span class="cx">     if (different) {
</span><span class="cx">       display.lastSizeC = display.wrapper.clientHeight;
</span><span class="cx">       startWorker(cm, 400);
</span><span class="cx">     }
</span><del>-    display.showingFrom = from; display.showingTo = to;
</del><span class="cx"> 
</span><span class="cx">     updateHeightsInViewport(cm);
</span><del>-    updateViewOffset(cm);
</del><span class="cx"> 
</span><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function adjustContentWidth(cm) {
+    var display = cm.display;
+    var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
+    display.maxLineChanged = false;
+    var minWidth = Math.max(0, width + 3);
+    var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth);
+    display.sizer.style.minWidth = minWidth + &quot;px&quot;;
+    if (maxScrollLeft &lt; cm.doc.scrollLeft)
+      setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
+  }
+
+  function setDocumentHeight(cm, measure) {
+    cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measure.docHeight + &quot;px&quot;;
+    cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + &quot;px&quot;;
+  }
+
+
+  function checkForWebkitWidthBug(cm, measure) {
+    // Work around Webkit bug where it sometimes reserves space for a
+    // non-existing phantom scrollbar in the scroller (Issue #2420)
+    if (cm.display.sizer.offsetWidth + cm.display.gutters.offsetWidth &lt; cm.display.scroller.clientWidth - 1) {
+      cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = &quot;0px&quot;;
+      cm.display.gutters.style.height = measure.docHeight + &quot;px&quot;;
+    }
+  }
+
+  // Read the actual heights of the rendered lines, and update their
+  // stored heights to match.
</ins><span class="cx">   function updateHeightsInViewport(cm) {
</span><span class="cx">     var display = cm.display;
</span><span class="cx">     var prevBottom = display.lineDiv.offsetTop;
</span><del>-    for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
-      if (ie_lt8) {
-        var bot = node.offsetTop + node.offsetHeight;
</del><ins>+    for (var i = 0; i &lt; display.view.length; i++) {
+      var cur = display.view[i], height;
+      if (cur.hidden) continue;
+      if (ie_upto7) {
+        var bot = cur.node.offsetTop + cur.node.offsetHeight;
</ins><span class="cx">         height = bot - prevBottom;
</span><span class="cx">         prevBottom = bot;
</span><span class="cx">       } else {
</span><del>-        var box = getRect(node);
</del><ins>+        var box = cur.node.getBoundingClientRect();
</ins><span class="cx">         height = box.bottom - box.top;
</span><span class="cx">       }
</span><del>-      var diff = node.lineObj.height - height;
</del><ins>+      var diff = cur.line.height - height;
</ins><span class="cx">       if (height &lt; 2) height = textHeight(display);
</span><span class="cx">       if (diff &gt; .001 || diff &lt; -.001) {
</span><del>-        updateLineHeight(node.lineObj, height);
-        var widgets = node.lineObj.widgets;
-        if (widgets) for (var i = 0; i &lt; widgets.length; ++i)
-          widgets[i].height = widgets[i].node.offsetHeight;
</del><ins>+        updateLineHeight(cur.line, height);
+        updateWidgetHeight(cur.line);
+        if (cur.rest) for (var j = 0; j &lt; cur.rest.length; j++)
+          updateWidgetHeight(cur.rest[j]);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function updateViewOffset(cm) {
-    var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
-    // Position the mover div to align with the current virtual scroll position
-    cm.display.mover.style.top = off + &quot;px&quot;;
</del><ins>+  // Read and store the height of line widgets associated with the
+  // given line.
+  function updateWidgetHeight(line) {
+    if (line.widgets) for (var i = 0; i &lt; line.widgets.length; ++i)
+      line.widgets[i].height = line.widgets[i].node.offsetHeight;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function computeIntact(intact, changes) {
-    for (var i = 0, l = changes.length || 0; i &lt; l; ++i) {
-      var change = changes[i], intact2 = [], diff = change.diff || 0;
-      for (var j = 0, l2 = intact.length; j &lt; l2; ++j) {
-        var range = intact[j];
-        if (change.to &lt;= range.from &amp;&amp; change.diff) {
-          intact2.push({from: range.from + diff, to: range.to + diff});
-        } else if (change.to &lt;= range.from || change.from &gt;= range.to) {
-          intact2.push(range);
-        } else {
-          if (change.from &gt; range.from)
-            intact2.push({from: range.from, to: change.from});
-          if (change.to &lt; range.to)
-            intact2.push({from: change.to + diff, to: range.to + diff});
-        }
-      }
-      intact = intact2;
-    }
-    return intact;
-  }
-
</del><ins>+  // Do a bulk-read of the DOM positions and sizes needed to draw the
+  // view, so that we don't interleave reading and writing to the DOM.
</ins><span class="cx">   function getDimensions(cm) {
</span><span class="cx">     var d = cm.display, left = {}, width = {};
</span><span class="cx">     for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
</span><span class="lines">@@ -604,154 +715,207 @@
</span><span class="cx">             wrapperWidth: d.wrapper.clientWidth};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
-    var dims = getDimensions(cm);
</del><ins>+  // Sync the actual display DOM structure with display.view, removing
+  // nodes for lines that are no longer in view, and creating the ones
+  // that are not there yet, and updating the ones that are out of
+  // date.
+  function patchDisplay(cm, updateNumbersFrom, dims) {
</ins><span class="cx">     var display = cm.display, lineNumbers = cm.options.lineNumbers;
</span><del>-    if (!intact.length &amp;&amp; (!webkit || !cm.display.currentWheelTarget))
-      removeChildren(display.lineDiv);
</del><span class="cx">     var container = display.lineDiv, cur = container.firstChild;
</span><span class="cx"> 
</span><span class="cx">     function rm(node) {
</span><span class="cx">       var next = node.nextSibling;
</span><del>-      if (webkit &amp;&amp; mac &amp;&amp; cm.display.currentWheelTarget == node) {
</del><ins>+      // Works around a throw-scroll bug in OS X Webkit
+      if (webkit &amp;&amp; mac &amp;&amp; cm.display.currentWheelTarget == node)
</ins><span class="cx">         node.style.display = &quot;none&quot;;
</span><del>-        node.lineObj = null;
-      } else {
</del><ins>+      else
</ins><span class="cx">         node.parentNode.removeChild(node);
</span><del>-      }
</del><span class="cx">       return next;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var nextIntact = intact.shift(), lineN = from;
-    cm.doc.iter(from, to, function(line) {
-      if (nextIntact &amp;&amp; nextIntact.to == lineN) nextIntact = intact.shift();
-      if (lineIsHidden(cm.doc, line)) {
-        if (line.height != 0) updateLineHeight(line, 0);
-        if (line.widgets &amp;&amp; cur &amp;&amp; cur.previousSibling) for (var i = 0; i &lt; line.widgets.length; ++i) {
-          var w = line.widgets[i];
-          if (w.showIfHidden) {
-            var prev = cur.previousSibling;
-            if (/pre/i.test(prev.nodeName)) {
-              var wrap = elt(&quot;div&quot;, null, null, &quot;position: relative&quot;);
-              prev.parentNode.replaceChild(wrap, prev);
-              wrap.appendChild(prev);
-              prev = wrap;
-            }
-            var wnode = prev.appendChild(elt(&quot;div&quot;, [w.node], &quot;CodeMirror-linewidget&quot;));
-            if (!w.handleMouseEvents) wnode.ignoreEvents = true;
-            positionLineWidget(w, wnode, prev, dims);
-          }
</del><ins>+    var view = display.view, lineN = display.viewFrom;
+    // Loop over the elements in the view, syncing cur (the DOM nodes
+    // in display.lineDiv) with the view as we go.
+    for (var i = 0; i &lt; view.length; i++) {
+      var lineView = view[i];
+      if (lineView.hidden) {
+      } else if (!lineView.node) { // Not drawn yet
+        var node = buildLineElement(cm, lineView, lineN, dims);
+        container.insertBefore(node, cur);
+      } else { // Already drawn
+        while (cur != lineView.node) cur = rm(cur);
+        var updateNumber = lineNumbers &amp;&amp; updateNumbersFrom != null &amp;&amp;
+          updateNumbersFrom &lt;= lineN &amp;&amp; lineView.lineNumber;
+        if (lineView.changes) {
+          if (indexOf(lineView.changes, &quot;gutter&quot;) &gt; -1) updateNumber = false;
+          updateLineForChanges(cm, lineView, lineN, dims);
</ins><span class="cx">         }
</span><del>-      } else if (nextIntact &amp;&amp; nextIntact.from &lt;= lineN &amp;&amp; nextIntact.to &gt; lineN) {
-        // This line is intact. Skip to the actual node. Update its
-        // line number if needed.
-        while (cur.lineObj != line) cur = rm(cur);
-        if (lineNumbers &amp;&amp; updateNumbersFrom &lt;= lineN &amp;&amp; cur.lineNumber)
-          setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
-        cur = cur.nextSibling;
-      } else {
-        // For lines with widgets, make an attempt to find and reuse
-        // the existing element, so that widgets aren't needlessly
-        // removed and re-inserted into the dom
-        if (line.widgets) for (var j = 0, search = cur, reuse; search &amp;&amp; j &lt; 20; ++j, search = search.nextSibling)
-          if (search.lineObj == line &amp;&amp; /div/i.test(search.nodeName)) { reuse = search; break; }
-        // This line needs to be generated.
-        var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
-        if (lineNode != reuse) {
-          container.insertBefore(lineNode, cur);
-        } else {
-          while (cur != reuse) cur = rm(cur);
-          cur = cur.nextSibling;
</del><ins>+        if (updateNumber) {
+          removeChildren(lineView.lineNumber);
+          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
</ins><span class="cx">         }
</span><del>-
-        lineNode.lineObj = line;
</del><ins>+        cur = lineView.node.nextSibling;
</ins><span class="cx">       }
</span><del>-      ++lineN;
-    });
</del><ins>+      lineN += lineView.size;
+    }
</ins><span class="cx">     while (cur) cur = rm(cur);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildLineElement(cm, line, lineNo, dims, reuse) {
-    var built = buildLineContent(cm, line), lineElement = built.pre;
-    var markers = line.gutterMarkers, display = cm.display, wrap;
</del><ins>+  // When an aspect of a line changes, a string is added to
+  // lineView.changes. This updates the relevant part of the line's
+  // DOM structure.
+  function updateLineForChanges(cm, lineView, lineN, dims) {
+    for (var j = 0; j &lt; lineView.changes.length; j++) {
+      var type = lineView.changes[j];
+      if (type == &quot;text&quot;) updateLineText(cm, lineView);
+      else if (type == &quot;gutter&quot;) updateLineGutter(cm, lineView, lineN, dims);
+      else if (type == &quot;class&quot;) updateLineClasses(lineView);
+      else if (type == &quot;widget&quot;) updateLineWidgets(lineView, dims);
+    }
+    lineView.changes = null;
+  }
</ins><span class="cx"> 
</span><del>-    var bgClass = built.bgClass ? built.bgClass + &quot; &quot; + (line.bgClass || &quot;&quot;) : line.bgClass;
-    if (!cm.options.lineNumbers &amp;&amp; !markers &amp;&amp; !bgClass &amp;&amp; !line.wrapClass &amp;&amp; !line.widgets)
-      return lineElement;
</del><ins>+  // Lines with gutter elements, widgets or a background class need to
+  // be wrapped, and have the extra elements added to the wrapper div
+  function ensureLineWrapped(lineView) {
+    if (lineView.node == lineView.text) {
+      lineView.node = elt(&quot;div&quot;, null, null, &quot;position: relative&quot;);
+      if (lineView.text.parentNode)
+        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
+      lineView.node.appendChild(lineView.text);
+      if (ie_upto7) lineView.node.style.zIndex = 2;
+    }
+    return lineView.node;
+  }
</ins><span class="cx"> 
</span><del>-    // Lines with gutter elements, widgets or a background class need
-    // to be wrapped again, and have the extra elements added to the
-    // wrapper div
</del><ins>+  function updateLineBackground(lineView) {
+    var cls = lineView.bgClass ? lineView.bgClass + &quot; &quot; + (lineView.line.bgClass || &quot;&quot;) : lineView.line.bgClass;
+    if (cls) cls += &quot; CodeMirror-linebackground&quot;;
+    if (lineView.background) {
+      if (cls) lineView.background.className = cls;
+      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
+    } else if (cls) {
+      var wrap = ensureLineWrapped(lineView);
+      lineView.background = wrap.insertBefore(elt(&quot;div&quot;, null, cls), wrap.firstChild);
+    }
+  }
</ins><span class="cx"> 
</span><del>-    if (reuse) {
-      reuse.alignable = null;
-      var isOk = true, widgetsSeen = 0, insertBefore = null;
-      for (var n = reuse.firstChild, next; n; n = next) {
-        next = n.nextSibling;
-        if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
-          reuse.removeChild(n);
-        } else {
-          for (var i = 0; i &lt; line.widgets.length; ++i) {
-            var widget = line.widgets[i];
-            if (widget.node == n.firstChild) {
-              if (!widget.above &amp;&amp; !insertBefore) insertBefore = n;
-              positionLineWidget(widget, n, reuse, dims);
-              ++widgetsSeen;
-              break;
-            }
-          }
-          if (i == line.widgets.length) { isOk = false; break; }
-        }
-      }
-      reuse.insertBefore(lineElement, insertBefore);
-      if (isOk &amp;&amp; widgetsSeen == line.widgets.length) {
-        wrap = reuse;
-        reuse.className = line.wrapClass || &quot;&quot;;
-      }
</del><ins>+  // Wrapper around buildLineContent which will reuse the structure
+  // in display.externalMeasured when possible.
+  function getLineContent(cm, lineView) {
+    var ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; ext.line == lineView.line) {
+      cm.display.externalMeasured = null;
+      lineView.measure = ext.measure;
+      return ext.built;
</ins><span class="cx">     }
</span><del>-    if (!wrap) {
-      wrap = elt(&quot;div&quot;, null, line.wrapClass, &quot;position: relative&quot;);
-      wrap.appendChild(lineElement);
</del><ins>+    return buildLineContent(cm, lineView);
+  }
+
+  // Redraw the line's text. Interacts with the background and text
+  // classes because the mode may output tokens that influence these
+  // classes.
+  function updateLineText(cm, lineView) {
+    var cls = lineView.text.className;
+    var built = getLineContent(cm, lineView);
+    if (lineView.text == lineView.node) lineView.node = built.pre;
+    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
+    lineView.text = built.pre;
+    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+      lineView.bgClass = built.bgClass;
+      lineView.textClass = built.textClass;
+      updateLineClasses(lineView);
+    } else if (cls) {
+      lineView.text.className = cls;
</ins><span class="cx">     }
</span><del>-    // Kludge to make sure the styled element lies behind the selection (by z-index)
-    if (bgClass)
-      wrap.insertBefore(elt(&quot;div&quot;, null, bgClass + &quot; CodeMirror-linebackground&quot;), wrap.firstChild);
</del><ins>+  }
+
+  function updateLineClasses(lineView) {
+    updateLineBackground(lineView);
+    if (lineView.line.wrapClass)
+      ensureLineWrapped(lineView).className = lineView.line.wrapClass;
+    else if (lineView.node != lineView.text)
+      lineView.node.className = &quot;&quot;;
+    var textClass = lineView.textClass ? lineView.textClass + &quot; &quot; + (lineView.line.textClass || &quot;&quot;) : lineView.line.textClass;
+    lineView.text.className = textClass || &quot;&quot;;
+  }
+
+  function updateLineGutter(cm, lineView, lineN, dims) {
+    if (lineView.gutter) {
+      lineView.node.removeChild(lineView.gutter);
+      lineView.gutter = null;
+    }
+    var markers = lineView.line.gutterMarkers;
</ins><span class="cx">     if (cm.options.lineNumbers || markers) {
</span><del>-      var gutterWrap = wrap.insertBefore(elt(&quot;div&quot;, null, null, &quot;position: absolute; left: &quot; +
-                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + &quot;px&quot;),
-                                         wrap.firstChild);
-      if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
</del><ins>+      var wrap = ensureLineWrapped(lineView);
+      var gutterWrap = lineView.gutter =
+        wrap.insertBefore(elt(&quot;div&quot;, null, &quot;CodeMirror-gutter-wrapper&quot;, &quot;position: absolute; left: &quot; +
+                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + &quot;px&quot;),
+                          lineView.text);
</ins><span class="cx">       if (cm.options.lineNumbers &amp;&amp; (!markers || !markers[&quot;CodeMirror-linenumbers&quot;]))
</span><del>-        wrap.lineNumber = gutterWrap.appendChild(
-          elt(&quot;div&quot;, lineNumberFor(cm.options, lineNo),
</del><ins>+        lineView.lineNumber = gutterWrap.appendChild(
+          elt(&quot;div&quot;, lineNumberFor(cm.options, lineN),
</ins><span class="cx">               &quot;CodeMirror-linenumber CodeMirror-gutter-elt&quot;,
</span><span class="cx">               &quot;left: &quot; + dims.gutterLeft[&quot;CodeMirror-linenumbers&quot;] + &quot;px; width: &quot;
</span><del>-              + display.lineNumInnerWidth + &quot;px&quot;));
-      if (markers)
-        for (var k = 0; k &lt; cm.options.gutters.length; ++k) {
-          var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) &amp;&amp; markers[id];
-          if (found)
-            gutterWrap.appendChild(elt(&quot;div&quot;, [found], &quot;CodeMirror-gutter-elt&quot;, &quot;left: &quot; +
-                                       dims.gutterLeft[id] + &quot;px; width: &quot; + dims.gutterWidth[id] + &quot;px&quot;));
-        }
</del><ins>+              + cm.display.lineNumInnerWidth + &quot;px&quot;));
+      if (markers) for (var k = 0; k &lt; cm.options.gutters.length; ++k) {
+        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) &amp;&amp; markers[id];
+        if (found)
+          gutterWrap.appendChild(elt(&quot;div&quot;, [found], &quot;CodeMirror-gutter-elt&quot;, &quot;left: &quot; +
+                                     dims.gutterLeft[id] + &quot;px; width: &quot; + dims.gutterWidth[id] + &quot;px&quot;));
+      }
</ins><span class="cx">     }
</span><del>-    if (ie_lt8) wrap.style.zIndex = 2;
-    if (line.widgets &amp;&amp; wrap != reuse) for (var i = 0, ws = line.widgets; i &lt; ws.length; ++i) {
</del><ins>+  }
+
+  function updateLineWidgets(lineView, dims) {
+    if (lineView.alignable) lineView.alignable = null;
+    for (var node = lineView.node.firstChild, next; node; node = next) {
+      var next = node.nextSibling;
+      if (node.className == &quot;CodeMirror-linewidget&quot;)
+        lineView.node.removeChild(node);
+    }
+    insertLineWidgets(lineView, dims);
+  }
+
+  // Build a line's DOM representation from scratch
+  function buildLineElement(cm, lineView, lineN, dims) {
+    var built = getLineContent(cm, lineView);
+    lineView.text = lineView.node = built.pre;
+    if (built.bgClass) lineView.bgClass = built.bgClass;
+    if (built.textClass) lineView.textClass = built.textClass;
+
+    updateLineClasses(lineView);
+    updateLineGutter(cm, lineView, lineN, dims);
+    insertLineWidgets(lineView, dims);
+    return lineView.node;
+  }
+
+  // A lineView may contain multiple logical lines (when merged by
+  // collapsed spans). The widgets for all of them need to be drawn.
+  function insertLineWidgets(lineView, dims) {
+    insertLineWidgetsFor(lineView.line, lineView, dims, true);
+    if (lineView.rest) for (var i = 0; i &lt; lineView.rest.length; i++)
+      insertLineWidgetsFor(lineView.rest[i], lineView, dims, false);
+  }
+
+  function insertLineWidgetsFor(line, lineView, dims, allowAbove) {
+    if (!line.widgets) return;
+    var wrap = ensureLineWrapped(lineView);
+    for (var i = 0, ws = line.widgets; i &lt; ws.length; ++i) {
</ins><span class="cx">       var widget = ws[i], node = elt(&quot;div&quot;, [widget.node], &quot;CodeMirror-linewidget&quot;);
</span><span class="cx">       if (!widget.handleMouseEvents) node.ignoreEvents = true;
</span><del>-      positionLineWidget(widget, node, wrap, dims);
-      if (widget.above)
-        wrap.insertBefore(node, cm.options.lineNumbers &amp;&amp; line.height != 0 ? gutterWrap : lineElement);
</del><ins>+      positionLineWidget(widget, node, lineView, dims);
+      if (allowAbove &amp;&amp; widget.above)
+        wrap.insertBefore(node, lineView.gutter || lineView.text);
</ins><span class="cx">       else
</span><span class="cx">         wrap.appendChild(node);
</span><span class="cx">       signalLater(widget, &quot;redraw&quot;);
</span><span class="cx">     }
</span><del>-    return wrap;
</del><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function positionLineWidget(widget, node, wrap, dims) {
</del><ins>+  function positionLineWidget(widget, node, lineView, dims) {
</ins><span class="cx">     if (widget.noHScroll) {
</span><del>-      (wrap.alignable || (wrap.alignable = [])).push(node);
</del><ins>+      (lineView.alignable || (lineView.alignable = [])).push(node);
</ins><span class="cx">       var width = dims.wrapperWidth;
</span><span class="cx">       node.style.left = dims.fixedPos + &quot;px&quot;;
</span><span class="cx">       if (!widget.coverGutter) {
</span><span class="lines">@@ -767,57 +931,370 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // POSITION OBJECT
+
+  // A Pos instance represents a position within the text.
+  var Pos = CodeMirror.Pos = function(line, ch) {
+    if (!(this instanceof Pos)) return new Pos(line, ch);
+    this.line = line; this.ch = ch;
+  };
+
+  // Compare two positions, return 0 if they are the same, a negative
+  // number when a is less, and a positive number otherwise.
+  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
+
+  function copyPos(x) {return Pos(x.line, x.ch);}
+  function maxPos(a, b) { return cmp(a, b) &lt; 0 ? b : a; }
+  function minPos(a, b) { return cmp(a, b) &lt; 0 ? a : b; }
+
</ins><span class="cx">   // SELECTION / CURSOR
</span><span class="cx"> 
</span><ins>+  // Selection objects are immutable. A new one is created every time
+  // the selection changes. A selection is one or more non-overlapping
+  // (and non-touching) ranges, sorted, and an integer that indicates
+  // which one is the primary selection (the one that's scrolled into
+  // view, that getCursor returns, etc).
+  function Selection(ranges, primIndex) {
+    this.ranges = ranges;
+    this.primIndex = primIndex;
+  }
+
+  Selection.prototype = {
+    primary: function() { return this.ranges[this.primIndex]; },
+    equals: function(other) {
+      if (other == this) return true;
+      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
+      for (var i = 0; i &lt; this.ranges.length; i++) {
+        var here = this.ranges[i], there = other.ranges[i];
+        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
+      }
+      return true;
+    },
+    deepCopy: function() {
+      for (var out = [], i = 0; i &lt; this.ranges.length; i++)
+        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
+      return new Selection(out, this.primIndex);
+    },
+    somethingSelected: function() {
+      for (var i = 0; i &lt; this.ranges.length; i++)
+        if (!this.ranges[i].empty()) return true;
+      return false;
+    },
+    contains: function(pos, end) {
+      if (!end) end = pos;
+      for (var i = 0; i &lt; this.ranges.length; i++) {
+        var range = this.ranges[i];
+        if (cmp(end, range.from()) &gt;= 0 &amp;&amp; cmp(pos, range.to()) &lt;= 0)
+          return i;
+      }
+      return -1;
+    }
+  };
+
+  function Range(anchor, head) {
+    this.anchor = anchor; this.head = head;
+  }
+
+  Range.prototype = {
+    from: function() { return minPos(this.anchor, this.head); },
+    to: function() { return maxPos(this.anchor, this.head); },
+    empty: function() {
+      return this.head.line == this.anchor.line &amp;&amp; this.head.ch == this.anchor.ch;
+    }
+  };
+
+  // Take an unsorted, potentially overlapping set of ranges, and
+  // build a selection out of it. 'Consumes' ranges array (modifying
+  // it).
+  function normalizeSelection(ranges, primIndex) {
+    var prim = ranges[primIndex];
+    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
+    primIndex = indexOf(ranges, prim);
+    for (var i = 1; i &lt; ranges.length; i++) {
+      var cur = ranges[i], prev = ranges[i - 1];
+      if (cmp(prev.to(), cur.from()) &gt;= 0) {
+        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
+        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
+        if (i &lt;= primIndex) --primIndex;
+        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
+      }
+    }
+    return new Selection(ranges, primIndex);
+  }
+
+  function simpleSelection(anchor, head) {
+    return new Selection([new Range(anchor, head || anchor)], 0);
+  }
+
+  // Most of the external API clips given positions to make sure they
+  // actually exist within the document.
+  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
+  function clipPos(doc, pos) {
+    if (pos.line &lt; doc.first) return Pos(doc.first, 0);
+    var last = doc.first + doc.size - 1;
+    if (pos.line &gt; last) return Pos(last, getLine(doc, last).text.length);
+    return clipToLen(pos, getLine(doc, pos.line).text.length);
+  }
+  function clipToLen(pos, linelen) {
+    var ch = pos.ch;
+    if (ch == null || ch &gt; linelen) return Pos(pos.line, linelen);
+    else if (ch &lt; 0) return Pos(pos.line, 0);
+    else return pos;
+  }
+  function isLine(doc, l) {return l &gt;= doc.first &amp;&amp; l &lt; doc.first + doc.size;}
+  function clipPosArray(doc, array) {
+    for (var out = [], i = 0; i &lt; array.length; i++) out[i] = clipPos(doc, array[i]);
+    return out;
+  }
+
+  // SELECTION UPDATES
+
+  // The 'scroll' parameter given to many of these indicated whether
+  // the new cursor position should be scrolled into view after
+  // modifying the selection.
+
+  // If shift is held or the extend flag is set, extends a range to
+  // include a given position (and optionally a second position).
+  // Otherwise, simply returns the range between the given positions.
+  // Used for cursor motion and such.
+  function extendRange(doc, range, head, other) {
+    if (doc.cm &amp;&amp; doc.cm.display.shift || doc.extend) {
+      var anchor = range.anchor;
+      if (other) {
+        var posBefore = cmp(head, anchor) &lt; 0;
+        if (posBefore != (cmp(other, anchor) &lt; 0)) {
+          anchor = head;
+          head = other;
+        } else if (posBefore != (cmp(head, other) &lt; 0)) {
+          head = other;
+        }
+      }
+      return new Range(anchor, head);
+    } else {
+      return new Range(other || head, head);
+    }
+  }
+
+  // Extend the primary selection range, discard the rest.
+  function extendSelection(doc, head, other, options) {
+    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
+  }
+
+  // Extend all selections (pos is an array of selections with length
+  // equal the number of selections)
+  function extendSelections(doc, heads, options) {
+    for (var out = [], i = 0; i &lt; doc.sel.ranges.length; i++)
+      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
+    var newSel = normalizeSelection(out, doc.sel.primIndex);
+    setSelection(doc, newSel, options);
+  }
+
+  // Updates a single range in the selection.
+  function replaceOneSelection(doc, i, range, options) {
+    var ranges = doc.sel.ranges.slice(0);
+    ranges[i] = range;
+    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
+  }
+
+  // Reset the selection to a single range.
+  function setSimpleSelection(doc, anchor, head, options) {
+    setSelection(doc, simpleSelection(anchor, head), options);
+  }
+
+  // Give beforeSelectionChange handlers a change to influence a
+  // selection update.
+  function filterSelectionChange(doc, sel) {
+    var obj = {
+      ranges: sel.ranges,
+      update: function(ranges) {
+        this.ranges = [];
+        for (var i = 0; i &lt; ranges.length; i++)
+          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
+                                     clipPos(doc, ranges[i].head));
+      }
+    };
+    signal(doc, &quot;beforeSelectionChange&quot;, doc, obj);
+    if (doc.cm) signal(doc.cm, &quot;beforeSelectionChange&quot;, doc.cm, obj);
+    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
+    else return sel;
+  }
+
+  function setSelectionReplaceHistory(doc, sel, options) {
+    var done = doc.history.done, last = lst(done);
+    if (last &amp;&amp; last.ranges) {
+      done[done.length - 1] = sel;
+      setSelectionNoUndo(doc, sel, options);
+    } else {
+      setSelection(doc, sel, options);
+    }
+  }
+
+  // Set a new selection.
+  function setSelection(doc, sel, options) {
+    if (options &amp;&amp; options.origin &amp;&amp; doc.cm) doc.cm.curOp.origin = options.origin;
+    setSelectionNoUndo(doc, sel, options);
+    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
+  }
+
+  function setSelectionNoUndo(doc, sel, options) {
+    if (hasHandler(doc, &quot;beforeSelectionChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeSelectionChange&quot;))
+      sel = filterSelectionChange(doc, sel);
+
+    var bias = cmp(sel.primary().head, doc.sel.primary().head) &lt; 0 ? -1 : 1;
+    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
+
+    if (!(options &amp;&amp; options.scroll === false) &amp;&amp; doc.cm)
+      ensureCursorVisible(doc.cm);
+  }
+
+  function setSelectionInner(doc, sel) {
+    if (sel.equals(doc.sel)) return;
+
+    doc.sel = sel;
+
+    if (doc.cm)
+      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
+        doc.cm.curOp.cursorActivity = true;
+    signalLater(doc, &quot;cursorActivity&quot;, doc);
+  }
+
+  // Verify that the selection does not partially select any atomic
+  // marked ranges.
+  function reCheckSelection(doc) {
+    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
+  }
+
+  // Return a selection that does not partially select any atomic
+  // ranges.
+  function skipAtomicInSelection(doc, sel, bias, mayClear) {
+    var out;
+    for (var i = 0; i &lt; sel.ranges.length; i++) {
+      var range = sel.ranges[i];
+      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
+      var newHead = skipAtomic(doc, range.head, bias, mayClear);
+      if (out || newAnchor != range.anchor || newHead != range.head) {
+        if (!out) out = sel.ranges.slice(0, i);
+        out[i] = new Range(newAnchor, newHead);
+      }
+    }
+    return out ? normalizeSelection(out, sel.primIndex) : sel;
+  }
+
+  // Ensure a given position is not inside an atomic range.
+  function skipAtomic(doc, pos, bias, mayClear) {
+    var flipped = false, curPos = pos;
+    var dir = bias || 1;
+    doc.cantEdit = false;
+    search: for (;;) {
+      var line = getLine(doc, curPos.line);
+      if (line.markedSpans) {
+        for (var i = 0; i &lt; line.markedSpans.length; ++i) {
+          var sp = line.markedSpans[i], m = sp.marker;
+          if ((sp.from == null || (m.inclusiveLeft ? sp.from &lt;= curPos.ch : sp.from &lt; curPos.ch)) &amp;&amp;
+              (sp.to == null || (m.inclusiveRight ? sp.to &gt;= curPos.ch : sp.to &gt; curPos.ch))) {
+            if (mayClear) {
+              signal(m, &quot;beforeCursorEnter&quot;);
+              if (m.explicitlyCleared) {
+                if (!line.markedSpans) break;
+                else {--i; continue;}
+              }
+            }
+            if (!m.atomic) continue;
+            var newPos = m.find(dir &lt; 0 ? -1 : 1);
+            if (cmp(newPos, curPos) == 0) {
+              newPos.ch += dir;
+              if (newPos.ch &lt; 0) {
+                if (newPos.line &gt; doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
+                else newPos = null;
+              } else if (newPos.ch &gt; line.text.length) {
+                if (newPos.line &lt; doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
+                else newPos = null;
+              }
+              if (!newPos) {
+                if (flipped) {
+                  // Driven in a corner -- no valid cursor position found at all
+                  // -- try again *with* clearing, if we didn't already
+                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
+                  // Otherwise, turn off editing until further notice, and return the start of the doc
+                  doc.cantEdit = true;
+                  return Pos(doc.first, 0);
+                }
+                flipped = true; newPos = pos; dir = -dir;
+              }
+            }
+            curPos = newPos;
+            continue search;
+          }
+        }
+      }
+      return curPos;
+    }
+  }
+
+  // SELECTION DRAWING
+
+  // Redraw the selection and/or cursor
</ins><span class="cx">   function updateSelection(cm) {
</span><del>-    var display = cm.display;
-    var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
-    if (collapsed || cm.options.showCursorWhenSelecting)
-      updateSelectionCursor(cm);
-    else
-      display.cursor.style.display = display.otherCursor.style.display = &quot;none&quot;;
-    if (!collapsed)
-      updateSelectionRange(cm);
-    else
-      display.selectionDiv.style.display = &quot;none&quot;;
</del><ins>+    var display = cm.display, doc = cm.doc;
+    var curFragment = document.createDocumentFragment();
+    var selFragment = document.createDocumentFragment();
</ins><span class="cx"> 
</span><ins>+    for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+      var range = doc.sel.ranges[i];
+      var collapsed = range.empty();
+      if (collapsed || cm.options.showCursorWhenSelecting)
+        drawSelectionCursor(cm, range, curFragment);
+      if (!collapsed)
+        drawSelectionRange(cm, range, selFragment);
+    }
+
</ins><span class="cx">     // Move the hidden textarea near the cursor to prevent scrolling artifacts
</span><span class="cx">     if (cm.options.moveInputWithCursor) {
</span><del>-      var headPos = cursorCoords(cm, cm.doc.sel.head, &quot;div&quot;);
-      var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
-      display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                                        headPos.top + lineOff.top - wrapOff.top)) + &quot;px&quot;;
-      display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                                         headPos.left + lineOff.left - wrapOff.left)) + &quot;px&quot;;
</del><ins>+      var headPos = cursorCoords(cm, doc.sel.primary().head, &quot;div&quot;);
+      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
+      var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+                                     headPos.top + lineOff.top - wrapOff.top));
+      var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+                                      headPos.left + lineOff.left - wrapOff.left));
+      display.inputDiv.style.top = top + &quot;px&quot;;
+      display.inputDiv.style.left = left + &quot;px&quot;;
</ins><span class="cx">     }
</span><ins>+
+    removeChildrenAndAdd(display.cursorDiv, curFragment);
+    removeChildrenAndAdd(display.selectionDiv, selFragment);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // No selection, plain cursor
-  function updateSelectionCursor(cm) {
-    var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, &quot;div&quot;);
-    display.cursor.style.left = pos.left + &quot;px&quot;;
-    display.cursor.style.top = pos.top + &quot;px&quot;;
-    display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + &quot;px&quot;;
-    display.cursor.style.display = &quot;&quot;;
</del><ins>+  // Draws a cursor for the given range
+  function drawSelectionCursor(cm, range, output) {
+    var pos = cursorCoords(cm, range.head, &quot;div&quot;);
</ins><span class="cx"> 
</span><ins>+    var cursor = output.appendChild(elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor&quot;));
+    cursor.style.left = pos.left + &quot;px&quot;;
+    cursor.style.top = pos.top + &quot;px&quot;;
+    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + &quot;px&quot;;
+
</ins><span class="cx">     if (pos.other) {
</span><del>-      display.otherCursor.style.display = &quot;&quot;;
-      display.otherCursor.style.left = pos.other.left + &quot;px&quot;;
-      display.otherCursor.style.top = pos.other.top + &quot;px&quot;;
-      display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + &quot;px&quot;;
-    } else { display.otherCursor.style.display = &quot;none&quot;; }
</del><ins>+      // Secondary cursor, shown when on a 'jump' in bi-directional text
+      var otherCursor = output.appendChild(elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor CodeMirror-secondarycursor&quot;));
+      otherCursor.style.display = &quot;&quot;;
+      otherCursor.style.left = pos.other.left + &quot;px&quot;;
+      otherCursor.style.top = pos.other.top + &quot;px&quot;;
+      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + &quot;px&quot;;
+    }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Highlight selection
-  function updateSelectionRange(cm) {
-    var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
</del><ins>+  // Draws the given range as a highlighted selection
+  function drawSelectionRange(cm, range, output) {
+    var display = cm.display, doc = cm.doc;
</ins><span class="cx">     var fragment = document.createDocumentFragment();
</span><del>-    var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
</del><ins>+    var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right;
</ins><span class="cx"> 
</span><span class="cx">     function add(left, top, width, bottom) {
</span><span class="cx">       if (top &lt; 0) top = 0;
</span><ins>+      top = Math.round(top);
+      bottom = Math.round(bottom);
</ins><span class="cx">       fragment.appendChild(elt(&quot;div&quot;, null, &quot;CodeMirror-selected&quot;, &quot;position: absolute; left: &quot; + left +
</span><del>-                               &quot;px; top: &quot; + top + &quot;px; width: &quot; + (width == null ? clientWidth - left : width) +
</del><ins>+                               &quot;px; top: &quot; + top + &quot;px; width: &quot; + (width == null ? rightSide - left : width) +
</ins><span class="cx">                                &quot;px; height: &quot; + (bottom - top) + &quot;px&quot;));
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -840,44 +1317,44 @@
</span><span class="cx">           left = leftPos.left;
</span><span class="cx">           right = rightPos.right;
</span><span class="cx">         }
</span><del>-        if (fromArg == null &amp;&amp; from == 0) left = pl;
</del><ins>+        if (fromArg == null &amp;&amp; from == 0) left = leftSide;
</ins><span class="cx">         if (rightPos.top - leftPos.top &gt; 3) { // Different lines, draw top part
</span><span class="cx">           add(left, leftPos.top, null, leftPos.bottom);
</span><del>-          left = pl;
</del><ins>+          left = leftSide;
</ins><span class="cx">           if (leftPos.bottom &lt; rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
</span><span class="cx">         }
</span><del>-        if (toArg == null &amp;&amp; to == lineLen) right = clientWidth;
</del><ins>+        if (toArg == null &amp;&amp; to == lineLen) right = rightSide;
</ins><span class="cx">         if (!start || leftPos.top &lt; start.top || leftPos.top == start.top &amp;&amp; leftPos.left &lt; start.left)
</span><span class="cx">           start = leftPos;
</span><span class="cx">         if (!end || rightPos.bottom &gt; end.bottom || rightPos.bottom == end.bottom &amp;&amp; rightPos.right &gt; end.right)
</span><span class="cx">           end = rightPos;
</span><del>-        if (left &lt; pl + 1) left = pl;
</del><ins>+        if (left &lt; leftSide + 1) left = leftSide;
</ins><span class="cx">         add(left, rightPos.top, right - left, rightPos.bottom);
</span><span class="cx">       });
</span><span class="cx">       return {start: start, end: end};
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (sel.from.line == sel.to.line) {
-      drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
</del><ins>+    var sFrom = range.from(), sTo = range.to();
+    if (sFrom.line == sTo.line) {
+      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
</ins><span class="cx">     } else {
</span><del>-      var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
-      var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
-      var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
-      var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
</del><ins>+      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
+      var singleVLine = visualLine(fromLine) == visualLine(toLine);
+      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
+      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
</ins><span class="cx">       if (singleVLine) {
</span><span class="cx">         if (leftEnd.top &lt; rightStart.top - 2) {
</span><span class="cx">           add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
</span><del>-          add(pl, rightStart.top, rightStart.left, rightStart.bottom);
</del><ins>+          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
</ins><span class="cx">         } else {
</span><span class="cx">           add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">       if (leftEnd.bottom &lt; rightStart.top)
</span><del>-        add(pl, leftEnd.bottom, null, rightStart.top);
</del><ins>+        add(leftSide, leftEnd.bottom, null, rightStart.top);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    removeChildrenAndAdd(display.selectionDiv, fragment);
-    display.selectionDiv.style.display = &quot;&quot;;
</del><ins>+    output.appendChild(fragment);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Cursor-blinking
</span><span class="lines">@@ -886,37 +1363,38 @@
</span><span class="cx">     var display = cm.display;
</span><span class="cx">     clearInterval(display.blinker);
</span><span class="cx">     var on = true;
</span><del>-    display.cursor.style.visibility = display.otherCursor.style.visibility = &quot;&quot;;
</del><ins>+    display.cursorDiv.style.visibility = &quot;&quot;;
</ins><span class="cx">     if (cm.options.cursorBlinkRate &gt; 0)
</span><span class="cx">       display.blinker = setInterval(function() {
</span><del>-        display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? &quot;&quot; : &quot;hidden&quot;;
</del><ins>+        display.cursorDiv.style.visibility = (on = !on) ? &quot;&quot; : &quot;hidden&quot;;
</ins><span class="cx">       }, cm.options.cursorBlinkRate);
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // HIGHLIGHT WORKER
</span><span class="cx"> 
</span><span class="cx">   function startWorker(cm, time) {
</span><del>-    if (cm.doc.mode.startState &amp;&amp; cm.doc.frontier &lt; cm.display.showingTo)
</del><ins>+    if (cm.doc.mode.startState &amp;&amp; cm.doc.frontier &lt; cm.display.viewTo)
</ins><span class="cx">       cm.state.highlight.set(time, bind(highlightWorker, cm));
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function highlightWorker(cm) {
</span><span class="cx">     var doc = cm.doc;
</span><span class="cx">     if (doc.frontier &lt; doc.first) doc.frontier = doc.first;
</span><del>-    if (doc.frontier &gt;= cm.display.showingTo) return;
</del><ins>+    if (doc.frontier &gt;= cm.display.viewTo) return;
</ins><span class="cx">     var end = +new Date + cm.options.workTime;
</span><span class="cx">     var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
</span><del>-    var changed = [], prevChange;
-    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
-      if (doc.frontier &gt;= cm.display.showingFrom) { // Visible
</del><ins>+
+    runInOp(cm, function() {
+    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
+      if (doc.frontier &gt;= cm.display.viewFrom) { // Visible
</ins><span class="cx">         var oldStyles = line.styles;
</span><del>-        line.styles = highlightLine(cm, line, state, true);
</del><ins>+        var highlighted = highlightLine(cm, line, state, true);
+        line.styles = highlighted.styles;
+        if (highlighted.classes) line.styleClasses = highlighted.classes;
+        else if (line.styleClasses) line.styleClasses = null;
</ins><span class="cx">         var ischange = !oldStyles || oldStyles.length != line.styles.length;
</span><span class="cx">         for (var i = 0; !ischange &amp;&amp; i &lt; oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
</span><del>-        if (ischange) {
-          if (prevChange &amp;&amp; prevChange.end == doc.frontier) prevChange.end++;
-          else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
-        }
</del><ins>+        if (ischange) regLineChange(cm, doc.frontier, &quot;text&quot;);
</ins><span class="cx">         line.stateAfter = copyState(doc.mode, state);
</span><span class="cx">       } else {
</span><span class="cx">         processLine(cm, line.text, state);
</span><span class="lines">@@ -928,11 +1406,7 @@
</span><span class="cx">         return true;
</span><span class="cx">       }
</span><span class="cx">     });
</span><del>-    if (changed.length)
-      operation(cm, function() {
-        for (var i = 0; i &lt; changed.length; ++i)
-          regChange(this, changed[i].start, changed[i].end);
-      })();
</del><ins>+    });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Finds the line to start with when starting a parse. Tries to
</span><span class="lines">@@ -964,7 +1438,7 @@
</span><span class="cx">     else state = copyState(doc.mode, state);
</span><span class="cx">     doc.iter(pos, n, function(line) {
</span><span class="cx">       processLine(cm, line.text, state);
</span><del>-      var save = pos == n - 1 || pos % 5 == 0 || pos &gt;= display.showingFrom &amp;&amp; pos &lt; display.showingTo;
</del><ins>+      var save = pos == n - 1 || pos % 5 == 0 || pos &gt;= display.viewFrom &amp;&amp; pos &lt; display.viewTo;
</ins><span class="cx">       line.stateAfter = save ? copyState(doc.mode, state) : null;
</span><span class="cx">       ++pos;
</span><span class="cx">     });
</span><span class="lines">@@ -976,183 +1450,222 @@
</span><span class="cx"> 
</span><span class="cx">   function paddingTop(display) {return display.lineSpace.offsetTop;}
</span><span class="cx">   function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
</span><del>-  function paddingLeft(display) {
-    var e = removeChildrenAndAdd(display.measure, elt(&quot;pre&quot;, null, null, &quot;text-align: left&quot;)).appendChild(elt(&quot;span&quot;, &quot;x&quot;));
-    return e.offsetLeft;
</del><ins>+  function paddingH(display) {
+    if (display.cachedPaddingH) return display.cachedPaddingH;
+    var e = removeChildrenAndAdd(display.measure, elt(&quot;pre&quot;, &quot;x&quot;));
+    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
+    return display.cachedPaddingH = {left: parseInt(style.paddingLeft),
+                                     right: parseInt(style.paddingRight)};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureChar(cm, line, ch, data, bias) {
-    var dir = -1;
-    data = data || measureLine(cm, line);
-    if (data.crude) {
-      var left = data.left + ch * data.width;
-      return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
</del><ins>+  // Ensure the lineView.wrapping.heights array is populated. This is
+  // an array of bottom offsets for the lines that make up a drawn
+  // line. When lineWrapping is on, there might be more than one
+  // height.
+  function ensureLineHeights(cm, lineView, rect) {
+    var wrapping = cm.options.lineWrapping;
+    var curWidth = wrapping &amp;&amp; cm.display.scroller.clientWidth;
+    if (!lineView.measure.heights || wrapping &amp;&amp; lineView.measure.width != curWidth) {
+      var heights = lineView.measure.heights = [];
+      if (wrapping) {
+        lineView.measure.width = curWidth;
+        var rects = lineView.text.firstChild.getClientRects();
+        for (var i = 0; i &lt; rects.length - 1; i++) {
+          var cur = rects[i], next = rects[i + 1];
+          if (Math.abs(cur.bottom - next.bottom) &gt; 2)
+            heights.push((cur.bottom + next.top) / 2 - rect.top);
+        }
+      }
+      heights.push(rect.bottom - rect.top);
</ins><span class="cx">     }
</span><ins>+  }
</ins><span class="cx"> 
</span><del>-    for (var pos = ch;; pos += dir) {
-      var r = data[pos];
-      if (r) break;
-      if (dir &lt; 0 &amp;&amp; pos == 0) dir = 1;
-    }
-    bias = pos &gt; ch ? &quot;left&quot; : pos &lt; ch ? &quot;right&quot; : bias;
-    if (bias == &quot;left&quot; &amp;&amp; r.leftSide) r = r.leftSide;
-    else if (bias == &quot;right&quot; &amp;&amp; r.rightSide) r = r.rightSide;
-    return {left: pos &lt; ch ? r.right : r.left,
-            right: pos &gt; ch ? r.left : r.right,
-            top: r.top,
-            bottom: r.bottom};
</del><ins>+  // Find a line map (mapping character offsets to text nodes) and a
+  // measurement cache for the given line number. (A line view might
+  // contain multiple lines when collapsed ranges are present.)
+  function mapFromLineView(lineView, line, lineN) {
+    if (lineView.line == line)
+      return {map: lineView.measure.map, cache: lineView.measure.cache};
+    for (var i = 0; i &lt; lineView.rest.length; i++)
+      if (lineView.rest[i] == line)
+        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
+    for (var i = 0; i &lt; lineView.rest.length; i++)
+      if (lineNo(lineView.rest[i]) &gt; lineN)
+        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function findCachedMeasurement(cm, line) {
-    var cache = cm.display.measureLineCache;
-    for (var i = 0; i &lt; cache.length; ++i) {
-      var memo = cache[i];
-      if (memo.text == line.text &amp;&amp; memo.markedSpans == line.markedSpans &amp;&amp;
-          cm.display.scroller.clientWidth == memo.width &amp;&amp;
-          memo.classes == line.textClass + &quot;|&quot; + line.wrapClass)
-        return memo;
-    }
</del><ins>+  // Render a line into the hidden node display.externalMeasured. Used
+  // when measurement is needed for a line that's not in the viewport.
+  function updateExternalMeasurement(cm, line) {
+    line = visualLine(line);
+    var lineN = lineNo(line);
+    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
+    view.lineN = lineN;
+    var built = view.built = buildLineContent(cm, view);
+    view.text = built.pre;
+    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
+    return view;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function clearCachedMeasurement(cm, line) {
-    var exists = findCachedMeasurement(cm, line);
-    if (exists) exists.text = exists.measure = exists.markedSpans = null;
</del><ins>+  // Get a {top, bottom, left, right} box (in line-local coordinates)
+  // for a given character.
+  function measureChar(cm, line, ch, bias) {
+    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLine(cm, line) {
-    // First look in the cache
-    var cached = findCachedMeasurement(cm, line);
-    if (cached) return cached.measure;
-
-    // Failing that, recompute and store result in cache
-    var measure = measureLineInner(cm, line);
-    var cache = cm.display.measureLineCache;
-    var memo = {text: line.text, width: cm.display.scroller.clientWidth,
-                markedSpans: line.markedSpans, measure: measure,
-                classes: line.textClass + &quot;|&quot; + line.wrapClass};
-    if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
-    else cache.push(memo);
-    return measure;
</del><ins>+  // Find a line view that corresponds to the given line number.
+  function findViewForLine(cm, lineN) {
+    if (lineN &gt;= cm.display.viewFrom &amp;&amp; lineN &lt; cm.display.viewTo)
+      return cm.display.view[findViewIndex(cm, lineN)];
+    var ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; lineN &gt;= ext.lineN &amp;&amp; lineN &lt; ext.lineN + ext.size)
+      return ext;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLineInner(cm, line) {
-    if (!cm.options.lineWrapping &amp;&amp; line.text.length &gt;= cm.options.crudeMeasuringFrom)
-      return crudelyMeasureLine(cm, line);
</del><ins>+  // Measurement can be split in two steps, the set-up work that
+  // applies to the whole line, and the measurement of the actual
+  // character. Functions like coordsChar, that need to do a lot of
+  // measurements in a row, can thus ensure that the set-up work is
+  // only done once.
+  function prepareMeasureForLine(cm, line) {
+    var lineN = lineNo(line);
+    var view = findViewForLine(cm, lineN);
+    if (view &amp;&amp; !view.text)
+      view = null;
+    else if (view &amp;&amp; view.changes)
+      updateLineForChanges(cm, view, lineN, getDimensions(cm));
+    if (!view)
+      view = updateExternalMeasurement(cm, line);
</ins><span class="cx"> 
</span><del>-    var display = cm.display, measure = emptyArray(line.text.length);
-    var pre = buildLineContent(cm, line, measure, true).pre;
</del><ins>+    var info = mapFromLineView(view, line, lineN);
+    return {
+      line: line, view: view, rect: null,
+      map: info.map, cache: info.cache, before: info.before,
+      hasHeights: false
+    };
+  }
</ins><span class="cx"> 
</span><del>-    // IE does not cache element positions of inline elements between
-    // calls to getBoundingClientRect. This makes the loop below,
-    // which gathers the positions of all the characters on the line,
-    // do an amount of layout work quadratic to the number of
-    // characters. When line wrapping is off, we try to improve things
-    // by first subdividing the line into a bunch of inline blocks, so
-    // that IE can reuse most of the layout information from caches
-    // for those blocks. This does interfere with line wrapping, so it
-    // doesn't work when wrapping is on, but in that case the
-    // situation is slightly better, since IE does cache line-wrapping
-    // information and only recomputes per-line.
-    if (ie &amp;&amp; !ie_lt8 &amp;&amp; !cm.options.lineWrapping &amp;&amp; pre.childNodes.length &gt; 100) {
-      var fragment = document.createDocumentFragment();
-      var chunk = 10, n = pre.childNodes.length;
-      for (var i = 0, chunks = Math.ceil(n / chunk); i &lt; chunks; ++i) {
-        var wrap = elt(&quot;div&quot;, null, null, &quot;display: inline-block&quot;);
-        for (var j = 0; j &lt; chunk &amp;&amp; n; ++j) {
-          wrap.appendChild(pre.firstChild);
-          --n;
-        }
-        fragment.appendChild(wrap);
</del><ins>+  // Given a prepared measurement object, measures the position of an
+  // actual character (or fetches it from the cache).
+  function measureCharPrepared(cm, prepared, ch, bias) {
+    if (prepared.before) ch = -1;
+    var key = ch + (bias || &quot;&quot;), found;
+    if (prepared.cache.hasOwnProperty(key)) {
+      found = prepared.cache[key];
+    } else {
+      if (!prepared.rect)
+        prepared.rect = prepared.view.text.getBoundingClientRect();
+      if (!prepared.hasHeights) {
+        ensureLineHeights(cm, prepared.view, prepared.rect);
+        prepared.hasHeights = true;
</ins><span class="cx">       }
</span><del>-      pre.appendChild(fragment);
</del><ins>+      found = measureCharInner(cm, prepared, ch, bias);
+      if (!found.bogus) prepared.cache[key] = found;
</ins><span class="cx">     }
</span><ins>+    return {left: found.left, right: found.right, top: found.top, bottom: found.bottom};
+  }
</ins><span class="cx"> 
</span><del>-    removeChildrenAndAdd(display.measure, pre);
</del><ins>+  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
</ins><span class="cx"> 
</span><del>-    var outer = getRect(display.lineDiv);
-    var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
-    // Work around an IE7/8 bug where it will sometimes have randomly
-    // replaced our pre with a clone at this point.
-    if (ie_lt9 &amp;&amp; display.measure.first != pre)
-      removeChildrenAndAdd(display.measure, pre);
</del><ins>+  function measureCharInner(cm, prepared, ch, bias) {
+    var map = prepared.map;
</ins><span class="cx"> 
</span><del>-    function measureRect(rect) {
-      var top = rect.top - outer.top, bot = rect.bottom - outer.top;
-      if (bot &gt; maxBot) bot = maxBot;
-      if (top &lt; 0) top = 0;
-      for (var i = vranges.length - 2; i &gt;= 0; i -= 2) {
-        var rtop = vranges[i], rbot = vranges[i+1];
-        if (rtop &gt; bot || rbot &lt; top) continue;
-        if (rtop &lt;= top &amp;&amp; rbot &gt;= bot ||
-            top &lt;= rtop &amp;&amp; bot &gt;= rbot ||
-            Math.min(bot, rbot) - Math.max(top, rtop) &gt;= (bot - top) &gt;&gt; 1) {
-          vranges[i] = Math.min(top, rtop);
-          vranges[i+1] = Math.max(bot, rbot);
-          break;
-        }
</del><ins>+    var node, start, end, collapse;
+    // First, search the line map for the text node corresponding to,
+    // or closest to, the target character.
+    for (var i = 0; i &lt; map.length; i += 3) {
+      var mStart = map[i], mEnd = map[i + 1];
+      if (ch &lt; mStart) {
+        start = 0; end = 1;
+        collapse = &quot;left&quot;;
+      } else if (ch &lt; mEnd) {
+        start = ch - mStart;
+        end = start + 1;
+      } else if (i == map.length - 3 || ch == mEnd &amp;&amp; map[i + 3] &gt; ch) {
+        end = mEnd - mStart;
+        start = end - 1;
+        if (ch &gt;= mEnd) collapse = &quot;right&quot;;
</ins><span class="cx">       }
</span><del>-      if (i &lt; 0) { i = vranges.length; vranges.push(top, bot); }
-      return {left: rect.left - outer.left,
-              right: rect.right - outer.left,
-              top: i, bottom: null};
</del><ins>+      if (start != null) {
+        node = map[i + 2];
+        if (mStart == mEnd &amp;&amp; bias == (node.insertLeft ? &quot;left&quot; : &quot;right&quot;))
+          collapse = bias;
+        if (bias == &quot;left&quot; &amp;&amp; start == 0)
+          while (i &amp;&amp; map[i - 2] == map[i - 3] &amp;&amp; map[i - 1].insertLeft) {
+            node = map[(i -= 3) + 2];
+            collapse = &quot;left&quot;;
+          }
+        if (bias == &quot;right&quot; &amp;&amp; start == mEnd - mStart)
+          while (i &lt; map.length - 3 &amp;&amp; map[i + 3] == map[i + 4] &amp;&amp; !map[i + 5].insertLeft) {
+            node = map[(i += 3) + 2];
+            collapse = &quot;right&quot;;
+          }
+        break;
+      }
</ins><span class="cx">     }
</span><del>-    function finishRect(rect) {
-      rect.bottom = vranges[rect.top+1];
-      rect.top = vranges[rect.top];
-    }
</del><span class="cx"> 
</span><del>-    for (var i = 0, cur; i &lt; measure.length; ++i) if (cur = measure[i]) {
-      var node = cur, rect = null;
-      // A widget might wrap, needs special care
-      if (/\bCodeMirror-widget\b/.test(cur.className) &amp;&amp; cur.getClientRects) {
-        if (cur.firstChild.nodeType == 1) node = cur.firstChild;
-        var rects = node.getClientRects();
-        if (rects.length &gt; 1) {
-          rect = data[i] = measureRect(rects[0]);
-          rect.rightSide = measureRect(rects[rects.length - 1]);
-        }
</del><ins>+    var rect;
+    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
+      while (start &amp;&amp; isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
+      while (mStart + end &lt; mEnd &amp;&amp; isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
+      if (ie_upto8 &amp;&amp; start == 0 &amp;&amp; end == mEnd - mStart) {
+        rect = node.parentNode.getBoundingClientRect();
+      } else if (ie &amp;&amp; cm.options.lineWrapping) {
+        var rects = range(node, start, end).getClientRects();
+        if (rects.length)
+          rect = rects[bias == &quot;right&quot; ? rects.length - 1 : 0];
+        else
+          rect = nullRect;
+      } else {
+        rect = range(node, start, end).getBoundingClientRect();
</ins><span class="cx">       }
</span><del>-      if (!rect) rect = data[i] = measureRect(getRect(node));
-      if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
-      if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
</del><ins>+    } else { // If it is a widget, simply get the box for the whole widget.
+      if (start &gt; 0) collapse = bias = &quot;right&quot;;
+      var rects;
+      if (cm.options.lineWrapping &amp;&amp; (rects = node.getClientRects()).length &gt; 1)
+        rect = rects[bias == &quot;right&quot; ? rects.length - 1 : 0];
+      else
+        rect = node.getBoundingClientRect();
</ins><span class="cx">     }
</span><del>-    removeChildren(cm.display.measure);
-    for (var i = 0, cur; i &lt; data.length; ++i) if (cur = data[i]) {
-      finishRect(cur);
-      if (cur.leftSide) finishRect(cur.leftSide);
-      if (cur.rightSide) finishRect(cur.rightSide);
</del><ins>+    if (ie_upto8 &amp;&amp; !start &amp;&amp; (!rect || !rect.left &amp;&amp; !rect.right)) {
+      var rSpan = node.parentNode.getClientRects()[0];
+      if (rSpan)
+        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
+      else
+        rect = nullRect;
</ins><span class="cx">     }
</span><del>-    return data;
-  }
</del><span class="cx"> 
</span><del>-  function crudelyMeasureLine(cm, line) {
-    var copy = new Line(line.text.slice(0, 100), null);
-    if (line.textClass) copy.textClass = line.textClass;
-    var measure = measureLineInner(cm, copy);
-    var left = measureChar(cm, copy, 0, measure, &quot;left&quot;);
-    var right = measureChar(cm, copy, 99, measure, &quot;right&quot;);
-    return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
</del><ins>+    var top, bot = (rect.bottom + rect.top) / 2 - prepared.rect.top;
+    var heights = prepared.view.measure.heights;
+    for (var i = 0; i &lt; heights.length - 1; i++)
+      if (bot &lt; heights[i]) break;
+    top = i ? heights[i - 1] : 0; bot = heights[i];
+    var result = {left: (collapse == &quot;right&quot; ? rect.right : rect.left) - prepared.rect.left,
+                  right: (collapse == &quot;left&quot; ? rect.left : rect.right) - prepared.rect.left,
+                  top: top, bottom: bot};
+    if (!rect.left &amp;&amp; !rect.right) result.bogus = true;
+    return result;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLineWidth(cm, line) {
-    var hasBadSpan = false;
-    if (line.markedSpans) for (var i = 0; i &lt; line.markedSpans; ++i) {
-      var sp = line.markedSpans[i];
-      if (sp.collapsed &amp;&amp; (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
</del><ins>+  function clearLineMeasurementCacheFor(lineView) {
+    if (lineView.measure) {
+      lineView.measure.cache = {};
+      lineView.measure.heights = null;
+      if (lineView.rest) for (var i = 0; i &lt; lineView.rest.length; i++)
+        lineView.measure.caches[i] = {};
</ins><span class="cx">     }
</span><del>-    var cached = !hasBadSpan &amp;&amp; findCachedMeasurement(cm, line);
-    if (cached || line.text.length &gt;= cm.options.crudeMeasuringFrom)
-      return measureChar(cm, line, line.text.length, cached &amp;&amp; cached.measure, &quot;right&quot;).right;
</del><ins>+  }
</ins><span class="cx"> 
</span><del>-    var pre = buildLineContent(cm, line, null, true).pre;
-    var end = pre.appendChild(zeroWidthElement(cm.display.measure));
-    removeChildrenAndAdd(cm.display.measure, pre);
-    return getRect(end).right - getRect(cm.display.lineDiv).left;
</del><ins>+  function clearLineMeasurementCache(cm) {
+    cm.display.externalMeasure = null;
+    removeChildren(cm.display.lineMeasure);
+    for (var i = 0; i &lt; cm.display.view.length; i++)
+      clearLineMeasurementCacheFor(cm.display.view[i]);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function clearCaches(cm) {
</span><del>-    cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
</del><ins>+    clearLineMeasurementCache(cm);
+    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
</ins><span class="cx">     if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
</span><span class="cx">     cm.display.lineNumChars = null;
</span><span class="cx">   }
</span><span class="lines">@@ -1160,7 +1673,9 @@
</span><span class="cx">   function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
</span><span class="cx">   function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
</span><span class="cx"> 
</span><del>-  // Context is one of &quot;line&quot;, &quot;div&quot; (display.lineDiv), &quot;local&quot;/null (editor), or &quot;page&quot;
</del><ins>+  // Converts a {top, bottom, left, right} box from line-local
+  // coordinates into another coordinate system. Context may be one of
+  // &quot;line&quot;, &quot;div&quot; (display.lineDiv), &quot;local&quot;/null (editor), or &quot;page&quot;.
</ins><span class="cx">   function intoCoordSystem(cm, lineObj, rect, context) {
</span><span class="cx">     if (lineObj.widgets) for (var i = 0; i &lt; lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
</span><span class="cx">       var size = widgetHeight(lineObj.widgets[i]);
</span><span class="lines">@@ -1168,11 +1683,11 @@
</span><span class="cx">     }
</span><span class="cx">     if (context == &quot;line&quot;) return rect;
</span><span class="cx">     if (!context) context = &quot;local&quot;;
</span><del>-    var yOff = heightAtLine(cm, lineObj);
</del><ins>+    var yOff = heightAtLine(lineObj);
</ins><span class="cx">     if (context == &quot;local&quot;) yOff += paddingTop(cm.display);
</span><span class="cx">     else yOff -= cm.display.viewOffset;
</span><span class="cx">     if (context == &quot;page&quot; || context == &quot;window&quot;) {
</span><del>-      var lOff = getRect(cm.display.lineSpace);
</del><ins>+      var lOff = cm.display.lineSpace.getBoundingClientRect();
</ins><span class="cx">       yOff += lOff.top + (context == &quot;window&quot; ? 0 : pageScrollY());
</span><span class="cx">       var xOff = lOff.left + (context == &quot;window&quot; ? 0 : pageScrollX());
</span><span class="cx">       rect.left += xOff; rect.right += xOff;
</span><span class="lines">@@ -1181,8 +1696,8 @@
</span><span class="cx">     return rect;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Context may be &quot;window&quot;, &quot;page&quot;, &quot;div&quot;, or &quot;local&quot;/null
-  // Result is in &quot;div&quot; coords
</del><ins>+  // Coverts a box from &quot;div&quot; coords to another coordinate system.
+  // Context may be &quot;window&quot;, &quot;page&quot;, &quot;div&quot;, or &quot;local&quot;/null.
</ins><span class="cx">   function fromCoordSystem(cm, coords, context) {
</span><span class="cx">     if (context == &quot;div&quot;) return coords;
</span><span class="cx">     var left = coords.left, top = coords.top;
</span><span class="lines">@@ -1191,25 +1706,28 @@
</span><span class="cx">       left -= pageScrollX();
</span><span class="cx">       top -= pageScrollY();
</span><span class="cx">     } else if (context == &quot;local&quot; || !context) {
</span><del>-      var localBox = getRect(cm.display.sizer);
</del><ins>+      var localBox = cm.display.sizer.getBoundingClientRect();
</ins><span class="cx">       left += localBox.left;
</span><span class="cx">       top += localBox.top;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var lineSpaceBox = getRect(cm.display.lineSpace);
</del><ins>+    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
</ins><span class="cx">     return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function charCoords(cm, pos, context, lineObj, bias) {
</span><span class="cx">     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
</span><del>-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
</del><ins>+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function cursorCoords(cm, pos, context, lineObj, measurement) {
</del><ins>+  // Returns a box for a given cursor position, which may have an
+  // 'other' property containing the position of the secondary cursor
+  // on a bidi boundary.
+  function cursorCoords(cm, pos, context, lineObj, preparedMeasure) {
</ins><span class="cx">     lineObj = lineObj || getLine(cm.doc, pos.line);
</span><del>-    if (!measurement) measurement = measureLine(cm, lineObj);
</del><ins>+    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
</ins><span class="cx">     function get(ch, right) {
</span><del>-      var m = measureChar(cm, lineObj, ch, measurement, right ? &quot;right&quot; : &quot;left&quot;);
</del><ins>+      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? &quot;right&quot; : &quot;left&quot;);
</ins><span class="cx">       if (right) m.left = m.right; else m.right = m.left;
</span><span class="cx">       return intoCoordSystem(cm, lineObj, m, context);
</span><span class="cx">     }
</span><span class="lines">@@ -1235,43 +1753,59 @@
</span><span class="cx">     return val;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to cheaply estimate the coordinates for a position. Used for
+  // intermediate scroll updates.
+  function estimateCoords(cm, pos) {
+    var left = 0, pos = clipPos(cm.doc, pos);
+    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
+    var lineObj = getLine(cm.doc, pos.line);
+    var top = heightAtLine(lineObj) + paddingTop(cm.display);
+    return {left: left, right: left, top: top, bottom: top + lineObj.height};
+  }
+
+  // Positions returned by coordsChar contain some extra information.
+  // xRel is the relative x position of the input coordinates compared
+  // to the found position (so xRel &gt; 0 means the coordinates are to
+  // the right of the character position, for example). When outside
+  // is true, that means the coordinates lie outside the line's
+  // vertical range.
</ins><span class="cx">   function PosWithInfo(line, ch, outside, xRel) {
</span><del>-    var pos = new Pos(line, ch);
</del><ins>+    var pos = Pos(line, ch);
</ins><span class="cx">     pos.xRel = xRel;
</span><span class="cx">     if (outside) pos.outside = true;
</span><span class="cx">     return pos;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Coords must be lineSpace-local
</del><ins>+  // Compute the character position closest to the given coordinates.
+  // Input must be lineSpace-local (&quot;div&quot; coordinate system).
</ins><span class="cx">   function coordsChar(cm, x, y) {
</span><span class="cx">     var doc = cm.doc;
</span><span class="cx">     y += cm.display.viewOffset;
</span><span class="cx">     if (y &lt; 0) return PosWithInfo(doc.first, 0, true, -1);
</span><del>-    var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineNo &gt; last)
</del><ins>+    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+    if (lineN &gt; last)
</ins><span class="cx">       return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
</span><span class="cx">     if (x &lt; 0) x = 0;
</span><span class="cx"> 
</span><ins>+    var lineObj = getLine(doc, lineN);
</ins><span class="cx">     for (;;) {
</span><del>-      var lineObj = getLine(doc, lineNo);
-      var found = coordsCharInner(cm, lineObj, lineNo, x, y);
</del><ins>+      var found = coordsCharInner(cm, lineObj, lineN, x, y);
</ins><span class="cx">       var merged = collapsedSpanAtEnd(lineObj);
</span><del>-      var mergedPos = merged &amp;&amp; merged.find();
</del><ins>+      var mergedPos = merged &amp;&amp; merged.find(0, true);
</ins><span class="cx">       if (merged &amp;&amp; (found.ch &gt; mergedPos.from.ch || found.ch == mergedPos.from.ch &amp;&amp; found.xRel &gt; 0))
</span><del>-        lineNo = mergedPos.to.line;
</del><ins>+        lineN = lineNo(lineObj = mergedPos.to.line);
</ins><span class="cx">       else
</span><span class="cx">         return found;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function coordsCharInner(cm, lineObj, lineNo, x, y) {
</span><del>-    var innerOff = y - heightAtLine(cm, lineObj);
</del><ins>+    var innerOff = y - heightAtLine(lineObj);
</ins><span class="cx">     var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
</span><del>-    var measurement = measureLine(cm, lineObj);
</del><ins>+    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
</ins><span class="cx"> 
</span><span class="cx">     function getX(ch) {
</span><del>-      var sp = cursorCoords(cm, Pos(lineNo, ch), &quot;line&quot;,
-                            lineObj, measurement);
</del><ins>+      var sp = cursorCoords(cm, Pos(lineNo, ch), &quot;line&quot;, lineObj, preparedMeasure);
</ins><span class="cx">       wrongLine = true;
</span><span class="cx">       if (innerOff &gt; sp.bottom) return sp.left - adjust;
</span><span class="cx">       else if (innerOff &lt; sp.top) return sp.left + adjust;
</span><span class="lines">@@ -1289,9 +1823,9 @@
</span><span class="cx">       if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from &lt;= 1) {
</span><span class="cx">         var ch = x &lt; fromX || x - fromX &lt;= toX - x ? from : to;
</span><span class="cx">         var xDiff = x - (ch == from ? fromX : toX);
</span><del>-        while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
</del><ins>+        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
</ins><span class="cx">         var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
</span><del>-                              xDiff &lt; 0 ? -1 : xDiff ? 1 : 0);
</del><ins>+                              xDiff &lt; -1 ? -1 : xDiff &gt; 1 ? 1 : 0);
</ins><span class="cx">         return pos;
</span><span class="cx">       }
</span><span class="cx">       var step = Math.ceil(dist / 2), middle = from + step;
</span><span class="lines">@@ -1306,6 +1840,7 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   var measureText;
</span><ins>+  // Compute the default text height.
</ins><span class="cx">   function textHeight(display) {
</span><span class="cx">     if (display.cachedTextHeight != null) return display.cachedTextHeight;
</span><span class="cx">     if (measureText == null) {
</span><span class="lines">@@ -1325,84 +1860,89 @@
</span><span class="cx">     return height || 1;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute the default character width.
</ins><span class="cx">   function charWidth(display) {
</span><span class="cx">     if (display.cachedCharWidth != null) return display.cachedCharWidth;
</span><del>-    var anchor = elt(&quot;span&quot;, &quot;x&quot;);
</del><ins>+    var anchor = elt(&quot;span&quot;, &quot;xxxxxxxxxx&quot;);
</ins><span class="cx">     var pre = elt(&quot;pre&quot;, [anchor]);
</span><span class="cx">     removeChildrenAndAdd(display.measure, pre);
</span><del>-    var width = anchor.offsetWidth;
</del><ins>+    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
</ins><span class="cx">     if (width &gt; 2) display.cachedCharWidth = width;
</span><span class="cx">     return width || 10;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // OPERATIONS
</span><span class="cx"> 
</span><del>-  // Operations are used to wrap changes in such a way that each
-  // change won't have to update the cursor and display (which would
-  // be awkward, slow, and error-prone), but instead updates are
-  // batched and then all combined and executed at once.
</del><ins>+  // Operations are used to wrap a series of changes to the editor
+  // state in such a way that each change won't have to update the
+  // cursor and display (which would be awkward, slow, and
+  // error-prone). Instead, display updates are batched and then all
+  // combined and executed at once.
</ins><span class="cx"> 
</span><span class="cx">   var nextOpId = 0;
</span><ins>+  // Start a new operation.
</ins><span class="cx">   function startOperation(cm) {
</span><span class="cx">     cm.curOp = {
</span><del>-      // An array of ranges of lines that have to be updated. See
-      // updateDisplay.
-      changes: [],
-      forceUpdate: false,
-      updateInput: null,
-      userSelChange: null,
-      textChanged: null,
-      selectionChanged: false,
-      cursorActivity: false,
-      updateMaxLine: false,
-      updateScrollPos: false,
-      id: ++nextOpId
</del><ins>+      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
+      startHeight: cm.doc.height, // Used to detect need to update scrollbar
+      forceUpdate: false,      // Used to force a redraw
+      updateInput: null,       // Whether to reset the input textarea
+      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
+      changeObjs: null,        // Accumulated changes, for firing change events
+      origin: null,            // Selection's origin
+      cursorActivity: false,   // Whether to fire a cursorActivity event
+      selectionChanged: false, // Whether the selection needs to be redrawn
+      updateMaxLine: false,    // Set when the widest line needs to be determined anew
+      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+      scrollToPos: null,       // Used to scroll to a specific position
+      id: ++nextOpId           // Unique ID
</ins><span class="cx">     };
</span><span class="cx">     if (!delayedCallbackDepth++) delayedCallbacks = [];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Finish an operation, updating the display and signalling delayed events
</ins><span class="cx">   function endOperation(cm) {
</span><span class="cx">     var op = cm.curOp, doc = cm.doc, display = cm.display;
</span><span class="cx">     cm.curOp = null;
</span><span class="cx"> 
</span><del>-    if (op.updateMaxLine) computeMaxLength(cm);
-    if (display.maxLineChanged &amp;&amp; !cm.options.lineWrapping &amp;&amp; display.maxLine) {
-      var width = measureLineWidth(cm, display.maxLine);
-      display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + &quot;px&quot;;
-      display.maxLineChanged = false;
-      var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
-      if (maxScrollLeft &lt; doc.scrollLeft &amp;&amp; !op.updateScrollPos)
-        setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
-    }
-    var newScrollPos, updated;
-    if (op.updateScrollPos) {
-      newScrollPos = op.updateScrollPos;
-    } else if (op.selectionChanged &amp;&amp; display.scroller.clientHeight) { // don't rescroll if not visible
-      var coords = cursorCoords(cm, doc.sel.head);
-      newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
-    }
-    if (op.changes.length || op.forceUpdate || newScrollPos &amp;&amp; newScrollPos.scrollTop != null) {
-      updated = updateDisplay(cm, op.changes, newScrollPos &amp;&amp; newScrollPos.scrollTop, op.forceUpdate);
</del><ins>+    if (op.updateMaxLine) findMaxLine(cm);
+
+    // If it looks like an update might be needed, call updateDisplay
+    if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
+        op.scrollToPos &amp;&amp; (op.scrollToPos.from.line &lt; display.viewFrom ||
+                           op.scrollToPos.to.line &gt;= display.viewTo) ||
+        display.maxLineChanged &amp;&amp; cm.options.lineWrapping) {
+      var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
</ins><span class="cx">       if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
</span><span class="cx">     }
</span><ins>+    // If no update was run, but the selection changed, redraw that.
</ins><span class="cx">     if (!updated &amp;&amp; op.selectionChanged) updateSelection(cm);
</span><del>-    if (op.updateScrollPos) {
-      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop));
-      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft));
</del><ins>+    if (!updated &amp;&amp; op.startHeight != cm.doc.height) updateScrollbars(cm);
+
+    // Propagate the scroll position to the actual DOM scroller
+    if (op.scrollTop != null &amp;&amp; display.scroller.scrollTop != op.scrollTop) {
+      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
</ins><span class="cx">       display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
</span><ins>+    }
+    if (op.scrollLeft != null &amp;&amp; display.scroller.scrollLeft != op.scrollLeft) {
+      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
</ins><span class="cx">       display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
</span><span class="cx">       alignHorizontally(cm);
</span><del>-      if (op.scrollToPos)
-        scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
-                          clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
-    } else if (newScrollPos) {
-      scrollCursorIntoView(cm);
</del><span class="cx">     }
</span><ins>+    // If we need to scroll a specific position into view, do so.
+    if (op.scrollToPos) {
+      var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
+                                     clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
+      if (op.scrollToPos.isCursor &amp;&amp; cm.state.focused) maybeScrollWindow(cm, coords);
+    }
+
</ins><span class="cx">     if (op.selectionChanged) restartBlink(cm);
</span><span class="cx"> 
</span><span class="cx">     if (cm.state.focused &amp;&amp; op.updateInput)
</span><del>-      resetInput(cm, op.userSelChange);
</del><ins>+      resetInput(cm, op.typing);
</ins><span class="cx"> 
</span><ins>+    // Fire events for markers that are hidden/unidden by editing or
+    // undoing
</ins><span class="cx">     var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
</span><span class="cx">     if (hidden) for (var i = 0; i &lt; hidden.length; ++i)
</span><span class="cx">       if (!hidden[i].lines.length) signal(hidden[i], &quot;hide&quot;);
</span><span class="lines">@@ -1414,47 +1954,242 @@
</span><span class="cx">       delayed = delayedCallbacks;
</span><span class="cx">       delayedCallbacks = null;
</span><span class="cx">     }
</span><del>-    if (op.textChanged)
-      signal(cm, &quot;change&quot;, cm, op.textChanged);
-    if (op.cursorActivity) signal(cm, &quot;cursorActivity&quot;, cm);
</del><ins>+    // Fire change events, and delayed event handlers
+    if (op.changeObjs) {
+      for (var i = 0; i &lt; op.changeObjs.length; i++)
+        signal(cm, &quot;change&quot;, cm, op.changeObjs[i]);
+      signal(cm, &quot;changes&quot;, cm, op.changeObjs);
+    }
+    if (op.cursorActivity) signal(cm, &quot;cursorActivity&quot;, cm, op.origin);
</ins><span class="cx">     if (delayed) for (var i = 0; i &lt; delayed.length; ++i) delayed[i]();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Run the given function in an operation
+  function runInOp(cm, f) {
+    if (cm.curOp) return f();
+    startOperation(cm);
+    try { return f(); }
+    finally { endOperation(cm); }
+  }
</ins><span class="cx">   // Wraps a function in an operation. Returns the wrapped function.
</span><del>-  function operation(cm1, f) {
</del><ins>+  function operation(cm, f) {
</ins><span class="cx">     return function() {
</span><del>-      var cm = cm1 || this, withOp = !cm.curOp;
-      if (withOp) startOperation(cm);
-      try { var result = f.apply(cm, arguments); }
-      finally { if (withOp) endOperation(cm); }
-      return result;
</del><ins>+      if (cm.curOp) return f.apply(cm, arguments);
+      startOperation(cm);
+      try { return f.apply(cm, arguments); }
+      finally { endOperation(cm); }
</ins><span class="cx">     };
</span><span class="cx">   }
</span><del>-  function docOperation(f) {
</del><ins>+  // Used to add methods to editor and doc instances, wrapping them in
+  // operations.
+  function methodOp(f) {
</ins><span class="cx">     return function() {
</span><del>-      var withOp = this.cm &amp;&amp; !this.cm.curOp, result;
-      if (withOp) startOperation(this.cm);
-      try { result = f.apply(this, arguments); }
-      finally { if (withOp) endOperation(this.cm); }
-      return result;
</del><ins>+      if (this.curOp) return f.apply(this, arguments);
+      startOperation(this);
+      try { return f.apply(this, arguments); }
+      finally { endOperation(this); }
</ins><span class="cx">     };
</span><span class="cx">   }
</span><del>-  function runInOp(cm, f) {
-    var withOp = !cm.curOp, result;
-    if (withOp) startOperation(cm);
-    try { result = f(); }
-    finally { if (withOp) endOperation(cm); }
-    return result;
</del><ins>+  function docMethodOp(f) {
+    return function() {
+      var cm = this.cm;
+      if (!cm || cm.curOp) return f.apply(this, arguments);
+      startOperation(cm);
+      try { return f.apply(this, arguments); }
+      finally { endOperation(cm); }
+    };
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // VIEW TRACKING
+
+  // These objects are used to represent the visible (currently drawn)
+  // part of the document. A LineView may correspond to multiple
+  // logical lines, if those are connected by collapsed ranges.
+  function LineView(doc, line, lineN) {
+    // The starting line
+    this.line = line;
+    // Continuing lines, if any
+    this.rest = visualLineContinued(line);
+    // Number of logical lines in this visual line
+    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
+    this.node = this.text = null;
+    this.hidden = lineIsHidden(doc, line);
+  }
+
+  // Create a range of LineView objects for the given lines.
+  function buildViewArray(cm, from, to) {
+    var array = [], nextPos;
+    for (var pos = from; pos &lt; to; pos = nextPos) {
+      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
+      nextPos = pos + view.size;
+      array.push(view);
+    }
+    return array;
+  }
+
+  // Updates the display.view data structure for a given change to the
+  // document. From and to are in pre-change coordinates. Lendiff is
+  // the amount of lines added or subtracted by the change. This is
+  // used for changes that span multiple lines, or change the way
+  // lines are divided into visual lines. regLineChange (below)
+  // registers single-line changes.
</ins><span class="cx">   function regChange(cm, from, to, lendiff) {
</span><span class="cx">     if (from == null) from = cm.doc.first;
</span><span class="cx">     if (to == null) to = cm.doc.first + cm.doc.size;
</span><del>-    cm.curOp.changes.push({from: from, to: to, diff: lendiff});
</del><ins>+    if (!lendiff) lendiff = 0;
+
+    var display = cm.display;
+    if (lendiff &amp;&amp; to &lt; display.viewTo &amp;&amp;
+        (display.updateLineNumbers == null || display.updateLineNumbers &gt; from))
+      display.updateLineNumbers = from;
+
+    cm.curOp.viewChanged = true;
+
+    if (from &gt;= display.viewTo) { // Change after
+      if (sawCollapsedSpans &amp;&amp; visualLineNo(cm.doc, from) &lt; display.viewTo)
+        resetView(cm);
+    } else if (to &lt;= display.viewFrom) { // Change before
+      if (sawCollapsedSpans &amp;&amp; visualLineEndNo(cm.doc, to + lendiff) &gt; display.viewFrom) {
+        resetView(cm);
+      } else {
+        display.viewFrom += lendiff;
+        display.viewTo += lendiff;
+      }
+    } else if (from &lt;= display.viewFrom &amp;&amp; to &gt;= display.viewTo) { // Full overlap
+      resetView(cm);
+    } else if (from &lt;= display.viewFrom) { // Top overlap
+      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cut) {
+        display.view = display.view.slice(cut.index);
+        display.viewFrom = cut.lineN;
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    } else if (to &gt;= display.viewTo) { // Bottom overlap
+      var cut = viewCuttingPoint(cm, from, from, -1);
+      if (cut) {
+        display.view = display.view.slice(0, cut.index);
+        display.viewTo = cut.lineN;
+      } else {
+        resetView(cm);
+      }
+    } else { // Gap in the middle
+      var cutTop = viewCuttingPoint(cm, from, from, -1);
+      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cutTop &amp;&amp; cutBot) {
+        display.view = display.view.slice(0, cutTop.index)
+          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+          .concat(display.view.slice(cutBot.index));
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    }
+
+    var ext = display.externalMeasured;
+    if (ext) {
+      if (to &lt; ext.lineN)
+        ext.lineN += lendiff;
+      else if (from &lt; ext.lineN + ext.size)
+        display.externalMeasured = null;
+    }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Register a change to a single line. Type must be one of &quot;text&quot;,
+  // &quot;gutter&quot;, &quot;class&quot;, &quot;widget&quot;
+  function regLineChange(cm, line, type) {
+    cm.curOp.viewChanged = true;
+    var display = cm.display, ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; line &gt;= ext.lineN &amp;&amp; line &lt; ext.lineN + ext.size)
+      display.externalMeasured = null;
+
+    if (line &lt; display.viewFrom || line &gt;= display.viewTo) return;
+    var lineView = display.view[findViewIndex(cm, line)];
+    if (lineView.node == null) return;
+    var arr = lineView.changes || (lineView.changes = []);
+    if (indexOf(arr, type) == -1) arr.push(type);
+  }
+
+  // Clear the view.
+  function resetView(cm) {
+    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
+    cm.display.view = [];
+    cm.display.viewOffset = 0;
+  }
+
+  // Find the view element corresponding to a given line. Return null
+  // when the line isn't visible.
+  function findViewIndex(cm, n) {
+    if (n &gt;= cm.display.viewTo) return null;
+    n -= cm.display.viewFrom;
+    if (n &lt; 0) return null;
+    var view = cm.display.view;
+    for (var i = 0; i &lt; view.length; i++) {
+      n -= view[i].size;
+      if (n &lt; 0) return i;
+    }
+  }
+
+  function viewCuttingPoint(cm, oldN, newN, dir) {
+    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
+    if (!sawCollapsedSpans) return {index: index, lineN: newN};
+    for (var i = 0, n = cm.display.viewFrom; i &lt; index; i++)
+      n += view[i].size;
+    if (n != oldN) {
+      if (dir &gt; 0) {
+        if (index == view.length - 1) return null;
+        diff = (n + view[index].size) - oldN;
+        index++;
+      } else {
+        diff = n - oldN;
+      }
+      oldN += diff; newN += diff;
+    }
+    while (visualLineNo(cm.doc, newN) != newN) {
+      if (index == (dir &lt; 0 ? 0 : view.length - 1)) return null;
+      newN += dir * view[index - (dir &lt; 0 ? 1 : 0)].size;
+      index += dir;
+    }
+    return {index: index, lineN: newN};
+  }
+
+  // Force the view to cover a given range, adding empty view element
+  // or clipping off existing ones as needed.
+  function adjustView(cm, from, to) {
+    var display = cm.display, view = display.view;
+    if (view.length == 0 || from &gt;= display.viewTo || to &lt;= display.viewFrom) {
+      display.view = buildViewArray(cm, from, to);
+      display.viewFrom = from;
+    } else {
+      if (display.viewFrom &gt; from)
+        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
+      else if (display.viewFrom &lt; from)
+        display.view = display.view.slice(findViewIndex(cm, from));
+      display.viewFrom = from;
+      if (display.viewTo &lt; to)
+        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
+      else if (display.viewTo &gt; to)
+        display.view = display.view.slice(0, findViewIndex(cm, to));
+    }
+    display.viewTo = to;
+  }
+
+  // Count the number of lines in the view whose DOM representation is
+  // out of date (or nonexistent).
+  function countDirtyView(cm) {
+    var view = cm.display.view, dirty = 0;
+    for (var i = 0; i &lt; view.length; i++) {
+      var lineView = view[i];
+      if (!lineView.hidden &amp;&amp; (!lineView.node || lineView.changes)) ++dirty;
+    }
+    return dirty;
+  }
+
</ins><span class="cx">   // INPUT HANDLING
</span><span class="cx"> 
</span><ins>+  // Poll for input changes, using the normal rate of polling. This
+  // runs as long as the editor is focused.
</ins><span class="cx">   function slowPoll(cm) {
</span><span class="cx">     if (cm.display.pollingFast) return;
</span><span class="cx">     cm.display.poll.set(cm.options.pollInterval, function() {
</span><span class="lines">@@ -1463,6 +2198,9 @@
</span><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // When an event has just come in that is likely to add or change
+  // something in the input textarea, we poll faster, to ensure that
+  // the change appears on the screen quickly.
</ins><span class="cx">   function fastPoll(cm) {
</span><span class="cx">     var missed = false;
</span><span class="cx">     cm.display.pollingFast = true;
</span><span class="lines">@@ -1474,100 +2212,147 @@
</span><span class="cx">     cm.display.poll.set(20, p);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // prevInput is a hack to work with IME. If we reset the textarea
-  // on every change, that breaks IME. So we look for changes
-  // compared to the previous content instead. (Modern browsers have
-  // events that indicate IME taking place, but these are not widely
-  // supported or compatible enough yet to rely on.)
</del><ins>+  // Read input from the textarea, and update the document to match.
+  // When something is selected, it is present in the textarea, and
+  // selected (unless it is huge, in which case a placeholder is
+  // used). When nothing is selected, the cursor sits after previously
+  // seen text (can be empty), which is stored in prevInput (we must
+  // not reset the textarea when typing, because that breaks IME).
</ins><span class="cx">   function readInput(cm) {
</span><del>-    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
-    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
-    if (cm.state.pasteIncoming &amp;&amp; cm.state.fakedLastChar) {
-      input.value = input.value.substring(0, input.value.length - 1);
-      cm.state.fakedLastChar = false;
-    }
</del><ins>+    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc;
+    // Since this is called a *lot*, try to bail out as cheaply as
+    // possible when it is clear that nothing happened. hasSelection
+    // will be the case when there is a lot of text in the textarea,
+    // in which case reading its value would be expensive.
+    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false;
</ins><span class="cx">     var text = input.value;
</span><del>-    if (text == prevInput &amp;&amp; posEq(sel.from, sel.to)) return false;
-    if (ie &amp;&amp; !ie_lt9 &amp;&amp; cm.display.inputHasSelection === text) {
-      resetInput(cm, true);
</del><ins>+    // If nothing changed, bail.
+    if (text == prevInput &amp;&amp; !cm.somethingSelected()) return false;
+    // Work around nonsensical selection resetting in IE9/10
+    if (ie &amp;&amp; !ie_upto8 &amp;&amp; cm.display.inputHasSelection === text) {
+      resetInput(cm);
</ins><span class="cx">       return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var withOp = !cm.curOp;
</span><span class="cx">     if (withOp) startOperation(cm);
</span><del>-    sel.shift = false;
</del><ins>+    cm.display.shift = false;
+
+    // Find the part of the input that is actually new
</ins><span class="cx">     var same = 0, l = Math.min(prevInput.length, text.length);
</span><span class="cx">     while (same &lt; l &amp;&amp; prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
</span><del>-    var from = sel.from, to = sel.to;
-    if (same &lt; prevInput.length)
-      from = Pos(from.line, from.ch - (prevInput.length - same));
-    else if (cm.state.overwrite &amp;&amp; posEq(from, to) &amp;&amp; !cm.state.pasteIncoming)
-      to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
</del><ins>+    var inserted = text.slice(same), textLines = splitLines(inserted);
</ins><span class="cx"> 
</span><del>-    var updateInput = cm.curOp.updateInput;
-    var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
-                       origin: cm.state.pasteIncoming ? &quot;paste&quot; : &quot;+input&quot;};
-    makeChange(cm.doc, changeEvent, &quot;end&quot;);
</del><ins>+    // When pasing N lines into N selections, insert one line per selection
+    var multiPaste = cm.state.pasteIncoming &amp;&amp; textLines.length &gt; 1 &amp;&amp; doc.sel.ranges.length == textLines.length;
+
+    // Normal behavior is to insert the new text into every selection
+    for (var i = doc.sel.ranges.length - 1; i &gt;= 0; i--) {
+      var range = doc.sel.ranges[i];
+      var from = range.from(), to = range.to();
+      // Handle deletion
+      if (same &lt; prevInput.length)
+        from = Pos(from.line, from.ch - (prevInput.length - same));
+      // Handle overwrite
+      else if (cm.state.overwrite &amp;&amp; range.empty() &amp;&amp; !cm.state.pasteIncoming)
+        to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
+      var updateInput = cm.curOp.updateInput;
+      var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines,
+                         origin: cm.state.pasteIncoming ? &quot;paste&quot; : cm.state.cutIncoming ? &quot;cut&quot; : &quot;+input&quot;};
+      makeChange(cm.doc, changeEvent);
+      signalLater(cm, &quot;inputRead&quot;, cm, changeEvent);
+      // When an 'electric' character is inserted, immediately trigger a reindent
+      if (inserted &amp;&amp; !cm.state.pasteIncoming &amp;&amp; cm.options.electricChars &amp;&amp;
+          cm.options.smartIndent &amp;&amp; range.head.ch &lt; 100 &amp;&amp;
+          (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
+        var mode = cm.getModeAt(range.head);
+        if (mode.electricChars) {
+          for (var j = 0; j &lt; mode.electricChars.length; j++)
+            if (inserted.indexOf(mode.electricChars.charAt(j)) &gt; -1) {
+              indentLine(cm, range.head.line, &quot;smart&quot;);
+              break;
+            }
+        } else if (mode.electricInput) {
+          var end = changeEnd(changeEvent);
+          if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
+            indentLine(cm, range.head.line, &quot;smart&quot;);
+        }
+      }
+    }
+    ensureCursorVisible(cm);
</ins><span class="cx">     cm.curOp.updateInput = updateInput;
</span><del>-    signalLater(cm, &quot;inputRead&quot;, cm, changeEvent);
</del><ins>+    cm.curOp.typing = true;
</ins><span class="cx"> 
</span><ins>+    // Don't leave long text in the textarea, since it makes further polling slow
</ins><span class="cx">     if (text.length &gt; 1000 || text.indexOf(&quot;\n&quot;) &gt; -1) input.value = cm.display.prevInput = &quot;&quot;;
</span><span class="cx">     else cm.display.prevInput = text;
</span><span class="cx">     if (withOp) endOperation(cm);
</span><del>-    cm.state.pasteIncoming = false;
</del><ins>+    cm.state.pasteIncoming = cm.state.cutIncoming = false;
</ins><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function resetInput(cm, user) {
</del><ins>+  // Reset the input to correspond to the selection (or to be empty,
+  // when not typing and nothing is selected)
+  function resetInput(cm, typing) {
</ins><span class="cx">     var minimal, selected, doc = cm.doc;
</span><del>-    if (!posEq(doc.sel.from, doc.sel.to)) {
</del><ins>+    if (cm.somethingSelected()) {
</ins><span class="cx">       cm.display.prevInput = &quot;&quot;;
</span><ins>+      var range = doc.sel.primary();
</ins><span class="cx">       minimal = hasCopyEvent &amp;&amp;
</span><del>-        (doc.sel.to.line - doc.sel.from.line &gt; 100 || (selected = cm.getSelection()).length &gt; 1000);
</del><ins>+        (range.to().line - range.from().line &gt; 100 || (selected = cm.getSelection()).length &gt; 1000);
</ins><span class="cx">       var content = minimal ? &quot;-&quot; : selected || cm.getSelection();
</span><span class="cx">       cm.display.input.value = content;
</span><span class="cx">       if (cm.state.focused) selectInput(cm.display.input);
</span><del>-      if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = content;
-    } else if (user) {
</del><ins>+      if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = content;
+    } else if (!typing) {
</ins><span class="cx">       cm.display.prevInput = cm.display.input.value = &quot;&quot;;
</span><del>-      if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = null;
</del><ins>+      if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = null;
</ins><span class="cx">     }
</span><span class="cx">     cm.display.inaccurateSelection = minimal;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function focusInput(cm) {
</span><del>-    if (cm.options.readOnly != &quot;nocursor&quot; &amp;&amp; (!mobile || document.activeElement != cm.display.input))
</del><ins>+    if (cm.options.readOnly != &quot;nocursor&quot; &amp;&amp; (!mobile || activeElt() != cm.display.input))
</ins><span class="cx">       cm.display.input.focus();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function ensureFocus(cm) {
+    if (!cm.state.focused) { focusInput(cm); onFocus(cm); }
+  }
+
</ins><span class="cx">   function isReadOnly(cm) {
</span><span class="cx">     return cm.options.readOnly || cm.doc.cantEdit;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // EVENT HANDLERS
</span><span class="cx"> 
</span><ins>+  // Attach the necessary event handlers when initializing the editor
</ins><span class="cx">   function registerEventHandlers(cm) {
</span><span class="cx">     var d = cm.display;
</span><span class="cx">     on(d.scroller, &quot;mousedown&quot;, operation(cm, onMouseDown));
</span><del>-    if (ie)
</del><ins>+    // Older IE's will not fire a second mousedown for a double click
+    if (ie_upto10)
</ins><span class="cx">       on(d.scroller, &quot;dblclick&quot;, operation(cm, function(e) {
</span><span class="cx">         if (signalDOMEvent(cm, e)) return;
</span><span class="cx">         var pos = posFromMouse(cm, e);
</span><span class="cx">         if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
</span><span class="cx">         e_preventDefault(e);
</span><del>-        var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
-        extendSelection(cm.doc, word.from, word.to);
</del><ins>+        var word = findWordAt(cm.doc, pos);
+        extendSelection(cm.doc, word.anchor, word.head);
</ins><span class="cx">       }));
</span><span class="cx">     else
</span><span class="cx">       on(d.scroller, &quot;dblclick&quot;, function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
</span><ins>+    // Prevent normal selection in the editor (we handle our own)
</ins><span class="cx">     on(d.lineSpace, &quot;selectstart&quot;, function(e) {
</span><span class="cx">       if (!eventInWidget(d, e)) e_preventDefault(e);
</span><span class="cx">     });
</span><del>-    // Gecko browsers fire contextmenu *after* opening the menu, at
</del><ins>+    // Some browsers fire contextmenu *after* opening the menu, at
</ins><span class="cx">     // which point we can't mess with it anymore. Context menu is
</span><del>-    // handled in onMouseDown for Gecko.
-    if (!captureMiddleClick) on(d.scroller, &quot;contextmenu&quot;, function(e) {onContextMenu(cm, e);});
</del><ins>+    // handled in onMouseDown for these browsers.
+    if (!captureRightClick) on(d.scroller, &quot;contextmenu&quot;, function(e) {onContextMenu(cm, e);});
</ins><span class="cx"> 
</span><ins>+    // Sync scrolling between fake scrollbars and real scrollable
+    // area, ensure viewport is updated when scrolling.
</ins><span class="cx">     on(d.scroller, &quot;scroll&quot;, function() {
</span><span class="cx">       if (d.scroller.clientHeight) {
</span><span class="cx">         setScrollTop(cm, d.scroller.scrollTop);
</span><span class="lines">@@ -1582,42 +2367,40 @@
</span><span class="cx">       if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
</span><span class="cx">     });
</span><span class="cx"> 
</span><ins>+    // Listen to wheel events in order to try and update the viewport on time.
</ins><span class="cx">     on(d.scroller, &quot;mousewheel&quot;, function(e){onScrollWheel(cm, e);});
</span><span class="cx">     on(d.scroller, &quot;DOMMouseScroll&quot;, function(e){onScrollWheel(cm, e);});
</span><span class="cx"> 
</span><ins>+    // Prevent clicks in the scrollbars from killing focus
</ins><span class="cx">     function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
</span><span class="cx">     on(d.scrollbarH, &quot;mousedown&quot;, reFocus);
</span><span class="cx">     on(d.scrollbarV, &quot;mousedown&quot;, reFocus);
</span><span class="cx">     // Prevent wrapper from ever scrolling
</span><span class="cx">     on(d.wrapper, &quot;scroll&quot;, function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
</span><span class="cx"> 
</span><ins>+    // When the window resizes, we need to refresh active editors.
</ins><span class="cx">     var resizeTimer;
</span><span class="cx">     function onResize() {
</span><span class="cx">       if (resizeTimer == null) resizeTimer = setTimeout(function() {
</span><span class="cx">         resizeTimer = null;
</span><span class="cx">         // Might be a text scaling operation, clear size caches.
</span><del>-        d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
-        clearCaches(cm);
-        runInOp(cm, bind(regChange, cm));
</del><ins>+        d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = knownScrollbarWidth = null;
+        cm.setSize();
</ins><span class="cx">       }, 100);
</span><span class="cx">     }
</span><span class="cx">     on(window, &quot;resize&quot;, onResize);
</span><del>-    // Above handler holds on to the editor and its data structures.
-    // Here we poll to unregister it when the editor is no longer in
-    // the document, so that it can be garbage-collected.
</del><ins>+    // The above handler holds on to the editor and its data
+    // structures. Here we poll to unregister it when the editor is no
+    // longer in the document, so that it can be garbage-collected.
</ins><span class="cx">     function unregister() {
</span><del>-      for (var p = d.wrapper.parentNode; p &amp;&amp; p != document.body; p = p.parentNode) {}
-      if (p) setTimeout(unregister, 5000);
</del><ins>+      if (contains(document.body, d.wrapper)) setTimeout(unregister, 5000);
</ins><span class="cx">       else off(window, &quot;resize&quot;, onResize);
</span><span class="cx">     }
</span><span class="cx">     setTimeout(unregister, 5000);
</span><span class="cx"> 
</span><del>-    on(d.input, &quot;keyup&quot;, operation(cm, function(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
-      if (e.keyCode == 16) cm.doc.sel.shift = false;
-    }));
</del><ins>+    on(d.input, &quot;keyup&quot;, operation(cm, onKeyUp));
</ins><span class="cx">     on(d.input, &quot;input&quot;, function() {
</span><del>-      if (ie &amp;&amp; !ie_lt9 &amp;&amp; cm.display.inputHasSelection) cm.display.inputHasSelection = null;
</del><ins>+      if (ie &amp;&amp; !ie_upto8 &amp;&amp; cm.display.inputHasSelection) cm.display.inputHasSelection = null;
</ins><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx">     on(d.input, &quot;keydown&quot;, operation(cm, onKeyDown));
</span><span class="lines">@@ -1626,8 +2409,7 @@
</span><span class="cx">     on(d.input, &quot;blur&quot;, bind(onBlur, cm));
</span><span class="cx"> 
</span><span class="cx">     function drag_(e) {
</span><del>-      if (signalDOMEvent(cm, e) || cm.options.onDragEvent &amp;&amp; cm.options.onDragEvent(cm, addStop(e))) return;
-      e_stop(e);
</del><ins>+      if (!signalDOMEvent(cm, e)) e_stop(e);
</ins><span class="cx">     }
</span><span class="cx">     if (cm.options.dragDrop) {
</span><span class="cx">       on(d.scroller, &quot;dragstart&quot;, function(e){onDragStart(cm, e);});
</span><span class="lines">@@ -1637,70 +2419,98 @@
</span><span class="cx">     }
</span><span class="cx">     on(d.scroller, &quot;paste&quot;, function(e) {
</span><span class="cx">       if (eventInWidget(d, e)) return;
</span><ins>+      cm.state.pasteIncoming = true;
</ins><span class="cx">       focusInput(cm);
</span><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx">     on(d.input, &quot;paste&quot;, function() {
</span><del>-      // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
-      // Add a char to the end of textarea before paste occur so that
-      // selection doesn't span to the end of textarea.
-      if (webkit &amp;&amp; !cm.state.fakedLastChar &amp;&amp; !(new Date - cm.state.lastMiddleDown &lt; 200)) {
-        var start = d.input.selectionStart, end = d.input.selectionEnd;
-        d.input.value += &quot;$&quot;;
-        d.input.selectionStart = start;
-        d.input.selectionEnd = end;
-        cm.state.fakedLastChar = true;
-      }
</del><span class="cx">       cm.state.pasteIncoming = true;
</span><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx"> 
</span><del>-    function prepareCopy() {
-      if (d.inaccurateSelection) {
-        d.prevInput = &quot;&quot;;
-        d.inaccurateSelection = false;
-        d.input.value = cm.getSelection();
-        selectInput(d.input);
</del><ins>+    function prepareCopyCut(e) {
+      if (cm.somethingSelected()) {
+        if (d.inaccurateSelection) {
+          d.prevInput = &quot;&quot;;
+          d.inaccurateSelection = false;
+          d.input.value = cm.getSelection();
+          selectInput(d.input);
+        }
+      } else {
+        var text = &quot;&quot;, ranges = [];
+        for (var i = 0; i &lt; cm.doc.sel.ranges.length; i++) {
+          var line = cm.doc.sel.ranges[i].head.line;
+          var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
+          ranges.push(lineRange);
+          text += cm.getRange(lineRange.anchor, lineRange.head);
+        }
+        if (e.type == &quot;cut&quot;) {
+          cm.setSelections(ranges, null, sel_dontScroll);
+        } else {
+          d.prevInput = &quot;&quot;;
+          d.input.value = text;
+          selectInput(d.input);
+        }
</ins><span class="cx">       }
</span><ins>+      if (e.type == &quot;cut&quot;) cm.state.cutIncoming = true;
</ins><span class="cx">     }
</span><del>-    on(d.input, &quot;cut&quot;, prepareCopy);
-    on(d.input, &quot;copy&quot;, prepareCopy);
</del><ins>+    on(d.input, &quot;cut&quot;, prepareCopyCut);
+    on(d.input, &quot;copy&quot;, prepareCopyCut);
</ins><span class="cx"> 
</span><span class="cx">     // Needed to handle Tab key in KHTML
</span><span class="cx">     if (khtml) on(d.sizer, &quot;mouseup&quot;, function() {
</span><del>-        if (document.activeElement == d.input) d.input.blur();
-        focusInput(cm);
</del><ins>+      if (activeElt() == d.input) d.input.blur();
+      focusInput(cm);
</ins><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // MOUSE EVENTS
+
+  // Return true when the given mouse event happened in a widget
</ins><span class="cx">   function eventInWidget(display, e) {
</span><span class="cx">     for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
</span><span class="cx">       if (!n || n.ignoreEvents || n.parentNode == display.sizer &amp;&amp; n != display.mover) return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function posFromMouse(cm, e, liberal) {
</del><ins>+  // Given a mouse event, find the corresponding position. If liberal
+  // is false, it checks whether a gutter or scrollbar was clicked,
+  // and returns null if it was. forRect is used by rectangular
+  // selections, and tries to estimate a character position even for
+  // coordinates beyond the right of the text.
+  function posFromMouse(cm, e, liberal, forRect) {
</ins><span class="cx">     var display = cm.display;
</span><span class="cx">     if (!liberal) {
</span><span class="cx">       var target = e_target(e);
</span><del>-      if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
-          target == display.scrollbarV || target == display.scrollbarV.firstChild ||
</del><ins>+      if (target == display.scrollbarH || target == display.scrollbarV ||
</ins><span class="cx">           target == display.scrollbarFiller || target == display.gutterFiller) return null;
</span><span class="cx">     }
</span><del>-    var x, y, space = getRect(display.lineSpace);
</del><ins>+    var x, y, space = display.lineSpace.getBoundingClientRect();
</ins><span class="cx">     // Fails unpredictably on IE[67] when mouse is dragged around quickly.
</span><del>-    try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
-    return coordsChar(cm, x - space.left, y - space.top);
</del><ins>+    try { x = e.clientX - space.left; y = e.clientY - space.top; }
+    catch (e) { return null; }
+    var coords = coordsChar(cm, x, y), line;
+    if (forRect &amp;&amp; coords.xRel == 1 &amp;&amp; (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
+      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
+      coords = Pos(coords.line, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff);
+    }
+    return coords;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var lastClick, lastDoubleClick;
</del><ins>+  // A mouse down can be a single click, double click, triple click,
+  // start of selection drag, start of text drag, new cursor
+  // (ctrl-click), rectangle drag (alt-drag), or xwin
+  // middle-click-paste. Or it might be a click on something we should
+  // not interfere with, such as a scrollbar or widget.
</ins><span class="cx">   function onMouseDown(e) {
</span><span class="cx">     if (signalDOMEvent(this, e)) return;
</span><del>-    var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
-    sel.shift = e.shiftKey;
</del><ins>+    var cm = this, display = cm.display;
+    display.shift = e.shiftKey;
</ins><span class="cx"> 
</span><span class="cx">     if (eventInWidget(display, e)) {
</span><span class="cx">       if (!webkit) {
</span><ins>+        // Briefly turn off draggability, to allow widgets to do
+        // normal dragging things.
</ins><span class="cx">         display.scroller.draggable = false;
</span><span class="cx">         setTimeout(function(){display.scroller.draggable = true;}, 100);
</span><span class="cx">       }
</span><span class="lines">@@ -1708,89 +2518,168 @@
</span><span class="cx">     }
</span><span class="cx">     if (clickInGutter(cm, e)) return;
</span><span class="cx">     var start = posFromMouse(cm, e);
</span><ins>+    window.focus();
</ins><span class="cx"> 
</span><span class="cx">     switch (e_button(e)) {
</span><del>-    case 3:
-      if (captureMiddleClick) onContextMenu.call(cm, cm, e);
-      return;
</del><ins>+    case 1:
+      if (start)
+        leftButtonDown(cm, e, start);
+      else if (e_target(e) == display.scroller)
+        e_preventDefault(e);
+      break;
</ins><span class="cx">     case 2:
</span><span class="cx">       if (webkit) cm.state.lastMiddleDown = +new Date;
</span><span class="cx">       if (start) extendSelection(cm.doc, start);
</span><span class="cx">       setTimeout(bind(focusInput, cm), 20);
</span><span class="cx">       e_preventDefault(e);
</span><del>-      return;
</del><ins>+      break;
+    case 3:
+      if (captureRightClick) onContextMenu(cm, e);
+      break;
</ins><span class="cx">     }
</span><del>-    // For button 1, if it was clicked inside the editor
-    // (posFromMouse returning non-null), we have to adjust the
-    // selection.
-    if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
</del><ins>+  }
</ins><span class="cx"> 
</span><del>-    if (!cm.state.focused) onFocus(cm);
</del><ins>+  var lastClick, lastDoubleClick;
+  function leftButtonDown(cm, e, start) {
+    setTimeout(bind(ensureFocus, cm), 0);
</ins><span class="cx"> 
</span><del>-    var now = +new Date, type = &quot;single&quot;;
-    if (lastDoubleClick &amp;&amp; lastDoubleClick.time &gt; now - 400 &amp;&amp; posEq(lastDoubleClick.pos, start)) {
</del><ins>+    var now = +new Date, type;
+    if (lastDoubleClick &amp;&amp; lastDoubleClick.time &gt; now - 400 &amp;&amp; cmp(lastDoubleClick.pos, start) == 0) {
</ins><span class="cx">       type = &quot;triple&quot;;
</span><del>-      e_preventDefault(e);
-      setTimeout(bind(focusInput, cm), 20);
-      selectLine(cm, start.line);
-    } else if (lastClick &amp;&amp; lastClick.time &gt; now - 400 &amp;&amp; posEq(lastClick.pos, start)) {
</del><ins>+    } else if (lastClick &amp;&amp; lastClick.time &gt; now - 400 &amp;&amp; cmp(lastClick.pos, start) == 0) {
</ins><span class="cx">       type = &quot;double&quot;;
</span><span class="cx">       lastDoubleClick = {time: now, pos: start};
</span><del>-      e_preventDefault(e);
-      var word = findWordAt(getLine(doc, start.line).text, start);
-      extendSelection(cm.doc, word.from, word.to);
-    } else { lastClick = {time: now, pos: start}; }
</del><ins>+    } else {
+      type = &quot;single&quot;;
+      lastClick = {time: now, pos: start};
+    }
</ins><span class="cx"> 
</span><del>-    var last = start;
-    if (cm.options.dragDrop &amp;&amp; dragAndDrop &amp;&amp; !isReadOnly(cm) &amp;&amp; !posEq(sel.from, sel.to) &amp;&amp;
-        !posLess(start, sel.from) &amp;&amp; !posLess(sel.to, start) &amp;&amp; type == &quot;single&quot;) {
-      var dragEnd = operation(cm, function(e2) {
-        if (webkit) display.scroller.draggable = false;
-        cm.state.draggingText = false;
-        off(document, &quot;mouseup&quot;, dragEnd);
-        off(display.scroller, &quot;drop&quot;, dragEnd);
-        if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) &lt; 10) {
-          e_preventDefault(e2);
-          extendSelection(cm.doc, start);
-          focusInput(cm);
-        }
-      });
-      // Let the drag handler handle this.
-      if (webkit) display.scroller.draggable = true;
-      cm.state.draggingText = dragEnd;
-      // IE's approach to draggable
-      if (display.scroller.dragDrop) display.scroller.dragDrop();
-      on(document, &quot;mouseup&quot;, dragEnd);
-      on(display.scroller, &quot;drop&quot;, dragEnd);
-      return;
-    }
</del><ins>+    var sel = cm.doc.sel, addNew = mac ? e.metaKey : e.ctrlKey;
+    if (cm.options.dragDrop &amp;&amp; dragAndDrop &amp;&amp; !addNew &amp;&amp; !isReadOnly(cm) &amp;&amp;
+        type == &quot;single&quot; &amp;&amp; sel.contains(start) &gt; -1 &amp;&amp; sel.somethingSelected())
+      leftButtonStartDrag(cm, e, start);
+    else
+      leftButtonSelect(cm, e, start, type, addNew);
+  }
+
+  // Start a text drag. When it ends, see if any dragging actually
+  // happen, and treat as a click if it didn't.
+  function leftButtonStartDrag(cm, e, start) {
+    var display = cm.display;
+    var dragEnd = operation(cm, function(e2) {
+      if (webkit) display.scroller.draggable = false;
+      cm.state.draggingText = false;
+      off(document, &quot;mouseup&quot;, dragEnd);
+      off(display.scroller, &quot;drop&quot;, dragEnd);
+      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) &lt; 10) {
+        e_preventDefault(e2);
+        extendSelection(cm.doc, start);
+        focusInput(cm);
+        // Work around unexplainable focus problem in IE9 (#2127)
+        if (ie_upto10 &amp;&amp; !ie_upto8)
+          setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
+      }
+    });
+    // Let the drag handler handle this.
+    if (webkit) display.scroller.draggable = true;
+    cm.state.draggingText = dragEnd;
+    // IE's approach to draggable
+    if (display.scroller.dragDrop) display.scroller.dragDrop();
+    on(document, &quot;mouseup&quot;, dragEnd);
+    on(display.scroller, &quot;drop&quot;, dragEnd);
+  }
+
+  // Normal selection, as opposed to text dragging.
+  function leftButtonSelect(cm, e, start, type, addNew) {
+    var display = cm.display, doc = cm.doc;
</ins><span class="cx">     e_preventDefault(e);
</span><del>-    if (type == &quot;single&quot;) extendSelection(cm.doc, clipPos(doc, start));
</del><span class="cx"> 
</span><del>-    var startstart = sel.from, startend = sel.to, lastPos = start;
</del><ins>+    var ourRange, ourIndex, startSel = doc.sel;
+    if (addNew) {
+      ourIndex = doc.sel.contains(start);
+      if (ourIndex &gt; -1)
+        ourRange = doc.sel.ranges[ourIndex];
+      else
+        ourRange = new Range(start, start);
+    } else {
+      ourRange = doc.sel.primary();
+    }
</ins><span class="cx"> 
</span><del>-    function doSelect(cur) {
-      if (posEq(lastPos, cur)) return;
-      lastPos = cur;
</del><ins>+    if (e.altKey) {
+      type = &quot;rect&quot;;
+      if (!addNew) ourRange = new Range(start, start);
+      start = posFromMouse(cm, e, true, true);
+      ourIndex = -1;
+    } else if (type == &quot;double&quot;) {
+      var word = findWordAt(doc, start);
+      if (cm.display.shift || doc.extend)
+        ourRange = extendRange(doc, ourRange, word.anchor, word.head);
+      else
+        ourRange = word;
+    } else if (type == &quot;triple&quot;) {
+      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
+      if (cm.display.shift || doc.extend)
+        ourRange = extendRange(doc, ourRange, line.anchor, line.head);
+      else
+        ourRange = line;
+    } else {
+      ourRange = extendRange(doc, ourRange, start);
+    }
</ins><span class="cx"> 
</span><del>-      if (type == &quot;single&quot;) {
-        extendSelection(cm.doc, clipPos(doc, start), cur);
-        return;
-      }
</del><ins>+    if (!addNew) {
+      ourIndex = 0;
+      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+    } else if (ourIndex &gt; -1) {
+      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
+    } else {
+      ourIndex = doc.sel.ranges.length;
+      setSelection(doc, normalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
+                   {scroll: false, origin: &quot;*mouse&quot;});
+    }
</ins><span class="cx"> 
</span><del>-      startstart = clipPos(doc, startstart);
-      startend = clipPos(doc, startend);
-      if (type == &quot;double&quot;) {
-        var word = findWordAt(getLine(doc, cur.line).text, cur);
-        if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
-        else extendSelection(cm.doc, startstart, word.to);
-      } else if (type == &quot;triple&quot;) {
-        if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
-        else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
</del><ins>+    var lastPos = start;
+    function extendTo(pos) {
+      if (cmp(lastPos, pos) == 0) return;
+      lastPos = pos;
+
+      if (type == &quot;rect&quot;) {
+        var ranges = [], tabSize = cm.options.tabSize;
+        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
+        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
+        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
+        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
+             line &lt;= end; line++) {
+          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
+          if (left == right)
+            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
+          else if (text.length &gt; leftPos)
+            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
+        }
+        if (!ranges.length) ranges.push(new Range(start, start));
+        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), sel_mouse);
+      } else {
+        var oldRange = ourRange;
+        var anchor = oldRange.anchor, head = pos;
+        if (type != &quot;single&quot;) {
+          if (type == &quot;double&quot;)
+            var range = findWordAt(doc, pos);
+          else
+            var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
+          if (cmp(range.anchor, anchor) &gt; 0) {
+            head = range.head;
+            anchor = minPos(oldRange.from(), range.anchor);
+          } else {
+            head = range.anchor;
+            anchor = maxPos(oldRange.to(), range.head);
+          }
+        }
+        var ranges = startSel.ranges.slice(0);
+        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
+        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var editorSize = getRect(display.wrapper);
</del><ins>+    var editorSize = display.wrapper.getBoundingClientRect();
</ins><span class="cx">     // Used to ensure timeout re-tries don't fire when another extend
</span><span class="cx">     // happened in the meantime (clearTimeout isn't reliable -- at
</span><span class="cx">     // least on Chrome, the timeouts still happen even when cleared,
</span><span class="lines">@@ -1799,12 +2688,11 @@
</span><span class="cx"> 
</span><span class="cx">     function extend(e) {
</span><span class="cx">       var curCount = ++counter;
</span><del>-      var cur = posFromMouse(cm, e, true);
</del><ins>+      var cur = posFromMouse(cm, e, true, type == &quot;rect&quot;);
</ins><span class="cx">       if (!cur) return;
</span><del>-      if (!posEq(cur, last)) {
-        if (!cm.state.focused) onFocus(cm);
-        last = cur;
-        doSelect(cur);
</del><ins>+      if (cmp(cur, lastPos) != 0) {
+        ensureFocus(cm);
+        extendTo(cur);
</ins><span class="cx">         var visible = visibleLines(display, doc);
</span><span class="cx">         if (cur.line &gt;= visible.to || cur.line &lt; visible.from)
</span><span class="cx">           setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
</span><span class="lines">@@ -1824,10 +2712,11 @@
</span><span class="cx">       focusInput(cm);
</span><span class="cx">       off(document, &quot;mousemove&quot;, move);
</span><span class="cx">       off(document, &quot;mouseup&quot;, up);
</span><ins>+      doc.history.lastSelOrigin = null;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var move = operation(cm, function(e) {
</span><del>-      if (!ie &amp;&amp; !e_button(e)) done(e);
</del><ins>+      if ((ie &amp;&amp; !ie_upto9) ?  !e.buttons : !e_button(e)) done(e);
</ins><span class="cx">       else extend(e);
</span><span class="cx">     });
</span><span class="cx">     var up = operation(cm, done);
</span><span class="lines">@@ -1835,21 +2724,23 @@
</span><span class="cx">     on(document, &quot;mouseup&quot;, up);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Determines whether an event happened in the gutter, and fires the
+  // handlers for the corresponding event.
</ins><span class="cx">   function gutterEvent(cm, e, type, prevent, signalfn) {
</span><span class="cx">     try { var mX = e.clientX, mY = e.clientY; }
</span><span class="cx">     catch(e) { return false; }
</span><del>-    if (mX &gt;= Math.floor(getRect(cm.display.gutters).right)) return false;
</del><ins>+    if (mX &gt;= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
</ins><span class="cx">     if (prevent) e_preventDefault(e);
</span><span class="cx"> 
</span><span class="cx">     var display = cm.display;
</span><del>-    var lineBox = getRect(display.lineDiv);
</del><ins>+    var lineBox = display.lineDiv.getBoundingClientRect();
</ins><span class="cx"> 
</span><span class="cx">     if (mY &gt; lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
</span><span class="cx">     mY -= lineBox.top - display.viewOffset;
</span><span class="cx"> 
</span><span class="cx">     for (var i = 0; i &lt; cm.options.gutters.length; ++i) {
</span><span class="cx">       var g = display.gutters.childNodes[i];
</span><del>-      if (g &amp;&amp; getRect(g).right &gt;= mX) {
</del><ins>+      if (g &amp;&amp; g.getBoundingClientRect().right &gt;= mX) {
</ins><span class="cx">         var line = lineAtHeight(cm.doc, mY);
</span><span class="cx">         var gutter = cm.options.gutters[i];
</span><span class="cx">         signalfn(cm, type, cm, line, gutter, e);
</span><span class="lines">@@ -1858,11 +2749,6 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, &quot;gutterContextMenu&quot;)) return false;
-    return gutterEvent(cm, e, &quot;gutterContextMenu&quot;, false, signal);
-  }
-
</del><span class="cx">   function clickInGutter(cm, e) {
</span><span class="cx">     return gutterEvent(cm, e, &quot;gutterClick&quot;, true, signalLater);
</span><span class="cx">   }
</span><span class="lines">@@ -1873,29 +2759,33 @@
</span><span class="cx"> 
</span><span class="cx">   function onDrop(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent &amp;&amp; cm.options.onDragEvent(cm, addStop(e))))
</del><ins>+    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
</ins><span class="cx">       return;
</span><span class="cx">     e_preventDefault(e);
</span><del>-    if (ie) lastDrop = +new Date;
</del><ins>+    if (ie_upto10) lastDrop = +new Date;
</ins><span class="cx">     var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
</span><span class="cx">     if (!pos || isReadOnly(cm)) return;
</span><ins>+    // Might be a file drop, in which case we simply extract the text
+    // and insert it.
</ins><span class="cx">     if (files &amp;&amp; files.length &amp;&amp; window.FileReader &amp;&amp; window.File) {
</span><span class="cx">       var n = files.length, text = Array(n), read = 0;
</span><span class="cx">       var loadFile = function(file, i) {
</span><span class="cx">         var reader = new FileReader;
</span><del>-        reader.onload = function() {
</del><ins>+        reader.onload = operation(cm, function() {
</ins><span class="cx">           text[i] = reader.result;
</span><span class="cx">           if (++read == n) {
</span><span class="cx">             pos = clipPos(cm.doc, pos);
</span><del>-            makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join(&quot;\n&quot;)), origin: &quot;paste&quot;}, &quot;around&quot;);
</del><ins>+            var change = {from: pos, to: pos, text: splitLines(text.join(&quot;\n&quot;)), origin: &quot;paste&quot;};
+            makeChange(cm.doc, change);
+            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
</ins><span class="cx">           }
</span><del>-        };
</del><ins>+        });
</ins><span class="cx">         reader.readAsText(file);
</span><span class="cx">       };
</span><span class="cx">       for (var i = 0; i &lt; n; ++i) loadFile(files[i], i);
</span><del>-    } else {
</del><ins>+    } else { // Normal drop
</ins><span class="cx">       // Don't do a replace if the drop happened inside of the selected text.
</span><del>-      if (cm.state.draggingText &amp;&amp; !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
</del><ins>+      if (cm.state.draggingText &amp;&amp; cm.doc.sel.contains(pos) &gt; -1) {
</ins><span class="cx">         cm.state.draggingText(e);
</span><span class="cx">         // Ensure the editor is re-focused
</span><span class="cx">         setTimeout(bind(focusInput, cm), 20);
</span><span class="lines">@@ -1904,10 +2794,11 @@
</span><span class="cx">       try {
</span><span class="cx">         var text = e.dataTransfer.getData(&quot;Text&quot;);
</span><span class="cx">         if (text) {
</span><del>-          var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
-          setSelection(cm.doc, pos, pos);
-          if (cm.state.draggingText) replaceRange(cm.doc, &quot;&quot;, curFrom, curTo, &quot;paste&quot;);
-          cm.replaceSelection(text, null, &quot;paste&quot;);
</del><ins>+          var selected = cm.state.draggingText &amp;&amp; cm.listSelections();
+          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+          if (selected) for (var i = 0; i &lt; selected.length; ++i)
+            replaceRange(cm.doc, &quot;&quot;, selected[i].anchor, selected[i].head, &quot;drag&quot;);
+          cm.replaceSelection(text, &quot;around&quot;, &quot;paste&quot;);
</ins><span class="cx">           focusInput(cm);
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="lines">@@ -1916,37 +2807,42 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function onDragStart(cm, e) {
</span><del>-    if (ie &amp;&amp; (!cm.state.draggingText || +new Date - lastDrop &lt; 100)) { e_stop(e); return; }
</del><ins>+    if (ie_upto10 &amp;&amp; (!cm.state.draggingText || +new Date - lastDrop &lt; 100)) { e_stop(e); return; }
</ins><span class="cx">     if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
</span><span class="cx"> 
</span><del>-    var txt = cm.getSelection();
-    e.dataTransfer.setData(&quot;Text&quot;, txt);
</del><ins>+    e.dataTransfer.setData(&quot;Text&quot;, cm.getSelection());
</ins><span class="cx"> 
</span><span class="cx">     // Use dummy image instead of default browsers image.
</span><span class="cx">     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
</span><span class="cx">     if (e.dataTransfer.setDragImage &amp;&amp; !safari) {
</span><span class="cx">       var img = elt(&quot;img&quot;, null, null, &quot;position: fixed; left: 0; top: 0;&quot;);
</span><span class="cx">       img.src = &quot;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;;
</span><del>-      if (opera) {
</del><ins>+      if (presto) {
</ins><span class="cx">         img.width = img.height = 1;
</span><span class="cx">         cm.display.wrapper.appendChild(img);
</span><span class="cx">         // Force a relayout, or Opera won't use our image for some obscure reason
</span><span class="cx">         img._top = img.offsetTop;
</span><span class="cx">       }
</span><span class="cx">       e.dataTransfer.setDragImage(img, 0, 0);
</span><del>-      if (opera) img.parentNode.removeChild(img);
</del><ins>+      if (presto) img.parentNode.removeChild(img);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // SCROLL EVENTS
+
+  // Sync the scrollable area and scrollbars, ensure the viewport
+  // covers the visible area.
</ins><span class="cx">   function setScrollTop(cm, val) {
</span><span class="cx">     if (Math.abs(cm.doc.scrollTop - val) &lt; 2) return;
</span><span class="cx">     cm.doc.scrollTop = val;
</span><del>-    if (!gecko) updateDisplay(cm, [], val);
</del><ins>+    if (!gecko) updateDisplay(cm, {top: val});
</ins><span class="cx">     if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
</span><span class="cx">     if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
</span><del>-    if (gecko) updateDisplay(cm, []);
</del><ins>+    if (gecko) updateDisplay(cm);
</ins><span class="cx">     startWorker(cm, 100);
</span><span class="cx">   }
</span><ins>+  // Sync scroller and scrollbar, ensure the gutter elements are
+  // aligned.
</ins><span class="cx">   function setScrollLeft(cm, val, isScroller) {
</span><span class="cx">     if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) &lt; 2) return;
</span><span class="cx">     val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
</span><span class="lines">@@ -1993,10 +2889,12 @@
</span><span class="cx">     // This hack (see related code in patchDisplay) makes sure the
</span><span class="cx">     // element is kept around.
</span><span class="cx">     if (dy &amp;&amp; mac &amp;&amp; webkit) {
</span><del>-      for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
-        if (cur.lineObj) {
-          cm.display.currentWheelTarget = cur;
-          break;
</del><ins>+      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
+        for (var i = 0; i &lt; view.length; i++) {
+          if (view[i].node == cur) {
+            cm.display.currentWheelTarget = cur;
+            break outer;
+          }
</ins><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="lines">@@ -2007,7 +2905,7 @@
</span><span class="cx">     // estimated pixels/delta value, we just handle horizontal
</span><span class="cx">     // scrolling entirely here. It'll be slightly off from native, but
</span><span class="cx">     // better than glitching out.
</span><del>-    if (dx &amp;&amp; !gecko &amp;&amp; !opera &amp;&amp; wheelPixelsPerUnit != null) {
</del><ins>+    if (dx &amp;&amp; !gecko &amp;&amp; !presto &amp;&amp; wheelPixelsPerUnit != null) {
</ins><span class="cx">       if (dy)
</span><span class="cx">         setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
</span><span class="cx">       setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
</span><span class="lines">@@ -2016,12 +2914,14 @@
</span><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    // 'Project' the visible viewport to cover the area that is being
+    // scrolled into view (if we know enough to estimate it).
</ins><span class="cx">     if (dy &amp;&amp; wheelPixelsPerUnit != null) {
</span><span class="cx">       var pixels = dy * wheelPixelsPerUnit;
</span><span class="cx">       var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
</span><span class="cx">       if (pixels &lt; 0) top = Math.max(0, top + pixels - 50);
</span><span class="cx">       else bot = Math.min(cm.doc.height, bot + pixels + 50);
</span><del>-      updateDisplay(cm, [], {top: top, bottom: bot});
</del><ins>+      updateDisplay(cm, {top: top, bottom: bot});
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (wheelSamples &lt; 20) {
</span><span class="lines">@@ -2045,6 +2945,9 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // KEY EVENTS
+
+  // Run a handler that was bound to a key.
</ins><span class="cx">   function doHandleBinding(cm, bound, dropShift) {
</span><span class="cx">     if (typeof bound == &quot;string&quot;) {
</span><span class="cx">       bound = commands[bound];
</span><span class="lines">@@ -2053,18 +2956,19 @@
</span><span class="cx">     // Ensure previous input has been read, so that the handler sees a
</span><span class="cx">     // consistent view of the document
</span><span class="cx">     if (cm.display.pollingFast &amp;&amp; readInput(cm)) cm.display.pollingFast = false;
</span><del>-    var doc = cm.doc, prevShift = doc.sel.shift, done = false;
</del><ins>+    var prevShift = cm.display.shift, done = false;
</ins><span class="cx">     try {
</span><span class="cx">       if (isReadOnly(cm)) cm.state.suppressEdits = true;
</span><del>-      if (dropShift) doc.sel.shift = false;
</del><ins>+      if (dropShift) cm.display.shift = false;
</ins><span class="cx">       done = bound(cm) != Pass;
</span><span class="cx">     } finally {
</span><del>-      doc.sel.shift = prevShift;
</del><ins>+      cm.display.shift = prevShift;
</ins><span class="cx">       cm.state.suppressEdits = false;
</span><span class="cx">     }
</span><span class="cx">     return done;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Collect the currently active keymaps.
</ins><span class="cx">   function allKeyMaps(cm) {
</span><span class="cx">     var maps = cm.state.keyMaps.slice(0);
</span><span class="cx">     if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
</span><span class="lines">@@ -2073,8 +2977,9 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   var maybeTransition;
</span><ins>+  // Handle a key from the keydown event.
</ins><span class="cx">   function handleKeyBinding(cm, e) {
</span><del>-    // Handle auto keymap transitions
</del><ins>+    // Handle automatic keymap transitions
</ins><span class="cx">     var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
</span><span class="cx">     clearTimeout(maybeTransition);
</span><span class="cx">     if (next &amp;&amp; !isModifierKey(e)) maybeTransition = setTimeout(function() {
</span><span class="lines">@@ -2104,12 +3009,12 @@
</span><span class="cx">     if (handled) {
</span><span class="cx">       e_preventDefault(e);
</span><span class="cx">       restartBlink(cm);
</span><del>-      if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
</del><span class="cx">       signalLater(cm, &quot;keyHandled&quot;, cm, name, e);
</span><span class="cx">     }
</span><span class="cx">     return handled;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Handle a key from the keypress event
</ins><span class="cx">   function handleCharBinding(cm, e, ch) {
</span><span class="cx">     var handled = lookupKey(&quot;'&quot; + ch + &quot;'&quot;, allKeyMaps(cm),
</span><span class="cx">                             function(b) { return doHandleBinding(cm, b, true); });
</span><span class="lines">@@ -2124,47 +3029,67 @@
</span><span class="cx">   var lastStoppedKey = null;
</span><span class="cx">   function onKeyDown(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (!cm.state.focused) onFocus(cm);
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
-    if (ie &amp;&amp; e.keyCode == 27) e.returnValue = false;
</del><ins>+    ensureFocus(cm);
+    if (signalDOMEvent(cm, e)) return;
+    // IE does strange things with escape.
+    if (ie_upto10 &amp;&amp; e.keyCode == 27) e.returnValue = false;
</ins><span class="cx">     var code = e.keyCode;
</span><del>-    // IE does strange things with escape.
-    cm.doc.sel.shift = code == 16 || e.shiftKey;
-    // First give onKeyEvent option a chance to handle this.
</del><ins>+    cm.display.shift = code == 16 || e.shiftKey;
</ins><span class="cx">     var handled = handleKeyBinding(cm, e);
</span><del>-    if (opera) {
</del><ins>+    if (presto) {
</ins><span class="cx">       lastStoppedKey = handled ? code : null;
</span><span class="cx">       // Opera has no cut event... we try to at least catch the key combo
</span><span class="cx">       if (!handled &amp;&amp; code == 88 &amp;&amp; !hasCopyEvent &amp;&amp; (mac ? e.metaKey : e.ctrlKey))
</span><del>-        cm.replaceSelection(&quot;&quot;);
</del><ins>+        cm.replaceSelection(&quot;&quot;, null, &quot;cut&quot;);
</ins><span class="cx">     }
</span><ins>+
+    // Turn mouse into crosshair when Alt is held on Mac.
+    if (code == 18 &amp;&amp; !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+      showCrossHair(cm);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function showCrossHair(cm) {
+    var lineDiv = cm.display.lineDiv;
+    addClass(lineDiv, &quot;CodeMirror-crosshair&quot;);
+
+    function up(e) {
+      if (e.keyCode == 18 || !e.altKey) {
+        rmClass(lineDiv, &quot;CodeMirror-crosshair&quot;);
+        off(document, &quot;keyup&quot;, up);
+        off(document, &quot;mouseover&quot;, up);
+      }
+    }
+    on(document, &quot;keyup&quot;, up);
+    on(document, &quot;mouseover&quot;, up);
+  }
+
+  function onKeyUp(e) {
+    if (signalDOMEvent(this, e)) return;
+    if (e.keyCode == 16) this.doc.sel.shift = false;
+  }
+
</ins><span class="cx">   function onKeyPress(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
</del><ins>+    if (signalDOMEvent(cm, e)) return;
</ins><span class="cx">     var keyCode = e.keyCode, charCode = e.charCode;
</span><del>-    if (opera &amp;&amp; keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
-    if (((opera &amp;&amp; (!e.which || e.which &lt; 10)) || khtml) &amp;&amp; handleKeyBinding(cm, e)) return;
</del><ins>+    if (presto &amp;&amp; keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+    if (((presto &amp;&amp; (!e.which || e.which &lt; 10)) || khtml) &amp;&amp; handleKeyBinding(cm, e)) return;
</ins><span class="cx">     var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
</span><del>-    if (this.options.electricChars &amp;&amp; this.doc.mode.electricChars &amp;&amp;
-        this.options.smartIndent &amp;&amp; !isReadOnly(this) &amp;&amp;
-        this.doc.mode.electricChars.indexOf(ch) &gt; -1)
-      setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, &quot;smart&quot;);}), 75);
</del><span class="cx">     if (handleCharBinding(cm, e, ch)) return;
</span><del>-    if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = null;
</del><ins>+    if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = null;
</ins><span class="cx">     fastPoll(cm);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // FOCUS/BLUR EVENTS
+
</ins><span class="cx">   function onFocus(cm) {
</span><span class="cx">     if (cm.options.readOnly == &quot;nocursor&quot;) return;
</span><span class="cx">     if (!cm.state.focused) {
</span><span class="cx">       signal(cm, &quot;focus&quot;, cm);
</span><span class="cx">       cm.state.focused = true;
</span><del>-      if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
-        cm.display.wrapper.className += &quot; CodeMirror-focused&quot;;
</del><ins>+      addClass(cm.display.wrapper, &quot;CodeMirror-focused&quot;);
</ins><span class="cx">       if (!cm.curOp) {
</span><del>-        resetInput(cm, true);
</del><ins>+        resetInput(cm);
</ins><span class="cx">         if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="lines">@@ -2175,40 +3100,49 @@
</span><span class="cx">     if (cm.state.focused) {
</span><span class="cx">       signal(cm, &quot;blur&quot;, cm);
</span><span class="cx">       cm.state.focused = false;
</span><del>-      cm.display.wrapper.className = cm.display.wrapper.className.replace(&quot; CodeMirror-focused&quot;, &quot;&quot;);
</del><ins>+      rmClass(cm.display.wrapper, &quot;CodeMirror-focused&quot;);
</ins><span class="cx">     }
</span><span class="cx">     clearInterval(cm.display.blinker);
</span><del>-    setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
</del><ins>+    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // CONTEXT MENU HANDLING
+
</ins><span class="cx">   var detectingSelectAll;
</span><ins>+  // To make the context menu work, we need to briefly unhide the
+  // textarea (making it as unobtrusive as possible) to let the
+  // right-click take effect on it.
</ins><span class="cx">   function onContextMenu(cm, e) {
</span><span class="cx">     if (signalDOMEvent(cm, e, &quot;contextmenu&quot;)) return;
</span><del>-    var display = cm.display, sel = cm.doc.sel;
</del><ins>+    var display = cm.display;
</ins><span class="cx">     if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
</span><span class="cx"> 
</span><span class="cx">     var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
</span><del>-    if (!pos || opera) return; // Opera is difficult.
</del><ins>+    if (!pos || presto) return; // Opera is difficult.
</ins><span class="cx"> 
</span><span class="cx">     // Reset the current text selection only if the click is done outside of the selection
</span><span class="cx">     // and 'resetSelectionOnContextMenu' option is true.
</span><span class="cx">     var reset = cm.options.resetSelectionOnContextMenu;
</span><del>-    if (reset &amp;&amp; (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)))
-      operation(cm, setSelection)(cm.doc, pos, pos);
</del><ins>+    if (reset &amp;&amp; cm.doc.sel.contains(pos) == -1)
+      operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
</ins><span class="cx"> 
</span><span class="cx">     var oldCSS = display.input.style.cssText;
</span><span class="cx">     display.inputDiv.style.position = &quot;absolute&quot;;
</span><span class="cx">     display.input.style.cssText = &quot;position: fixed; width: 30px; height: 30px; top: &quot; + (e.clientY - 5) +
</span><del>-      &quot;px; left: &quot; + (e.clientX - 5) + &quot;px; z-index: 1000; background: white; outline: none;&quot; +
-      &quot;border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);&quot;;
</del><ins>+      &quot;px; left: &quot; + (e.clientX - 5) + &quot;px; z-index: 1000; background: &quot; +
+      (ie ? &quot;rgba(255, 255, 255, .05)&quot; : &quot;transparent&quot;) +
+      &quot;; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);&quot;;
</ins><span class="cx">     focusInput(cm);
</span><del>-    resetInput(cm, true);
</del><ins>+    resetInput(cm);
</ins><span class="cx">     // Adds &quot;Select all&quot; to context menu in FF
</span><del>-    if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = &quot; &quot;;
</del><ins>+    if (!cm.somethingSelected()) display.input.value = display.prevInput = &quot; &quot;;
</ins><span class="cx"> 
</span><ins>+    // Select-all will be greyed out if there's nothing to select, so
+    // this adds a zero-width space so that we can later check whether
+    // it got selected.
</ins><span class="cx">     function prepareSelectAllHack() {
</span><span class="cx">       if (display.input.selectionStart != null) {
</span><del>-        var extval = display.input.value = &quot;\u200b&quot; + (posEq(sel.from, sel.to) ? &quot;&quot; : display.input.value);
</del><ins>+        var extval = display.input.value = &quot;\u200b&quot; + (cm.somethingSelected() ? display.input.value : &quot;&quot;);
</ins><span class="cx">         display.prevInput = &quot;\u200b&quot;;
</span><span class="cx">         display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
</span><span class="cx">       }
</span><span class="lines">@@ -2216,15 +3150,15 @@
</span><span class="cx">     function rehide() {
</span><span class="cx">       display.inputDiv.style.position = &quot;relative&quot;;
</span><span class="cx">       display.input.style.cssText = oldCSS;
</span><del>-      if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
</del><ins>+      if (ie_upto8) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
</ins><span class="cx">       slowPoll(cm);
</span><span class="cx"> 
</span><span class="cx">       // Try to detect the user choosing select-all
</span><span class="cx">       if (display.input.selectionStart != null) {
</span><del>-        if (!ie || ie_lt9) prepareSelectAllHack();
</del><ins>+        if (!ie || ie_upto8) prepareSelectAllHack();
</ins><span class="cx">         clearTimeout(detectingSelectAll);
</span><span class="cx">         var i = 0, poll = function(){
</span><del>-          if (display.prevInput == &quot; &quot; &amp;&amp; display.input.selectionStart == 0)
</del><ins>+          if (display.prevInput == &quot;\u200b&quot; &amp;&amp; display.input.selectionStart == 0)
</ins><span class="cx">             operation(cm, commands.selectAll)(cm);
</span><span class="cx">           else if (i++ &lt; 10) detectingSelectAll = setTimeout(poll, 500);
</span><span class="cx">           else resetInput(cm);
</span><span class="lines">@@ -2233,8 +3167,8 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (ie &amp;&amp; !ie_lt9) prepareSelectAllHack();
-    if (captureMiddleClick) {
</del><ins>+    if (ie &amp;&amp; !ie_upto8) prepareSelectAllHack();
+    if (captureRightClick) {
</ins><span class="cx">       e_stop(e);
</span><span class="cx">       var mouseup = function() {
</span><span class="cx">         off(window, &quot;mouseup&quot;, mouseup);
</span><span class="lines">@@ -2246,54 +3180,71 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function contextMenuInGutter(cm, e) {
+    if (!hasHandler(cm, &quot;gutterContextMenu&quot;)) return false;
+    return gutterEvent(cm, e, &quot;gutterContextMenu&quot;, false, signal);
+  }
+
</ins><span class="cx">   // UPDATING
</span><span class="cx"> 
</span><ins>+  // Compute the position of the end of a change (its 'to' property
+  // refers to the pre-change end).
</ins><span class="cx">   var changeEnd = CodeMirror.changeEnd = function(change) {
</span><span class="cx">     if (!change.text) return change.to;
</span><span class="cx">     return Pos(change.from.line + change.text.length - 1,
</span><span class="cx">                lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
</span><span class="cx">   };
</span><span class="cx"> 
</span><del>-  // Make sure a position will be valid after the given change.
-  function clipPostChange(doc, change, pos) {
-    if (!posLess(change.from, pos)) return clipPos(doc, pos);
-    var diff = (change.text.length - 1) - (change.to.line - change.from.line);
-    if (pos.line &gt; change.to.line + diff) {
-      var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
-      if (preLine &gt; lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
-      return clipToLen(pos, getLine(doc, preLine).text.length);
</del><ins>+  // Adjust a position to refer to the post-change position of the
+  // same text, or the end of the change if the change covers it.
+  function adjustForChange(pos, change) {
+    if (cmp(pos, change.from) &lt; 0) return pos;
+    if (cmp(pos, change.to) &lt;= 0) return changeEnd(change);
+
+    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
+    return Pos(line, ch);
+  }
+
+  function computeSelAfterChange(doc, change) {
+    var out = [];
+    for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+      var range = doc.sel.ranges[i];
+      out.push(new Range(adjustForChange(range.anchor, change),
+                         adjustForChange(range.head, change)));
</ins><span class="cx">     }
</span><del>-    if (pos.line == change.to.line + diff)
-      return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
-                       getLine(doc, change.to.line).text.length - change.to.ch);
-    var inside = pos.line - change.from.line;
-    return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
</del><ins>+    return normalizeSelection(out, doc.sel.primIndex);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Hint can be null|&quot;end&quot;|&quot;start&quot;|&quot;around&quot;|{anchor,head}
-  function computeSelAfterChange(doc, change, hint) {
-    if (hint &amp;&amp; typeof hint == &quot;object&quot;) // Assumed to be {anchor, head} object
-      return {anchor: clipPostChange(doc, change, hint.anchor),
-              head: clipPostChange(doc, change, hint.head)};
</del><ins>+  function offsetPos(pos, old, nw) {
+    if (pos.line == old.line)
+      return Pos(nw.line, pos.ch - old.ch + nw.ch);
+    else
+      return Pos(nw.line + (pos.line - old.line), pos.ch);
+  }
</ins><span class="cx"> 
</span><del>-    if (hint == &quot;start&quot;) return {anchor: change.from, head: change.from};
-
-    var end = changeEnd(change);
-    if (hint == &quot;around&quot;) return {anchor: change.from, head: end};
-    if (hint == &quot;end&quot;) return {anchor: end, head: end};
-
-    // hint is null, leave the selection alone as much as possible
-    var adjustPos = function(pos) {
-      if (posLess(pos, change.from)) return pos;
-      if (!posLess(change.to, pos)) return end;
-
-      var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-      if (pos.line == change.to.line) ch += end.ch - change.to.ch;
-      return Pos(line, ch);
-    };
-    return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
</del><ins>+  // Used by replaceSelections to allow moving the selection to the
+  // start or around the replaced test. Hint may be &quot;start&quot; or &quot;around&quot;.
+  function computeReplacedSel(doc, changes, hint) {
+    var out = [];
+    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
+    for (var i = 0; i &lt; changes.length; i++) {
+      var change = changes[i];
+      var from = offsetPos(change.from, oldPrev, newPrev);
+      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
+      oldPrev = change.to;
+      newPrev = to;
+      if (hint == &quot;around&quot;) {
+        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) &lt; 0;
+        out[i] = new Range(inv ? to : from, inv ? from : to);
+      } else {
+        out[i] = new Range(from, from);
+      }
+    }
+    return new Selection(out, doc.sel.primIndex);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Allow &quot;beforeChange&quot; event handlers to influence a change
</ins><span class="cx">   function filterChange(doc, change, update) {
</span><span class="cx">     var obj = {
</span><span class="cx">       canceled: false,
</span><span class="lines">@@ -2316,11 +3267,11 @@
</span><span class="cx">     return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Replace the range from from to to by the strings in replacement.
-  // change is a {from, to, text [, origin]} object
-  function makeChange(doc, change, selUpdate, ignoreReadOnly) {
</del><ins>+  // Apply a change to a document, and add it to the document's
+  // history, and propagating it to all linked documents.
+  function makeChange(doc, change, ignoreReadOnly) {
</ins><span class="cx">     if (doc.cm) {
</span><del>-      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
</del><ins>+      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
</ins><span class="cx">       if (doc.cm.state.suppressEdits) return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -2333,19 +3284,17 @@
</span><span class="cx">     // of read-only spans in its range.
</span><span class="cx">     var split = sawReadOnlySpans &amp;&amp; !ignoreReadOnly &amp;&amp; removeReadOnlyRanges(doc, change.from, change.to);
</span><span class="cx">     if (split) {
</span><del>-      for (var i = split.length - 1; i &gt;= 1; --i)
-        makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [&quot;&quot;]});
-      if (split.length)
-        makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
</del><ins>+      for (var i = split.length - 1; i &gt;= 0; --i)
+        makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [&quot;&quot;] : change.text});
</ins><span class="cx">     } else {
</span><del>-      makeChangeNoReadonly(doc, change, selUpdate);
</del><ins>+      makeChangeInner(doc, change);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeNoReadonly(doc, change, selUpdate) {
-    if (change.text.length == 1 &amp;&amp; change.text[0] == &quot;&quot; &amp;&amp; posEq(change.from, change.to)) return;
-    var selAfter = computeSelAfterChange(doc, change, selUpdate);
-    addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
</del><ins>+  function makeChangeInner(doc, change) {
+    if (change.text.length == 1 &amp;&amp; change.text[0] == &quot;&quot; &amp;&amp; cmp(change.from, change.to) == 0) return;
+    var selAfter = computeSelAfterChange(doc, change);
+    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
</ins><span class="cx"> 
</span><span class="cx">     makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
</span><span class="cx">     var rebased = [];
</span><span class="lines">@@ -2359,17 +3308,41 @@
</span><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeFromHistory(doc, type) {
</del><ins>+  // Revert a change stored in a document's history.
+  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
</ins><span class="cx">     if (doc.cm &amp;&amp; doc.cm.state.suppressEdits) return;
</span><span class="cx"> 
</span><del>-    var hist = doc.history;
-    var event = (type == &quot;undo&quot; ? hist.done : hist.undone).pop();
-    if (!event) return;
</del><ins>+    var hist = doc.history, event, selAfter = doc.sel;
+    var source = type == &quot;undo&quot; ? hist.done : hist.undone, dest = type == &quot;undo&quot; ? hist.undone : hist.done;
</ins><span class="cx"> 
</span><del>-    var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
-                anchorAfter: event.anchorBefore, headAfter: event.headBefore,
-                generation: hist.generation};
-    (type == &quot;undo&quot; ? hist.undone : hist.done).push(anti);
</del><ins>+    // Verify that there is a useable event (so that ctrl-z won't
+    // needlessly clear selection events)
+    for (var i = 0; i &lt; source.length; i++) {
+      event = source[i];
+      if (allowSelectionOnly ? event.ranges &amp;&amp; !event.equals(doc.sel) : !event.ranges)
+        break;
+    }
+    if (i == source.length) return;
+    hist.lastOrigin = hist.lastSelOrigin = null;
+
+    for (;;) {
+      event = source.pop();
+      if (event.ranges) {
+        pushSelectionToHistory(event, dest);
+        if (allowSelectionOnly &amp;&amp; !event.equals(doc.sel)) {
+          setSelection(doc, event, {clearRedo: false});
+          return;
+        }
+        selAfter = event;
+      }
+      else break;
+    }
+
+    // Build up a reverse change object to add to the opposite history
+    // stack (redo when undoing, and vice versa).
+    var antiChanges = [];
+    pushSelectionToHistory(selAfter, dest);
+    dest.push({changes: antiChanges, generation: hist.generation});
</ins><span class="cx">     hist.generation = event.generation || ++hist.maxGeneration;
</span><span class="cx"> 
</span><span class="cx">     var filter = hasHandler(doc, &quot;beforeChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeChange&quot;);
</span><span class="lines">@@ -2378,17 +3351,18 @@
</span><span class="cx">       var change = event.changes[i];
</span><span class="cx">       change.origin = type;
</span><span class="cx">       if (filter &amp;&amp; !filterChange(doc, change, false)) {
</span><del>-        (type == &quot;undo&quot; ? hist.done : hist.undone).length = 0;
</del><ins>+        source.length = 0;
</ins><span class="cx">         return;
</span><span class="cx">       }
</span><span class="cx"> 
</span><del>-      anti.changes.push(historyChangeFromChange(doc, change));
</del><ins>+      antiChanges.push(historyChangeFromChange(doc, change));
</ins><span class="cx"> 
</span><del>-      var after = i ? computeSelAfterChange(doc, change, null)
-                    : {anchor: event.anchorBefore, head: event.headBefore};
</del><ins>+      var after = i ? computeSelAfterChange(doc, change, null) : lst(source);
</ins><span class="cx">       makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
</span><ins>+      if (doc.cm) ensureCursorVisible(doc.cm);
</ins><span class="cx">       var rebased = [];
</span><span class="cx"> 
</span><ins>+      // Propagate to the linked documents
</ins><span class="cx">       linkedDocs(doc, function(doc, sharedHist) {
</span><span class="cx">         if (!sharedHist &amp;&amp; indexOf(rebased, doc.history) == -1) {
</span><span class="cx">           rebaseHist(doc.history, change);
</span><span class="lines">@@ -2399,14 +3373,19 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Sub-views need their line numbers shifted when text is added
+  // above or below them in the parent document.
</ins><span class="cx">   function shiftDoc(doc, distance) {
</span><del>-    function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
</del><span class="cx">     doc.first += distance;
</span><del>-    if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
-    doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
-    doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
</del><ins>+    doc.sel = new Selection(map(doc.sel.ranges, function(range) {
+      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
+                       Pos(range.head.line + distance, range.head.ch));
+    }), doc.sel.primIndex);
+    if (doc.cm) regChange(doc.cm, doc.first, doc.first - distance, distance);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // More lower-level change function, handling only a single document
+  // (not linked ones).
</ins><span class="cx">   function makeChangeSingleDoc(doc, change, selAfter, spans) {
</span><span class="cx">     if (doc.cm &amp;&amp; !doc.cm.curOp)
</span><span class="cx">       return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
</span><span class="lines">@@ -2433,16 +3412,19 @@
</span><span class="cx">     change.removed = getBetween(doc, change.from, change.to);
</span><span class="cx"> 
</span><span class="cx">     if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
</span><del>-    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
-    else updateDoc(doc, change, spans, selAfter);
</del><ins>+    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
+    else updateDoc(doc, change, spans);
+    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
</del><ins>+  // Handle the interaction of a change to a document with the editor
+  // that this document is part of.
+  function makeChangeSingleDocInEditor(cm, change, spans) {
</ins><span class="cx">     var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
</span><span class="cx"> 
</span><span class="cx">     var recomputeMaxLength = false, checkWidthStart = from.line;
</span><span class="cx">     if (!cm.options.lineWrapping) {
</span><del>-      checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
</del><ins>+      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
</ins><span class="cx">       doc.iter(checkWidthStart, to.line + 1, function(line) {
</span><span class="cx">         if (line == display.maxLine) {
</span><span class="cx">           recomputeMaxLength = true;
</span><span class="lines">@@ -2451,14 +3433,14 @@
</span><span class="cx">       });
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!posLess(doc.sel.head, change.from) &amp;&amp; !posLess(change.to, doc.sel.head))
</del><ins>+    if (doc.sel.contains(change.from, change.to) &gt; -1)
</ins><span class="cx">       cm.curOp.cursorActivity = true;
</span><span class="cx"> 
</span><del>-    updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
</del><ins>+    updateDoc(doc, change, spans, estimateHeight(cm));
</ins><span class="cx"> 
</span><span class="cx">     if (!cm.options.lineWrapping) {
</span><span class="cx">       doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
</span><del>-        var len = lineLength(doc, line);
</del><ins>+        var len = lineLength(line);
</ins><span class="cx">         if (len &gt; display.maxLineLength) {
</span><span class="cx">           display.maxLine = line;
</span><span class="cx">           display.maxLineLength = len;
</span><span class="lines">@@ -2475,192 +3457,49 @@
</span><span class="cx"> 
</span><span class="cx">     var lendiff = change.text.length - (to.line - from.line) - 1;
</span><span class="cx">     // Remember that these lines changed, for updating the display
</span><del>-    regChange(cm, from.line, to.line + 1, lendiff);
</del><ins>+    if (from.line == to.line &amp;&amp; change.text.length == 1 &amp;&amp; !isWholeLineUpdate(cm.doc, change))
+      regLineChange(cm, from.line, &quot;text&quot;);
+    else
+      regChange(cm, from.line, to.line + 1, lendiff);
</ins><span class="cx"> 
</span><del>-    if (hasHandler(cm, &quot;change&quot;)) {
-      var changeObj = {from: from, to: to,
-                       text: change.text,
-                       removed: change.removed,
-                       origin: change.origin};
-      if (cm.curOp.textChanged) {
-        for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
-        cur.next = changeObj;
-      } else cm.curOp.textChanged = changeObj;
-    }
</del><ins>+    if (hasHandler(cm, &quot;change&quot;) || hasHandler(cm, &quot;changes&quot;))
+      (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push({
+        from: from, to: to,
+        text: change.text,
+        removed: change.removed,
+        origin: change.origin
+      });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function replaceRange(doc, code, from, to, origin) {
</span><span class="cx">     if (!to) to = from;
</span><del>-    if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
</del><ins>+    if (cmp(to, from) &lt; 0) { var tmp = to; to = from; from = tmp; }
</ins><span class="cx">     if (typeof code == &quot;string&quot;) code = splitLines(code);
</span><del>-    makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
</del><ins>+    makeChange(doc, {from: from, to: to, text: code, origin: origin});
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // POSITION OBJECT
</del><ins>+  // SCROLLING THINGS INTO VIEW
</ins><span class="cx"> 
</span><del>-  function Pos(line, ch) {
-    if (!(this instanceof Pos)) return new Pos(line, ch);
-    this.line = line; this.ch = ch;
-  }
-  CodeMirror.Pos = Pos;
-
-  function posEq(a, b) {return a.line == b.line &amp;&amp; a.ch == b.ch;}
-  function posLess(a, b) {return a.line &lt; b.line || (a.line == b.line &amp;&amp; a.ch &lt; b.ch);}
-  function copyPos(x) {return Pos(x.line, x.ch);}
-
-  // SELECTION
-
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
-  function clipPos(doc, pos) {
-    if (pos.line &lt; doc.first) return Pos(doc.first, 0);
-    var last = doc.first + doc.size - 1;
-    if (pos.line &gt; last) return Pos(last, getLine(doc, last).text.length);
-    return clipToLen(pos, getLine(doc, pos.line).text.length);
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch &gt; linelen) return Pos(pos.line, linelen);
-    else if (ch &lt; 0) return Pos(pos.line, 0);
-    else return pos;
-  }
-  function isLine(doc, l) {return l &gt;= doc.first &amp;&amp; l &lt; doc.first + doc.size;}
-
-  // If shift is held, this will move the selection anchor. Otherwise,
-  // it'll set the whole selection.
-  function extendSelection(doc, pos, other, bias) {
-    if (doc.sel.shift || doc.sel.extend) {
-      var anchor = doc.sel.anchor;
-      if (other) {
-        var posBefore = posLess(pos, anchor);
-        if (posBefore != posLess(other, anchor)) {
-          anchor = pos;
-          pos = other;
-        } else if (posBefore != posLess(pos, other)) {
-          pos = other;
-        }
-      }
-      setSelection(doc, anchor, pos, bias);
-    } else {
-      setSelection(doc, pos, other || pos, bias);
-    }
-    if (doc.cm) doc.cm.curOp.userSelChange = true;
-  }
-
-  function filterSelectionChange(doc, anchor, head) {
-    var obj = {anchor: anchor, head: head};
-    signal(doc, &quot;beforeSelectionChange&quot;, doc, obj);
-    if (doc.cm) signal(doc.cm, &quot;beforeSelectionChange&quot;, doc.cm, obj);
-    obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
-    return obj;
-  }
-
-  // Update the selection. Last two args are only used by
-  // updateDoc, since they have to be expressed in the line
-  // numbers before the update.
-  function setSelection(doc, anchor, head, bias, checkAtomic) {
-    if (!checkAtomic &amp;&amp; hasHandler(doc, &quot;beforeSelectionChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeSelectionChange&quot;)) {
-      var filtered = filterSelectionChange(doc, anchor, head);
-      head = filtered.head;
-      anchor = filtered.anchor;
-    }
-
-    var sel = doc.sel;
-    sel.goalColumn = null;
-    if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
-    // Skip over atomic spans.
-    if (checkAtomic || !posEq(anchor, sel.anchor))
-      anchor = skipAtomic(doc, anchor, bias, checkAtomic != &quot;push&quot;);
-    if (checkAtomic || !posEq(head, sel.head))
-      head = skipAtomic(doc, head, bias, checkAtomic != &quot;push&quot;);
-
-    if (posEq(sel.anchor, anchor) &amp;&amp; posEq(sel.head, head)) return;
-
-    sel.anchor = anchor; sel.head = head;
-    var inv = posLess(head, anchor);
-    sel.from = inv ? head : anchor;
-    sel.to = inv ? anchor : head;
-
-    if (doc.cm)
-      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
-        doc.cm.curOp.cursorActivity = true;
-
-    signalLater(doc, &quot;cursorActivity&quot;, doc);
-  }
-
-  function reCheckSelection(cm) {
-    setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, &quot;push&quot;);
-  }
-
-  function skipAtomic(doc, pos, bias, mayClear) {
-    var flipped = false, curPos = pos;
-    var dir = bias || 1;
-    doc.cantEdit = false;
-    search: for (;;) {
-      var line = getLine(doc, curPos.line);
-      if (line.markedSpans) {
-        for (var i = 0; i &lt; line.markedSpans.length; ++i) {
-          var sp = line.markedSpans[i], m = sp.marker;
-          if ((sp.from == null || (m.inclusiveLeft ? sp.from &lt;= curPos.ch : sp.from &lt; curPos.ch)) &amp;&amp;
-              (sp.to == null || (m.inclusiveRight ? sp.to &gt;= curPos.ch : sp.to &gt; curPos.ch))) {
-            if (mayClear) {
-              signal(m, &quot;beforeCursorEnter&quot;);
-              if (m.explicitlyCleared) {
-                if (!line.markedSpans) break;
-                else {--i; continue;}
-              }
-            }
-            if (!m.atomic) continue;
-            var newPos = m.find()[dir &lt; 0 ? &quot;from&quot; : &quot;to&quot;];
-            if (posEq(newPos, curPos)) {
-              newPos.ch += dir;
-              if (newPos.ch &lt; 0) {
-                if (newPos.line &gt; doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
-                else newPos = null;
-              } else if (newPos.ch &gt; line.text.length) {
-                if (newPos.line &lt; doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
-                else newPos = null;
-              }
-              if (!newPos) {
-                if (flipped) {
-                  // Driven in a corner -- no valid cursor position found at all
-                  // -- try again *with* clearing, if we didn't already
-                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
-                  // Otherwise, turn off editing until further notice, and return the start of the doc
-                  doc.cantEdit = true;
-                  return Pos(doc.first, 0);
-                }
-                flipped = true; newPos = pos; dir = -dir;
-              }
-            }
-            curPos = newPos;
-            continue search;
-          }
-        }
-      }
-      return curPos;
-    }
-  }
-
-  // SCROLLING
-
-  function scrollCursorIntoView(cm) {
-    var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin);
-    if (!cm.state.focused) return;
-    var display = cm.display, box = getRect(display.sizer), doScroll = null;
</del><ins>+  // If an editor sits on the top or bottom of the window, partially
+  // scrolled out of view, this ensures that the cursor is visible.
+  function maybeScrollWindow(cm, coords) {
+    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
</ins><span class="cx">     if (coords.top + box.top &lt; 0) doScroll = true;
</span><span class="cx">     else if (coords.bottom + box.top &gt; (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
</span><span class="cx">     if (doScroll != null &amp;&amp; !phantom) {
</span><del>-      var hidden = display.cursor.style.display == &quot;none&quot;;
-      if (hidden) {
-        display.cursor.style.display = &quot;&quot;;
-        display.cursor.style.left = coords.left + &quot;px&quot;;
-        display.cursor.style.top = (coords.top - display.viewOffset) + &quot;px&quot;;
-      }
-      display.cursor.scrollIntoView(doScroll);
-      if (hidden) display.cursor.style.display = &quot;none&quot;;
</del><ins>+      var scrollNode = elt(&quot;div&quot;, &quot;\u200b&quot;, null, &quot;position: absolute; top: &quot; +
+                           (coords.top - display.viewOffset - paddingTop(cm.display)) + &quot;px; height: &quot; +
+                           (coords.bottom - coords.top + scrollerCutOff) + &quot;px; left: &quot; +
+                           coords.left + &quot;px; width: 2px;&quot;);
+      cm.display.lineSpace.appendChild(scrollNode);
+      scrollNode.scrollIntoView(doScroll);
+      cm.display.lineSpace.removeChild(scrollNode);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Scroll a given position into view (immediately), verifying that
+  // it actually became visible (as line heights are accurately
+  // measured, the position of something may 'drift' during drawing).
</ins><span class="cx">   function scrollPosIntoView(cm, pos, end, margin) {
</span><span class="cx">     if (margin == null) margin = 0;
</span><span class="cx">     for (;;) {
</span><span class="lines">@@ -2683,16 +3522,22 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Scroll a given set of coordinates into view (immediately).
</ins><span class="cx">   function scrollIntoView(cm, x1, y1, x2, y2) {
</span><span class="cx">     var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
</span><span class="cx">     if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
</span><span class="cx">     if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Calculate a new scroll position needed to scroll the given
+  // rectangle into view. Returns an object with scrollTop and
+  // scrollLeft properties. When these are undefined, the
+  // vertical/horizontal position does not need to be adjusted.
</ins><span class="cx">   function calculateScrollPos(cm, x1, y1, x2, y2) {
</span><span class="cx">     var display = cm.display, snapMargin = textHeight(cm.display);
</span><span class="cx">     if (y1 &lt; 0) y1 = 0;
</span><del>-    var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
</del><ins>+    var screentop = cm.curOp &amp;&amp; cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+    var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
</ins><span class="cx">     var docBottom = cm.doc.height + paddingVert(display);
</span><span class="cx">     var atTop = y1 &lt; snapMargin, atBottom = y2 &gt; docBottom - snapMargin;
</span><span class="cx">     if (y1 &lt; screentop) {
</span><span class="lines">@@ -2702,7 +3547,8 @@
</span><span class="cx">       if (newTop != screentop) result.scrollTop = newTop;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
</del><ins>+    var screenleft = cm.curOp &amp;&amp; cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
+    var screenw = display.scroller.clientWidth - scrollerCutOff;
</ins><span class="cx">     x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
</span><span class="cx">     var gutterw = display.gutters.offsetWidth;
</span><span class="cx">     var atLeft = x1 &lt; gutterw + 10;
</span><span class="lines">@@ -2715,32 +3561,70 @@
</span><span class="cx">     return result;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function updateScrollPos(cm, left, top) {
-    cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
-                                scrollTop: top == null ? cm.doc.scrollTop : top};
</del><ins>+  // Store a relative adjustment to the scroll position in the current
+  // operation (to be applied when the operation finishes).
+  function addToScrollPos(cm, left, top) {
+    if (left != null || top != null) resolveScrollToPos(cm);
+    if (left != null)
+      cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
+    if (top != null)
+      cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function addToScrollPos(cm, left, top) {
-    var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
-    var scroll = cm.display.scroller;
-    pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
-    pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
</del><ins>+  // Make sure that at the end of the operation the current cursor is
+  // shown.
+  function ensureCursorVisible(cm) {
+    resolveScrollToPos(cm);
+    var cur = cm.getCursor(), from = cur, to = cur;
+    if (!cm.options.lineWrapping) {
+      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
+      to = Pos(cur.line, cur.ch + 1);
+    }
+    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // When an operation has its scrollToPos property set, and another
+  // scroll action is applied before the end of the operation, this
+  // 'simulates' scrolling that position into view in a cheap way, so
+  // that the effect of intermediate scroll commands is not ignored.
+  function resolveScrollToPos(cm) {
+    var range = cm.curOp.scrollToPos;
+    if (range) {
+      cm.curOp.scrollToPos = null;
+      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
+      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
+                                    Math.min(from.top, to.top) - range.margin,
+                                    Math.max(from.right, to.right),
+                                    Math.max(from.bottom, to.bottom) + range.margin);
+      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+    }
+  }
+
</ins><span class="cx">   // API UTILITIES
</span><span class="cx"> 
</span><ins>+  // Indent the given line. The how parameter can be &quot;smart&quot;,
+  // &quot;add&quot;/null, &quot;subtract&quot;, or &quot;prev&quot;. When aggressive is false
+  // (typically set to true for forced single-line indents), empty
+  // lines are not indented, and places where the mode returns Pass
+  // are left alone.
</ins><span class="cx">   function indentLine(cm, n, how, aggressive) {
</span><del>-    var doc = cm.doc;
</del><ins>+    var doc = cm.doc, state;
</ins><span class="cx">     if (how == null) how = &quot;add&quot;;
</span><span class="cx">     if (how == &quot;smart&quot;) {
</span><ins>+      // Fall back to &quot;prev&quot; when the mode doesn't have an indentation
+      // method.
</ins><span class="cx">       if (!cm.doc.mode.indent) how = &quot;prev&quot;;
</span><del>-      else var state = getStateBefore(cm, n);
</del><ins>+      else state = getStateBefore(cm, n);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var tabSize = cm.options.tabSize;
</span><span class="cx">     var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
</span><ins>+    if (line.stateAfter) line.stateAfter = null;
</ins><span class="cx">     var curSpaceString = line.text.match(/^\s*/)[0], indentation;
</span><del>-    if (how == &quot;smart&quot;) {
</del><ins>+    if (!aggressive &amp;&amp; !/\S/.test(line.text)) {
+      indentation = 0;
+      how = &quot;not&quot;;
+    } else if (how == &quot;smart&quot;) {
</ins><span class="cx">       indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
</span><span class="cx">       if (indentation == Pass) {
</span><span class="cx">         if (!aggressive) return;
</span><span class="lines">@@ -2764,23 +3648,69 @@
</span><span class="cx">       for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += &quot;\t&quot;;}
</span><span class="cx">     if (pos &lt; indentation) indentString += spaceStr(indentation - pos);
</span><span class="cx"> 
</span><del>-    if (indentString != curSpaceString)
</del><ins>+    if (indentString != curSpaceString) {
</ins><span class="cx">       replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), &quot;+input&quot;);
</span><del>-    else if (doc.sel.head.line == n &amp;&amp; doc.sel.head.ch &lt; curSpaceString.length)
-      setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1);
</del><ins>+    } else {
+      // Ensure that, if the cursor was in the whitespace at the start
+      // of the line, it is moved to the end of that space.
+      for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+        var range = doc.sel.ranges[i];
+        if (range.head.line == n &amp;&amp; range.head.ch &lt; curSpaceString.length) {
+          var pos = Pos(n, curSpaceString.length);
+          replaceOneSelection(doc, i, new Range(pos, pos));
+          break;
+        }
+      }
+    }
</ins><span class="cx">     line.stateAfter = null;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function changeLine(cm, handle, op) {
</del><ins>+  // Utility for applying a change to a line by handle or number,
+  // returning the number and optionally registering the line as
+  // changed.
+  function changeLine(cm, handle, changeType, op) {
</ins><span class="cx">     var no = handle, line = handle, doc = cm.doc;
</span><span class="cx">     if (typeof handle == &quot;number&quot;) line = getLine(doc, clipLine(doc, handle));
</span><span class="cx">     else no = lineNo(handle);
</span><span class="cx">     if (no == null) return null;
</span><del>-    if (op(line, no)) regChange(cm, no, no + 1);
-    else return null;
</del><ins>+    if (op(line, no)) regLineChange(cm, no, changeType);
</ins><span class="cx">     return line;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Helper for deleting text near the selection(s), used to implement
+  // backspace, delete, and similar functionality.
+  function deleteNearSelection(cm, compute) {
+    var ranges = cm.doc.sel.ranges, kill = [];
+    // Build up a set of ranges to kill first, merging overlapping
+    // ranges.
+    for (var i = 0; i &lt; ranges.length; i++) {
+      var toKill = compute(ranges[i]);
+      while (kill.length &amp;&amp; cmp(toKill.from, lst(kill).to) &lt;= 0) {
+        var replaced = kill.pop();
+        if (cmp(replaced.from, toKill.from) &lt; 0) {
+          toKill.from = replaced.from;
+          break;
+        }
+      }
+      kill.push(toKill);
+    }
+    // Next, remove those actual ranges.
+    runInOp(cm, function() {
+      for (var i = kill.length - 1; i &gt;= 0; i--)
+        replaceRange(cm.doc, &quot;&quot;, kill[i].from, kill[i].to, &quot;+delete&quot;);
+      ensureCursorVisible(cm);
+    });
+  }
+
+  // Used for horizontal relative motion. Dir is -1 or 1 (left or
+  // right), unit can be &quot;char&quot;, &quot;column&quot; (like char, but doesn't
+  // cross line boundaries), &quot;word&quot; (across next word), or &quot;group&quot; (to
+  // the start of next group of word or non-word-non-whitespace
+  // chars). The visually param controls whether, in right-to-left
+  // text, direction 1 means to move towards the next index in the
+  // string, or towards the character to the right of the current
+  // position. The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
</ins><span class="cx">   function findPosH(doc, pos, dir, unit, visually) {
</span><span class="cx">     var line = pos.line, ch = pos.ch, origDir = dir;
</span><span class="cx">     var lineObj = getLine(doc, line);
</span><span class="lines">@@ -2810,13 +3740,15 @@
</span><span class="cx">         if (dir &lt; 0 &amp;&amp; !moveOnce(!first)) break;
</span><span class="cx">         var cur = lineObj.text.charAt(ch) || &quot;\n&quot;;
</span><span class="cx">         var type = isWordChar(cur) ? &quot;w&quot;
</span><del>-          : !group ? null
-          : /\s/.test(cur) ? null
</del><ins>+          : group &amp;&amp; cur == &quot;\n&quot; ? &quot;n&quot;
+          : !group || /\s/.test(cur) ? null
</ins><span class="cx">           : &quot;p&quot;;
</span><ins>+        if (group &amp;&amp; !first &amp;&amp; !type) type = &quot;s&quot;;
</ins><span class="cx">         if (sawType &amp;&amp; sawType != type) {
</span><span class="cx">           if (dir &lt; 0) {dir = 1; moveOnce();}
</span><span class="cx">           break;
</span><span class="cx">         }
</span><ins>+
</ins><span class="cx">         if (type) sawType = type;
</span><span class="cx">         if (dir &gt; 0 &amp;&amp; !moveOnce(!first)) break;
</span><span class="cx">       }
</span><span class="lines">@@ -2826,6 +3758,9 @@
</span><span class="cx">     return result;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // For relative vertical movement. Dir may be -1 or 1. Unit can be
+  // &quot;page&quot; or &quot;line&quot;. The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
</ins><span class="cx">   function findPosV(cm, pos, dir, unit) {
</span><span class="cx">     var doc = cm.doc, x = pos.left, y;
</span><span class="cx">     if (unit == &quot;page&quot;) {
</span><span class="lines">@@ -2843,7 +3778,9 @@
</span><span class="cx">     return target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function findWordAt(line, pos) {
</del><ins>+  // Find the word at the given position (as returned by coordsChar).
+  function findWordAt(doc, pos) {
+    var line = getLine(doc, pos.line).text;
</ins><span class="cx">     var start = pos.ch, end = pos.ch;
</span><span class="cx">     if (line) {
</span><span class="cx">       if ((pos.xRel &lt; 0 || end == line.length) &amp;&amp; start) --start; else ++end;
</span><span class="lines">@@ -2854,17 +3791,18 @@
</span><span class="cx">       while (start &gt; 0 &amp;&amp; check(line.charAt(start - 1))) --start;
</span><span class="cx">       while (end &lt; line.length &amp;&amp; check(line.charAt(end))) ++end;
</span><span class="cx">     }
</span><del>-    return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
</del><ins>+    return new Range(Pos(pos.line, start), Pos(pos.line, end));
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function selectLine(cm, line) {
-    extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
-  }
</del><ins>+  // EDITOR METHODS
</ins><span class="cx"> 
</span><del>-  // PROTOTYPE
</del><ins>+  // The publicly visible API. Note that methodOp(f) means
+  // 'wrap f in an operation, performed on its `this` parameter'.
</ins><span class="cx"> 
</span><del>-  // The publicly visible API. Note that operation(null, f) means
-  // 'wrap f in an operation, performed on its `this` parameter'
</del><ins>+  // This is not the complete set of editor methods. Most of the
+  // methods defined on the Doc type are also injected into
+  // CodeMirror.prototype, for backwards compatibility and
+  // convenience.
</ins><span class="cx"> 
</span><span class="cx">   CodeMirror.prototype = {
</span><span class="cx">     constructor: CodeMirror,
</span><span class="lines">@@ -2893,14 +3831,14 @@
</span><span class="cx">         }
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    addOverlay: operation(null, function(spec, options) {
</del><ins>+    addOverlay: methodOp(function(spec, options) {
</ins><span class="cx">       var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
</span><span class="cx">       if (mode.startState) throw new Error(&quot;Overlays may not be stateful.&quot;);
</span><span class="cx">       this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options &amp;&amp; options.opaque});
</span><span class="cx">       this.state.modeGen++;
</span><span class="cx">       regChange(this);
</span><span class="cx">     }),
</span><del>-    removeOverlay: operation(null, function(spec) {
</del><ins>+    removeOverlay: methodOp(function(spec) {
</ins><span class="cx">       var overlays = this.state.overlays;
</span><span class="cx">       for (var i = 0; i &lt; overlays.length; ++i) {
</span><span class="cx">         var cur = overlays[i].modeSpec;
</span><span class="lines">@@ -2913,18 +3851,29 @@
</span><span class="cx">       }
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    indentLine: operation(null, function(n, dir, aggressive) {
</del><ins>+    indentLine: methodOp(function(n, dir, aggressive) {
</ins><span class="cx">       if (typeof dir != &quot;string&quot; &amp;&amp; typeof dir != &quot;number&quot;) {
</span><span class="cx">         if (dir == null) dir = this.options.smartIndent ? &quot;smart&quot; : &quot;prev&quot;;
</span><span class="cx">         else dir = dir ? &quot;add&quot; : &quot;subtract&quot;;
</span><span class="cx">       }
</span><span class="cx">       if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
</span><span class="cx">     }),
</span><del>-    indentSelection: operation(null, function(how) {
-      var sel = this.doc.sel;
-      if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
-      var e = sel.to.line - (sel.to.ch ? 0 : 1);
-      for (var i = sel.from.line; i &lt;= e; ++i) indentLine(this, i, how);
</del><ins>+    indentSelection: methodOp(function(how) {
+      var ranges = this.doc.sel.ranges, end = -1;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var range = ranges[i];
+        if (!range.empty()) {
+          var start = Math.max(end, range.from().line);
+          var to = range.to();
+          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
+          for (var j = start; j &lt; end; ++j)
+            indentLine(this, j, how);
+        } else if (range.head.line &gt; end) {
+          indentLine(this, range.head.line, how, true);
+          end = range.head.line;
+          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
+        }
+      }
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     // Fetch the parser token for a given character. Useful for hacks
</span><span class="lines">@@ -2937,12 +3886,11 @@
</span><span class="cx">       var stream = new StringStream(line.text, this.options.tabSize);
</span><span class="cx">       while (stream.pos &lt; pos.ch &amp;&amp; !stream.eol()) {
</span><span class="cx">         stream.start = stream.pos;
</span><del>-        var style = mode.token(stream, state);
</del><ins>+        var style = readToken(mode, stream, state);
</ins><span class="cx">       }
</span><span class="cx">       return {start: stream.start,
</span><span class="cx">               end: stream.pos,
</span><span class="cx">               string: stream.current(),
</span><del>-              className: style || null, // Deprecated, use 'type' instead
</del><span class="cx">               type: style || null,
</span><span class="cx">               state: state};
</span><span class="cx">     },
</span><span class="lines">@@ -2967,11 +3915,31 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getHelper: function(pos, type) {
</span><del>-      if (!helpers.hasOwnProperty(type)) return;
</del><ins>+      return this.getHelpers(pos, type)[0];
+    },
+
+    getHelpers: function(pos, type) {
+      var found = [];
+      if (!helpers.hasOwnProperty(type)) return helpers;
</ins><span class="cx">       var help = helpers[type], mode = this.getModeAt(pos);
</span><del>-      return mode[type] &amp;&amp; help[mode[type]] ||
-        mode.helperType &amp;&amp; help[mode.helperType] ||
-        help[mode.name];
</del><ins>+      if (typeof mode[type] == &quot;string&quot;) {
+        if (help[mode[type]]) found.push(help[mode[type]]);
+      } else if (mode[type]) {
+        for (var i = 0; i &lt; mode[type].length; i++) {
+          var val = help[mode[type][i]];
+          if (val) found.push(val);
+        }
+      } else if (mode.helperType &amp;&amp; help[mode.helperType]) {
+        found.push(help[mode.helperType]);
+      } else if (help[mode.name]) {
+        found.push(help[mode.name]);
+      }
+      for (var i = 0; i &lt; help._global.length; i++) {
+        var cur = help._global[i];
+        if (cur.pred(mode, this) &amp;&amp; indexOf(found, cur.val) == -1)
+          found.push(cur.val);
+      }
+      return found;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getStateAfter: function(line, precise) {
</span><span class="lines">@@ -2981,10 +3949,10 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     cursorCoords: function(start, mode) {
</span><del>-      var pos, sel = this.doc.sel;
-      if (start == null) pos = sel.head;
</del><ins>+      var pos, range = this.doc.sel.primary();
+      if (start == null) pos = range.head;
</ins><span class="cx">       else if (typeof start == &quot;object&quot;) pos = clipPos(this.doc, start);
</span><del>-      else pos = start ? sel.from : sel.to;
</del><ins>+      else pos = start ? range.from() : range.to();
</ins><span class="cx">       return cursorCoords(this, pos, mode || &quot;page&quot;);
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -3006,15 +3974,15 @@
</span><span class="cx">       if (line &lt; this.doc.first) line = this.doc.first;
</span><span class="cx">       else if (line &gt; last) { line = last; end = true; }
</span><span class="cx">       var lineObj = getLine(this.doc, line);
</span><del>-      return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || &quot;page&quot;).top +
-        (end ? lineObj.height : 0);
</del><ins>+      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || &quot;page&quot;).top +
+        (end ? this.doc.height - heightAtLine(lineObj) : 0);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     defaultTextHeight: function() { return textHeight(this.display); },
</span><span class="cx">     defaultCharWidth: function() { return charWidth(this.display); },
</span><span class="cx"> 
</span><del>-    setGutterMarker: operation(null, function(line, gutterID, value) {
-      return changeLine(this, line, function(line) {
</del><ins>+    setGutterMarker: methodOp(function(line, gutterID, value) {
+      return changeLine(this, line, &quot;gutter&quot;, function(line) {
</ins><span class="cx">         var markers = line.gutterMarkers || (line.gutterMarkers = {});
</span><span class="cx">         markers[gutterID] = value;
</span><span class="cx">         if (!value &amp;&amp; isEmpty(markers)) line.gutterMarkers = null;
</span><span class="lines">@@ -3022,20 +3990,20 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    clearGutter: operation(null, function(gutterID) {
</del><ins>+    clearGutter: methodOp(function(gutterID) {
</ins><span class="cx">       var cm = this, doc = cm.doc, i = doc.first;
</span><span class="cx">       doc.iter(function(line) {
</span><span class="cx">         if (line.gutterMarkers &amp;&amp; line.gutterMarkers[gutterID]) {
</span><span class="cx">           line.gutterMarkers[gutterID] = null;
</span><del>-          regChange(cm, i, i + 1);
</del><ins>+          regLineChange(cm, i, &quot;gutter&quot;);
</ins><span class="cx">           if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
</span><span class="cx">         }
</span><span class="cx">         ++i;
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    addLineClass: operation(null, function(handle, where, cls) {
-      return changeLine(this, handle, function(line) {
</del><ins>+    addLineClass: methodOp(function(handle, where, cls) {
+      return changeLine(this, handle, &quot;class&quot;, function(line) {
</ins><span class="cx">         var prop = where == &quot;text&quot; ? &quot;textClass&quot; : where == &quot;background&quot; ? &quot;bgClass&quot; : &quot;wrapClass&quot;;
</span><span class="cx">         if (!line[prop]) line[prop] = cls;
</span><span class="cx">         else if (new RegExp(&quot;(?:^|\\s)&quot; + cls + &quot;(?:$|\\s)&quot;).test(line[prop])) return false;
</span><span class="lines">@@ -3044,8 +4012,8 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    removeLineClass: operation(null, function(handle, where, cls) {
-      return changeLine(this, handle, function(line) {
</del><ins>+    removeLineClass: methodOp(function(handle, where, cls) {
+      return changeLine(this, handle, &quot;class&quot;, function(line) {
</ins><span class="cx">         var prop = where == &quot;text&quot; ? &quot;textClass&quot; : where == &quot;background&quot; ? &quot;bgClass&quot; : &quot;wrapClass&quot;;
</span><span class="cx">         var cur = line[prop];
</span><span class="cx">         if (!cur) return false;
</span><span class="lines">@@ -3060,7 +4028,7 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    addLineWidget: operation(null, function(handle, node, options) {
</del><ins>+    addLineWidget: methodOp(function(handle, node, options) {
</ins><span class="cx">       return addLineWidget(this, handle, node, options);
</span><span class="cx">     }),
</span><span class="cx"> 
</span><span class="lines">@@ -3081,7 +4049,7 @@
</span><span class="cx">               widgets: line.widgets};
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
</del><ins>+    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
</ins><span class="cx"> 
</span><span class="cx">     addWidget: function(pos, node, scroll, vert, horiz) {
</span><span class="cx">       var display = this.display;
</span><span class="lines">@@ -3116,9 +4084,14 @@
</span><span class="cx">         scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    triggerOnKeyDown: operation(null, onKeyDown),
</del><ins>+    triggerOnKeyDown: methodOp(onKeyDown),
+    triggerOnKeyPress: methodOp(onKeyPress),
+    triggerOnKeyUp: methodOp(onKeyUp),
</ins><span class="cx"> 
</span><del>-    execCommand: function(cmd) {return commands[cmd](this);},
</del><ins>+    execCommand: function(cmd) {
+      if (commands.hasOwnProperty(cmd))
+        return commands[cmd](this);
+    },
</ins><span class="cx"> 
</span><span class="cx">     findPosH: function(from, amount, unit, visually) {
</span><span class="cx">       var dir = 1;
</span><span class="lines">@@ -3130,20 +4103,25 @@
</span><span class="cx">       return cur;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    moveH: operation(null, function(dir, unit) {
-      var sel = this.doc.sel, pos;
-      if (sel.shift || sel.extend || posEq(sel.from, sel.to))
-        pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
-      else
-        pos = dir &lt; 0 ? sel.from : sel.to;
-      extendSelection(this.doc, pos, pos, dir);
</del><ins>+    moveH: methodOp(function(dir, unit) {
+      var cm = this;
+      cm.extendSelectionsBy(function(range) {
+        if (cm.display.shift || cm.doc.extend || range.empty())
+          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
+        else
+          return dir &lt; 0 ? range.from() : range.to();
+      }, sel_move);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    deleteH: operation(null, function(dir, unit) {
-      var sel = this.doc.sel;
-      if (!posEq(sel.from, sel.to)) replaceRange(this.doc, &quot;&quot;, sel.from, sel.to, &quot;+delete&quot;);
-      else replaceRange(this.doc, &quot;&quot;, sel.from, findPosH(this.doc, sel.head, dir, unit, false), &quot;+delete&quot;);
-      this.curOp.userSelChange = true;
</del><ins>+    deleteH: methodOp(function(dir, unit) {
+      var sel = this.doc.sel, doc = this.doc;
+      if (sel.somethingSelected())
+        doc.replaceSelection(&quot;&quot;, null, &quot;+delete&quot;);
+      else
+        deleteNearSelection(this, function(range) {
+          var other = findPosH(doc, range.head, dir, unit, false);
+          return dir &lt; 0 ? {from: other, to: range.head} : {from: range.head, to: other};
+        });
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     findPosV: function(from, amount, unit, goalColumn) {
</span><span class="lines">@@ -3159,28 +4137,39 @@
</span><span class="cx">       return cur;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    moveV: operation(null, function(dir, unit) {
-      var sel = this.doc.sel;
-      var pos = cursorCoords(this, sel.head, &quot;div&quot;);
-      if (sel.goalColumn != null) pos.left = sel.goalColumn;
-      var target = findPosV(this, pos, dir, unit);
-
-      if (unit == &quot;page&quot;) addToScrollPos(this, 0, charCoords(this, target, &quot;div&quot;).top - pos.top);
-      extendSelection(this.doc, target, target, dir);
-      sel.goalColumn = pos.left;
</del><ins>+    moveV: methodOp(function(dir, unit) {
+      var cm = this, doc = this.doc, goals = [];
+      var collapse = !cm.display.shift &amp;&amp; !doc.extend &amp;&amp; doc.sel.somethingSelected();
+      doc.extendSelectionsBy(function(range) {
+        if (collapse)
+          return dir &lt; 0 ? range.from() : range.to();
+        var headPos = cursorCoords(cm, range.head, &quot;div&quot;);
+        if (range.goalColumn != null) headPos.left = range.goalColumn;
+        goals.push(headPos.left);
+        var pos = findPosV(cm, headPos, dir, unit);
+        if (unit == &quot;page&quot; &amp;&amp; range == doc.sel.primary())
+          addToScrollPos(cm, null, charCoords(cm, pos, &quot;div&quot;).top - headPos.top);
+        return pos;
+      }, sel_move);
+      if (goals.length) for (var i = 0; i &lt; doc.sel.ranges.length; i++)
+        doc.sel.ranges[i].goalColumn = goals[i];
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     toggleOverwrite: function(value) {
</span><span class="cx">       if (value != null &amp;&amp; value == this.state.overwrite) return;
</span><span class="cx">       if (this.state.overwrite = !this.state.overwrite)
</span><del>-        this.display.cursor.className += &quot; CodeMirror-overwrite&quot;;
</del><ins>+        addClass(this.display.cursorDiv, &quot;CodeMirror-overwrite&quot;);
</ins><span class="cx">       else
</span><del>-        this.display.cursor.className = this.display.cursor.className.replace(&quot; CodeMirror-overwrite&quot;, &quot;&quot;);
</del><ins>+        rmClass(this.display.cursorDiv, &quot;CodeMirror-overwrite&quot;);
+
+      signal(this, &quot;overwriteToggle&quot;, this, this.state.overwrite);
</ins><span class="cx">     },
</span><del>-    hasFocus: function() { return this.state.focused; },
</del><ins>+    hasFocus: function() { return activeElt() == this.display.input; },
</ins><span class="cx"> 
</span><del>-    scrollTo: operation(null, function(x, y) {
-      updateScrollPos(this, x, y);
</del><ins>+    scrollTo: methodOp(function(x, y) {
+      if (x != null || y != null) resolveScrollToPos(this);
+      if (x != null) this.curOp.scrollLeft = x;
+      if (y != null) this.curOp.scrollTop = y;
</ins><span class="cx">     }),
</span><span class="cx">     getScrollInfo: function() {
</span><span class="cx">       var scroller = this.display.scroller, co = scrollerCutOff;
</span><span class="lines">@@ -3189,54 +4178,62 @@
</span><span class="cx">               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    scrollIntoView: operation(null, function(range, margin) {
-      if (range == null) range = {from: this.doc.sel.head, to: null};
-      else if (typeof range == &quot;number&quot;) range = {from: Pos(range, 0), to: null};
-      else if (range.from == null) range = {from: range, to: null};
</del><ins>+    scrollIntoView: methodOp(function(range, margin) {
+      if (range == null) {
+        range = {from: this.doc.sel.primary().head, to: null};
+        if (margin == null) margin = this.options.cursorScrollMargin;
+      } else if (typeof range == &quot;number&quot;) {
+        range = {from: Pos(range, 0), to: null};
+      } else if (range.from == null) {
+        range = {from: range, to: null};
+      }
</ins><span class="cx">       if (!range.to) range.to = range.from;
</span><del>-      if (!margin) margin = 0;
</del><ins>+      range.margin = margin || 0;
</ins><span class="cx"> 
</span><del>-      var coords = range;
</del><span class="cx">       if (range.from.line != null) {
</span><del>-        this.curOp.scrollToPos = {from: range.from, to: range.to, margin: margin};
-        coords = {from: cursorCoords(this, range.from),
-                  to: cursorCoords(this, range.to)};
</del><ins>+        resolveScrollToPos(this);
+        this.curOp.scrollToPos = range;
+      } else {
+        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
+                                      Math.min(range.from.top, range.to.top) - range.margin,
+                                      Math.max(range.from.right, range.to.right),
+                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);
+        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
</ins><span class="cx">       }
</span><del>-      var sPos = calculateScrollPos(this, Math.min(coords.from.left, coords.to.left),
-                                    Math.min(coords.from.top, coords.to.top) - margin,
-                                    Math.max(coords.from.right, coords.to.right),
-                                    Math.max(coords.from.bottom, coords.to.bottom) + margin);
-      updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
</del><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    setSize: operation(null, function(width, height) {
</del><ins>+    setSize: methodOp(function(width, height) {
</ins><span class="cx">       function interpret(val) {
</span><span class="cx">         return typeof val == &quot;number&quot; || /^\d+$/.test(String(val)) ? val + &quot;px&quot; : val;
</span><span class="cx">       }
</span><span class="cx">       if (width != null) this.display.wrapper.style.width = interpret(width);
</span><span class="cx">       if (height != null) this.display.wrapper.style.height = interpret(height);
</span><del>-      if (this.options.lineWrapping)
-        this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
</del><ins>+      if (this.options.lineWrapping) clearLineMeasurementCache(this);
</ins><span class="cx">       this.curOp.forceUpdate = true;
</span><ins>+      signal(this, &quot;refresh&quot;, this);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     operation: function(f){return runInOp(this, f);},
</span><span class="cx"> 
</span><del>-    refresh: operation(null, function() {
-      var badHeight = this.display.cachedTextHeight == null;
</del><ins>+    refresh: methodOp(function() {
+      var oldHeight = this.display.cachedTextHeight;
+      regChange(this);
+      this.curOp.forceUpdate = true;
</ins><span class="cx">       clearCaches(this);
</span><del>-      updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
-      regChange(this);
-      if (badHeight) estimateLineHeights(this);
</del><ins>+      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
+      updateGutterSpace(this);
+      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) &gt; .5)
+        estimateLineHeights(this);
+      signal(this, &quot;refresh&quot;, this);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    swapDoc: operation(null, function(doc) {
</del><ins>+    swapDoc: methodOp(function(doc) {
</ins><span class="cx">       var old = this.doc;
</span><span class="cx">       old.cm = null;
</span><span class="cx">       attachDoc(this, doc);
</span><span class="cx">       clearCaches(this);
</span><del>-      resetInput(this, true);
-      updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
</del><ins>+      resetInput(this);
+      this.scrollTo(doc.scrollLeft, doc.scrollTop);
</ins><span class="cx">       signalLater(this, &quot;swapDoc&quot;, this, old);
</span><span class="cx">       return old;
</span><span class="cx">     }),
</span><span class="lines">@@ -3250,10 +4247,10 @@
</span><span class="cx"> 
</span><span class="cx">   // OPTION DEFAULTS
</span><span class="cx"> 
</span><del>-  var optionHandlers = CodeMirror.optionHandlers = {};
-
</del><span class="cx">   // The default configuration options.
</span><span class="cx">   var defaults = CodeMirror.defaults = {};
</span><ins>+  // Functions to run when options are changed.
+  var optionHandlers = CodeMirror.optionHandlers = {};
</ins><span class="cx"> 
</span><span class="cx">   function option(name, deflt, handle, notOnInit) {
</span><span class="cx">     CodeMirror.defaults[name] = deflt;
</span><span class="lines">@@ -3261,6 +4258,7 @@
</span><span class="cx">       notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Passed to option handlers when there is no old value.
</ins><span class="cx">   var Init = CodeMirror.Init = {toString: function(){return &quot;CodeMirror.Init&quot;;}};
</span><span class="cx"> 
</span><span class="cx">   // These two are, on init, called from the constructor because they
</span><span class="lines">@@ -3277,7 +4275,7 @@
</span><span class="cx">   option(&quot;indentWithTabs&quot;, false);
</span><span class="cx">   option(&quot;smartIndent&quot;, true);
</span><span class="cx">   option(&quot;tabSize&quot;, 4, function(cm) {
</span><del>-    loadMode(cm);
</del><ins>+    resetModeState(cm);
</ins><span class="cx">     clearCaches(cm);
</span><span class="cx">     regChange(cm);
</span><span class="cx">   }, true);
</span><span class="lines">@@ -3297,9 +4295,6 @@
</span><span class="cx">   option(&quot;keyMap&quot;, &quot;default&quot;, keyMapChanged);
</span><span class="cx">   option(&quot;extraKeys&quot;, null);
</span><span class="cx"> 
</span><del>-  option(&quot;onKeyEvent&quot;, null);
-  option(&quot;onDragEvent&quot;, null);
-
</del><span class="cx">   option(&quot;lineWrapping&quot;, false, wrappingChanged, true);
</span><span class="cx">   option(&quot;gutters&quot;, [], function(cm) {
</span><span class="cx">     setGuttersForLineNumbers(cm.options);
</span><span class="lines">@@ -3327,9 +4322,10 @@
</span><span class="cx">       cm.display.disabled = true;
</span><span class="cx">     } else {
</span><span class="cx">       cm.display.disabled = false;
</span><del>-      if (!val) resetInput(cm, true);
</del><ins>+      if (!val) resetInput(cm);
</ins><span class="cx">     }
</span><span class="cx">   });
</span><ins>+  option(&quot;disableInput&quot;, false, function(cm, val) {if (!val) resetInput(cm);}, true);
</ins><span class="cx">   option(&quot;dragDrop&quot;, true);
</span><span class="cx"> 
</span><span class="cx">   option(&quot;cursorBlinkRate&quot;, 530);
</span><span class="lines">@@ -3337,13 +4333,13 @@
</span><span class="cx">   option(&quot;cursorHeight&quot;, 1);
</span><span class="cx">   option(&quot;workTime&quot;, 100);
</span><span class="cx">   option(&quot;workDelay&quot;, 100);
</span><del>-  option(&quot;flattenSpans&quot;, true);
</del><ins>+  option(&quot;flattenSpans&quot;, true, resetModeState, true);
+  option(&quot;addModeClass&quot;, false, resetModeState, true);
</ins><span class="cx">   option(&quot;pollInterval&quot;, 100);
</span><del>-  option(&quot;undoDepth&quot;, 40, function(cm, val){cm.doc.history.undoDepth = val;});
-  option(&quot;historyEventDelay&quot;, 500);
</del><ins>+  option(&quot;undoDepth&quot;, 200, function(cm, val){cm.doc.history.undoDepth = val;});
+  option(&quot;historyEventDelay&quot;, 1250);
</ins><span class="cx">   option(&quot;viewportMargin&quot;, 10, function(cm){cm.refresh();}, true);
</span><del>-  option(&quot;maxHighlightLength&quot;, 10000, function(cm){loadMode(cm); cm.refresh();}, true);
-  option(&quot;crudeMeasuringFrom&quot;, 10000);
</del><ins>+  option(&quot;maxHighlightLength&quot;, 10000, resetModeState, true);
</ins><span class="cx">   option(&quot;moveInputWithCursor&quot;, true, function(cm, val) {
</span><span class="cx">     if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
</span><span class="cx">   });
</span><span class="lines">@@ -3358,6 +4354,9 @@
</span><span class="cx">   // Known modes, by name and by MIME
</span><span class="cx">   var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
</span><span class="cx"> 
</span><ins>+  // Extra arguments are stored as the mode's dependencies, which is
+  // used by (legacy) mechanisms like loadmode.js to automatically
+  // load a mode. (Preferred mechanism is the require/define calls.)
</ins><span class="cx">   CodeMirror.defineMode = function(name, mode) {
</span><span class="cx">     if (!CodeMirror.defaults.mode &amp;&amp; name != &quot;null&quot;) CodeMirror.defaults.mode = name;
</span><span class="cx">     if (arguments.length &gt; 2) {
</span><span class="lines">@@ -3371,11 +4370,14 @@
</span><span class="cx">     mimeModes[mime] = spec;
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Given a MIME type, a {name, ...options} config object, or a name
+  // string, return a mode config object.
</ins><span class="cx">   CodeMirror.resolveMode = function(spec) {
</span><span class="cx">     if (typeof spec == &quot;string&quot; &amp;&amp; mimeModes.hasOwnProperty(spec)) {
</span><span class="cx">       spec = mimeModes[spec];
</span><span class="cx">     } else if (spec &amp;&amp; typeof spec.name == &quot;string&quot; &amp;&amp; mimeModes.hasOwnProperty(spec.name)) {
</span><span class="cx">       var found = mimeModes[spec.name];
</span><ins>+      if (typeof found == &quot;string&quot;) found = {name: found};
</ins><span class="cx">       spec = createObj(found, spec);
</span><span class="cx">       spec.name = found.name;
</span><span class="cx">     } else if (typeof spec == &quot;string&quot; &amp;&amp; /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
</span><span class="lines">@@ -3385,6 +4387,8 @@
</span><span class="cx">     else return spec || {name: &quot;null&quot;};
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Given a mode spec (anything that resolveMode accepts), find and
+  // initialize an actual mode object.
</ins><span class="cx">   CodeMirror.getMode = function(options, spec) {
</span><span class="cx">     var spec = CodeMirror.resolveMode(spec);
</span><span class="cx">     var mfactory = modes[spec.name];
</span><span class="lines">@@ -3399,15 +4403,21 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     modeObj.name = spec.name;
</span><ins>+    if (spec.helperType) modeObj.helperType = spec.helperType;
+    if (spec.modeProps) for (var prop in spec.modeProps)
+      modeObj[prop] = spec.modeProps[prop];
</ins><span class="cx"> 
</span><span class="cx">     return modeObj;
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Minimal default mode.
</ins><span class="cx">   CodeMirror.defineMode(&quot;null&quot;, function() {
</span><span class="cx">     return {token: function(stream) {stream.skipToEnd();}};
</span><span class="cx">   });
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/plain&quot;, &quot;null&quot;);
</span><span class="cx"> 
</span><ins>+  // This can be used to attach properties to mode objects from
+  // outside the actual mode definition.
</ins><span class="cx">   var modeExtensions = CodeMirror.modeExtensions = {};
</span><span class="cx">   CodeMirror.extendMode = function(mode, properties) {
</span><span class="cx">     var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
</span><span class="lines">@@ -3429,19 +4439,20 @@
</span><span class="cx"> 
</span><span class="cx">   var helpers = CodeMirror.helpers = {};
</span><span class="cx">   CodeMirror.registerHelper = function(type, name, value) {
</span><del>-    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
</del><ins>+    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
</ins><span class="cx">     helpers[type][name] = value;
</span><span class="cx">   };
</span><ins>+  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
+    CodeMirror.registerHelper(type, name, value);
+    helpers[type]._global.push({pred: predicate, val: value});
+  };
</ins><span class="cx"> 
</span><del>-  // UTILITIES
</del><ins>+  // MODE STATE HANDLING
</ins><span class="cx"> 
</span><del>-  CodeMirror.isWordChar = isWordChar;
</del><ins>+  // Utility functions for working with state. Exported because nested
+  // modes need to do this for their inner modes.
</ins><span class="cx"> 
</span><del>-  // MODE STATE HANDLING
-
-  // Utility functions for working with state. Exported because modes
-  // sometimes need to do this.
-  function copyState(mode, state) {
</del><ins>+  var copyState = CodeMirror.copyState = function(mode, state) {
</ins><span class="cx">     if (state === true) return state;
</span><span class="cx">     if (mode.copyState) return mode.copyState(state);
</span><span class="cx">     var nstate = {};
</span><span class="lines">@@ -3451,14 +4462,14 @@
</span><span class="cx">       nstate[n] = val;
</span><span class="cx">     }
</span><span class="cx">     return nstate;
</span><del>-  }
-  CodeMirror.copyState = copyState;
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function startState(mode, a1, a2) {
</del><ins>+  var startState = CodeMirror.startState = function(mode, a1, a2) {
</ins><span class="cx">     return mode.startState ? mode.startState(a1, a2) : true;
</span><del>-  }
-  CodeMirror.startState = startState;
</del><ins>+  };
</ins><span class="cx"> 
</span><ins>+  // Given a mode and a state (for that mode), find the inner mode and
+  // state at the position that the state refers to.
</ins><span class="cx">   CodeMirror.innerMode = function(mode, state) {
</span><span class="cx">     while (mode.innerMode) {
</span><span class="cx">       var info = mode.innerMode(state);
</span><span class="lines">@@ -3471,49 +4482,73 @@
</span><span class="cx"> 
</span><span class="cx">   // STANDARD COMMANDS
</span><span class="cx"> 
</span><ins>+  // Commands are parameter-less actions that can be performed on an
+  // editor, mostly used for keybindings.
</ins><span class="cx">   var commands = CodeMirror.commands = {
</span><del>-    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
</del><ins>+    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
+    singleSelection: function(cm) {
+      cm.setSelection(cm.getCursor(&quot;anchor&quot;), cm.getCursor(&quot;head&quot;), sel_dontScroll);
+    },
</ins><span class="cx">     killLine: function(cm) {
</span><del>-      var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
-      if (!sel &amp;&amp; cm.getLine(from.line).length == from.ch)
-        cm.replaceRange(&quot;&quot;, from, Pos(from.line + 1, 0), &quot;+delete&quot;);
-      else cm.replaceRange(&quot;&quot;, from, sel ? to : Pos(from.line), &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        if (range.empty()) {
+          var len = getLine(cm.doc, range.head.line).text.length;
+          if (range.head.ch == len &amp;&amp; range.head.line &lt; cm.lastLine())
+            return {from: range.head, to: Pos(range.head.line + 1, 0)};
+          else
+            return {from: range.head, to: Pos(range.head.line, len)};
+        } else {
+          return {from: range.from(), to: range.to()};
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     deleteLine: function(cm) {
</span><del>-      var l = cm.getCursor().line;
-      cm.replaceRange(&quot;&quot;, Pos(l, 0), Pos(l), &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        return {from: Pos(range.from().line, 0),
+                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
+      });
</ins><span class="cx">     },
</span><span class="cx">     delLineLeft: function(cm) {
</span><del>-      var cur = cm.getCursor();
-      cm.replaceRange(&quot;&quot;, Pos(cur.line, 0), cur, &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        return {from: Pos(range.from().line, 0), to: range.from()};
+      });
</ins><span class="cx">     },
</span><span class="cx">     undo: function(cm) {cm.undo();},
</span><span class="cx">     redo: function(cm) {cm.redo();},
</span><ins>+    undoSelection: function(cm) {cm.undoSelection();},
+    redoSelection: function(cm) {cm.redoSelection();},
</ins><span class="cx">     goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
</span><span class="cx">     goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
</span><span class="cx">     goLineStart: function(cm) {
</span><del>-      cm.extendSelection(lineStart(cm, cm.getCursor().line));
</del><ins>+      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineStartSmart: function(cm) {
</span><del>-      var cur = cm.getCursor(), start = lineStart(cm, cur.line);
-      var line = cm.getLineHandle(start.line);
-      var order = getOrder(line);
-      if (!order || order[0].level == 0) {
-        var firstNonWS = Math.max(0, line.text.search(/\S/));
-        var inWS = cur.line == start.line &amp;&amp; cur.ch &lt;= firstNonWS &amp;&amp; cur.ch;
-        cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
-      } else cm.extendSelection(start);
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var start = lineStart(cm, range.head.line);
+        var line = cm.getLineHandle(start.line);
+        var order = getOrder(line);
+        if (!order || order[0].level == 0) {
+          var firstNonWS = Math.max(0, line.text.search(/\S/));
+          var inWS = range.head.line == start.line &amp;&amp; range.head.ch &lt;= firstNonWS &amp;&amp; range.head.ch;
+          return Pos(start.line, inWS ? 0 : firstNonWS);
+        }
+        return start;
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineEnd: function(cm) {
</span><del>-      cm.extendSelection(lineEnd(cm, cm.getCursor().line));
</del><ins>+      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineRight: function(cm) {
</span><del>-      var top = cm.charCoords(cm.getCursor(), &quot;div&quot;).top + 5;
-      cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, &quot;div&quot;));
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var top = cm.charCoords(range.head, &quot;div&quot;).top + 5;
+        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, &quot;div&quot;);
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineLeft: function(cm) {
</span><del>-      var top = cm.charCoords(cm.getCursor(), &quot;div&quot;).top + 5;
-      cm.extendSelection(cm.coordsChar({left: 0, top: top}, &quot;div&quot;));
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var top = cm.charCoords(range.head, &quot;div&quot;).top + 5;
+        return cm.coordsChar({left: 0, top: top}, &quot;div&quot;);
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineUp: function(cm) {cm.moveV(-1, &quot;line&quot;);},
</span><span class="cx">     goLineDown: function(cm) {cm.moveV(1, &quot;line&quot;);},
</span><span class="lines">@@ -3536,22 +4571,41 @@
</span><span class="cx">     indentAuto: function(cm) {cm.indentSelection(&quot;smart&quot;);},
</span><span class="cx">     indentMore: function(cm) {cm.indentSelection(&quot;add&quot;);},
</span><span class="cx">     indentLess: function(cm) {cm.indentSelection(&quot;subtract&quot;);},
</span><del>-    insertTab: function(cm) {cm.replaceSelection(&quot;\t&quot;, &quot;end&quot;, &quot;+input&quot;);},
</del><ins>+    insertTab: function(cm) {cm.replaceSelection(&quot;\t&quot;);},
+    insertSoftTab: function(cm) {
+      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var pos = ranges[i].from();
+        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
+        spaces.push(new Array(tabSize - col % tabSize + 1).join(&quot; &quot;));
+      }
+      cm.replaceSelections(spaces);
+    },
</ins><span class="cx">     defaultTab: function(cm) {
</span><span class="cx">       if (cm.somethingSelected()) cm.indentSelection(&quot;add&quot;);
</span><del>-      else cm.replaceSelection(&quot;\t&quot;, &quot;end&quot;, &quot;+input&quot;);
</del><ins>+      else cm.execCommand(&quot;insertTab&quot;);
</ins><span class="cx">     },
</span><span class="cx">     transposeChars: function(cm) {
</span><del>-      var cur = cm.getCursor(), line = cm.getLine(cur.line);
-      if (cur.ch &gt; 0 &amp;&amp; cur.ch &lt; line.length - 1)
-        cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
-                        Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
</del><ins>+      runInOp(cm, function() {
+        var ranges = cm.listSelections();
+        for (var i = 0; i &lt; ranges.length; i++) {
+          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
+          if (cur.ch &gt; 0 &amp;&amp; cur.ch &lt; line.length - 1)
+            cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+                            Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     newlineAndIndent: function(cm) {
</span><del>-      operation(cm, function() {
-        cm.replaceSelection(&quot;\n&quot;, &quot;end&quot;, &quot;+input&quot;);
-        cm.indentLine(cm.getCursor().line, null, true);
-      })();
</del><ins>+      runInOp(cm, function() {
+        var len = cm.listSelections().length;
+        for (var i = 0; i &lt; len; i++) {
+          var range = cm.listSelections()[i];
+          cm.replaceRange(&quot;\n&quot;, range.anchor, range.head, &quot;+input&quot;);
+          cm.indentLine(range.from().line + 1, null, true);
+          ensureCursorVisible(cm);
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     toggleOverwrite: function(cm) {cm.toggleOverwrite();}
</span><span class="cx">   };
</span><span class="lines">@@ -3564,17 +4618,20 @@
</span><span class="cx">     &quot;End&quot;: &quot;goLineEnd&quot;, &quot;Home&quot;: &quot;goLineStartSmart&quot;, &quot;PageUp&quot;: &quot;goPageUp&quot;, &quot;PageDown&quot;: &quot;goPageDown&quot;,
</span><span class="cx">     &quot;Delete&quot;: &quot;delCharAfter&quot;, &quot;Backspace&quot;: &quot;delCharBefore&quot;, &quot;Shift-Backspace&quot;: &quot;delCharBefore&quot;,
</span><span class="cx">     &quot;Tab&quot;: &quot;defaultTab&quot;, &quot;Shift-Tab&quot;: &quot;indentAuto&quot;,
</span><del>-    &quot;Enter&quot;: &quot;newlineAndIndent&quot;, &quot;Insert&quot;: &quot;toggleOverwrite&quot;
</del><ins>+    &quot;Enter&quot;: &quot;newlineAndIndent&quot;, &quot;Insert&quot;: &quot;toggleOverwrite&quot;,
+    &quot;Esc&quot;: &quot;singleSelection&quot;
</ins><span class="cx">   };
</span><span class="cx">   // Note that the save and find-related commands aren't defined by
</span><del>-  // default. Unknown commands are simply ignored.
</del><ins>+  // default. User code or addons can define them. Unknown commands
+  // are simply ignored.
</ins><span class="cx">   keyMap.pcDefault = {
</span><span class="cx">     &quot;Ctrl-A&quot;: &quot;selectAll&quot;, &quot;Ctrl-D&quot;: &quot;deleteLine&quot;, &quot;Ctrl-Z&quot;: &quot;undo&quot;, &quot;Shift-Ctrl-Z&quot;: &quot;redo&quot;, &quot;Ctrl-Y&quot;: &quot;redo&quot;,
</span><del>-    &quot;Ctrl-Home&quot;: &quot;goDocStart&quot;, &quot;Alt-Up&quot;: &quot;goDocStart&quot;, &quot;Ctrl-End&quot;: &quot;goDocEnd&quot;, &quot;Ctrl-Down&quot;: &quot;goDocEnd&quot;,
</del><ins>+    &quot;Ctrl-Home&quot;: &quot;goDocStart&quot;, &quot;Ctrl-Up&quot;: &quot;goDocStart&quot;, &quot;Ctrl-End&quot;: &quot;goDocEnd&quot;, &quot;Ctrl-Down&quot;: &quot;goDocEnd&quot;,
</ins><span class="cx">     &quot;Ctrl-Left&quot;: &quot;goGroupLeft&quot;, &quot;Ctrl-Right&quot;: &quot;goGroupRight&quot;, &quot;Alt-Left&quot;: &quot;goLineStart&quot;, &quot;Alt-Right&quot;: &quot;goLineEnd&quot;,
</span><span class="cx">     &quot;Ctrl-Backspace&quot;: &quot;delGroupBefore&quot;, &quot;Ctrl-Delete&quot;: &quot;delGroupAfter&quot;, &quot;Ctrl-S&quot;: &quot;save&quot;, &quot;Ctrl-F&quot;: &quot;find&quot;,
</span><span class="cx">     &quot;Ctrl-G&quot;: &quot;findNext&quot;, &quot;Shift-Ctrl-G&quot;: &quot;findPrev&quot;, &quot;Shift-Ctrl-F&quot;: &quot;replace&quot;, &quot;Shift-Ctrl-R&quot;: &quot;replaceAll&quot;,
</span><span class="cx">     &quot;Ctrl-[&quot;: &quot;indentLess&quot;, &quot;Ctrl-]&quot;: &quot;indentMore&quot;,
</span><ins>+    &quot;Ctrl-U&quot;: &quot;undoSelection&quot;, &quot;Shift-Ctrl-U&quot;: &quot;redoSelection&quot;, &quot;Alt-U&quot;: &quot;redoSelection&quot;,
</ins><span class="cx">     fallthrough: &quot;basic&quot;
</span><span class="cx">   };
</span><span class="cx">   keyMap.macDefault = {
</span><span class="lines">@@ -3584,15 +4641,17 @@
</span><span class="cx">     &quot;Ctrl-Alt-Backspace&quot;: &quot;delGroupAfter&quot;, &quot;Alt-Delete&quot;: &quot;delGroupAfter&quot;, &quot;Cmd-S&quot;: &quot;save&quot;, &quot;Cmd-F&quot;: &quot;find&quot;,
</span><span class="cx">     &quot;Cmd-G&quot;: &quot;findNext&quot;, &quot;Shift-Cmd-G&quot;: &quot;findPrev&quot;, &quot;Cmd-Alt-F&quot;: &quot;replace&quot;, &quot;Shift-Cmd-Alt-F&quot;: &quot;replaceAll&quot;,
</span><span class="cx">     &quot;Cmd-[&quot;: &quot;indentLess&quot;, &quot;Cmd-]&quot;: &quot;indentMore&quot;, &quot;Cmd-Backspace&quot;: &quot;delLineLeft&quot;,
</span><ins>+    &quot;Cmd-U&quot;: &quot;undoSelection&quot;, &quot;Shift-Cmd-U&quot;: &quot;redoSelection&quot;,
</ins><span class="cx">     fallthrough: [&quot;basic&quot;, &quot;emacsy&quot;]
</span><span class="cx">   };
</span><del>-  keyMap[&quot;default&quot;] = mac ? keyMap.macDefault : keyMap.pcDefault;
</del><ins>+  // Very basic readline/emacs-style bindings, which are standard on Mac.
</ins><span class="cx">   keyMap.emacsy = {
</span><span class="cx">     &quot;Ctrl-F&quot;: &quot;goCharRight&quot;, &quot;Ctrl-B&quot;: &quot;goCharLeft&quot;, &quot;Ctrl-P&quot;: &quot;goLineUp&quot;, &quot;Ctrl-N&quot;: &quot;goLineDown&quot;,
</span><span class="cx">     &quot;Alt-F&quot;: &quot;goWordRight&quot;, &quot;Alt-B&quot;: &quot;goWordLeft&quot;, &quot;Ctrl-A&quot;: &quot;goLineStart&quot;, &quot;Ctrl-E&quot;: &quot;goLineEnd&quot;,
</span><span class="cx">     &quot;Ctrl-V&quot;: &quot;goPageDown&quot;, &quot;Shift-Ctrl-V&quot;: &quot;goPageUp&quot;, &quot;Ctrl-D&quot;: &quot;delCharAfter&quot;, &quot;Ctrl-H&quot;: &quot;delCharBefore&quot;,
</span><span class="cx">     &quot;Alt-D&quot;: &quot;delWordAfter&quot;, &quot;Alt-Backspace&quot;: &quot;delWordBefore&quot;, &quot;Ctrl-K&quot;: &quot;killLine&quot;, &quot;Ctrl-T&quot;: &quot;transposeChars&quot;
</span><span class="cx">   };
</span><ins>+  keyMap[&quot;default&quot;] = mac ? keyMap.macDefault : keyMap.pcDefault;
</ins><span class="cx"> 
</span><span class="cx">   // KEYMAP DISPATCH
</span><span class="cx"> 
</span><span class="lines">@@ -3601,7 +4660,11 @@
</span><span class="cx">     else return val;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function lookupKey(name, maps, handle) {
</del><ins>+  // Given an array of keymaps and a key name, call handle on any
+  // bindings found, until that returns a truthy value, at which point
+  // we consider the key handled. Implements things like binding a key
+  // to false stopping further handling and keymap fallthrough.
+  var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
</ins><span class="cx">     function lookup(map) {
</span><span class="cx">       map = getKeyMap(map);
</span><span class="cx">       var found = map[name];
</span><span class="lines">@@ -3613,7 +4676,7 @@
</span><span class="cx">       if (fallthrough == null) return false;
</span><span class="cx">       if (Object.prototype.toString.call(fallthrough) != &quot;[object Array]&quot;)
</span><span class="cx">         return lookup(fallthrough);
</span><del>-      for (var i = 0, e = fallthrough.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; fallthrough.length; ++i) {
</ins><span class="cx">         var done = lookup(fallthrough[i]);
</span><span class="cx">         if (done) return done;
</span><span class="cx">       }
</span><span class="lines">@@ -3624,13 +4687,18 @@
</span><span class="cx">       var done = lookup(maps[i]);
</span><span class="cx">       if (done) return done != &quot;stop&quot;;
</span><span class="cx">     }
</span><del>-  }
-  function isModifierKey(event) {
</del><ins>+  };
+
+  // Modifier key presses don't count as 'real' key presses for the
+  // purpose of keymap fallthrough.
+  var isModifierKey = CodeMirror.isModifierKey = function(event) {
</ins><span class="cx">     var name = keyNames[event.keyCode];
</span><span class="cx">     return name == &quot;Ctrl&quot; || name == &quot;Alt&quot; || name == &quot;Shift&quot; || name == &quot;Mod&quot;;
</span><del>-  }
-  function keyName(event, noShift) {
-    if (opera &amp;&amp; event.keyCode == 34 &amp;&amp; event[&quot;char&quot;]) return false;
</del><ins>+  };
+
+  // Look up the name of a key as indicated by an event object.
+  var keyName = CodeMirror.keyName = function(event, noShift) {
+    if (presto &amp;&amp; event.keyCode == 34 &amp;&amp; event[&quot;char&quot;]) return false;
</ins><span class="cx">     var name = keyNames[event.keyCode];
</span><span class="cx">     if (name == null || event.altGraphKey) return false;
</span><span class="cx">     if (event.altKey) name = &quot;Alt-&quot; + name;
</span><span class="lines">@@ -3638,10 +4706,7 @@
</span><span class="cx">     if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = &quot;Cmd-&quot; + name;
</span><span class="cx">     if (!noShift &amp;&amp; event.shiftKey) name = &quot;Shift-&quot; + name;
</span><span class="cx">     return name;
</span><del>-  }
-  CodeMirror.lookupKey = lookupKey;
-  CodeMirror.isModifierKey = isModifierKey;
-  CodeMirror.keyName = keyName;
</del><ins>+  };
</ins><span class="cx"> 
</span><span class="cx">   // FROMTEXTAREA
</span><span class="cx"> 
</span><span class="lines">@@ -3655,9 +4720,7 @@
</span><span class="cx">     // Set autofocus to true if this textarea is focused, or if it has
</span><span class="cx">     // autofocus and no other element is focused.
</span><span class="cx">     if (options.autofocus == null) {
</span><del>-      var hasFocus = document.body;
-      // doc.activeElement occasionally throws on IE
-      try { hasFocus = document.activeElement; } catch(e) {}
</del><ins>+      var hasFocus = activeElt();
</ins><span class="cx">       options.autofocus = hasFocus == textarea ||
</span><span class="cx">         textarea.getAttribute(&quot;autofocus&quot;) != null &amp;&amp; hasFocus == document.body;
</span><span class="cx">     }
</span><span class="lines">@@ -3703,17 +4766,17 @@
</span><span class="cx">   // Fed to the mode parsers, provides helper functions to make
</span><span class="cx">   // parsers more succinct.
</span><span class="cx"> 
</span><del>-  // The character stream used by a mode's parser.
-  function StringStream(string, tabSize) {
</del><ins>+  var StringStream = CodeMirror.StringStream = function(string, tabSize) {
</ins><span class="cx">     this.pos = this.start = 0;
</span><span class="cx">     this.string = string;
</span><span class="cx">     this.tabSize = tabSize || 8;
</span><span class="cx">     this.lastColumnPos = this.lastColumnValue = 0;
</span><del>-  }
</del><ins>+    this.lineStart = 0;
+  };
</ins><span class="cx"> 
</span><span class="cx">   StringStream.prototype = {
</span><span class="cx">     eol: function() {return this.pos &gt;= this.string.length;},
</span><del>-    sol: function() {return this.pos == 0;},
</del><ins>+    sol: function() {return this.pos == this.lineStart;},
</ins><span class="cx">     peek: function() {return this.string.charAt(this.pos) || undefined;},
</span><span class="cx">     next: function() {
</span><span class="cx">       if (this.pos &lt; this.string.length)
</span><span class="lines">@@ -3746,9 +4809,12 @@
</span><span class="cx">         this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
</span><span class="cx">         this.lastColumnPos = this.start;
</span><span class="cx">       }
</span><del>-      return this.lastColumnValue;
</del><ins>+      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
</ins><span class="cx">     },
</span><del>-    indentation: function() {return countColumn(this.string, null, this.tabSize);},
</del><ins>+    indentation: function() {
+      return countColumn(this.string, null, this.tabSize) -
+        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+    },
</ins><span class="cx">     match: function(pattern, consume, caseInsensitive) {
</span><span class="cx">       if (typeof pattern == &quot;string&quot;) {
</span><span class="cx">         var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
</span><span class="lines">@@ -3764,20 +4830,34 @@
</span><span class="cx">         return match;
</span><span class="cx">       }
</span><span class="cx">     },
</span><del>-    current: function(){return this.string.slice(this.start, this.pos);}
</del><ins>+    current: function(){return this.string.slice(this.start, this.pos);},
+    hideFirstChars: function(n, inner) {
+      this.lineStart += n;
+      try { return inner(); }
+      finally { this.lineStart -= n; }
+    }
</ins><span class="cx">   };
</span><del>-  CodeMirror.StringStream = StringStream;
</del><span class="cx"> 
</span><span class="cx">   // TEXTMARKERS
</span><span class="cx"> 
</span><del>-  function TextMarker(doc, type) {
</del><ins>+  // Created with markText and setBookmark methods. A TextMarker is a
+  // handle that can be used to clear or find a marked position in the
+  // document. Line objects hold arrays (markedSpans) containing
+  // {from, to, marker} object pointing to such marker objects, and
+  // indicating that such a marker is present on that line. Multiple
+  // lines may point to the same marker when it spans across lines.
+  // The spans will have null for their from/to properties when the
+  // marker continues beyond the start/end of the line. Markers have
+  // links back to the lines they currently touch.
+
+  var TextMarker = CodeMirror.TextMarker = function(doc, type) {
</ins><span class="cx">     this.lines = [];
</span><span class="cx">     this.type = type;
</span><span class="cx">     this.doc = doc;
</span><del>-  }
-  CodeMirror.TextMarker = TextMarker;
</del><ins>+  };
</ins><span class="cx">   eventMixin(TextMarker);
</span><span class="cx"> 
</span><ins>+  // Clear the marker.
</ins><span class="cx">   TextMarker.prototype.clear = function() {
</span><span class="cx">     if (this.explicitlyCleared) return;
</span><span class="cx">     var cm = this.doc.cm, withOp = cm &amp;&amp; !cm.curOp;
</span><span class="lines">@@ -3790,15 +4870,17 @@
</span><span class="cx">     for (var i = 0; i &lt; this.lines.length; ++i) {
</span><span class="cx">       var line = this.lines[i];
</span><span class="cx">       var span = getMarkedSpanFor(line.markedSpans, this);
</span><del>-      if (span.to != null) max = lineNo(line);
</del><ins>+      if (cm &amp;&amp; !this.collapsed) regLineChange(cm, lineNo(line), &quot;text&quot;);
+      else if (cm) {
+        if (span.to != null) max = lineNo(line);
+        if (span.from != null) min = lineNo(line);
+      }
</ins><span class="cx">       line.markedSpans = removeMarkedSpan(line.markedSpans, span);
</span><del>-      if (span.from != null)
-        min = lineNo(line);
-      else if (this.collapsed &amp;&amp; !lineIsHidden(this.doc, line) &amp;&amp; cm)
</del><ins>+      if (span.from == null &amp;&amp; this.collapsed &amp;&amp; !lineIsHidden(this.doc, line) &amp;&amp; cm)
</ins><span class="cx">         updateLineHeight(line, textHeight(cm.display));
</span><span class="cx">     }
</span><span class="cx">     if (cm &amp;&amp; this.collapsed &amp;&amp; !cm.options.lineWrapping) for (var i = 0; i &lt; this.lines.length; ++i) {
</span><del>-      var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
</del><ins>+      var visual = visualLine(this.lines[i]), len = lineLength(visual);
</ins><span class="cx">       if (len &gt; cm.display.maxLineLength) {
</span><span class="cx">         cm.display.maxLine = visual;
</span><span class="cx">         cm.display.maxLineLength = len;
</span><span class="lines">@@ -3806,46 +4888,62 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (min != null &amp;&amp; cm) regChange(cm, min, max + 1);
</del><ins>+    if (min != null &amp;&amp; cm &amp;&amp; this.collapsed) regChange(cm, min, max + 1);
</ins><span class="cx">     this.lines.length = 0;
</span><span class="cx">     this.explicitlyCleared = true;
</span><span class="cx">     if (this.atomic &amp;&amp; this.doc.cantEdit) {
</span><span class="cx">       this.doc.cantEdit = false;
</span><del>-      if (cm) reCheckSelection(cm);
</del><ins>+      if (cm) reCheckSelection(cm.doc);
</ins><span class="cx">     }
</span><ins>+    if (cm) signalLater(cm, &quot;markerCleared&quot;, cm, this);
</ins><span class="cx">     if (withOp) endOperation(cm);
</span><ins>+    if (this.parent) this.parent.clear();
</ins><span class="cx">   };
</span><span class="cx"> 
</span><del>-  TextMarker.prototype.find = function() {
</del><ins>+  // Find the position of the marker in the document. Returns a {from,
+  // to} object by default. Side can be passed to get a specific side
+  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
+  // Pos objects returned contain a line object, rather than a line
+  // number (used to prevent looking up the same line twice).
+  TextMarker.prototype.find = function(side, lineObj) {
+    if (side == null &amp;&amp; this.type == &quot;bookmark&quot;) side = 1;
</ins><span class="cx">     var from, to;
</span><span class="cx">     for (var i = 0; i &lt; this.lines.length; ++i) {
</span><span class="cx">       var line = this.lines[i];
</span><span class="cx">       var span = getMarkedSpanFor(line.markedSpans, this);
</span><del>-      if (span.from != null || span.to != null) {
-        var found = lineNo(line);
-        if (span.from != null) from = Pos(found, span.from);
-        if (span.to != null) to = Pos(found, span.to);
</del><ins>+      if (span.from != null) {
+        from = Pos(lineObj ? line : lineNo(line), span.from);
+        if (side == -1) return from;
</ins><span class="cx">       }
</span><ins>+      if (span.to != null) {
+        to = Pos(lineObj ? line : lineNo(line), span.to);
+        if (side == 1) return to;
+      }
</ins><span class="cx">     }
</span><del>-    if (this.type == &quot;bookmark&quot;) return from;
</del><span class="cx">     return from &amp;&amp; {from: from, to: to};
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Signals that the marker's widget changed, and surrounding layout
+  // should be recomputed.
</ins><span class="cx">   TextMarker.prototype.changed = function() {
</span><del>-    var pos = this.find(), cm = this.doc.cm;
</del><ins>+    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
</ins><span class="cx">     if (!pos || !cm) return;
</span><del>-    if (this.type != &quot;bookmark&quot;) pos = pos.from;
-    var line = getLine(this.doc, pos.line);
-    clearCachedMeasurement(cm, line);
-    if (pos.line &gt;= cm.display.showingFrom &amp;&amp; pos.line &lt; cm.display.showingTo) {
-      for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
-        if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
-        break;
</del><ins>+    runInOp(cm, function() {
+      var line = pos.line, lineN = lineNo(pos.line);
+      var view = findViewForLine(cm, lineN);
+      if (view) {
+        clearLineMeasurementCacheFor(view);
+        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
</ins><span class="cx">       }
</span><del>-      runInOp(cm, function() {
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
-      });
-    }
</del><ins>+      cm.curOp.updateMaxLine = true;
+      if (!lineIsHidden(widget.doc, line) &amp;&amp; widget.height != null) {
+        var oldHeight = widget.height;
+        widget.height = null;
+        var dHeight = widgetHeight(widget) - oldHeight;
+        if (dHeight)
+          updateLineHeight(line, line.height + dHeight);
+      }
+    });
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   TextMarker.prototype.attachLine = function(line) {
</span><span class="lines">@@ -3864,42 +4962,53 @@
</span><span class="cx">     }
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Collapsed markers have unique ids, in order to be able to order
+  // them, which is needed for uniquely determining an outer marker
+  // when they overlap (they may nest, but not partially overlap).
+  var nextMarkerId = 0;
+
+  // Create a marker, wire it up to the right lines, and
</ins><span class="cx">   function markText(doc, from, to, options, type) {
</span><ins>+    // Shared markers (across linked documents) are handled separately
+    // (markTextShared will call out to this again, once per
+    // document).
</ins><span class="cx">     if (options &amp;&amp; options.shared) return markTextShared(doc, from, to, options, type);
</span><ins>+    // Ensure we are in an operation.
</ins><span class="cx">     if (doc.cm &amp;&amp; !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
</span><span class="cx"> 
</span><del>-    var marker = new TextMarker(doc, type);
-    if (posLess(to, from) || posEq(from, to) &amp;&amp; type == &quot;range&quot; &amp;&amp;
-        !(options.inclusiveLeft &amp;&amp; options.inclusiveRight))
</del><ins>+    var marker = new TextMarker(doc, type), diff = cmp(from, to);
+    if (options) copyObj(options, marker, false);
+    // Don't connect empty markers unless clearWhenEmpty is false
+    if (diff &gt; 0 || diff == 0 &amp;&amp; marker.clearWhenEmpty !== false)
</ins><span class="cx">       return marker;
</span><del>-    if (options) copyObj(options, marker);
</del><span class="cx">     if (marker.replacedWith) {
</span><ins>+      // Showing up as a widget implies collapsed (widget replaces text)
</ins><span class="cx">       marker.collapsed = true;
</span><del>-      marker.replacedWith = elt(&quot;span&quot;, [marker.replacedWith], &quot;CodeMirror-widget&quot;);
-      if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
</del><ins>+      marker.widgetNode = elt(&quot;span&quot;, [marker.replacedWith], &quot;CodeMirror-widget&quot;);
+      if (!options.handleMouseEvents) marker.widgetNode.ignoreEvents = true;
+      if (options.insertLeft) marker.widgetNode.insertLeft = true;
</ins><span class="cx">     }
</span><del>-    if (marker.collapsed) sawCollapsedSpans = true;
</del><ins>+    if (marker.collapsed) {
+      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
+          from.line != to.line &amp;&amp; conflictingCollapsedRange(doc, to.line, from, to, marker))
+        throw new Error(&quot;Inserting collapsed marker partially overlapping an existing one&quot;);
+      sawCollapsedSpans = true;
+    }
</ins><span class="cx"> 
</span><span class="cx">     if (marker.addToHistory)
</span><del>-      addToHistory(doc, {from: from, to: to, origin: &quot;markText&quot;},
-                   {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
</del><ins>+      addChangeToHistory(doc, {from: from, to: to, origin: &quot;markText&quot;}, doc.sel, NaN);
</ins><span class="cx"> 
</span><del>-    var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
</del><ins>+    var curLine = from.line, cm = doc.cm, updateMaxLine;
</ins><span class="cx">     doc.iter(curLine, to.line + 1, function(line) {
</span><del>-      if (cm &amp;&amp; marker.collapsed &amp;&amp; !cm.options.lineWrapping &amp;&amp; visualLine(doc, line) == cm.display.maxLine)
</del><ins>+      if (cm &amp;&amp; marker.collapsed &amp;&amp; !cm.options.lineWrapping &amp;&amp; visualLine(line) == cm.display.maxLine)
</ins><span class="cx">         updateMaxLine = true;
</span><del>-      var span = {from: null, to: null, marker: marker};
-      size += line.text.length;
-      if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
-      if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
-      if (marker.collapsed) {
-        if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
-        if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
-        else updateLineHeight(line, 0);
-      }
-      addMarkedSpan(line, span);
</del><ins>+      if (marker.collapsed &amp;&amp; curLine != from.line) updateLineHeight(line, 0);
+      addMarkedSpan(line, new MarkedSpan(marker,
+                                         curLine == from.line ? from.ch : null,
+                                         curLine == to.line ? to.ch : null));
</ins><span class="cx">       ++curLine;
</span><span class="cx">     });
</span><ins>+    // lineIsHidden depends on the presence of the spans, so needs a second pass
</ins><span class="cx">     if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
</span><span class="cx">       if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
</span><span class="cx">     });
</span><span class="lines">@@ -3912,31 +5021,33 @@
</span><span class="cx">         doc.clearHistory();
</span><span class="cx">     }
</span><span class="cx">     if (marker.collapsed) {
</span><del>-      if (collapsedAtStart != collapsedAtEnd)
-        throw new Error(&quot;Inserting collapsed marker overlapping an existing one&quot;);
-      marker.size = size;
</del><ins>+      marker.id = ++nextMarkerId;
</ins><span class="cx">       marker.atomic = true;
</span><span class="cx">     }
</span><span class="cx">     if (cm) {
</span><ins>+      // Sync editor state
</ins><span class="cx">       if (updateMaxLine) cm.curOp.updateMaxLine = true;
</span><del>-      if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
</del><ins>+      if (marker.collapsed)
</ins><span class="cx">         regChange(cm, from.line, to.line + 1);
</span><del>-      if (marker.atomic) reCheckSelection(cm);
</del><ins>+      else if (marker.className || marker.title || marker.startStyle || marker.endStyle)
+        for (var i = from.line; i &lt;= to.line; i++) regLineChange(cm, i, &quot;text&quot;);
+      if (marker.atomic) reCheckSelection(cm.doc);
+      signalLater(cm, &quot;markerAdded&quot;, cm, marker);
</ins><span class="cx">     }
</span><span class="cx">     return marker;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // SHARED TEXTMARKERS
</span><span class="cx"> 
</span><del>-  function SharedTextMarker(markers, primary) {
</del><ins>+  // A shared marker spans multiple linked documents. It is
+  // implemented as a meta-marker-object controlling multiple normal
+  // markers.
+  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
</ins><span class="cx">     this.markers = markers;
</span><span class="cx">     this.primary = primary;
</span><del>-    for (var i = 0, me = this; i &lt; markers.length; ++i) {
</del><ins>+    for (var i = 0; i &lt; markers.length; ++i)
</ins><span class="cx">       markers[i].parent = this;
</span><del>-      on(markers[i], &quot;clear&quot;, function(){me.clear();});
-    }
-  }
-  CodeMirror.SharedTextMarker = SharedTextMarker;
</del><ins>+  };
</ins><span class="cx">   eventMixin(SharedTextMarker);
</span><span class="cx"> 
</span><span class="cx">   SharedTextMarker.prototype.clear = function() {
</span><span class="lines">@@ -3946,17 +5057,17 @@
</span><span class="cx">       this.markers[i].clear();
</span><span class="cx">     signalLater(this, &quot;clear&quot;);
</span><span class="cx">   };
</span><del>-  SharedTextMarker.prototype.find = function() {
-    return this.primary.find();
</del><ins>+  SharedTextMarker.prototype.find = function(side, lineObj) {
+    return this.primary.find(side, lineObj);
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   function markTextShared(doc, from, to, options, type) {
</span><span class="cx">     options = copyObj(options);
</span><span class="cx">     options.shared = false;
</span><span class="cx">     var markers = [markText(doc, from, to, options, type)], primary = markers[0];
</span><del>-    var widget = options.replacedWith;
</del><ins>+    var widget = options.widgetNode;
</ins><span class="cx">     linkedDocs(doc, function(doc) {
</span><del>-      if (widget) options.replacedWith = widget.cloneNode(true);
</del><ins>+      if (widget) options.widgetNode = widget.cloneNode(true);
</ins><span class="cx">       markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
</span><span class="cx">       for (var i = 0; i &lt; doc.linked.length; ++i)
</span><span class="cx">         if (doc.linked[i].isParent) return;
</span><span class="lines">@@ -3965,60 +5076,104 @@
</span><span class="cx">     return new SharedTextMarker(markers, primary);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function findSharedMarkers(doc) {
+    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
+                         function(m) { return m.parent; });
+  }
+
+  function copySharedMarkers(doc, markers) {
+    for (var i = 0; i &lt; markers.length; i++) {
+      var marker = markers[i], pos = marker.find();
+      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
+      if (cmp(mFrom, mTo)) {
+        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
+        marker.markers.push(subMark);
+        subMark.parent = marker;
+      }
+    }
+  }
+
+  function detachSharedMarkers(markers) {
+    for (var i = 0; i &lt; markers.length; i++) {
+      var marker = markers[i], linked = [marker.primary.doc];;
+      linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
+      for (var j = 0; j &lt; marker.markers.length; j++) {
+        var subMarker = marker.markers[j];
+        if (indexOf(linked, subMarker.doc) == -1) {
+          subMarker.parent = null;
+          marker.markers.splice(j--, 1);
+        }
+      }
+    }
+  }
+
</ins><span class="cx">   // TEXTMARKER SPANS
</span><span class="cx"> 
</span><ins>+  function MarkedSpan(marker, from, to) {
+    this.marker = marker;
+    this.from = from; this.to = to;
+  }
+
+  // Search an array of spans for a span matching the given marker.
</ins><span class="cx">   function getMarkedSpanFor(spans, marker) {
</span><span class="cx">     if (spans) for (var i = 0; i &lt; spans.length; ++i) {
</span><span class="cx">       var span = spans[i];
</span><span class="cx">       if (span.marker == marker) return span;
</span><span class="cx">     }
</span><span class="cx">   }
</span><ins>+  // Remove a span from an array, returning undefined if no spans are
+  // left (we don't store arrays for lines without spans).
</ins><span class="cx">   function removeMarkedSpan(spans, span) {
</span><span class="cx">     for (var r, i = 0; i &lt; spans.length; ++i)
</span><span class="cx">       if (spans[i] != span) (r || (r = [])).push(spans[i]);
</span><span class="cx">     return r;
</span><span class="cx">   }
</span><ins>+  // Add a span to a line.
</ins><span class="cx">   function addMarkedSpan(line, span) {
</span><span class="cx">     line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
</span><span class="cx">     span.marker.attachLine(line);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used for the algorithm that adjusts markers for a change in the
+  // document. These functions cut an array of spans at a given
+  // character position, returning an array of remaining chunks (or
+  // undefined if nothing remains).
</ins><span class="cx">   function markedSpansBefore(old, startCh, isInsert) {
</span><span class="cx">     if (old) for (var i = 0, nw; i &lt; old.length; ++i) {
</span><span class="cx">       var span = old[i], marker = span.marker;
</span><span class="cx">       var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from &lt;= startCh : span.from &lt; startCh);
</span><del>-      if (startsBefore ||
-          (marker.inclusiveLeft &amp;&amp; marker.inclusiveRight || marker.type == &quot;bookmark&quot;) &amp;&amp;
-          span.from == startCh &amp;&amp; (!isInsert || !span.marker.insertLeft)) {
</del><ins>+      if (startsBefore || span.from == startCh &amp;&amp; marker.type == &quot;bookmark&quot; &amp;&amp; (!isInsert || !span.marker.insertLeft)) {
</ins><span class="cx">         var endsAfter = span.to == null || (marker.inclusiveRight ? span.to &gt;= startCh : span.to &gt; startCh);
</span><del>-        (nw || (nw = [])).push({from: span.from,
-                                to: endsAfter ? null : span.to,
-                                marker: marker});
</del><ins>+        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     return nw;
</span><span class="cx">   }
</span><del>-
</del><span class="cx">   function markedSpansAfter(old, endCh, isInsert) {
</span><span class="cx">     if (old) for (var i = 0, nw; i &lt; old.length; ++i) {
</span><span class="cx">       var span = old[i], marker = span.marker;
</span><span class="cx">       var endsAfter = span.to == null || (marker.inclusiveRight ? span.to &gt;= endCh : span.to &gt; endCh);
</span><del>-      if (endsAfter || marker.type == &quot;bookmark&quot; &amp;&amp; span.from == endCh &amp;&amp; (!isInsert || span.marker.insertLeft)) {
</del><ins>+      if (endsAfter || span.from == endCh &amp;&amp; marker.type == &quot;bookmark&quot; &amp;&amp; (!isInsert || span.marker.insertLeft)) {
</ins><span class="cx">         var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from &lt;= endCh : span.from &lt; endCh);
</span><del>-        (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
-                                to: span.to == null ? null : span.to - endCh,
-                                marker: marker});
</del><ins>+        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
+                                              span.to == null ? null : span.to - endCh));
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     return nw;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Given a change object, compute the new set of marker spans that
+  // cover the line in which the change took place. Removes spans
+  // entirely within the change, reconnects spans belonging to the
+  // same marker that appear on both sides of the change, and cuts off
+  // spans partially within the change. Returns an array of span
+  // arrays with one element for each line in (after) the change.
</ins><span class="cx">   function stretchSpansOverChange(doc, change) {
</span><span class="cx">     var oldFirst = isLine(doc, change.from.line) &amp;&amp; getLine(doc, change.from.line).markedSpans;
</span><span class="cx">     var oldLast = isLine(doc, change.to.line) &amp;&amp; getLine(doc, change.to.line).markedSpans;
</span><span class="cx">     if (!oldFirst &amp;&amp; !oldLast) return null;
</span><span class="cx"> 
</span><del>-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
</del><ins>+    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
</ins><span class="cx">     // Get the spans that 'stick out' on both sides
</span><span class="cx">     var first = markedSpansBefore(oldFirst, startCh, isInsert);
</span><span class="cx">     var last = markedSpansAfter(oldLast, endCh, isInsert);
</span><span class="lines">@@ -4053,13 +5208,9 @@
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><del>-    if (sameLine &amp;&amp; first) {
-      // Make sure we didn't create any zero-length spans
-      for (var i = 0; i &lt; first.length; ++i)
-        if (first[i].from != null &amp;&amp; first[i].from == first[i].to &amp;&amp; first[i].marker.type != &quot;bookmark&quot;)
-          first.splice(i--, 1);
-      if (!first.length) first = null;
-    }
</del><ins>+    // Make sure we didn't create any zero-length spans
+    if (first) first = clearEmptySpans(first);
+    if (last &amp;&amp; last != first) last = clearEmptySpans(last);
</ins><span class="cx"> 
</span><span class="cx">     var newMarkers = [first];
</span><span class="cx">     if (!sameLine) {
</span><span class="lines">@@ -4068,7 +5219,7 @@
</span><span class="cx">       if (gap &gt; 0 &amp;&amp; first)
</span><span class="cx">         for (var i = 0; i &lt; first.length; ++i)
</span><span class="cx">           if (first[i].to == null)
</span><del>-            (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
</del><ins>+            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
</ins><span class="cx">       for (var i = 0; i &lt; gap; ++i)
</span><span class="cx">         newMarkers.push(gapMarkers);
</span><span class="cx">       newMarkers.push(last);
</span><span class="lines">@@ -4076,6 +5227,22 @@
</span><span class="cx">     return newMarkers;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Remove spans that are empty and don't have a clearWhenEmpty
+  // option of false.
+  function clearEmptySpans(spans) {
+    for (var i = 0; i &lt; spans.length; ++i) {
+      var span = spans[i];
+      if (span.from != null &amp;&amp; span.from == span.to &amp;&amp; span.marker.clearWhenEmpty !== false)
+        spans.splice(i--, 1);
+    }
+    if (!spans.length) return null;
+    return spans;
+  }
+
+  // Used for un/re-doing changes from the history. Combines the
+  // result of computing the existing spans with the set of spans that
+  // existed in the history (so that deleting around a span and then
+  // undoing brings back the span).
</ins><span class="cx">   function mergeOldSpans(doc, change) {
</span><span class="cx">     var old = getOldSpans(doc, change);
</span><span class="cx">     var stretched = stretchSpansOverChange(doc, change);
</span><span class="lines">@@ -4098,6 +5265,7 @@
</span><span class="cx">     return old;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to 'clip' out readOnly ranges when making a change.
</ins><span class="cx">   function removeReadOnlyRanges(doc, from, to) {
</span><span class="cx">     var markers = null;
</span><span class="cx">     doc.iter(from.line, to.line + 1, function(line) {
</span><span class="lines">@@ -4110,14 +5278,14 @@
</span><span class="cx">     if (!markers) return null;
</span><span class="cx">     var parts = [{from: from, to: to}];
</span><span class="cx">     for (var i = 0; i &lt; markers.length; ++i) {
</span><del>-      var mk = markers[i], m = mk.find();
</del><ins>+      var mk = markers[i], m = mk.find(0);
</ins><span class="cx">       for (var j = 0; j &lt; parts.length; ++j) {
</span><span class="cx">         var p = parts[j];
</span><del>-        if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
-        var newParts = [j, 1];
-        if (posLess(p.from, m.from) || !mk.inclusiveLeft &amp;&amp; posEq(p.from, m.from))
</del><ins>+        if (cmp(p.to, m.from) &lt; 0 || cmp(p.from, m.to) &gt; 0) continue;
+        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
+        if (dfrom &lt; 0 || !mk.inclusiveLeft &amp;&amp; !dfrom)
</ins><span class="cx">           newParts.push({from: p.from, to: m.from});
</span><del>-        if (posLess(m.to, p.to) || !mk.inclusiveRight &amp;&amp; posEq(p.to, m.to))
</del><ins>+        if (dto &gt; 0 || !mk.inclusiveRight &amp;&amp; !dto)
</ins><span class="cx">           newParts.push({from: m.to, to: p.to});
</span><span class="cx">         parts.splice.apply(parts, newParts);
</span><span class="cx">         j += newParts.length - 1;
</span><span class="lines">@@ -4126,71 +5294,148 @@
</span><span class="cx">     return parts;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function collapsedSpanAt(line, ch) {
</del><ins>+  // Connect or disconnect spans from a line.
+  function detachMarkedSpans(line) {
+    var spans = line.markedSpans;
+    if (!spans) return;
+    for (var i = 0; i &lt; spans.length; ++i)
+      spans[i].marker.detachLine(line);
+    line.markedSpans = null;
+  }
+  function attachMarkedSpans(line, spans) {
+    if (!spans) return;
+    for (var i = 0; i &lt; spans.length; ++i)
+      spans[i].marker.attachLine(line);
+    line.markedSpans = spans;
+  }
+
+  // Helpers used when computing which overlapping collapsed span
+  // counts as the larger one.
+  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
+  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
+
+  // Returns a number indicating which of two overlapping collapsed
+  // spans is larger (and thus includes the other). Falls back to
+  // comparing ids when the spans cover exactly the same range.
+  function compareCollapsedMarkers(a, b) {
+    var lenDiff = a.lines.length - b.lines.length;
+    if (lenDiff != 0) return lenDiff;
+    var aPos = a.find(), bPos = b.find();
+    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
+    if (fromCmp) return -fromCmp;
+    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
+    if (toCmp) return toCmp;
+    return b.id - a.id;
+  }
+
+  // Find out whether a line ends or starts in a collapsed span. If
+  // so, return the marker for that span.
+  function collapsedSpanAtSide(line, start) {
</ins><span class="cx">     var sps = sawCollapsedSpans &amp;&amp; line.markedSpans, found;
</span><span class="cx">     if (sps) for (var sp, i = 0; i &lt; sps.length; ++i) {
</span><span class="cx">       sp = sps[i];
</span><del>-      if (!sp.marker.collapsed) continue;
-      if ((sp.from == null || sp.from &lt; ch) &amp;&amp;
-          (sp.to == null || sp.to &gt; ch) &amp;&amp;
-          (!found || found.width &lt; sp.marker.width))
</del><ins>+      if (sp.marker.collapsed &amp;&amp; (start ? sp.from : sp.to) == null &amp;&amp;
+          (!found || compareCollapsedMarkers(found, sp.marker) &lt; 0))
</ins><span class="cx">         found = sp.marker;
</span><span class="cx">     }
</span><span class="cx">     return found;
</span><span class="cx">   }
</span><del>-  function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
</del><ins>+  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
+  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
</ins><span class="cx"> 
</span><del>-  function visualLine(doc, line) {
</del><ins>+  // Test whether there exists a collapsed span that partially
+  // overlaps (covers the start or end, but not both) of a new span.
+  // Such overlap is not allowed.
+  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
+    var line = getLine(doc, lineNo);
+    var sps = sawCollapsedSpans &amp;&amp; line.markedSpans;
+    if (sps) for (var i = 0; i &lt; sps.length; ++i) {
+      var sp = sps[i];
+      if (!sp.marker.collapsed) continue;
+      var found = sp.marker.find(0);
+      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
+      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
+      if (fromCmp &gt;= 0 &amp;&amp; toCmp &lt;= 0 || fromCmp &lt;= 0 &amp;&amp; toCmp &gt;= 0) continue;
+      if (fromCmp &lt;= 0 &amp;&amp; (cmp(found.to, from) || extraRight(sp.marker) - extraLeft(marker)) &gt; 0 ||
+          fromCmp &gt;= 0 &amp;&amp; (cmp(found.from, to) || extraLeft(sp.marker) - extraRight(marker)) &lt; 0)
+        return true;
+    }
+  }
+
+  // A visual line is a line as drawn on the screen. Folding, for
+  // example, can cause multiple logical lines to appear on the same
+  // visual line. This finds the start of the visual line that the
+  // given line is part of (usually that is the line itself).
+  function visualLine(line) {
</ins><span class="cx">     var merged;
</span><span class="cx">     while (merged = collapsedSpanAtStart(line))
</span><del>-      line = getLine(doc, merged.find().from.line);
</del><ins>+      line = merged.find(-1, true).line;
</ins><span class="cx">     return line;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Returns an array of logical lines that continue the visual line
+  // started by the argument, or undefined if there are no such lines.
+  function visualLineContinued(line) {
+    var merged, lines;
+    while (merged = collapsedSpanAtEnd(line)) {
+      line = merged.find(1, true).line;
+      (lines || (lines = [])).push(line);
+    }
+    return lines;
+  }
+
+  // Get the line number of the start of the visual line that the
+  // given line number is part of.
+  function visualLineNo(doc, lineN) {
+    var line = getLine(doc, lineN), vis = visualLine(line);
+    if (line == vis) return lineN;
+    return lineNo(vis);
+  }
+  // Get the line number of the start of the next visual line after
+  // the given line.
+  function visualLineEndNo(doc, lineN) {
+    if (lineN &gt; doc.lastLine()) return lineN;
+    var line = getLine(doc, lineN), merged;
+    if (!lineIsHidden(doc, line)) return lineN;
+    while (merged = collapsedSpanAtEnd(line))
+      line = merged.find(1, true).line;
+    return lineNo(line) + 1;
+  }
+
+  // Compute whether a line is hidden. Lines count as hidden when they
+  // are part of a visual line that starts with another line, or when
+  // they are entirely covered by collapsed, non-widget span.
</ins><span class="cx">   function lineIsHidden(doc, line) {
</span><span class="cx">     var sps = sawCollapsedSpans &amp;&amp; line.markedSpans;
</span><span class="cx">     if (sps) for (var sp, i = 0; i &lt; sps.length; ++i) {
</span><span class="cx">       sp = sps[i];
</span><span class="cx">       if (!sp.marker.collapsed) continue;
</span><span class="cx">       if (sp.from == null) return true;
</span><del>-      if (sp.marker.replacedWith) continue;
</del><ins>+      if (sp.marker.widgetNode) continue;
</ins><span class="cx">       if (sp.from == 0 &amp;&amp; sp.marker.inclusiveLeft &amp;&amp; lineIsHiddenInner(doc, line, sp))
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx">   function lineIsHiddenInner(doc, line, span) {
</span><span class="cx">     if (span.to == null) {
</span><del>-      var end = span.marker.find().to, endLine = getLine(doc, end.line);
-      return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
</del><ins>+      var end = span.marker.find(1, true);
+      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
</ins><span class="cx">     }
</span><span class="cx">     if (span.marker.inclusiveRight &amp;&amp; span.to == line.text.length)
</span><span class="cx">       return true;
</span><span class="cx">     for (var sp, i = 0; i &lt; line.markedSpans.length; ++i) {
</span><span class="cx">       sp = line.markedSpans[i];
</span><del>-      if (sp.marker.collapsed &amp;&amp; !sp.marker.replacedWith &amp;&amp; sp.from == span.to &amp;&amp;
</del><ins>+      if (sp.marker.collapsed &amp;&amp; !sp.marker.widgetNode &amp;&amp; sp.from == span.to &amp;&amp;
+          (sp.to == null || sp.to != span.from) &amp;&amp;
</ins><span class="cx">           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &amp;&amp;
</span><span class="cx">           lineIsHiddenInner(doc, line, sp)) return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) return;
-    for (var i = 0; i &lt; spans.length; ++i)
-      spans[i].marker.detachLine(line);
-    line.markedSpans = null;
-  }
</del><ins>+  // LINE WIDGETS
</ins><span class="cx"> 
</span><del>-  function attachMarkedSpans(line, spans) {
-    if (!spans) return;
-    for (var i = 0; i &lt; spans.length; ++i)
-      spans[i].marker.attachLine(line);
-    line.markedSpans = spans;
-  }
</del><ins>+  // Line widgets are block elements displayed above or below a line.
</ins><span class="cx"> 
</span><del>-  // LINE WIDGETS
-
</del><span class="cx">   var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
</span><span class="cx">     if (options) for (var opt in options) if (options.hasOwnProperty(opt))
</span><span class="cx">       this[opt] = options[opt];
</span><span class="lines">@@ -4198,38 +5443,39 @@
</span><span class="cx">     this.node = node;
</span><span class="cx">   };
</span><span class="cx">   eventMixin(LineWidget);
</span><del>-  function widgetOperation(f) {
-    return function() {
-      var withOp = !this.cm.curOp;
-      if (withOp) startOperation(this.cm);
-      try {var result = f.apply(this, arguments);}
-      finally {if (withOp) endOperation(this.cm);}
-      return result;
-    };
</del><ins>+
+  function adjustScrollWhenAboveVisible(cm, line, diff) {
+    if (heightAtLine(line) &lt; ((cm.curOp &amp;&amp; cm.curOp.scrollTop) || cm.doc.scrollTop))
+      addToScrollPos(cm, null, diff);
</ins><span class="cx">   }
</span><del>-  LineWidget.prototype.clear = widgetOperation(function() {
-    var ws = this.line.widgets, no = lineNo(this.line);
</del><ins>+
+  LineWidget.prototype.clear = function() {
+    var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
</ins><span class="cx">     if (no == null || !ws) return;
</span><span class="cx">     for (var i = 0; i &lt; ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
</span><del>-    if (!ws.length) this.line.widgets = null;
-    var aboveVisible = heightAtLine(this.cm, this.line) &lt; this.cm.doc.scrollTop;
-    updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
-    if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
-    regChange(this.cm, no, no + 1);
-  });
-  LineWidget.prototype.changed = widgetOperation(function() {
-    var oldH = this.height;
</del><ins>+    if (!ws.length) line.widgets = null;
+    var height = widgetHeight(this);
+    runInOp(cm, function() {
+      adjustScrollWhenAboveVisible(cm, line, -height);
+      regLineChange(cm, no, &quot;widget&quot;);
+      updateLineHeight(line, Math.max(0, line.height - height));
+    });
+  };
+  LineWidget.prototype.changed = function() {
+    var oldH = this.height, cm = this.cm, line = this.line;
</ins><span class="cx">     this.height = null;
</span><span class="cx">     var diff = widgetHeight(this) - oldH;
</span><span class="cx">     if (!diff) return;
</span><del>-    updateLineHeight(this.line, this.line.height + diff);
-    var no = lineNo(this.line);
-    regChange(this.cm, no, no + 1);
-  });
</del><ins>+    runInOp(cm, function() {
+      cm.curOp.forceUpdate = true;
+      adjustScrollWhenAboveVisible(cm, line, diff);
+      updateLineHeight(line, line.height + diff);
+    });
+  };
</ins><span class="cx"> 
</span><span class="cx">   function widgetHeight(widget) {
</span><span class="cx">     if (widget.height != null) return widget.height;
</span><del>-    if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
</del><ins>+    if (!contains(document.body, widget.node))
</ins><span class="cx">       removeChildrenAndAdd(widget.cm.display.measure, elt(&quot;div&quot;, [widget.node], null, &quot;position: relative&quot;));
</span><span class="cx">     return widget.height = widget.node.offsetHeight;
</span><span class="cx">   }
</span><span class="lines">@@ -4237,15 +5483,16 @@
</span><span class="cx">   function addLineWidget(cm, handle, node, options) {
</span><span class="cx">     var widget = new LineWidget(cm, node, options);
</span><span class="cx">     if (widget.noHScroll) cm.display.alignWidgets = true;
</span><del>-    changeLine(cm, handle, function(line) {
</del><ins>+    changeLine(cm, handle, &quot;widget&quot;, function(line) {
</ins><span class="cx">       var widgets = line.widgets || (line.widgets = []);
</span><span class="cx">       if (widget.insertAt == null) widgets.push(widget);
</span><span class="cx">       else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
</span><span class="cx">       widget.line = line;
</span><del>-      if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
-        var aboveVisible = heightAtLine(cm, line) &lt; cm.doc.scrollTop;
</del><ins>+      if (!lineIsHidden(cm.doc, line)) {
+        var aboveVisible = heightAtLine(line) &lt; cm.doc.scrollTop;
</ins><span class="cx">         updateLineHeight(line, line.height + widgetHeight(widget));
</span><del>-        if (aboveVisible) addToScrollPos(cm, 0, widget.height);
</del><ins>+        if (aboveVisible) addToScrollPos(cm, null, widget.height);
+        cm.curOp.forceUpdate = true;
</ins><span class="cx">       }
</span><span class="cx">       return true;
</span><span class="cx">     });
</span><span class="lines">@@ -4264,6 +5511,9 @@
</span><span class="cx">   eventMixin(Line);
</span><span class="cx">   Line.prototype.lineNo = function() { return lineNo(this); };
</span><span class="cx"> 
</span><ins>+  // Change the content (text, markers) of a line. Automatically
+  // invalidates cached information and tries to re-estimate the
+  // line's height.
</ins><span class="cx">   function updateLine(line, text, markedSpans, estimateHeight) {
</span><span class="cx">     line.text = text;
</span><span class="cx">     if (line.stateAfter) line.stateAfter = null;
</span><span class="lines">@@ -4275,20 +5525,47 @@
</span><span class="cx">     if (estHeight != line.height) updateLineHeight(line, estHeight);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Detach a line from the document tree and its markers.
</ins><span class="cx">   function cleanUpLine(line) {
</span><span class="cx">     line.parent = null;
</span><span class="cx">     detachMarkedSpans(line);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Run the given mode's parser over a line, update the styles
-  // array, which contains alternating fragments of text and CSS
-  // classes.
-  function runMode(cm, text, mode, state, f, forceToEnd) {
</del><ins>+  function extractLineClasses(type, output) {
+    if (type) for (;;) {
+      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
+      if (!lineClass) break;
+      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
+      var prop = lineClass[1] ? &quot;bgClass&quot; : &quot;textClass&quot;;
+      if (output[prop] == null)
+        output[prop] = lineClass[2];
+      else if (!(new RegExp(&quot;(?:^|\s)&quot; + lineClass[2] + &quot;(?:$|\s)&quot;)).test(output[prop]))
+        output[prop] += &quot; &quot; + lineClass[2];
+    }
+    return type;
+  }
+
+  function callBlankLine(mode, state) {
+    if (mode.blankLine) return mode.blankLine(state);
+    if (!mode.innerMode) return;
+    var inner = CodeMirror.innerMode(mode, state);
+    if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
+  }
+
+  function readToken(mode, stream, state) {
+    var style = mode.token(stream, state);
+    if (stream.pos &lt;= stream.start)
+      throw new Error(&quot;Mode &quot; + mode.name + &quot; failed to advance stream.&quot;);
+    return style;
+  }
+
+  // Run the given mode's parser over a line, calling f for each token.
+  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
</ins><span class="cx">     var flattenSpans = mode.flattenSpans;
</span><span class="cx">     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
</span><span class="cx">     var curStart = 0, curStyle = null;
</span><span class="cx">     var stream = new StringStream(text, cm.options.tabSize), style;
</span><del>-    if (text == &quot;&quot; &amp;&amp; mode.blankLine) mode.blankLine(state);
</del><ins>+    if (text == &quot;&quot;) extractLineClasses(callBlankLine(mode, state), lineClasses);
</ins><span class="cx">     while (!stream.eol()) {
</span><span class="cx">       if (stream.pos &gt; cm.options.maxHighlightLength) {
</span><span class="cx">         flattenSpans = false;
</span><span class="lines">@@ -4296,8 +5573,12 @@
</span><span class="cx">         stream.pos = text.length;
</span><span class="cx">         style = null;
</span><span class="cx">       } else {
</span><del>-        style = mode.token(stream, state);
</del><ins>+        style = extractLineClasses(readToken(mode, stream, state), lineClasses);
</ins><span class="cx">       }
</span><ins>+      if (cm.options.addModeClass) {
+        var mName = CodeMirror.innerMode(mode, state).mode.name;
+        if (mName) style = &quot;m-&quot; + (style ? mName + &quot; &quot; + style : mName);
+      }
</ins><span class="cx">       if (!flattenSpans || curStyle != style) {
</span><span class="cx">         if (curStart &lt; stream.start) f(stream.start, curStyle);
</span><span class="cx">         curStart = stream.start; curStyle = style;
</span><span class="lines">@@ -4312,14 +5593,18 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute a style array (an array starting with a mode generation
+  // -- for invalidation -- followed by pairs of end positions and
+  // style strings), which is used to highlight the tokens on the
+  // line.
</ins><span class="cx">   function highlightLine(cm, line, state, forceToEnd) {
</span><span class="cx">     // A styles array always starts with a number identifying the
</span><span class="cx">     // mode/overlays that it is based on (for easy invalidation).
</span><del>-    var st = [cm.state.modeGen];
</del><ins>+    var st = [cm.state.modeGen], lineClasses = {};
</ins><span class="cx">     // Compute the base array of styles
</span><span class="cx">     runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
</span><span class="cx">       st.push(end, style);
</span><del>-    }, forceToEnd);
</del><ins>+    }, lineClasses, forceToEnd);
</ins><span class="cx"> 
</span><span class="cx">     // Run overlays, adjust style array.
</span><span class="cx">     for (var o = 0; o &lt; cm.state.overlays.length; ++o) {
</span><span class="lines">@@ -4344,96 +5629,95 @@
</span><span class="cx">             st[start+1] = cur ? cur + &quot; &quot; + style : style;
</span><span class="cx">           }
</span><span class="cx">         }
</span><del>-      });
</del><ins>+      }, lineClasses);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return st;
</del><ins>+    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function getLineStyles(cm, line) {
</span><del>-    if (!line.styles || line.styles[0] != cm.state.modeGen)
-      line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
</del><ins>+    if (!line.styles || line.styles[0] != cm.state.modeGen) {
+      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+      line.styles = result.styles;
+      if (result.classes) line.styleClasses = result.classes;
+      else if (line.styleClasses) line.styleClasses = null;
+    }
</ins><span class="cx">     return line.styles;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Lightweight form of highlight -- proceed over this line and
</span><del>-  // update state, but don't save a style array.
</del><ins>+  // update state, but don't save a style array. Used for lines that
+  // aren't currently visible.
</ins><span class="cx">   function processLine(cm, text, state, startAt) {
</span><span class="cx">     var mode = cm.doc.mode;
</span><span class="cx">     var stream = new StringStream(text, cm.options.tabSize);
</span><span class="cx">     stream.start = stream.pos = startAt || 0;
</span><del>-    if (text == &quot;&quot; &amp;&amp; mode.blankLine) mode.blankLine(state);
</del><ins>+    if (text == &quot;&quot;) callBlankLine(mode, state);
</ins><span class="cx">     while (!stream.eol() &amp;&amp; stream.pos &lt;= cm.options.maxHighlightLength) {
</span><del>-      mode.token(stream, state);
</del><ins>+      readToken(mode, stream, state);
</ins><span class="cx">       stream.start = stream.pos;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var styleToClassCache = {};
-  function interpretTokenStyle(style, builder) {
-    if (!style) return null;
-    for (;;) {
-      var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/);
-      if (!lineClass) break;
-      style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? &quot;bgClass&quot; : &quot;textClass&quot;;
-      if (builder[prop] == null)
-        builder[prop] = lineClass[2];
-      else if (!(new RegExp(&quot;(?:^|\s)&quot; + lineClass[2] + &quot;(?:$|\s)&quot;)).test(builder[prop]))
-        builder[prop] += &quot; &quot; + lineClass[2];
-    }
-    return styleToClassCache[style] ||
-      (styleToClassCache[style] = &quot;cm-&quot; + style.replace(/ +/g, &quot; cm-&quot;));
</del><ins>+  // Convert a style as returned by a mode (either null, or a string
+  // containing one or more styles) to a CSS style. This is cached,
+  // and also looks for line-wide styles.
+  var styleToClassCache = {}, styleToClassCacheWithMode = {};
+  function interpretTokenStyle(style, options) {
+    if (!style || /^\s*$/.test(style)) return null;
+    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+    return cache[style] ||
+      (cache[style] = style.replace(/\S+/g, &quot;cm-$&amp;&quot;));
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildLineContent(cm, realLine, measure, copyWidgets) {
-    var merged, line = realLine, empty = true;
-    while (merged = collapsedSpanAtStart(line))
-      line = getLine(cm.doc, merged.find().from.line);
</del><ins>+  // Render the DOM representation of the text of a line. Also builds
+  // up a 'line map', which points at the DOM nodes that represent
+  // specific stretches of text, and is used by the measuring code.
+  // The returned object contains the DOM node, this map, and
+  // information about line-wide styles that were set by the mode.
+  function buildLineContent(cm, lineView) {
+    // The padding-right forces the element to have a 'border', which
+    // is needed on Webkit to be able to get line-level bounding
+    // rectangles for it (in measureChar).
+    var content = elt(&quot;span&quot;, null, null, webkit ? &quot;padding-right: .1px&quot; : null);
+    var builder = {pre: elt(&quot;pre&quot;, [content]), content: content, col: 0, pos: 0, cm: cm};
+    lineView.measure = {};
</ins><span class="cx"> 
</span><del>-    var builder = {pre: elt(&quot;pre&quot;), col: 0, pos: 0,
-                   measure: null, measuredSomething: false, cm: cm,
-                   copyWidgets: copyWidgets};
-
-    do {
-      if (line.text) empty = false;
-      builder.measure = line == realLine &amp;&amp; measure;
</del><ins>+    // Iterate over the logical lines that make up this visual line.
+    for (var i = 0; i &lt;= (lineView.rest ? lineView.rest.length : 0); i++) {
+      var line = i ? lineView.rest[i - 1] : lineView.line, order;
</ins><span class="cx">       builder.pos = 0;
</span><del>-      builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
</del><ins>+      builder.addToken = buildToken;
+      // Optionally wire in some hacks into the token-rendering
+      // algorithm, to deal with browser quirks.
</ins><span class="cx">       if ((ie || webkit) &amp;&amp; cm.getOption(&quot;lineWrapping&quot;))
</span><span class="cx">         builder.addToken = buildTokenSplitSpaces(builder.addToken);
</span><del>-      var next = insertLineContent(line, builder, getLineStyles(cm, line));
-      if (measure &amp;&amp; line == realLine &amp;&amp; !builder.measuredSomething) {
-        measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
-        builder.measuredSomething = true;
</del><ins>+      if (hasBadBidiRects(cm.display.measure) &amp;&amp; (order = getOrder(line)))
+        builder.addToken = buildTokenBadBidi(builder.addToken, order);
+      builder.map = [];
+      insertLineContent(line, builder, getLineStyles(cm, line));
+      if (line.styleClasses) {
+        if (line.styleClasses.bgClass)
+          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || &quot;&quot;);
+        if (line.styleClasses.textClass)
+          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || &quot;&quot;);
</ins><span class="cx">       }
</span><del>-      if (next) line = getLine(cm.doc, next.to.line);
-    } while (next);
</del><span class="cx"> 
</span><del>-    if (measure &amp;&amp; !builder.measuredSomething &amp;&amp; !measure[0])
-      measure[0] = builder.pre.appendChild(empty ? elt(&quot;span&quot;, &quot;\u00a0&quot;) : zeroWidthElement(cm.display.measure));
-    if (!builder.pre.firstChild &amp;&amp; !lineIsHidden(cm.doc, realLine))
-      builder.pre.appendChild(document.createTextNode(&quot;\u00a0&quot;));
</del><ins>+      // Ensure at least a single node is present, for measuring.
+      if (builder.map.length == 0)
+        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
</ins><span class="cx"> 
</span><del>-    var order;
-    // Work around problem with the reported dimensions of single-char
-    // direction spans on IE (issue #1129). See also the comment in
-    // cursorCoords.
-    if (measure &amp;&amp; (ie || ie_gt10) &amp;&amp; (order = getOrder(line))) {
-      var l = order.length - 1;
-      if (order[l].from == order[l].to) --l;
-      var last = order[l], prev = order[l - 1];
-      if (last.from + 1 == last.to &amp;&amp; prev &amp;&amp; last.level &lt; prev.level) {
-        var span = measure[builder.pos - 1];
-        if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
-                                               span.nextSibling);
</del><ins>+      // Store the map and a cache object for the current logical line
+      if (i == 0) {
+        lineView.measure.map = builder.map;
+        lineView.measure.cache = {};
+      } else {
+        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
+        (lineView.measure.caches || (lineView.measure.caches = [])).push({});
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var textClass = builder.textClass ? builder.textClass + &quot; &quot; + (realLine.textClass || &quot;&quot;) : realLine.textClass;
-    if (textClass) builder.pre.className = textClass;
-
-    signal(cm, &quot;renderLine&quot;, cm, realLine, builder.pre);
</del><ins>+    signal(cm, &quot;renderLine&quot;, cm, lineView.line, builder.pre);
</ins><span class="cx">     return builder;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -4443,12 +5727,17 @@
</span><span class="cx">     return token;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Build up the DOM representation for a single token, and add it to
+  // the line map. Takes care to render special characters separately.
</ins><span class="cx">   function buildToken(builder, text, style, startStyle, endStyle, title) {
</span><span class="cx">     if (!text) return;
</span><del>-    var special = builder.cm.options.specialChars;
</del><ins>+    var special = builder.cm.options.specialChars, mustWrap = false;
</ins><span class="cx">     if (!special.test(text)) {
</span><span class="cx">       builder.col += text.length;
</span><span class="cx">       var content = document.createTextNode(text);
</span><ins>+      builder.map.push(builder.pos, builder.pos + text.length, content);
+      if (ie_upto8) mustWrap = true;
+      builder.pos += text.length;
</ins><span class="cx">     } else {
</span><span class="cx">       var content = document.createDocumentFragment(), pos = 0;
</span><span class="cx">       while (true) {
</span><span class="lines">@@ -4456,59 +5745,40 @@
</span><span class="cx">         var m = special.exec(text);
</span><span class="cx">         var skipped = m ? m.index - pos : text.length - pos;
</span><span class="cx">         if (skipped) {
</span><del>-          content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
</del><ins>+          var txt = document.createTextNode(text.slice(pos, pos + skipped));
+          if (ie_upto8) content.appendChild(elt(&quot;span&quot;, [txt]));
+          else content.appendChild(txt);
+          builder.map.push(builder.pos, builder.pos + skipped, txt);
</ins><span class="cx">           builder.col += skipped;
</span><ins>+          builder.pos += skipped;
</ins><span class="cx">         }
</span><span class="cx">         if (!m) break;
</span><span class="cx">         pos += skipped + 1;
</span><span class="cx">         if (m[0] == &quot;\t&quot;) {
</span><span class="cx">           var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
</span><del>-          content.appendChild(elt(&quot;span&quot;, spaceStr(tabWidth), &quot;cm-tab&quot;));
</del><ins>+          var txt = content.appendChild(elt(&quot;span&quot;, spaceStr(tabWidth), &quot;cm-tab&quot;));
</ins><span class="cx">           builder.col += tabWidth;
</span><span class="cx">         } else {
</span><del>-          var token = builder.cm.options.specialCharPlaceholder(m[0]);
-          content.appendChild(token);
</del><ins>+          var txt = builder.cm.options.specialCharPlaceholder(m[0]);
+          if (ie_upto8) content.appendChild(elt(&quot;span&quot;, [txt]));
+          else content.appendChild(txt);
</ins><span class="cx">           builder.col += 1;
</span><span class="cx">         }
</span><ins>+        builder.map.push(builder.pos, builder.pos + 1, txt);
+        builder.pos++;
</ins><span class="cx">       }
</span><span class="cx">     }
</span><del>-    if (style || startStyle || endStyle || builder.measure) {
</del><ins>+    if (style || startStyle || endStyle || mustWrap) {
</ins><span class="cx">       var fullStyle = style || &quot;&quot;;
</span><span class="cx">       if (startStyle) fullStyle += startStyle;
</span><span class="cx">       if (endStyle) fullStyle += endStyle;
</span><span class="cx">       var token = elt(&quot;span&quot;, [content], fullStyle);
</span><span class="cx">       if (title) token.title = title;
</span><del>-      return builder.pre.appendChild(token);
</del><ins>+      return builder.content.appendChild(token);
</ins><span class="cx">     }
</span><del>-    builder.pre.appendChild(content);
</del><ins>+    builder.content.appendChild(content);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
-    var wrapping = builder.cm.options.lineWrapping;
-    for (var i = 0; i &lt; text.length; ++i) {
-      var ch = text.charAt(i), start = i == 0;
-      if (ch &gt;= &quot;\ud800&quot; &amp;&amp; ch &lt; &quot;\udbff&quot; &amp;&amp; i &lt; text.length - 1) {
-        ch = text.slice(i, i + 2);
-        ++i;
-      } else if (i &amp;&amp; wrapping &amp;&amp; spanAffectsWrapping(text, i)) {
-        builder.pre.appendChild(elt(&quot;wbr&quot;));
-      }
-      var old = builder.measure[builder.pos];
-      var span = builder.measure[builder.pos] =
-        buildToken(builder, ch, style,
-                   start &amp;&amp; startStyle, i == text.length - 1 &amp;&amp; endStyle);
-      if (old) span.leftSide = old.leftSide || old;
-      // In IE single-space nodes wrap differently than spaces
-      // embedded in larger text nodes, except when set to
-      // white-space: normal (issue #1268).
-      if (ie &amp;&amp; wrapping &amp;&amp; ch == &quot; &quot; &amp;&amp; i &amp;&amp; !/\s/.test(text.charAt(i - 1)) &amp;&amp;
-          i &lt; text.length - 1 &amp;&amp; !/\s/.test(text.charAt(i + 1)))
-        span.style.whiteSpace = &quot;normal&quot;;
-      builder.pos += ch.length;
-    }
-    if (text.length) builder.measuredSomething = true;
-  }
-
</del><span class="cx">   function buildTokenSplitSpaces(inner) {
</span><span class="cx">     function split(old) {
</span><span class="cx">       var out = &quot; &quot;;
</span><span class="lines">@@ -4517,29 +5787,36 @@
</span><span class="cx">       return out;
</span><span class="cx">     }
</span><span class="cx">     return function(builder, text, style, startStyle, endStyle, title) {
</span><del>-      return inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
</del><ins>+      inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
</ins><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Work around nonsense dimensions being reported for stretches of
+  // right-to-left text.
+  function buildTokenBadBidi(inner, order) {
+    return function(builder, text, style, startStyle, endStyle, title) {
+      style = style ? style + &quot; cm-force-border&quot; : &quot;cm-force-border&quot;;
+      var start = builder.pos, end = start + text.length;
+      for (;;) {
+        // Find the part that overlaps with the start of this text
+        for (var i = 0; i &lt; order.length; i++) {
+          var part = order[i];
+          if (part.to &gt; start &amp;&amp; part.from &lt;= start) break;
+        }
+        if (part.to &gt;= end) return inner(builder, text, style, startStyle, endStyle, title);
+        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title);
+        startStyle = null;
+        text = text.slice(part.to - start);
+        start = part.to;
+      }
+    };
+  }
+
</ins><span class="cx">   function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
</span><del>-    var widget = !ignoreWidget &amp;&amp; marker.replacedWith;
</del><ins>+    var widget = !ignoreWidget &amp;&amp; marker.widgetNode;
</ins><span class="cx">     if (widget) {
</span><del>-      if (builder.copyWidgets) widget = widget.cloneNode(true);
-      builder.pre.appendChild(widget);
-      if (builder.measure) {
-        if (size) {
-          builder.measure[builder.pos] = widget;
-        } else {
-          var elt = zeroWidthElement(builder.cm.display.measure);
-          if (marker.type == &quot;bookmark&quot; &amp;&amp; !marker.insertLeft)
-            builder.measure[builder.pos] = builder.pre.appendChild(elt);
-          else if (builder.measure[builder.pos])
-            return;
-          else
-            builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget);
-        }
-        builder.measuredSomething = true;
-      }
</del><ins>+      builder.map.push(builder.pos, builder.pos + size, widget);
+      builder.content.appendChild(widget);
</ins><span class="cx">     }
</span><span class="cx">     builder.pos += size;
</span><span class="cx">   }
</span><span class="lines">@@ -4550,7 +5827,7 @@
</span><span class="cx">     var spans = line.markedSpans, allText = line.text, at = 0;
</span><span class="cx">     if (!spans) {
</span><span class="cx">       for (var i = 1; i &lt; styles.length; i+=2)
</span><del>-        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
</del><ins>+        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
</ins><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -4569,17 +5846,17 @@
</span><span class="cx">             if (m.startStyle &amp;&amp; sp.from == pos) spanStartStyle += &quot; &quot; + m.startStyle;
</span><span class="cx">             if (m.endStyle &amp;&amp; sp.to == nextChange) spanEndStyle += &quot; &quot; + m.endStyle;
</span><span class="cx">             if (m.title &amp;&amp; !title) title = m.title;
</span><del>-            if (m.collapsed &amp;&amp; (!collapsed || collapsed.marker.size &lt; m.size))
</del><ins>+            if (m.collapsed &amp;&amp; (!collapsed || compareCollapsedMarkers(collapsed.marker, m) &lt; 0))
</ins><span class="cx">               collapsed = sp;
</span><span class="cx">           } else if (sp.from &gt; pos &amp;&amp; nextChange &gt; sp.from) {
</span><span class="cx">             nextChange = sp.from;
</span><span class="cx">           }
</span><del>-          if (m.type == &quot;bookmark&quot; &amp;&amp; sp.from == pos &amp;&amp; m.replacedWith) foundBookmarks.push(m);
</del><ins>+          if (m.type == &quot;bookmark&quot; &amp;&amp; sp.from == pos &amp;&amp; m.widgetNode) foundBookmarks.push(m);
</ins><span class="cx">         }
</span><span class="cx">         if (collapsed &amp;&amp; (collapsed.from || 0) == pos) {
</span><del>-          buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
</del><ins>+          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
</ins><span class="cx">                              collapsed.marker, collapsed.from == null);
</span><del>-          if (collapsed.to == null) return collapsed.marker.find();
</del><ins>+          if (collapsed.to == null) return;
</ins><span class="cx">         }
</span><span class="cx">         if (!collapsed &amp;&amp; foundBookmarks.length) for (var j = 0; j &lt; foundBookmarks.length; ++j)
</span><span class="cx">           buildCollapsedSpan(builder, 0, foundBookmarks[j]);
</span><span class="lines">@@ -4600,14 +5877,23 @@
</span><span class="cx">           spanStartStyle = &quot;&quot;;
</span><span class="cx">         }
</span><span class="cx">         text = allText.slice(at, at = styles[i++]);
</span><del>-        style = interpretTokenStyle(styles[i++], builder);
</del><ins>+        style = interpretTokenStyle(styles[i++], builder.cm.options);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DOCUMENT DATA STRUCTURE
</span><span class="cx"> 
</span><del>-  function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
</del><ins>+  // By default, updates that start and end at the beginning of a line
+  // are treated specially, in order to make the association of line
+  // widgets and marker elements with the text behave more intuitive.
+  function isWholeLineUpdate(doc, change) {
+    return change.from.ch == 0 &amp;&amp; change.to.ch == 0 &amp;&amp; lst(change.text) == &quot;&quot; &amp;&amp;
+      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
+  }
+
+  // Perform a change on the document data structure.
+  function updateDoc(doc, change, markedSpans, estimateHeight) {
</ins><span class="cx">     function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
</span><span class="cx">     function update(line, text, spans) {
</span><span class="cx">       updateLine(line, text, spans, estimateHeight);
</span><span class="lines">@@ -4618,12 +5904,11 @@
</span><span class="cx">     var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
</span><span class="cx">     var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
</span><span class="cx"> 
</span><del>-    // First adjust the line structure
-    if (from.ch == 0 &amp;&amp; to.ch == 0 &amp;&amp; lastText == &quot;&quot; &amp;&amp;
-        (!doc.cm || doc.cm.options.wholeLineUpdateBefore)) {
</del><ins>+    // Adjust the line structure
+    if (isWholeLineUpdate(doc, change)) {
</ins><span class="cx">       // This is a whole-line replace. Treated specially to make
</span><span class="cx">       // sure line objects move the way they are supposed to.
</span><del>-      for (var i = 0, e = text.length - 1, added = []; i &lt; e; ++i)
</del><ins>+      for (var i = 0, added = []; i &lt; text.length - 1; ++i)
</ins><span class="cx">         added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">       update(lastLine, lastLine.text, lastSpans);
</span><span class="cx">       if (nlines) doc.remove(from.line, nlines);
</span><span class="lines">@@ -4632,7 +5917,7 @@
</span><span class="cx">       if (text.length == 1) {
</span><span class="cx">         update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
</span><span class="cx">       } else {
</span><del>-        for (var added = [], i = 1, e = text.length - 1; i &lt; e; ++i)
</del><ins>+        for (var added = [], i = 1; i &lt; text.length - 1; ++i)
</ins><span class="cx">           added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">         added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
</span><span class="cx">         update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
</span><span class="lines">@@ -4644,20 +5929,32 @@
</span><span class="cx">     } else {
</span><span class="cx">       update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
</span><span class="cx">       update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
</span><del>-      for (var i = 1, e = text.length - 1, added = []; i &lt; e; ++i)
</del><ins>+      for (var i = 1, added = []; i &lt; text.length - 1; ++i)
</ins><span class="cx">         added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">       if (nlines &gt; 1) doc.remove(from.line + 1, nlines - 1);
</span><span class="cx">       doc.insert(from.line + 1, added);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     signalLater(doc, &quot;change&quot;, doc, change);
</span><del>-    setSelection(doc, selAfter.anchor, selAfter.head, null, true);
</del><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // The document is represented as a BTree consisting of leaves, with
+  // chunk of lines in them, and branches, with up to ten leaves or
+  // other branch nodes below them. The top node is always a branch
+  // node, and is the document object itself (meaning it has
+  // additional methods and properties).
+  //
+  // All nodes have parent links. The tree is used both to go from
+  // line numbers to line objects, and to go from objects to numbers.
+  // It also indexes by height, and is used to convert between height
+  // and line object, and to find the total height of the document.
+  //
+  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
</ins><span class="cx">   function LeafChunk(lines) {
</span><span class="cx">     this.lines = lines;
</span><span class="cx">     this.parent = null;
</span><del>-    for (var i = 0, e = lines.length, height = 0; i &lt; e; ++i) {
</del><ins>+    for (var i = 0, height = 0; i &lt; lines.length; ++i) {
</ins><span class="cx">       lines[i].parent = this;
</span><span class="cx">       height += lines[i].height;
</span><span class="cx">     }
</span><span class="lines">@@ -4666,6 +5963,7 @@
</span><span class="cx"> 
</span><span class="cx">   LeafChunk.prototype = {
</span><span class="cx">     chunkSize: function() { return this.lines.length; },
</span><ins>+    // Remove the n lines at offset 'at'.
</ins><span class="cx">     removeInner: function(at, n) {
</span><span class="cx">       for (var i = at, e = at + n; i &lt; e; ++i) {
</span><span class="cx">         var line = this.lines[i];
</span><span class="lines">@@ -4675,14 +5973,18 @@
</span><span class="cx">       }
</span><span class="cx">       this.lines.splice(at, n);
</span><span class="cx">     },
</span><ins>+    // Helper used to collapse a small branch into a single leaf.
</ins><span class="cx">     collapse: function(lines) {
</span><del>-      lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
</del><ins>+      lines.push.apply(lines, this.lines);
</ins><span class="cx">     },
</span><ins>+    // Insert the given array of lines at offset 'at', count them as
+    // having the given height.
</ins><span class="cx">     insertInner: function(at, lines, height) {
</span><span class="cx">       this.height += height;
</span><span class="cx">       this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
</span><del>-      for (var i = 0, e = lines.length; i &lt; e; ++i) lines[i].parent = this;
</del><ins>+      for (var i = 0; i &lt; lines.length; ++i) lines[i].parent = this;
</ins><span class="cx">     },
</span><ins>+    // Used to iterate over a part of the tree.
</ins><span class="cx">     iterN: function(at, n, op) {
</span><span class="cx">       for (var e = at + n; at &lt; e; ++at)
</span><span class="cx">         if (op(this.lines[at])) return true;
</span><span class="lines">@@ -4692,7 +5994,7 @@
</span><span class="cx">   function BranchChunk(children) {
</span><span class="cx">     this.children = children;
</span><span class="cx">     var size = 0, height = 0;
</span><del>-    for (var i = 0, e = children.length; i &lt; e; ++i) {
</del><ins>+    for (var i = 0; i &lt; children.length; ++i) {
</ins><span class="cx">       var ch = children[i];
</span><span class="cx">       size += ch.chunkSize(); height += ch.height;
</span><span class="cx">       ch.parent = this;
</span><span class="lines">@@ -4717,7 +6019,10 @@
</span><span class="cx">           at = 0;
</span><span class="cx">         } else at -= sz;
</span><span class="cx">       }
</span><del>-      if (this.size - n &lt; 25) {
</del><ins>+      // If the result is smaller than 25 lines, ensure that it is a
+      // single leaf node.
+      if (this.size - n &lt; 25 &amp;&amp;
+          (this.children.length &gt; 1 || !(this.children[0] instanceof LeafChunk))) {
</ins><span class="cx">         var lines = [];
</span><span class="cx">         this.collapse(lines);
</span><span class="cx">         this.children = [new LeafChunk(lines)];
</span><span class="lines">@@ -4725,12 +6030,12 @@
</span><span class="cx">       }
</span><span class="cx">     },
</span><span class="cx">     collapse: function(lines) {
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) this.children[i].collapse(lines);
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) this.children[i].collapse(lines);
</ins><span class="cx">     },
</span><span class="cx">     insertInner: function(at, lines, height) {
</span><span class="cx">       this.size += lines.length;
</span><span class="cx">       this.height += height;
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) {
</ins><span class="cx">         var child = this.children[i], sz = child.chunkSize();
</span><span class="cx">         if (at &lt;= sz) {
</span><span class="cx">           child.insertInner(at, lines, height);
</span><span class="lines">@@ -4749,6 +6054,7 @@
</span><span class="cx">         at -= sz;
</span><span class="cx">       }
</span><span class="cx">     },
</span><ins>+    // When a node has grown, check whether it should be split.
</ins><span class="cx">     maybeSpill: function() {
</span><span class="cx">       if (this.children.length &lt;= 10) return;
</span><span class="cx">       var me = this;
</span><span class="lines">@@ -4771,7 +6077,7 @@
</span><span class="cx">       me.parent.maybeSpill();
</span><span class="cx">     },
</span><span class="cx">     iterN: function(at, n, op) {
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) {
</ins><span class="cx">         var child = this.children[i], sz = child.chunkSize();
</span><span class="cx">         if (at &lt; sz) {
</span><span class="cx">           var used = Math.min(n, sz - at);
</span><span class="lines">@@ -4792,43 +6098,52 @@
</span><span class="cx">     this.first = firstLine;
</span><span class="cx">     this.scrollTop = this.scrollLeft = 0;
</span><span class="cx">     this.cantEdit = false;
</span><del>-    this.history = makeHistory();
</del><span class="cx">     this.cleanGeneration = 1;
</span><span class="cx">     this.frontier = firstLine;
</span><span class="cx">     var start = Pos(firstLine, 0);
</span><del>-    this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
</del><ins>+    this.sel = simpleSelection(start);
+    this.history = new History(null);
</ins><span class="cx">     this.id = ++nextDocId;
</span><span class="cx">     this.modeOption = mode;
</span><span class="cx"> 
</span><span class="cx">     if (typeof text == &quot;string&quot;) text = splitLines(text);
</span><del>-    updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
</del><ins>+    updateDoc(this, {from: start, to: start, text: text});
+    setSelection(this, simpleSelection(start), sel_dontScroll);
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   Doc.prototype = createObj(BranchChunk.prototype, {
</span><span class="cx">     constructor: Doc,
</span><ins>+    // Iterate over the document. Supports two forms -- with only one
+    // argument, it calls that for each line in the document. With
+    // three, it iterates over the range given by the first two (with
+    // the second being non-inclusive).
</ins><span class="cx">     iter: function(from, to, op) {
</span><span class="cx">       if (op) this.iterN(from - this.first, to - from, op);
</span><span class="cx">       else this.iterN(this.first, this.first + this.size, from);
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    // Non-public interface for adding and removing lines.
</ins><span class="cx">     insert: function(at, lines) {
</span><span class="cx">       var height = 0;
</span><del>-      for (var i = 0, e = lines.length; i &lt; e; ++i) height += lines[i].height;
</del><ins>+      for (var i = 0; i &lt; lines.length; ++i) height += lines[i].height;
</ins><span class="cx">       this.insertInner(at - this.first, lines, height);
</span><span class="cx">     },
</span><span class="cx">     remove: function(at, n) { this.removeInner(at - this.first, n); },
</span><span class="cx"> 
</span><ins>+    // From here, the methods are part of the public interface. Most
+    // are also available from CodeMirror (editor) instances.
+
</ins><span class="cx">     getValue: function(lineSep) {
</span><span class="cx">       var lines = getLines(this, this.first, this.first + this.size);
</span><span class="cx">       if (lineSep === false) return lines;
</span><span class="cx">       return lines.join(lineSep || &quot;\n&quot;);
</span><span class="cx">     },
</span><del>-    setValue: function(code) {
</del><ins>+    setValue: docMethodOp(function(code) {
</ins><span class="cx">       var top = Pos(this.first, 0), last = this.first + this.size - 1;
</span><span class="cx">       makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
</span><del>-                        text: splitLines(code), origin: &quot;setValue&quot;},
-                 {head: top, anchor: top}, true);
-    },
</del><ins>+                        text: splitLines(code), origin: &quot;setValue&quot;}, true);
+      setSelection(this, simpleSelection(top));
+    }),
</ins><span class="cx">     replaceRange: function(code, from, to, origin) {
</span><span class="cx">       from = clipPos(this, from);
</span><span class="cx">       to = to ? clipPos(this, to) : from;
</span><span class="lines">@@ -4841,21 +6156,13 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getLine: function(line) {var l = this.getLineHandle(line); return l &amp;&amp; l.text;},
</span><del>-    setLine: function(line, text) {
-      if (isLine(this, line))
-        replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
-    },
-    removeLine: function(line) {
-      if (line) replaceRange(this, &quot;&quot;, clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
-      else replaceRange(this, &quot;&quot;, Pos(0, 0), clipPos(this, Pos(1, 0)));
-    },
</del><span class="cx"> 
</span><span class="cx">     getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
</span><span class="cx">     getLineNumber: function(line) {return lineNo(line);},
</span><span class="cx"> 
</span><span class="cx">     getLineHandleVisualStart: function(line) {
</span><span class="cx">       if (typeof line == &quot;number&quot;) line = getLine(this, line);
</span><del>-      return visualLine(this, line);
</del><ins>+      return visualLine(line);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     lineCount: function() {return this.size;},
</span><span class="lines">@@ -4865,47 +6172,103 @@
</span><span class="cx">     clipPos: function(pos) {return clipPos(this, pos);},
</span><span class="cx"> 
</span><span class="cx">     getCursor: function(start) {
</span><del>-      var sel = this.sel, pos;
-      if (start == null || start == &quot;head&quot;) pos = sel.head;
-      else if (start == &quot;anchor&quot;) pos = sel.anchor;
-      else if (start == &quot;end&quot; || start === false) pos = sel.to;
-      else pos = sel.from;
-      return copyPos(pos);
</del><ins>+      var range = this.sel.primary(), pos;
+      if (start == null || start == &quot;head&quot;) pos = range.head;
+      else if (start == &quot;anchor&quot;) pos = range.anchor;
+      else if (start == &quot;end&quot; || start == &quot;to&quot; || start === false) pos = range.to();
+      else pos = range.from();
+      return pos;
</ins><span class="cx">     },
</span><del>-    somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
</del><ins>+    listSelections: function() { return this.sel.ranges; },
+    somethingSelected: function() {return this.sel.somethingSelected();},
</ins><span class="cx"> 
</span><del>-    setCursor: docOperation(function(line, ch, extend) {
-      var pos = clipPos(this, typeof line == &quot;number&quot; ? Pos(line, ch || 0) : line);
-      if (extend) extendSelection(this, pos);
-      else setSelection(this, pos, pos);
</del><ins>+    setCursor: docMethodOp(function(line, ch, options) {
+      setSimpleSelection(this, clipPos(this, typeof line == &quot;number&quot; ? Pos(line, ch || 0) : line), null, options);
</ins><span class="cx">     }),
</span><del>-    setSelection: docOperation(function(anchor, head, bias) {
-      setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias);
</del><ins>+    setSelection: docMethodOp(function(anchor, head, options) {
+      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
</ins><span class="cx">     }),
</span><del>-    extendSelection: docOperation(function(from, to, bias) {
-      extendSelection(this, clipPos(this, from), to &amp;&amp; clipPos(this, to), bias);
</del><ins>+    extendSelection: docMethodOp(function(head, other, options) {
+      extendSelection(this, clipPos(this, head), other &amp;&amp; clipPos(this, other), options);
</ins><span class="cx">     }),
</span><ins>+    extendSelections: docMethodOp(function(heads, options) {
+      extendSelections(this, clipPosArray(this, heads, options));
+    }),
+    extendSelectionsBy: docMethodOp(function(f, options) {
+      extendSelections(this, map(this.sel.ranges, f), options);
+    }),
+    setSelections: docMethodOp(function(ranges, primary, options) {
+      if (!ranges.length) return;
+      for (var i = 0, out = []; i &lt; ranges.length; i++)
+        out[i] = new Range(clipPos(this, ranges[i].anchor),
+                           clipPos(this, ranges[i].head));
+      if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
+      setSelection(this, normalizeSelection(out, primary), options);
+    }),
+    addSelection: docMethodOp(function(anchor, head, options) {
+      var ranges = this.sel.ranges.slice(0);
+      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
+      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
+    }),
</ins><span class="cx"> 
</span><del>-    getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
-    replaceSelection: function(code, collapse, origin) {
-      makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || &quot;around&quot;);
</del><ins>+    getSelection: function(lineSep) {
+      var ranges = this.sel.ranges, lines;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+        lines = lines ? lines.concat(sel) : sel;
+      }
+      if (lineSep === false) return lines;
+      else return lines.join(lineSep || &quot;\n&quot;);
</ins><span class="cx">     },
</span><del>-    undo: docOperation(function() {makeChangeFromHistory(this, &quot;undo&quot;);}),
-    redo: docOperation(function() {makeChangeFromHistory(this, &quot;redo&quot;);}),
</del><ins>+    getSelections: function(lineSep) {
+      var parts = [], ranges = this.sel.ranges;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+        if (lineSep !== false) sel = sel.join(lineSep || &quot;\n&quot;);
+        parts[i] = sel;
+      }
+      return parts;
+    },
+    replaceSelection: docMethodOp(function(code, collapse, origin) {
+      var dup = [];
+      for (var i = 0; i &lt; this.sel.ranges.length; i++)
+        dup[i] = code;
+      this.replaceSelections(dup, collapse, origin || &quot;+input&quot;);
+    }),
+    replaceSelections: function(code, collapse, origin) {
+      var changes = [], sel = this.sel;
+      for (var i = 0; i &lt; sel.ranges.length; i++) {
+        var range = sel.ranges[i];
+        changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
+      }
+      var newSel = collapse &amp;&amp; collapse != &quot;end&quot; &amp;&amp; computeReplacedSel(this, changes, collapse);
+      for (var i = changes.length - 1; i &gt;= 0; i--)
+        makeChange(this, changes[i]);
+      if (newSel) setSelectionReplaceHistory(this, newSel);
+      else if (this.cm) ensureCursorVisible(this.cm);
+    },
+    undo: docMethodOp(function() {makeChangeFromHistory(this, &quot;undo&quot;);}),
+    redo: docMethodOp(function() {makeChangeFromHistory(this, &quot;redo&quot;);}),
+    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, &quot;undo&quot;, true);}),
+    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, &quot;redo&quot;, true);}),
</ins><span class="cx"> 
</span><del>-    setExtending: function(val) {this.sel.extend = val;},
</del><ins>+    setExtending: function(val) {this.extend = val;},
+    getExtending: function() {return this.extend;},
</ins><span class="cx"> 
</span><span class="cx">     historySize: function() {
</span><del>-      var hist = this.history;
-      return {undo: hist.done.length, redo: hist.undone.length};
</del><ins>+      var hist = this.history, done = 0, undone = 0;
+      for (var i = 0; i &lt; hist.done.length; i++) if (!hist.done[i].ranges) ++done;
+      for (var i = 0; i &lt; hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
+      return {undo: done, redo: undone};
</ins><span class="cx">     },
</span><del>-    clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
</del><ins>+    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
</ins><span class="cx"> 
</span><span class="cx">     markClean: function() {
</span><del>-      this.cleanGeneration = this.changeGeneration();
</del><ins>+      this.cleanGeneration = this.changeGeneration(true);
</ins><span class="cx">     },
</span><del>-    changeGeneration: function() {
-      this.history.lastOp = this.history.lastOrigin = null;
</del><ins>+    changeGeneration: function(forceSplit) {
+      if (forceSplit)
+        this.history.lastOp = this.history.lastOrigin = null;
</ins><span class="cx">       return this.history.generation;
</span><span class="cx">     },
</span><span class="cx">     isClean: function (gen) {
</span><span class="lines">@@ -4917,9 +6280,9 @@
</span><span class="cx">               undone: copyHistoryArray(this.history.undone)};
</span><span class="cx">     },
</span><span class="cx">     setHistory: function(histData) {
</span><del>-      var hist = this.history = makeHistory(this.history.maxGeneration);
-      hist.done = histData.done.slice(0);
-      hist.undone = histData.undone.slice(0);
</del><ins>+      var hist = this.history = new History(this.history.maxGeneration);
+      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
+      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     markText: function(from, to, options) {
</span><span class="lines">@@ -4927,7 +6290,8 @@
</span><span class="cx">     },
</span><span class="cx">     setBookmark: function(pos, options) {
</span><span class="cx">       var realOpts = {replacedWith: options &amp;&amp; (options.nodeType == null ? options.widget : options),
</span><del>-                      insertLeft: options &amp;&amp; options.insertLeft};
</del><ins>+                      insertLeft: options &amp;&amp; options.insertLeft,
+                      clearWhenEmpty: false, shared: options &amp;&amp; options.shared};
</ins><span class="cx">       pos = clipPos(this, pos);
</span><span class="cx">       return markText(this, pos, pos, realOpts, &quot;bookmark&quot;);
</span><span class="cx">     },
</span><span class="lines">@@ -4942,6 +6306,23 @@
</span><span class="cx">       }
</span><span class="cx">       return markers;
</span><span class="cx">     },
</span><ins>+    findMarks: function(from, to, filter) {
+      from = clipPos(this, from); to = clipPos(this, to);
+      var found = [], lineNo = from.line;
+      this.iter(from.line, to.line + 1, function(line) {
+        var spans = line.markedSpans;
+        if (spans) for (var i = 0; i &lt; spans.length; i++) {
+          var span = spans[i];
+          if (!(lineNo == from.line &amp;&amp; from.ch &gt; span.to ||
+                span.from == null &amp;&amp; lineNo != from.line||
+                lineNo == to.line &amp;&amp; span.from &gt; to.ch) &amp;&amp;
+              (!filter || filter(span.marker)))
+            found.push(span.marker.parent || span.marker);
+        }
+        ++lineNo;
+      });
+      return found;
+    },
</ins><span class="cx">     getAllMarks: function() {
</span><span class="cx">       var markers = [];
</span><span class="cx">       this.iter(function(line) {
</span><span class="lines">@@ -4975,8 +6356,8 @@
</span><span class="cx">     copy: function(copyHistory) {
</span><span class="cx">       var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
</span><span class="cx">       doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
</span><del>-      doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
-                 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
</del><ins>+      doc.sel = this.sel;
+      doc.extend = false;
</ins><span class="cx">       if (copyHistory) {
</span><span class="cx">         doc.history.undoDepth = this.history.undoDepth;
</span><span class="cx">         doc.setHistory(this.getHistory());
</span><span class="lines">@@ -4993,6 +6374,7 @@
</span><span class="cx">       if (options.sharedHist) copy.history = this.history;
</span><span class="cx">       (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
</span><span class="cx">       copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
</span><ins>+      copySharedMarkers(copy, findSharedMarkers(this));
</ins><span class="cx">       return copy;
</span><span class="cx">     },
</span><span class="cx">     unlinkDoc: function(other) {
</span><span class="lines">@@ -5002,13 +6384,14 @@
</span><span class="cx">         if (link.doc != other) continue;
</span><span class="cx">         this.linked.splice(i, 1);
</span><span class="cx">         other.unlinkDoc(this);
</span><ins>+        detachSharedMarkers(findSharedMarkers(this));
</ins><span class="cx">         break;
</span><span class="cx">       }
</span><span class="cx">       // If the histories were shared, split them again
</span><span class="cx">       if (other.history == this.history) {
</span><span class="cx">         var splitIds = [other.id];
</span><span class="cx">         linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
</span><del>-        other.history = makeHistory();
</del><ins>+        other.history = new History(null);
</ins><span class="cx">         other.history.done = copyHistoryArray(this.history.done, splitIds);
</span><span class="cx">         other.history.undone = copyHistoryArray(this.history.undone, splitIds);
</span><span class="cx">       }
</span><span class="lines">@@ -5019,9 +6402,10 @@
</span><span class="cx">     getEditor: function() {return this.cm;}
</span><span class="cx">   });
</span><span class="cx"> 
</span><ins>+  // Public alias.
</ins><span class="cx">   Doc.prototype.eachLine = Doc.prototype.iter;
</span><span class="cx"> 
</span><del>-  // The Doc methods that should be available on CodeMirror instances
</del><ins>+  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
</ins><span class="cx">   var dontDelegate = &quot;iter insert remove copy getEditor&quot;.split(&quot; &quot;);
</span><span class="cx">   for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) &amp;&amp; indexOf(dontDelegate, prop) &lt; 0)
</span><span class="cx">     CodeMirror.prototype[prop] = (function(method) {
</span><span class="lines">@@ -5030,6 +6414,7 @@
</span><span class="cx"> 
</span><span class="cx">   eventMixin(Doc);
</span><span class="cx"> 
</span><ins>+  // Call f for all linked documents.
</ins><span class="cx">   function linkedDocs(doc, f, sharedHistOnly) {
</span><span class="cx">     function propagate(doc, skip, sharedHist) {
</span><span class="cx">       if (doc.linked) for (var i = 0; i &lt; doc.linked.length; ++i) {
</span><span class="lines">@@ -5044,22 +6429,25 @@
</span><span class="cx">     propagate(doc, null, true);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Attach a document to an editor.
</ins><span class="cx">   function attachDoc(cm, doc) {
</span><span class="cx">     if (doc.cm) throw new Error(&quot;This document is already in use.&quot;);
</span><span class="cx">     cm.doc = doc;
</span><span class="cx">     doc.cm = cm;
</span><span class="cx">     estimateLineHeights(cm);
</span><span class="cx">     loadMode(cm);
</span><del>-    if (!cm.options.lineWrapping) computeMaxLength(cm);
</del><ins>+    if (!cm.options.lineWrapping) findMaxLine(cm);
</ins><span class="cx">     cm.options.mode = doc.modeOption;
</span><span class="cx">     regChange(cm);
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // LINE UTILITIES
</span><span class="cx"> 
</span><del>-  function getLine(chunk, n) {
-    n -= chunk.first;
-    while (!chunk.lines) {
</del><ins>+  // Find the line object corresponding to the given line number.
+  function getLine(doc, n) {
+    n -= doc.first;
+    if (n &lt; 0 || n &gt;= doc.size) throw new Error(&quot;There is no line &quot; + (n + doc.first) + &quot; in the document.&quot;);
+    for (var chunk = doc; !chunk.lines;) {
</ins><span class="cx">       for (var i = 0;; ++i) {
</span><span class="cx">         var child = chunk.children[i], sz = child.chunkSize();
</span><span class="cx">         if (n &lt; sz) { chunk = child; break; }
</span><span class="lines">@@ -5069,6 +6457,8 @@
</span><span class="cx">     return chunk.lines[n];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Get the part of a document between two positions, as an array of
+  // strings.
</ins><span class="cx">   function getBetween(doc, start, end) {
</span><span class="cx">     var out = [], n = start.line;
</span><span class="cx">     doc.iter(start.line, end.line + 1, function(line) {
</span><span class="lines">@@ -5080,17 +6470,22 @@
</span><span class="cx">     });
</span><span class="cx">     return out;
</span><span class="cx">   }
</span><ins>+  // Get the lines between from and to, as array of strings.
</ins><span class="cx">   function getLines(doc, from, to) {
</span><span class="cx">     var out = [];
</span><span class="cx">     doc.iter(from, to, function(line) { out.push(line.text); });
</span><span class="cx">     return out;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Update the height of a line, propagating the height change
+  // upwards to parent nodes.
</ins><span class="cx">   function updateLineHeight(line, height) {
</span><span class="cx">     var diff = height - line.height;
</span><del>-    for (var n = line; n; n = n.parent) n.height += diff;
</del><ins>+    if (diff) for (var n = line; n; n = n.parent) n.height += diff;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Given a line object, find its line number by walking up through
+  // its parent links.
</ins><span class="cx">   function lineNo(line) {
</span><span class="cx">     if (line.parent == null) return null;
</span><span class="cx">     var cur = line.parent, no = indexOf(cur.lines, line);
</span><span class="lines">@@ -5103,10 +6498,12 @@
</span><span class="cx">     return no + cur.first;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Find the line at the given vertical position, using the height
+  // information in the document tree.
</ins><span class="cx">   function lineAtHeight(chunk, h) {
</span><span class="cx">     var n = chunk.first;
</span><span class="cx">     outer: do {
</span><del>-      for (var i = 0, e = chunk.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; chunk.children.length; ++i) {
</ins><span class="cx">         var child = chunk.children[i], ch = child.height;
</span><span class="cx">         if (h &lt; ch) { chunk = child; continue outer; }
</span><span class="cx">         h -= ch;
</span><span class="lines">@@ -5114,7 +6511,7 @@
</span><span class="cx">       }
</span><span class="cx">       return n;
</span><span class="cx">     } while (!chunk.lines);
</span><del>-    for (var i = 0, e = chunk.lines.length; i &lt; e; ++i) {
</del><ins>+    for (var i = 0; i &lt; chunk.lines.length; ++i) {
</ins><span class="cx">       var line = chunk.lines[i], lh = line.height;
</span><span class="cx">       if (h &lt; lh) break;
</span><span class="cx">       h -= lh;
</span><span class="lines">@@ -5122,9 +6519,11 @@
</span><span class="cx">     return n + i;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function heightAtLine(cm, lineObj) {
-    lineObj = visualLine(cm.doc, lineObj);
</del><span class="cx"> 
</span><ins>+  // Find the height above the given line.
+  function heightAtLine(lineObj) {
+    lineObj = visualLine(lineObj);
+
</ins><span class="cx">     var h = 0, chunk = lineObj.parent;
</span><span class="cx">     for (var i = 0; i &lt; chunk.lines.length; ++i) {
</span><span class="cx">       var line = chunk.lines[i];
</span><span class="lines">@@ -5141,6 +6540,9 @@
</span><span class="cx">     return h;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Get the bidi ordering for the given line (and cache it). Returns
+  // false for lines that are fully left-to-right, and an array of
+  // BidiSpan objects otherwise.
</ins><span class="cx">   function getOrder(line) {
</span><span class="cx">     var order = line.order;
</span><span class="cx">     if (order == null) order = line.order = bidiOrdering(line.text);
</span><span class="lines">@@ -5149,50 +6551,70 @@
</span><span class="cx"> 
</span><span class="cx">   // HISTORY
</span><span class="cx"> 
</span><del>-  function makeHistory(startGen) {
-    return {
-      // Arrays of history events. Doing something adds an event to
-      // done and clears undo. Undoing moves events from done to
-      // undone, redoing moves them in the other direction.
-      done: [], undone: [], undoDepth: Infinity,
-      // Used to track when changes can be merged into a single undo
-      // event
-      lastTime: 0, lastOp: null, lastOrigin: null,
-      // Used by the isClean() method
-      generation: startGen || 1, maxGeneration: startGen || 1
-    };
</del><ins>+  function History(startGen) {
+    // Arrays of change events and selections. Doing something adds an
+    // event to done and clears undo. Undoing moves events from done
+    // to undone, redoing moves them in the other direction.
+    this.done = []; this.undone = [];
+    this.undoDepth = Infinity;
+    // Used to track when changes can be merged into a single undo
+    // event
+    this.lastModTime = this.lastSelTime = 0;
+    this.lastOp = null;
+    this.lastOrigin = this.lastSelOrigin = null;
+    // Used by the isClean() method
+    this.generation = this.maxGeneration = startGen || 1;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change[&quot;spans_&quot; + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
-      if (line.markedSpans)
-        (existing || (existing = change[&quot;spans_&quot; + doc.id] = {}))[n] = line.markedSpans;
-      ++n;
-    });
-  }
-
</del><ins>+  // Create a history change event from an updateDoc-style change
+  // object.
</ins><span class="cx">   function historyChangeFromChange(doc, change) {
</span><del>-    var from = { line: change.from.line, ch: change.from.ch };
-    var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
</del><ins>+    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
</ins><span class="cx">     attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
</span><span class="cx">     linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
</span><span class="cx">     return histChange;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function addToHistory(doc, change, selAfter, opId) {
</del><ins>+  // Pop all selection events off the end of a history array. Stop at
+  // a change event.
+  function clearSelectionEvents(array) {
+    while (array.length) {
+      var last = lst(array);
+      if (last.ranges) array.pop();
+      else break;
+    }
+  }
+
+  // Find the top change event in the history. Pop off selection
+  // events that are in the way.
+  function lastChangeEvent(hist, force) {
+    if (force) {
+      clearSelectionEvents(hist.done);
+      return lst(hist.done);
+    } else if (hist.done.length &amp;&amp; !lst(hist.done).ranges) {
+      return lst(hist.done);
+    } else if (hist.done.length &gt; 1 &amp;&amp; !hist.done[hist.done.length - 2].ranges) {
+      hist.done.pop();
+      return lst(hist.done);
+    }
+  }
+
+  // Register a change in the history. Merges changes that are within
+  // a single operation, ore are close together with an origin that
+  // allows merging (starting with &quot;+&quot;) into a single event.
+  function addChangeToHistory(doc, change, selAfter, opId) {
</ins><span class="cx">     var hist = doc.history;
</span><span class="cx">     hist.undone.length = 0;
</span><del>-    var time = +new Date, cur = lst(hist.done);
</del><ins>+    var time = +new Date, cur;
</ins><span class="cx"> 
</span><del>-    if (cur &amp;&amp;
-        (hist.lastOp == opId ||
</del><ins>+    if ((hist.lastOp == opId ||
</ins><span class="cx">          hist.lastOrigin == change.origin &amp;&amp; change.origin &amp;&amp;
</span><del>-         ((change.origin.charAt(0) == &quot;+&quot; &amp;&amp; doc.cm &amp;&amp; hist.lastTime &gt; time - doc.cm.options.historyEventDelay) ||
-          change.origin.charAt(0) == &quot;*&quot;))) {
</del><ins>+         ((change.origin.charAt(0) == &quot;+&quot; &amp;&amp; doc.cm &amp;&amp; hist.lastModTime &gt; time - doc.cm.options.historyEventDelay) ||
+          change.origin.charAt(0) == &quot;*&quot;)) &amp;&amp;
+        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
</ins><span class="cx">       // Merge this change into the last event
</span><span class="cx">       var last = lst(cur.changes);
</span><del>-      if (posEq(change.from, change.to) &amp;&amp; posEq(change.from, last.to)) {
</del><ins>+      if (cmp(change.from, change.to) == 0 &amp;&amp; cmp(change.from, last.to) == 0) {
</ins><span class="cx">         // Optimized case for simple insertion -- don't want to add
</span><span class="cx">         // new changesets for every character typed
</span><span class="cx">         last.to = changeEnd(change);
</span><span class="lines">@@ -5200,23 +6622,81 @@
</span><span class="cx">         // Add new sub-event
</span><span class="cx">         cur.changes.push(historyChangeFromChange(doc, change));
</span><span class="cx">       }
</span><del>-      cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
</del><span class="cx">     } else {
</span><span class="cx">       // Can not be merged, start a new event.
</span><ins>+      var before = lst(hist.done);
+      if (!before || !before.ranges)
+        pushSelectionToHistory(doc.sel, hist.done);
</ins><span class="cx">       cur = {changes: [historyChangeFromChange(doc, change)],
</span><del>-             generation: hist.generation,
-             anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
-             anchorAfter: selAfter.anchor, headAfter: selAfter.head};
</del><ins>+             generation: hist.generation};
</ins><span class="cx">       hist.done.push(cur);
</span><del>-      hist.generation = ++hist.maxGeneration;
-      while (hist.done.length &gt; hist.undoDepth)
</del><ins>+      while (hist.done.length &gt; hist.undoDepth) {
</ins><span class="cx">         hist.done.shift();
</span><ins>+        if (!hist.done[0].ranges) hist.done.shift();
+      }
</ins><span class="cx">     }
</span><del>-    hist.lastTime = time;
</del><ins>+    hist.done.push(selAfter);
+    hist.generation = ++hist.maxGeneration;
+    hist.lastModTime = hist.lastSelTime = time;
</ins><span class="cx">     hist.lastOp = opId;
</span><del>-    hist.lastOrigin = change.origin;
</del><ins>+    hist.lastOrigin = hist.lastSelOrigin = change.origin;
+
+    if (!last) signal(doc, &quot;historyAdded&quot;);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function selectionEventCanBeMerged(doc, origin, prev, sel) {
+    var ch = origin.charAt(0);
+    return ch == &quot;*&quot; ||
+      ch == &quot;+&quot; &amp;&amp;
+      prev.ranges.length == sel.ranges.length &amp;&amp;
+      prev.somethingSelected() == sel.somethingSelected() &amp;&amp;
+      new Date - doc.history.lastSelTime &lt;= (doc.cm ? doc.cm.options.historyEventDelay : 500);
+  }
+
+  // Called whenever the selection changes, sets the new selection as
+  // the pending selection in the history, and pushes the old pending
+  // selection into the 'done' array when it was significantly
+  // different (in number of selected ranges, emptiness, or time).
+  function addSelectionToHistory(doc, sel, opId, options) {
+    var hist = doc.history, origin = options &amp;&amp; options.origin;
+
+    // A new event is started when the previous origin does not match
+    // the current, or the origins don't allow matching. Origins
+    // starting with * are always merged, those starting with + are
+    // merged when similar and close together in time.
+    if (opId == hist.lastOp ||
+        (origin &amp;&amp; hist.lastSelOrigin == origin &amp;&amp;
+         (hist.lastModTime == hist.lastSelTime &amp;&amp; hist.lastOrigin == origin ||
+          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
+      hist.done[hist.done.length - 1] = sel;
+    else
+      pushSelectionToHistory(sel, hist.done);
+
+    hist.lastSelTime = +new Date;
+    hist.lastSelOrigin = origin;
+    hist.lastOp = opId;
+    if (options &amp;&amp; options.clearRedo !== false)
+      clearSelectionEvents(hist.undone);
+  }
+
+  function pushSelectionToHistory(sel, dest) {
+    var top = lst(dest);
+    if (!(top &amp;&amp; top.ranges &amp;&amp; top.equals(sel)))
+      dest.push(sel);
+  }
+
+  // Used to store marked span information in the history.
+  function attachLocalSpans(doc, change, from, to) {
+    var existing = change[&quot;spans_&quot; + doc.id], n = 0;
+    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
+      if (line.markedSpans)
+        (existing || (existing = change[&quot;spans_&quot; + doc.id] = {}))[n] = line.markedSpans;
+      ++n;
+    });
+  }
+
+  // When un/re-doing restores text containing marked spans, those
+  // that have been explicitly cleared should not be restored.
</ins><span class="cx">   function removeClearedSpans(spans) {
</span><span class="cx">     if (!spans) return null;
</span><span class="cx">     for (var i = 0, out; i &lt; spans.length; ++i) {
</span><span class="lines">@@ -5226,6 +6706,7 @@
</span><span class="cx">     return !out ? spans : out.length ? out : null;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Retrieve and filter the old marked spans stored in a change event.
</ins><span class="cx">   function getOldSpans(doc, change) {
</span><span class="cx">     var found = change[&quot;spans_&quot; + doc.id];
</span><span class="cx">     if (!found) return null;
</span><span class="lines">@@ -5236,11 +6717,15 @@
</span><span class="cx"> 
</span><span class="cx">   // Used both to provide a JSON-safe object in .getHistory, and, when
</span><span class="cx">   // detaching a document, to split the history in two
</span><del>-  function copyHistoryArray(events, newGroup) {
</del><ins>+  function copyHistoryArray(events, newGroup, instantiateSel) {
</ins><span class="cx">     for (var i = 0, copy = []; i &lt; events.length; ++i) {
</span><del>-      var event = events[i], changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
-                 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
</del><ins>+      var event = events[i];
+      if (event.ranges) {
+        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
+        continue;
+      }
+      var changes = event.changes, newChanges = [];
+      copy.push({changes: newChanges});
</ins><span class="cx">       for (var j = 0; j &lt; changes.length; ++j) {
</span><span class="cx">         var change = changes[j], m;
</span><span class="cx">         newChanges.push({from: change.from, to: change.to, text: change.text});
</span><span class="lines">@@ -5257,7 +6742,7 @@
</span><span class="cx"> 
</span><span class="cx">   // Rebasing/resetting history to deal with externally-sourced changes
</span><span class="cx"> 
</span><del>-  function rebaseHistSel(pos, from, to, diff) {
</del><ins>+  function rebaseHistSelSingle(pos, from, to, diff) {
</ins><span class="cx">     if (to &lt; pos.line) {
</span><span class="cx">       pos.line += diff;
</span><span class="cx">     } else if (from &lt; pos.line) {
</span><span class="lines">@@ -5276,28 +6761,27 @@
</span><span class="cx">   function rebaseHistArray(array, from, to, diff) {
</span><span class="cx">     for (var i = 0; i &lt; array.length; ++i) {
</span><span class="cx">       var sub = array[i], ok = true;
</span><ins>+      if (sub.ranges) {
+        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
+        for (var j = 0; j &lt; sub.ranges.length; j++) {
+          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
+          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
+        }
+        continue;
+      }
</ins><span class="cx">       for (var j = 0; j &lt; sub.changes.length; ++j) {
</span><span class="cx">         var cur = sub.changes[j];
</span><del>-        if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
</del><span class="cx">         if (to &lt; cur.from.line) {
</span><del>-          cur.from.line += diff;
-          cur.to.line += diff;
</del><ins>+          cur.from = Pos(cur.from.line + diff, cur.from.ch);
+          cur.to = Pos(cur.to.line + diff, cur.to.ch);
</ins><span class="cx">         } else if (from &lt;= cur.to.line) {
</span><span class="cx">           ok = false;
</span><span class="cx">           break;
</span><span class="cx">         }
</span><span class="cx">       }
</span><del>-      if (!sub.copied) {
-        sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
-        sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
-        sub.copied = true;
-      }
</del><span class="cx">       if (!ok) {
</span><span class="cx">         array.splice(0, i + 1);
</span><span class="cx">         i = 0;
</span><del>-      } else {
-        rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
-        rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
</del><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="lines">@@ -5308,30 +6792,23 @@
</span><span class="cx">     rebaseHistArray(hist.undone, from, to, diff);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // EVENT OPERATORS
</del><ins>+  // EVENT UTILITIES
</ins><span class="cx"> 
</span><del>-  function stopMethod() {e_stop(this);}
-  // Ensure an event has a stop method.
-  function addStop(event) {
-    if (!event.stop) event.stop = stopMethod;
-    return event;
-  }
</del><ins>+  // Due to the fact that we still support jurassic IE versions, some
+  // compatibility wrappers are needed.
</ins><span class="cx"> 
</span><del>-  function e_preventDefault(e) {
</del><ins>+  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
</ins><span class="cx">     if (e.preventDefault) e.preventDefault();
</span><span class="cx">     else e.returnValue = false;
</span><del>-  }
-  function e_stopPropagation(e) {
</del><ins>+  };
+  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
</ins><span class="cx">     if (e.stopPropagation) e.stopPropagation();
</span><span class="cx">     else e.cancelBubble = true;
</span><del>-  }
</del><ins>+  };
</ins><span class="cx">   function e_defaultPrevented(e) {
</span><span class="cx">     return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
</span><span class="cx">   }
</span><del>-  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
-  CodeMirror.e_stop = e_stop;
-  CodeMirror.e_preventDefault = e_preventDefault;
-  CodeMirror.e_stopPropagation = e_stopPropagation;
</del><ins>+  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
</ins><span class="cx"> 
</span><span class="cx">   function e_target(e) {return e.target || e.srcElement;}
</span><span class="cx">   function e_button(e) {
</span><span class="lines">@@ -5347,7 +6824,10 @@
</span><span class="cx"> 
</span><span class="cx">   // EVENT HANDLING
</span><span class="cx"> 
</span><del>-  function on(emitter, type, f) {
</del><ins>+  // Lightweight event framework. on/off also work on DOM nodes,
+  // registering native DOM handlers.
+
+  var on = CodeMirror.on = function(emitter, type, f) {
</ins><span class="cx">     if (emitter.addEventListener)
</span><span class="cx">       emitter.addEventListener(type, f, false);
</span><span class="cx">     else if (emitter.attachEvent)
</span><span class="lines">@@ -5357,9 +6837,9 @@
</span><span class="cx">       var arr = map[type] || (map[type] = []);
</span><span class="cx">       arr.push(f);
</span><span class="cx">     }
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function off(emitter, type, f) {
</del><ins>+  var off = CodeMirror.off = function(emitter, type, f) {
</ins><span class="cx">     if (emitter.removeEventListener)
</span><span class="cx">       emitter.removeEventListener(type, f, false);
</span><span class="cx">     else if (emitter.detachEvent)
</span><span class="lines">@@ -5370,15 +6850,22 @@
</span><span class="cx">       for (var i = 0; i &lt; arr.length; ++i)
</span><span class="cx">         if (arr[i] == f) { arr.splice(i, 1); break; }
</span><span class="cx">     }
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function signal(emitter, type /*, values...*/) {
</del><ins>+  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
</ins><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="cx">     if (!arr) return;
</span><span class="cx">     var args = Array.prototype.slice.call(arguments, 2);
</span><span class="cx">     for (var i = 0; i &lt; arr.length; ++i) arr[i].apply(null, args);
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><ins>+  // Often, we want to signal events at a point where we are in the
+  // middle of some work, but don't want the handler to start calling
+  // other methods on the editor, which might be in an inconsistent
+  // state or simply not expect any other events to happen.
+  // signalLater looks whether there are any handlers, and schedules
+  // them to be executed when the last operation ends, or, if no
+  // operation is active, when a timeout fires.
</ins><span class="cx">   var delayedCallbacks, delayedCallbackDepth = 0;
</span><span class="cx">   function signalLater(emitter, type /*, values...*/) {
</span><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="lines">@@ -5394,11 +6881,6 @@
</span><span class="cx">       delayedCallbacks.push(bnd(arr[i]));
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function signalDOMEvent(cm, e, override) {
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore;
-  }
-
</del><span class="cx">   function fireDelayed() {
</span><span class="cx">     --delayedCallbackDepth;
</span><span class="cx">     var delayed = delayedCallbacks;
</span><span class="lines">@@ -5406,13 +6888,21 @@
</span><span class="cx">     for (var i = 0; i &lt; delayed.length; ++i) delayed[i]();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // The DOM events that CodeMirror handles can be overridden by
+  // registering a (non-DOM) handler on the editor for the event name,
+  // and preventDefault-ing the event in that handler.
+  function signalDOMEvent(cm, e, override) {
+    signal(cm, override || e.type, cm, e);
+    return e_defaultPrevented(e) || e.codemirrorIgnore;
+  }
+
</ins><span class="cx">   function hasHandler(emitter, type) {
</span><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="cx">     return arr &amp;&amp; arr.length &gt; 0;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
-
</del><ins>+  // Add on and off methods to a constructor's prototype, to make
+  // registering events on such objects more convenient.
</ins><span class="cx">   function eventMixin(ctor) {
</span><span class="cx">     ctor.prototype.on = function(type, f) {on(this, type, f);};
</span><span class="cx">     ctor.prototype.off = function(type, f) {off(this, type, f);};
</span><span class="lines">@@ -5427,23 +6917,47 @@
</span><span class="cx">   // handling this'.
</span><span class="cx">   var Pass = CodeMirror.Pass = {toString: function(){return &quot;CodeMirror.Pass&quot;;}};
</span><span class="cx"> 
</span><ins>+  // Reused option objects for setSelection &amp; friends
+  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: &quot;*mouse&quot;}, sel_move = {origin: &quot;+move&quot;};
+
</ins><span class="cx">   function Delayed() {this.id = null;}
</span><del>-  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
</del><ins>+  Delayed.prototype.set = function(ms, f) {
+    clearTimeout(this.id);
+    this.id = setTimeout(f, ms);
+  };
</ins><span class="cx"> 
</span><span class="cx">   // Counts the column offset in a string, taking tabs into account.
</span><span class="cx">   // Used mostly to find indentation.
</span><del>-  function countColumn(string, end, tabSize, startIndex, startValue) {
</del><ins>+  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
</ins><span class="cx">     if (end == null) {
</span><span class="cx">       end = string.search(/[^\s\u00a0]/);
</span><span class="cx">       if (end == -1) end = string.length;
</span><span class="cx">     }
</span><del>-    for (var i = startIndex || 0, n = startValue || 0; i &lt; end; ++i) {
-      if (string.charAt(i) == &quot;\t&quot;) n += tabSize - (n % tabSize);
-      else ++n;
</del><ins>+    for (var i = startIndex || 0, n = startValue || 0;;) {
+      var nextTab = string.indexOf(&quot;\t&quot;, i);
+      if (nextTab &lt; 0 || nextTab &gt;= end)
+        return n + (end - i);
+      n += nextTab - i;
+      n += tabSize - (n % tabSize);
+      i = nextTab + 1;
</ins><span class="cx">     }
</span><del>-    return n;
</del><ins>+  };
+
+  // The inverse of countColumn -- find the offset that corresponds to
+  // a particular column.
+  function findColumn(string, goal, tabSize) {
+    for (var pos = 0, col = 0;;) {
+      var nextTab = string.indexOf(&quot;\t&quot;, pos);
+      if (nextTab == -1) nextTab = string.length;
+      var skipped = nextTab - pos;
+      if (nextTab == string.length || col + skipped &gt;= goal)
+        return pos + Math.min(skipped, goal - col);
+      col += nextTab - pos;
+      col += tabSize - (col % tabSize);
+      pos = nextTab + 1;
+      if (col &gt;= goal) return pos;
+    }
</ins><span class="cx">   }
</span><del>-  CodeMirror.countColumn = countColumn;
</del><span class="cx"> 
</span><span class="cx">   var spaceStrs = [&quot;&quot;];
</span><span class="cx">   function spaceStr(n) {
</span><span class="lines">@@ -5454,60 +6968,69 @@
</span><span class="cx"> 
</span><span class="cx">   function lst(arr) { return arr[arr.length-1]; }
</span><span class="cx"> 
</span><del>-  function selectInput(node) {
-    if (ios) { // Mobile Safari apparently has a bug where select() is broken.
-      node.selectionStart = 0;
-      node.selectionEnd = node.value.length;
-    } else {
-      // Suppress mysterious IE10 errors
-      try { node.select(); }
-      catch(_e) {}
-    }
-  }
</del><ins>+  var selectInput = function(node) { node.select(); };
+  if (ios) // Mobile Safari apparently has a bug where select() is broken.
+    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
+  else if (ie) // Suppress mysterious IE10 errors
+    selectInput = function(node) { try { node.select(); } catch(_e) {} };
</ins><span class="cx"> 
</span><del>-  function indexOf(collection, elt) {
-    if (collection.indexOf) return collection.indexOf(elt);
-    for (var i = 0, e = collection.length; i &lt; e; ++i)
-      if (collection[i] == elt) return i;
</del><ins>+  function indexOf(array, elt) {
+    for (var i = 0; i &lt; array.length; ++i)
+      if (array[i] == elt) return i;
</ins><span class="cx">     return -1;
</span><span class="cx">   }
</span><ins>+  if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); };
+  function map(array, f) {
+    var out = [];
+    for (var i = 0; i &lt; array.length; i++) out[i] = f(array[i], i);
+    return out;
+  }
+  if ([].map) map = function(array, f) { return array.map(f); };
</ins><span class="cx"> 
</span><span class="cx">   function createObj(base, props) {
</span><del>-    function Obj() {}
-    Obj.prototype = base;
-    var inst = new Obj();
</del><ins>+    var inst;
+    if (Object.create) {
+      inst = Object.create(base);
+    } else {
+      var ctor = function() {};
+      ctor.prototype = base;
+      inst = new ctor();
+    }
</ins><span class="cx">     if (props) copyObj(props, inst);
</span><span class="cx">     return inst;
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function copyObj(obj, target) {
</del><ins>+  function copyObj(obj, target, overwrite) {
</ins><span class="cx">     if (!target) target = {};
</span><del>-    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
</del><ins>+    for (var prop in obj)
+      if (obj.hasOwnProperty(prop) &amp;&amp; (overwrite !== false || !target.hasOwnProperty(prop)))
+        target[prop] = obj[prop];
</ins><span class="cx">     return target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function emptyArray(size) {
-    for (var a = [], i = 0; i &lt; size; ++i) a.push(undefined);
-    return a;
-  }
-
</del><span class="cx">   function bind(f) {
</span><span class="cx">     var args = Array.prototype.slice.call(arguments, 1);
</span><span class="cx">     return function(){return f.apply(null, args);};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  function isWordChar(ch) {
</del><ins>+  var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+  var isWordChar = CodeMirror.isWordChar = function(ch) {
</ins><span class="cx">     return /\w/.test(ch) || ch &gt; &quot;\x80&quot; &amp;&amp;
</span><span class="cx">       (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><span class="cx">   function isEmpty(obj) {
</span><span class="cx">     for (var n in obj) if (obj.hasOwnProperty(n) &amp;&amp; obj[n]) return false;
</span><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0–\u1DFF\u20D0–\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20–\uFE2F]/;
</del><ins>+  // Extending unicode characters. A series of a non-extending char +
+  // any number of extending chars is treated as a single unit as far
+  // as editing and measuring is concerned. This is not fully correct,
+  // since some scripts/fonts/browsers also treat other configurations
+  // of code points as a group.
+  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
+  function isExtendingChar(ch) { return ch.charCodeAt(0) &gt;= 768 &amp;&amp; extendingChars.test(ch); }
</ins><span class="cx"> 
</span><span class="cx">   // DOM UTILITIES
</span><span class="cx"> 
</span><span class="lines">@@ -5515,11 +7038,27 @@
</span><span class="cx">     var e = document.createElement(tag);
</span><span class="cx">     if (className) e.className = className;
</span><span class="cx">     if (style) e.style.cssText = style;
</span><del>-    if (typeof content == &quot;string&quot;) setTextContent(e, content);
</del><ins>+    if (typeof content == &quot;string&quot;) e.appendChild(document.createTextNode(content));
</ins><span class="cx">     else if (content) for (var i = 0; i &lt; content.length; ++i) e.appendChild(content[i]);
</span><span class="cx">     return e;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  var range;
+  if (document.createRange) range = function(node, start, end) {
+    var r = document.createRange();
+    r.setEnd(node, end);
+    r.setStart(node, start);
+    return r;
+  };
+  else range = function(node, start, end) {
+    var r = document.body.createTextRange();
+    r.moveToElementText(node.parentNode);
+    r.collapse(true);
+    r.moveEnd(&quot;character&quot;, end);
+    r.moveStart(&quot;character&quot;, start);
+    return r;
+  };
+
</ins><span class="cx">   function removeChildren(e) {
</span><span class="cx">     for (var count = e.childNodes.length; count &gt; 0; --count)
</span><span class="cx">       e.removeChild(e.firstChild);
</span><span class="lines">@@ -5530,17 +7069,35 @@
</span><span class="cx">     return removeChildren(parent).appendChild(e);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function setTextContent(e, str) {
-    if (ie_lt9) {
-      e.innerHTML = &quot;&quot;;
-      e.appendChild(document.createTextNode(str));
-    } else e.textContent = str;
</del><ins>+  function contains(parent, child) {
+    if (parent.contains)
+      return parent.contains(child);
+    while (child = child.parentNode)
+      if (child == parent) return true;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function getRect(node) {
-    return node.getBoundingClientRect();
</del><ins>+  function activeElt() { return document.activeElement; }
+  // Older versions of IE throws unspecified error when touching
+  // document.activeElement in some cases (during loading, in iframe)
+  if (ie_upto10) activeElt = function() {
+    try { return document.activeElement; }
+    catch(e) { return document.body; }
+  };
+
+  function classTest(cls) { return new RegExp(&quot;\\b&quot; + cls + &quot;\\b\\s*&quot;); }
+  function rmClass(node, cls) {
+    var test = classTest(cls);
+    if (test.test(node.className)) node.className = node.className.replace(test, &quot;&quot;);
</ins><span class="cx">   }
</span><del>-  CodeMirror.replaceGetRect = function(f) { getRect = f; };
</del><ins>+  function addClass(node, cls) {
+    if (!classTest(cls).test(node.className)) node.className += &quot; &quot; + cls;
+  }
+  function joinClasses(a, b) {
+    var as = a.split(&quot; &quot;);
+    for (var i = 0; i &lt; as.length; i++)
+      if (as[i] &amp;&amp; !classTest(as[i]).test(b)) b += &quot; &quot; + as[i];
+    return b;
+  }
</ins><span class="cx"> 
</span><span class="cx">   // FEATURE DETECTION
</span><span class="cx"> 
</span><span class="lines">@@ -5548,43 +7105,11 @@
</span><span class="cx">   var dragAndDrop = function() {
</span><span class="cx">     // There is *some* kind of drag-and-drop support in IE6-8, but I
</span><span class="cx">     // couldn't get it to work yet.
</span><del>-    if (ie_lt9) return false;
</del><ins>+    if (ie_upto8) return false;
</ins><span class="cx">     var div = elt('div');
</span><span class="cx">     return &quot;draggable&quot; in div || &quot;dragDrop&quot; in div;
</span><span class="cx">   }();
</span><span class="cx"> 
</span><del>-  // For a reason I have yet to figure out, some browsers disallow
-  // word wrapping between certain characters *only* if a new inline
-  // element is started between them. This makes it hard to reliably
-  // measure the position of things, since that requires inserting an
-  // extra span. This terribly fragile set of tests matches the
-  // character combinations that suffer from this phenomenon on the
-  // various browsers.
-  function spanAffectsWrapping() { return false; }
-  if (gecko) // Only for &quot;$'&quot;
-    spanAffectsWrapping = function(str, i) {
-      return str.charCodeAt(i - 1) == 36 &amp;&amp; str.charCodeAt(i) == 39;
-    };
-  else if (safari &amp;&amp; !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      var result = /\-[^ \-?]|\?[^ !\'\&quot;\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
-      return result;
-    };
-  else if (webkit &amp;&amp; /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      var code = str.charCodeAt(i - 1);
-      return code &gt;= 8208 &amp;&amp; code &lt;= 8212;
-    };
-  else if (webkit)
-    spanAffectsWrapping = function(str, i) {
-      if (i &gt; 1 &amp;&amp; str.charCodeAt(i - 1) == 45) {
-        if (/\w/.test(str.charAt(i - 2)) &amp;&amp; /[^\-?\.]/.test(str.charAt(i))) return true;
-        if (i &gt; 2 &amp;&amp; /[\d\.,]/.test(str.charAt(i - 2)) &amp;&amp; /[\d\.,]/.test(str.charAt(i))) return false;
-      }
-      var result = /[~!#%&amp;*)=+}\]\\|\&quot;\.&gt;,:;][({[&lt;]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&amp;*(_=+{[|&gt;&lt;]|…[\w~`@#$%\^&amp;*(_=+{[&gt;&lt;]/.test(str.slice(i - 1, i + 1));
-      return result;
-    };
-
</del><span class="cx">   var knownScrollbarWidth;
</span><span class="cx">   function scrollbarWidth(measure) {
</span><span class="cx">     if (knownScrollbarWidth != null) return knownScrollbarWidth;
</span><span class="lines">@@ -5601,15 +7126,26 @@
</span><span class="cx">       var test = elt(&quot;span&quot;, &quot;\u200b&quot;);
</span><span class="cx">       removeChildrenAndAdd(measure, elt(&quot;span&quot;, [test, document.createTextNode(&quot;x&quot;)]));
</span><span class="cx">       if (measure.firstChild.offsetHeight != 0)
</span><del>-        zwspSupported = test.offsetWidth &lt;= 1 &amp;&amp; test.offsetHeight &gt; 2 &amp;&amp; !ie_lt8;
</del><ins>+        zwspSupported = test.offsetWidth &lt;= 1 &amp;&amp; test.offsetHeight &gt; 2 &amp;&amp; !ie_upto7;
</ins><span class="cx">     }
</span><span class="cx">     if (zwspSupported) return elt(&quot;span&quot;, &quot;\u200b&quot;);
</span><span class="cx">     else return elt(&quot;span&quot;, &quot;\u00a0&quot;, null, &quot;display: inline-block; width: 1px; margin-right: -1px&quot;);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Feature-detect IE's crummy client rect reporting for bidi text
+  var badBidiRects;
+  function hasBadBidiRects(measure) {
+    if (badBidiRects != null) return badBidiRects;
+    var txt = removeChildrenAndAdd(measure, document.createTextNode(&quot;A\u062eA&quot;));
+    var r0 = range(txt, 0, 1).getBoundingClientRect();
+    if (r0.left == r0.right) return false;
+    var r1 = range(txt, 1, 2).getBoundingClientRect();
+    return badBidiRects = (r1.right - r0.right &lt; 3);
+  }
+
</ins><span class="cx">   // See if &quot;&quot;.split is the broken IE version, if so, provide an
</span><span class="cx">   // alternative way to split lines.
</span><del>-  var splitLines = &quot;\n\nb&quot;.split(/\n/).length != 3 ? function(string) {
</del><ins>+  var splitLines = CodeMirror.splitLines = &quot;\n\nb&quot;.split(/\n/).length != 3 ? function(string) {
</ins><span class="cx">     var pos = 0, result = [], l = string.length;
</span><span class="cx">     while (pos &lt;= l) {
</span><span class="cx">       var nl = string.indexOf(&quot;\n&quot;, pos);
</span><span class="lines">@@ -5626,7 +7162,6 @@
</span><span class="cx">     }
</span><span class="cx">     return result;
</span><span class="cx">   } : function(string){return string.split(/\r\n?|\n/);};
</span><del>-  CodeMirror.splitLines = splitLines;
</del><span class="cx"> 
</span><span class="cx">   var hasSelection = window.getSelection ? function(te) {
</span><span class="cx">     try { return te.selectionStart != te.selectionEnd; }
</span><span class="lines">@@ -5642,22 +7177,22 @@
</span><span class="cx">     var e = elt(&quot;div&quot;);
</span><span class="cx">     if (&quot;oncopy&quot; in e) return true;
</span><span class="cx">     e.setAttribute(&quot;oncopy&quot;, &quot;return;&quot;);
</span><del>-    return typeof e.oncopy == 'function';
</del><ins>+    return typeof e.oncopy == &quot;function&quot;;
</ins><span class="cx">   })();
</span><span class="cx"> 
</span><del>-  // KEY NAMING
</del><ins>+  // KEY NAMES
</ins><span class="cx"> 
</span><span class="cx">   var keyNames = {3: &quot;Enter&quot;, 8: &quot;Backspace&quot;, 9: &quot;Tab&quot;, 13: &quot;Enter&quot;, 16: &quot;Shift&quot;, 17: &quot;Ctrl&quot;, 18: &quot;Alt&quot;,
</span><span class="cx">                   19: &quot;Pause&quot;, 20: &quot;CapsLock&quot;, 27: &quot;Esc&quot;, 32: &quot;Space&quot;, 33: &quot;PageUp&quot;, 34: &quot;PageDown&quot;, 35: &quot;End&quot;,
</span><span class="cx">                   36: &quot;Home&quot;, 37: &quot;Left&quot;, 38: &quot;Up&quot;, 39: &quot;Right&quot;, 40: &quot;Down&quot;, 44: &quot;PrintScrn&quot;, 45: &quot;Insert&quot;,
</span><del>-                  46: &quot;Delete&quot;, 59: &quot;;&quot;, 91: &quot;Mod&quot;, 92: &quot;Mod&quot;, 93: &quot;Mod&quot;, 109: &quot;-&quot;, 107: &quot;=&quot;, 127: &quot;Delete&quot;,
-                  186: &quot;;&quot;, 187: &quot;=&quot;, 188: &quot;,&quot;, 189: &quot;-&quot;, 190: &quot;.&quot;, 191: &quot;/&quot;, 192: &quot;`&quot;, 219: &quot;[&quot;, 220: &quot;\\&quot;,
-                  221: &quot;]&quot;, 222: &quot;'&quot;, 63276: &quot;PageUp&quot;, 63277: &quot;PageDown&quot;, 63275: &quot;End&quot;, 63273: &quot;Home&quot;,
-                  63234: &quot;Left&quot;, 63232: &quot;Up&quot;, 63235: &quot;Right&quot;, 63233: &quot;Down&quot;, 63302: &quot;Insert&quot;, 63272: &quot;Delete&quot;};
</del><ins>+                  46: &quot;Delete&quot;, 59: &quot;;&quot;, 61: &quot;=&quot;, 91: &quot;Mod&quot;, 92: &quot;Mod&quot;, 93: &quot;Mod&quot;, 107: &quot;=&quot;, 109: &quot;-&quot;, 127: &quot;Delete&quot;,
+                  173: &quot;-&quot;, 186: &quot;;&quot;, 187: &quot;=&quot;, 188: &quot;,&quot;, 189: &quot;-&quot;, 190: &quot;.&quot;, 191: &quot;/&quot;, 192: &quot;`&quot;, 219: &quot;[&quot;, 220: &quot;\\&quot;,
+                  221: &quot;]&quot;, 222: &quot;'&quot;, 63232: &quot;Up&quot;, 63233: &quot;Down&quot;, 63234: &quot;Left&quot;, 63235: &quot;Right&quot;, 63272: &quot;Delete&quot;,
+                  63273: &quot;Home&quot;, 63275: &quot;End&quot;, 63276: &quot;PageUp&quot;, 63277: &quot;PageDown&quot;, 63302: &quot;Insert&quot;};
</ins><span class="cx">   CodeMirror.keyNames = keyNames;
</span><span class="cx">   (function() {
</span><span class="cx">     // Number keys
</span><del>-    for (var i = 0; i &lt; 10; i++) keyNames[i + 48] = String(i);
</del><ins>+    for (var i = 0; i &lt; 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
</ins><span class="cx">     // Alphabetic keys
</span><span class="cx">     for (var i = 65; i &lt;= 90; i++) keyNames[i] = String.fromCharCode(i);
</span><span class="cx">     // Function keys
</span><span class="lines">@@ -5691,19 +7226,21 @@
</span><span class="cx"> 
</span><span class="cx">   function lineStart(cm, lineN) {
</span><span class="cx">     var line = getLine(cm.doc, lineN);
</span><del>-    var visual = visualLine(cm.doc, line);
</del><ins>+    var visual = visualLine(line);
</ins><span class="cx">     if (visual != line) lineN = lineNo(visual);
</span><span class="cx">     var order = getOrder(visual);
</span><span class="cx">     var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
</span><span class="cx">     return Pos(lineN, ch);
</span><span class="cx">   }
</span><span class="cx">   function lineEnd(cm, lineN) {
</span><del>-    var merged, line;
-    while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
-      lineN = merged.find().to.line;
</del><ins>+    var merged, line = getLine(cm.doc, lineN);
+    while (merged = collapsedSpanAtEnd(line)) {
+      line = merged.find(1, true).line;
+      lineN = null;
+    }
</ins><span class="cx">     var order = getOrder(line);
</span><span class="cx">     var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
</span><del>-    return Pos(lineN, ch);
</del><ins>+    return Pos(lineN == null ? lineNo(line) : lineN, ch);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function compareBidiLevel(order, a, b) {
</span><span class="lines">@@ -5714,38 +7251,37 @@
</span><span class="cx">   }
</span><span class="cx">   var bidiOther;
</span><span class="cx">   function getBidiPartAt(order, pos) {
</span><ins>+    bidiOther = null;
</ins><span class="cx">     for (var i = 0, found; i &lt; order.length; ++i) {
</span><span class="cx">       var cur = order[i];
</span><del>-      if (cur.from &lt; pos &amp;&amp; cur.to &gt; pos) { bidiOther = null; return i; }
-      if (cur.from == pos || cur.to == pos) {
</del><ins>+      if (cur.from &lt; pos &amp;&amp; cur.to &gt; pos) return i;
+      if ((cur.from == pos || cur.to == pos)) {
</ins><span class="cx">         if (found == null) {
</span><span class="cx">           found = i;
</span><span class="cx">         } else if (compareBidiLevel(order, cur.level, order[found].level)) {
</span><del>-          bidiOther = found;
</del><ins>+          if (cur.from != cur.to) bidiOther = found;
</ins><span class="cx">           return i;
</span><span class="cx">         } else {
</span><del>-          bidiOther = i;
</del><ins>+          if (cur.from != cur.to) bidiOther = i;
</ins><span class="cx">           return found;
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><del>-    bidiOther = null;
</del><span class="cx">     return found;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function moveInLine(line, pos, dir, byUnit) {
</span><span class="cx">     if (!byUnit) return pos + dir;
</span><span class="cx">     do pos += dir;
</span><del>-    while (pos &gt; 0 &amp;&amp; isExtendingChar.test(line.text.charAt(pos)));
</del><ins>+    while (pos &gt; 0 &amp;&amp; isExtendingChar(line.text.charAt(pos)));
</ins><span class="cx">     return pos;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // This is somewhat involved. It is needed in order to move
-  // 'visually' through bi-directional text -- i.e., pressing left
-  // should make the cursor go left, even when in RTL text. The
-  // tricky part is the 'jumps', where RTL and LTR text touch each
-  // other. This often requires the cursor offset to move more than
-  // one unit, in order to visually move one unit.
</del><ins>+  // This is needed in order to move 'visually' through bi-directional
+  // text -- i.e., pressing left should make the cursor go left, even
+  // when in RTL text. The tricky part is the 'jumps', where RTL and
+  // LTR text touch each other. This often requires the cursor offset
+  // to move more than one unit, in order to visually move one unit.
</ins><span class="cx">   function moveVisually(line, start, dir, byUnit) {
</span><span class="cx">     var bidi = getOrder(line);
</span><span class="cx">     if (!bidi) return moveLogically(line, start, dir, byUnit);
</span><span class="lines">@@ -5771,7 +7307,7 @@
</span><span class="cx"> 
</span><span class="cx">   function moveLogically(line, start, dir, byUnit) {
</span><span class="cx">     var target = start + dir;
</span><del>-    if (byUnit) while (target &gt; 0 &amp;&amp; isExtendingChar.test(line.text.charAt(target))) target += dir;
</del><ins>+    if (byUnit) while (target &gt; 0 &amp;&amp; isExtendingChar(line.text.charAt(target))) target += dir;
</ins><span class="cx">     return target &lt; 0 || target &gt; line.text.length ? null : target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -5800,14 +7336,16 @@
</span><span class="cx">   // objects) in the order in which they occur visually.
</span><span class="cx">   var bidiOrdering = (function() {
</span><span class="cx">     // Character types for codepoints 0 to 0xff
</span><del>-    var lowTypes = &quot;bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL&quot;;
</del><ins>+    var lowTypes = &quot;bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN&quot;;
</ins><span class="cx">     // Character types for codepoints 0x600 to 0x6ff
</span><del>-    var arabicTypes = &quot;rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr&quot;;
</del><ins>+    var arabicTypes = &quot;rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm&quot;;
</ins><span class="cx">     function charType(code) {
</span><del>-      if (code &lt;= 0xff) return lowTypes.charAt(code);
</del><ins>+      if (code &lt;= 0xf7) return lowTypes.charAt(code);
</ins><span class="cx">       else if (0x590 &lt;= code &amp;&amp; code &lt;= 0x5f4) return &quot;R&quot;;
</span><del>-      else if (0x600 &lt;= code &amp;&amp; code &lt;= 0x6ff) return arabicTypes.charAt(code - 0x600);
-      else if (0x700 &lt;= code &amp;&amp; code &lt;= 0x8ac) return &quot;r&quot;;
</del><ins>+      else if (0x600 &lt;= code &amp;&amp; code &lt;= 0x6ed) return arabicTypes.charAt(code - 0x600);
+      else if (0x6ee &lt;= code &amp;&amp; code &lt;= 0x8ac) return &quot;r&quot;;
+      else if (0x2000 &lt;= code &amp;&amp; code &lt;= 0x200b) return &quot;w&quot;;
+      else if (code == 0x200c) return &quot;b&quot;;
</ins><span class="cx">       else return &quot;L&quot;;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -5816,6 +7354,11 @@
</span><span class="cx">     // Browsers seem to always treat the boundaries of block elements as being L.
</span><span class="cx">     var outerType = &quot;L&quot;;
</span><span class="cx"> 
</span><ins>+    function BidiSpan(level, from, to) {
+      this.level = level;
+      this.from = from; this.to = to;
+    }
+
</ins><span class="cx">     return function(str) {
</span><span class="cx">       if (!bidiRE.test(str)) return false;
</span><span class="cx">       var len = str.length, types = [];
</span><span class="lines">@@ -5863,7 +7406,7 @@
</span><span class="cx">         if (type == &quot;,&quot;) types[i] = &quot;N&quot;;
</span><span class="cx">         else if (type == &quot;%&quot;) {
</span><span class="cx">           for (var end = i + 1; end &lt; len &amp;&amp; types[end] == &quot;%&quot;; ++end) {}
</span><del>-          var replace = (i &amp;&amp; types[i-1] == &quot;!&quot;) || (end &lt; len - 1 &amp;&amp; types[end] == &quot;1&quot;) ? &quot;1&quot; : &quot;N&quot;;
</del><ins>+          var replace = (i &amp;&amp; types[i-1] == &quot;!&quot;) || (end &lt; len &amp;&amp; types[end] == &quot;1&quot;) ? &quot;1&quot; : &quot;N&quot;;
</ins><span class="cx">           for (var j = i; j &lt; end; ++j) types[j] = replace;
</span><span class="cx">           i = end - 1;
</span><span class="cx">         }
</span><span class="lines">@@ -5888,7 +7431,7 @@
</span><span class="cx">         if (isNeutral.test(types[i])) {
</span><span class="cx">           for (var end = i + 1; end &lt; len &amp;&amp; isNeutral.test(types[end]); ++end) {}
</span><span class="cx">           var before = (i ? types[i-1] : outerType) == &quot;L&quot;;
</span><del>-          var after = (end &lt; len - 1 ? types[end] : outerType) == &quot;L&quot;;
</del><ins>+          var after = (end &lt; len ? types[end] : outerType) == &quot;L&quot;;
</ins><span class="cx">           var replace = before || after ? &quot;L&quot; : &quot;R&quot;;
</span><span class="cx">           for (var j = i; j &lt; end; ++j) types[j] = replace;
</span><span class="cx">           i = end - 1;
</span><span class="lines">@@ -5905,32 +7448,32 @@
</span><span class="cx">         if (countsAsLeft.test(types[i])) {
</span><span class="cx">           var start = i;
</span><span class="cx">           for (++i; i &lt; len &amp;&amp; countsAsLeft.test(types[i]); ++i) {}
</span><del>-          order.push({from: start, to: i, level: 0});
</del><ins>+          order.push(new BidiSpan(0, start, i));
</ins><span class="cx">         } else {
</span><span class="cx">           var pos = i, at = order.length;
</span><span class="cx">           for (++i; i &lt; len &amp;&amp; types[i] != &quot;L&quot;; ++i) {}
</span><span class="cx">           for (var j = pos; j &lt; i;) {
</span><span class="cx">             if (countsAsNum.test(types[j])) {
</span><del>-              if (pos &lt; j) order.splice(at, 0, {from: pos, to: j, level: 1});
</del><ins>+              if (pos &lt; j) order.splice(at, 0, new BidiSpan(1, pos, j));
</ins><span class="cx">               var nstart = j;
</span><span class="cx">               for (++j; j &lt; i &amp;&amp; countsAsNum.test(types[j]); ++j) {}
</span><del>-              order.splice(at, 0, {from: nstart, to: j, level: 2});
</del><ins>+              order.splice(at, 0, new BidiSpan(2, nstart, j));
</ins><span class="cx">               pos = j;
</span><span class="cx">             } else ++j;
</span><span class="cx">           }
</span><del>-          if (pos &lt; i) order.splice(at, 0, {from: pos, to: i, level: 1});
</del><ins>+          if (pos &lt; i) order.splice(at, 0, new BidiSpan(1, pos, i));
</ins><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">       if (order[0].level == 1 &amp;&amp; (m = str.match(/^\s+/))) {
</span><span class="cx">         order[0].from = m[0].length;
</span><del>-        order.unshift({from: 0, to: m[0].length, level: 0});
</del><ins>+        order.unshift(new BidiSpan(0, 0, m[0].length));
</ins><span class="cx">       }
</span><span class="cx">       if (lst(order).level == 1 &amp;&amp; (m = str.match(/\s+$/))) {
</span><span class="cx">         lst(order).to -= m[0].length;
</span><del>-        order.push({from: len - m[0].length, to: len, level: 0});
</del><ins>+        order.push(new BidiSpan(0, len - m[0].length, len));
</ins><span class="cx">       }
</span><span class="cx">       if (order[0].level != lst(order).level)
</span><del>-        order.push({from: len, to: len, level: order[0].level});
</del><ins>+        order.push(new BidiSpan(order[0].level, len, len));
</ins><span class="cx"> 
</span><span class="cx">       return order;
</span><span class="cx">     };
</span><span class="lines">@@ -5938,7 +7481,7 @@
</span><span class="cx"> 
</span><span class="cx">   // THE END
</span><span class="cx"> 
</span><del>-  CodeMirror.version = &quot;3.20.0&quot;;
</del><ins>+  CodeMirror.version = &quot;4.0.4&quot;;
</ins><span class="cx"> 
</span><span class="cx">   return CodeMirror;
</span><del>-})();
</del><ins>+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIToolsPrettyPrintingcssjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/css.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/css.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/css.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,89 +1,91 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;css&quot;, function(config, parserConfig) {
</span><del>-  &quot;use strict&quot;;
-
</del><span class="cx">   if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode(&quot;text/css&quot;);
</span><span class="cx"> 
</span><del>-  var indentUnit = config.indentUnit || config.tabSize || 2,
-      hooks = parserConfig.hooks || {},
-      atMediaTypes = parserConfig.atMediaTypes || {},
-      atMediaFeatures = parserConfig.atMediaFeatures || {},
</del><ins>+  var indentUnit = config.indentUnit,
+      tokenHooks = parserConfig.tokenHooks,
+      mediaTypes = parserConfig.mediaTypes || {},
+      mediaFeatures = parserConfig.mediaFeatures || {},
</ins><span class="cx">       propertyKeywords = parserConfig.propertyKeywords || {},
</span><ins>+      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
</ins><span class="cx">       colorKeywords = parserConfig.colorKeywords || {},
</span><span class="cx">       valueKeywords = parserConfig.valueKeywords || {},
</span><del>-      allowNested = !!parserConfig.allowNested,
-      type = null;
</del><ins>+      fontProperties = parserConfig.fontProperties || {},
+      allowNested = parserConfig.allowNested;
</ins><span class="cx"> 
</span><ins>+  var type, override;
</ins><span class="cx">   function ret(style, tp) { type = tp; return style; }
</span><span class="cx"> 
</span><ins>+  // Tokenizers
+
</ins><span class="cx">   function tokenBase(stream, state) {
</span><span class="cx">     var ch = stream.next();
</span><del>-    if (hooks[ch]) {
-      // result[0] is style and result[1] is type
-      var result = hooks[ch](stream, state);
</del><ins>+    if (tokenHooks[ch]) {
+      var result = tokenHooks[ch](stream, state);
</ins><span class="cx">       if (result !== false) return result;
</span><span class="cx">     }
</span><del>-    if (ch == &quot;@&quot;) {stream.eatWhile(/[\w\\\-]/); return ret(&quot;def&quot;, stream.current());}
-    else if (ch == &quot;=&quot;) ret(null, &quot;compare&quot;);
-    else if ((ch == &quot;~&quot; || ch == &quot;|&quot;) &amp;&amp; stream.eat(&quot;=&quot;)) return ret(null, &quot;compare&quot;);
-    else if (ch == &quot;\&quot;&quot; || ch == &quot;'&quot;) {
</del><ins>+    if (ch == &quot;@&quot;) {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret(&quot;def&quot;, stream.current());
+    } else if (ch == &quot;=&quot; || (ch == &quot;~&quot; || ch == &quot;|&quot;) &amp;&amp; stream.eat(&quot;=&quot;)) {
+      return ret(null, &quot;compare&quot;);
+    } else if (ch == &quot;\&quot;&quot; || ch == &quot;'&quot;) {
</ins><span class="cx">       state.tokenize = tokenString(ch);
</span><span class="cx">       return state.tokenize(stream, state);
</span><del>-    }
-    else if (ch == &quot;#&quot;) {
</del><ins>+    } else if (ch == &quot;#&quot;) {
</ins><span class="cx">       stream.eatWhile(/[\w\\\-]/);
</span><span class="cx">       return ret(&quot;atom&quot;, &quot;hash&quot;);
</span><del>-    }
-    else if (ch == &quot;!&quot;) {
</del><ins>+    } else if (ch == &quot;!&quot;) {
</ins><span class="cx">       stream.match(/^\s*\w*/);
</span><span class="cx">       return ret(&quot;keyword&quot;, &quot;important&quot;);
</span><del>-    }
-    else if (/\d/.test(ch) || ch == &quot;.&quot; &amp;&amp; stream.eat(/\d/)) {
</del><ins>+    } else if (/\d/.test(ch) || ch == &quot;.&quot; &amp;&amp; stream.eat(/\d/)) {
</ins><span class="cx">       stream.eatWhile(/[\w.%]/);
</span><span class="cx">       return ret(&quot;number&quot;, &quot;unit&quot;);
</span><del>-    }
-    else if (ch === &quot;-&quot;) {
-      if (/\d/.test(stream.peek())) {
</del><ins>+    } else if (ch === &quot;-&quot;) {
+      if (/[\d.]/.test(stream.peek())) {
</ins><span class="cx">         stream.eatWhile(/[\w.%]/);
</span><span class="cx">         return ret(&quot;number&quot;, &quot;unit&quot;);
</span><span class="cx">       } else if (stream.match(/^[^-]+-/)) {
</span><span class="cx">         return ret(&quot;meta&quot;, &quot;meta&quot;);
</span><span class="cx">       }
</span><del>-    }
-    else if (/[,+&gt;*\/]/.test(ch)) {
</del><ins>+    } else if (/[,+&gt;*\/]/.test(ch)) {
</ins><span class="cx">       return ret(null, &quot;select-op&quot;);
</span><del>-    }
-    else if (ch == &quot;.&quot; &amp;&amp; stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
</del><ins>+    } else if (ch == &quot;.&quot; &amp;&amp; stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
</ins><span class="cx">       return ret(&quot;qualifier&quot;, &quot;qualifier&quot;);
</span><del>-    }
-    else if (ch == &quot;:&quot;) {
-      return ret(&quot;operator&quot;, ch);
-    }
-    else if (/[;{}\[\]\(\)]/.test(ch)) {
</del><ins>+    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
</ins><span class="cx">       return ret(null, ch);
</span><del>-    }
-    else if (ch == &quot;u&quot; &amp;&amp; stream.match(&quot;rl(&quot;)) {
</del><ins>+    } else if (ch == &quot;u&quot; &amp;&amp; stream.match(&quot;rl(&quot;)) {
</ins><span class="cx">       stream.backUp(1);
</span><span class="cx">       state.tokenize = tokenParenthesized;
</span><del>-      return ret(&quot;property&quot;, &quot;variable&quot;);
-    }
-    else {
</del><ins>+      return ret(&quot;property&quot;, &quot;word&quot;);
+    } else if (/[\w\\\-]/.test(ch)) {
</ins><span class="cx">       stream.eatWhile(/[\w\\\-]/);
</span><del>-      return ret(&quot;property&quot;, &quot;variable&quot;);
</del><ins>+      return ret(&quot;property&quot;, &quot;word&quot;);
+    } else {
+      return ret(null, null);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function tokenString(quote, nonInclusive) {
</del><ins>+  function tokenString(quote) {
</ins><span class="cx">     return function(stream, state) {
</span><span class="cx">       var escaped = false, ch;
</span><span class="cx">       while ((ch = stream.next()) != null) {
</span><del>-        if (ch == quote &amp;&amp; !escaped)
</del><ins>+        if (ch == quote &amp;&amp; !escaped) {
+          if (quote == &quot;)&quot;) stream.backUp(1);
</ins><span class="cx">           break;
</span><ins>+        }
</ins><span class="cx">         escaped = !escaped &amp;&amp; ch == &quot;\\&quot;;
</span><span class="cx">       }
</span><del>-      if (!escaped) {
-        if (nonInclusive) stream.backUp(1);
-        state.tokenize = tokenBase;
-      }
</del><ins>+      if (ch == quote || !escaped &amp;&amp; quote != &quot;)&quot;) state.tokenize = null;
</ins><span class="cx">       return ret(&quot;string&quot;, &quot;string&quot;);
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="lines">@@ -91,218 +93,251 @@
</span><span class="cx">   function tokenParenthesized(stream, state) {
</span><span class="cx">     stream.next(); // Must be '('
</span><span class="cx">     if (!stream.match(/\s*[\&quot;\']/, false))
</span><del>-      state.tokenize = tokenString(&quot;)&quot;, true);
</del><ins>+      state.tokenize = tokenString(&quot;)&quot;);
</ins><span class="cx">     else
</span><del>-      state.tokenize = tokenBase;
</del><ins>+      state.tokenize = null;
</ins><span class="cx">     return ret(null, &quot;(&quot;);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  return {
-    startState: function(base) {
-      return {tokenize: tokenBase,
-              baseIndent: base || 0,
-              stack: [],
-              lastToken: null};
-    },
</del><ins>+  // Context management
</ins><span class="cx"> 
</span><del>-    token: function(stream, state) {
</del><ins>+  function Context(type, indent, prev) {
+    this.type = type;
+    this.indent = indent;
+    this.prev = prev;
+  }
</ins><span class="cx"> 
</span><del>-      // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
-      //
-      // rule** or **ruleset:
-      // A selector + braces combo, or an at-rule.
-      //
-      // declaration block:
-      // A sequence of declarations.
-      //
-      // declaration:
-      // A property + colon + value combo.
-      //
-      // property value:
-      // The entire value of a property.
-      //
-      // component value:
-      // A single piece of a property value. Like the 5px in
-      // text-shadow: 0 0 5px blue;. Can also refer to things that are
-      // multiple terms, like the 1-4 terms that make up the background-size
-      // portion of the background shorthand.
-      //
-      // term:
-      // The basic unit of author-facing CSS, like a single number (5),
-      // dimension (5px), string (&quot;foo&quot;), or function. Officially defined
-      //  by the CSS 2.1 grammar (look for the 'term' production)
-      //
-      //
-      // simple selector:
-      // A single atomic selector, like a type selector, an attr selector, a
-      // class selector, etc.
-      //
-      // compound selector:
-      // One or more simple selectors without a combinator. div.example is
-      // compound, div &gt; .example is not.
-      //
-      // complex selector:
-      // One or more compound selectors chained with combinators.
-      //
-      // combinator:
-      // The parts of selectors that express relationships. There are four
-      // currently - the space (descendant combinator), the greater-than
-      // bracket (child combinator), the plus sign (next sibling combinator),
-      // and the tilda (following sibling combinator).
-      //
-      // sequence of selectors:
-      // One or more of the named type of selector chained with commas.
</del><ins>+  function pushContext(state, stream, type) {
+    state.context = new Context(type, stream.indentation() + indentUnit, state.context);
+    return type;
+  }
</ins><span class="cx"> 
</span><del>-      state.tokenize = state.tokenize || tokenBase;
-      if (state.tokenize == tokenBase &amp;&amp; stream.eatSpace()) return null;
-      var style = state.tokenize(stream, state);
-      if (style &amp;&amp; typeof style != &quot;string&quot;) style = ret(style[0], style[1]);
</del><ins>+  function popContext(state) {
+    state.context = state.context.prev;
+    return state.context.type;
+  }
</ins><span class="cx"> 
</span><del>-      // Changing style returned based on context
-      var context = state.stack[state.stack.length-1];
-      if (style == &quot;variable&quot;) {
-        if (type == &quot;variable-definition&quot;) state.stack.push(&quot;propertyValue&quot;);
-        return state.lastToken = &quot;variable-2&quot;;
-      } else if (style == &quot;property&quot;) {
-        var word = stream.current().toLowerCase();
-        if (context == &quot;propertyValue&quot;) {
-          if (valueKeywords.hasOwnProperty(word)) {
-            style = &quot;string-2&quot;;
-          } else if (colorKeywords.hasOwnProperty(word)) {
-            style = &quot;keyword&quot;;
-          } else {
-            style = &quot;variable-2&quot;;
-          }
-        } else if (context == &quot;rule&quot;) {
-          if (!propertyKeywords.hasOwnProperty(word)) {
-            style += &quot; error&quot;;
-          }
-        } else if (context == &quot;block&quot;) {
-          // if a value is present in both property, value, or color, the order
-          // of preference is property -&gt; color -&gt; value
-          if (propertyKeywords.hasOwnProperty(word)) {
-            style = &quot;property&quot;;
-          } else if (colorKeywords.hasOwnProperty(word)) {
-            style = &quot;keyword&quot;;
-          } else if (valueKeywords.hasOwnProperty(word)) {
-            style = &quot;string-2&quot;;
-          } else {
-            style = &quot;tag&quot;;
-          }
-        } else if (!context || context == &quot;@media{&quot;) {
-          style = &quot;tag&quot;;
-        } else if (context == &quot;@media&quot;) {
-          if (atMediaTypes[stream.current()]) {
-            style = &quot;attribute&quot;; // Known attribute
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;keyword&quot;;
-          } else if (word == &quot;and&quot;) {
-            style = &quot;error&quot;; // &quot;and&quot; is only allowed in @mediaType
-          } else if (atMediaFeatures.hasOwnProperty(word)) {
-            style = &quot;error&quot;; // Known property, should be in @mediaType(
-          } else {
-            // Unknown, expecting keyword or attribute, assuming attribute
-            style = &quot;attribute error&quot;;
-          }
-        } else if (context == &quot;@mediaType&quot;) {
-          if (atMediaTypes.hasOwnProperty(word)) {
-            style = &quot;attribute&quot;;
-          } else if (word == &quot;and&quot;) {
-            style = &quot;operator&quot;;
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;error&quot;; // Only allowed in @media
-          } else {
-            // Unknown attribute or property, but expecting property (preceded
-            // by &quot;and&quot;). Should be in parentheses
-            style = &quot;error&quot;;
-          }
-        } else if (context == &quot;@mediaType(&quot;) {
-          if (propertyKeywords.hasOwnProperty(word)) {
-            // do nothing, remains &quot;property&quot;
-          } else if (atMediaTypes.hasOwnProperty(word)) {
-            style = &quot;error&quot;; // Known property, should be in parentheses
-          } else if (word == &quot;and&quot;) {
-            style = &quot;operator&quot;;
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;error&quot;; // Only allowed in @media
-          } else {
-            style += &quot; error&quot;;
-          }
-        } else if (context == &quot;@import&quot;) {
-          style = &quot;tag&quot;;
-        } else {
-          style = &quot;error&quot;;
-        }
-      } else if (style == &quot;atom&quot;) {
-        if(!context || context == &quot;@media{&quot; || context == &quot;block&quot;) {
-          style = &quot;builtin&quot;;
-        } else if (context == &quot;propertyValue&quot;) {
-          if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
-            style += &quot; error&quot;;
-          }
-        } else {
-          style = &quot;error&quot;;
-        }
-      } else if (context == &quot;@media&quot; &amp;&amp; type == &quot;{&quot;) {
-        style = &quot;error&quot;;
-      }
</del><ins>+  function pass(type, stream, state) {
+    return states[state.context.type](type, stream, state);
+  }
+  function popAndPass(type, stream, state, n) {
+    for (var i = n || 1; i &gt; 0; i--)
+      state.context = state.context.prev;
+    return pass(type, stream, state);
+  }
</ins><span class="cx"> 
</span><del>-      // Push/pop context stack
-      if (type == &quot;{&quot;) {
-        if (context == &quot;@media&quot; || context == &quot;@mediaType&quot;) {
-          state.stack[state.stack.length-1] = &quot;@media{&quot;;
-        }
-        else {
-          var newContext = allowNested ? &quot;block&quot; : &quot;rule&quot;;
-          state.stack.push(newContext);
-        }
</del><ins>+  // Parser
+
+  function wordAsValue(stream) {
+    var word = stream.current().toLowerCase();
+    if (valueKeywords.hasOwnProperty(word))
+      override = &quot;atom&quot;;
+    else if (colorKeywords.hasOwnProperty(word))
+      override = &quot;keyword&quot;;
+    else
+      override = &quot;variable&quot;;
+  }
+
+  var states = {};
+
+  states.top = function(type, stream, state) {
+    if (type == &quot;{&quot;) {
+      return pushContext(state, stream, &quot;block&quot;);
+    } else if (type == &quot;}&quot; &amp;&amp; state.context.prev) {
+      return popContext(state);
+    } else if (type == &quot;@media&quot;) {
+      return pushContext(state, stream, &quot;media&quot;);
+    } else if (type == &quot;@font-face&quot;) {
+      return &quot;font_face_before&quot;;
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+      return &quot;keyframes&quot;;
+    } else if (type &amp;&amp; type.charAt(0) == &quot;@&quot;) {
+      return pushContext(state, stream, &quot;at&quot;);
+    } else if (type == &quot;hash&quot;) {
+      override = &quot;builtin&quot;;
+    } else if (type == &quot;word&quot;) {
+      override = &quot;tag&quot;;
+    } else if (type == &quot;variable-definition&quot;) {
+      return &quot;maybeprop&quot;;
+    } else if (type == &quot;interpolation&quot;) {
+      return pushContext(state, stream, &quot;interpolation&quot;);
+    } else if (type == &quot;:&quot;) {
+      return &quot;pseudo&quot;;
+    } else if (allowNested &amp;&amp; type == &quot;(&quot;) {
+      return pushContext(state, stream, &quot;params&quot;);
+    }
+    return state.context.type;
+  };
+
+  states.block = function(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      var word = stream.current().toLowerCase();
+      if (propertyKeywords.hasOwnProperty(word)) {
+        override = &quot;property&quot;;
+        return &quot;maybeprop&quot;;
+      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+        override = &quot;string-2&quot;;
+        return &quot;maybeprop&quot;;
+      } else if (allowNested) {
+        override = stream.match(/^\s*:/, false) ? &quot;property&quot; : &quot;tag&quot;;
+        return &quot;block&quot;;
+      } else {
+        override += &quot; error&quot;;
+        return &quot;maybeprop&quot;;
</ins><span class="cx">       }
</span><del>-      else if (type == &quot;}&quot;) {
-        if (context == &quot;interpolation&quot;) style = &quot;operator&quot;;
-        // Pop off end of array until { is reached
-        while(state.stack.length){
-          var removed = state.stack.pop();
-          if(removed.indexOf(&quot;{&quot;) &gt; -1 || removed == &quot;block&quot; || removed == &quot;rule&quot;){
-            break;
-          }
-        }
-      }
-      else if (type == &quot;interpolation&quot;) state.stack.push(&quot;interpolation&quot;);
-      else if (type == &quot;@media&quot;) state.stack.push(&quot;@media&quot;);
-      else if (type == &quot;@import&quot;) state.stack.push(&quot;@import&quot;);
-      else if (context == &quot;@media&quot; &amp;&amp; /\b(keyword|attribute)\b/.test(style))
-        state.stack[state.stack.length-1] = &quot;@mediaType&quot;;
-      else if (context == &quot;@mediaType&quot; &amp;&amp; stream.current() == &quot;,&quot;)
-        state.stack[state.stack.length-1] = &quot;@media&quot;;
-      else if (type == &quot;(&quot;) {
-        if (context == &quot;@media&quot; || context == &quot;@mediaType&quot;) {
-          // Make sure @mediaType is used to avoid error on {
-          state.stack[state.stack.length-1] = &quot;@mediaType&quot;;
-          state.stack.push(&quot;@mediaType(&quot;);
-        }
-        else state.stack.push(&quot;(&quot;);
-      }
-      else if (type == &quot;)&quot;) {
-        // Pop off end of array until ( is reached
-        while(state.stack.length){
-          var removed = state.stack.pop();
-          if(removed.indexOf(&quot;(&quot;) &gt; -1){
-            break;
-          }
-        }
-      }
-      else if (type == &quot;:&quot; &amp;&amp; state.lastToken == &quot;property&quot;) state.stack.push(&quot;propertyValue&quot;);
-      else if (context == &quot;propertyValue&quot; &amp;&amp; type == &quot;;&quot;) state.stack.pop();
-      else if (context == &quot;@import&quot; &amp;&amp; type == &quot;;&quot;) state.stack.pop();
</del><ins>+    } else if (type == &quot;meta&quot;) {
+      return &quot;block&quot;;
+    } else if (!allowNested &amp;&amp; (type == &quot;hash&quot; || type == &quot;qualifier&quot;)) {
+      override = &quot;error&quot;;
+      return &quot;block&quot;;
+    } else {
+      return states.top(type, stream, state);
+    }
+  };
</ins><span class="cx"> 
</span><del>-      return state.lastToken = style;
</del><ins>+  states.maybeprop = function(type, stream, state) {
+    if (type == &quot;:&quot;) return pushContext(state, stream, &quot;prop&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.prop = function(type, stream, state) {
+    if (type == &quot;;&quot;) return popContext(state);
+    if (type == &quot;{&quot; &amp;&amp; allowNested) return pushContext(state, stream, &quot;propBlock&quot;);
+    if (type == &quot;}&quot; || type == &quot;{&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;(&quot;) return pushContext(state, stream, &quot;parens&quot;);
+
+    if (type == &quot;hash&quot; &amp;&amp; !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
+      override += &quot; error&quot;;
+    } else if (type == &quot;word&quot;) {
+      wordAsValue(stream);
+    } else if (type == &quot;interpolation&quot;) {
+      return pushContext(state, stream, &quot;interpolation&quot;);
+    }
+    return &quot;prop&quot;;
+  };
+
+  states.propBlock = function(type, _stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;word&quot;) { override = &quot;property&quot;; return &quot;maybeprop&quot;; }
+    return state.context.type;
+  };
+
+  states.parens = function(type, stream, state) {
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;)&quot;) return popContext(state);
+    return &quot;parens&quot;;
+  };
+
+  states.pseudo = function(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      override = &quot;variable-3&quot;;
+      return state.context.type;
+    }
+    return pass(type, stream, state);
+  };
+
+  states.media = function(type, stream, state) {
+    if (type == &quot;(&quot;) return pushContext(state, stream, &quot;media_parens&quot;);
+    if (type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;{&quot;) return popContext(state) &amp;&amp; pushContext(state, stream, allowNested ? &quot;block&quot; : &quot;top&quot;);
+
+    if (type == &quot;word&quot;) {
+      var word = stream.current().toLowerCase();
+      if (word == &quot;only&quot; || word == &quot;not&quot; || word == &quot;and&quot;)
+        override = &quot;keyword&quot;;
+      else if (mediaTypes.hasOwnProperty(word))
+        override = &quot;attribute&quot;;
+      else if (mediaFeatures.hasOwnProperty(word))
+        override = &quot;property&quot;;
+      else
+        override = &quot;error&quot;;
+    }
+    return state.context.type;
+  };
+
+  states.media_parens = function(type, stream, state) {
+    if (type == &quot;)&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state, 2);
+    return states.media(type, stream, state);
+  };
+
+  states.font_face_before = function(type, stream, state) {
+    if (type == &quot;{&quot;)
+      return pushContext(state, stream, &quot;font_face&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.font_face = function(type, stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;word&quot;) {
+      if (!fontProperties.hasOwnProperty(stream.current().toLowerCase()))
+        override = &quot;error&quot;;
+      else
+        override = &quot;property&quot;;
+      return &quot;maybeprop&quot;;
+    }
+    return &quot;font_face&quot;;
+  };
+
+  states.keyframes = function(type, stream, state) {
+    if (type == &quot;word&quot;) { override = &quot;variable&quot;; return &quot;keyframes&quot;; }
+    if (type == &quot;{&quot;) return pushContext(state, stream, &quot;top&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.at = function(type, stream, state) {
+    if (type == &quot;;&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;word&quot;) override = &quot;tag&quot;;
+    else if (type == &quot;hash&quot;) override = &quot;builtin&quot;;
+    return &quot;at&quot;;
+  };
+
+  states.interpolation = function(type, stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;;&quot;) return popAndPass(type, stream, state);
+    if (type != &quot;variable&quot;) override = &quot;error&quot;;
+    return &quot;interpolation&quot;;
+  };
+
+  states.params = function(type, stream, state) {
+    if (type == &quot;)&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;word&quot;) wordAsValue(stream);
+    return &quot;params&quot;;
+  };
+
+  return {
+    startState: function(base) {
+      return {tokenize: null,
+              state: &quot;top&quot;,
+              context: new Context(&quot;top&quot;, base || 0, null)};
</ins><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    token: function(stream, state) {
+      if (!state.tokenize &amp;&amp; stream.eatSpace()) return null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style &amp;&amp; typeof style == &quot;object&quot;) {
+        type = style[1];
+        style = style[0];
+      }
+      override = style;
+      state.state = states[state.state](type, stream, state);
+      return override;
+    },
+
</ins><span class="cx">     indent: function(state, textAfter) {
</span><del>-      var n = state.stack.length;
-      if (/^\}/.test(textAfter))
-        n -= state.stack[n-1] == &quot;propertyValue&quot; ? 2 : 1;
-      return state.baseIndent + n * indentUnit;
</del><ins>+      var cx = state.context, ch = textAfter &amp;&amp; textAfter.charAt(0);
+      var indent = cx.indent;
+      if (cx.type == &quot;prop&quot; &amp;&amp; ch == &quot;}&quot;) cx = cx.prev;
+      if (cx.prev &amp;&amp;
+          (ch == &quot;}&quot; &amp;&amp; (cx.type == &quot;block&quot; || cx.type == &quot;top&quot; || cx.type == &quot;interpolation&quot; || cx.type == &quot;font_face&quot;) ||
+           ch == &quot;)&quot; &amp;&amp; (cx.type == &quot;parens&quot; || cx.type == &quot;params&quot; || cx.type == &quot;media_parens&quot;) ||
+           ch == &quot;{&quot; &amp;&amp; (cx.type == &quot;at&quot; || cx.type == &quot;media&quot;))) {
+        indent = cx.indent - indentUnit;
+        cx = cx.prev;
+      }
+      return indent;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     electricChars: &quot;}&quot;,
</span><span class="lines">@@ -312,7 +347,6 @@
</span><span class="cx">   };
</span><span class="cx"> });
</span><span class="cx"> 
</span><del>-(function() {
</del><span class="cx">   function keySet(array) {
</span><span class="cx">     var keys = {};
</span><span class="cx">     for (var i = 0; i &lt; array.length; ++i) {
</span><span class="lines">@@ -321,12 +355,12 @@
</span><span class="cx">     return keys;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var atMediaTypes = keySet([
</del><ins>+  var mediaTypes_ = [
</ins><span class="cx">     &quot;all&quot;, &quot;aural&quot;, &quot;braille&quot;, &quot;handheld&quot;, &quot;print&quot;, &quot;projection&quot;, &quot;screen&quot;,
</span><span class="cx">     &quot;tty&quot;, &quot;tv&quot;, &quot;embossed&quot;
</span><del>-  ]);
</del><ins>+  ], mediaTypes = keySet(mediaTypes_);
</ins><span class="cx"> 
</span><del>-  var atMediaFeatures = keySet([
</del><ins>+  var mediaFeatures_ = [
</ins><span class="cx">     &quot;width&quot;, &quot;min-width&quot;, &quot;max-width&quot;, &quot;height&quot;, &quot;min-height&quot;, &quot;max-height&quot;,
</span><span class="cx">     &quot;device-width&quot;, &quot;min-device-width&quot;, &quot;max-device-width&quot;, &quot;device-height&quot;,
</span><span class="cx">     &quot;min-device-height&quot;, &quot;max-device-height&quot;, &quot;aspect-ratio&quot;,
</span><span class="lines">@@ -335,15 +369,15 @@
</span><span class="cx">     &quot;max-color&quot;, &quot;color-index&quot;, &quot;min-color-index&quot;, &quot;max-color-index&quot;,
</span><span class="cx">     &quot;monochrome&quot;, &quot;min-monochrome&quot;, &quot;max-monochrome&quot;, &quot;resolution&quot;,
</span><span class="cx">     &quot;min-resolution&quot;, &quot;max-resolution&quot;, &quot;scan&quot;, &quot;grid&quot;
</span><del>-  ]);
</del><ins>+  ], mediaFeatures = keySet(mediaFeatures_);
</ins><span class="cx"> 
</span><del>-  var propertyKeywords = keySet([
</del><ins>+  var propertyKeywords_ = [
</ins><span class="cx">     &quot;align-content&quot;, &quot;align-items&quot;, &quot;align-self&quot;, &quot;alignment-adjust&quot;,
</span><span class="cx">     &quot;alignment-baseline&quot;, &quot;anchor-point&quot;, &quot;animation&quot;, &quot;animation-delay&quot;,
</span><del>-    &quot;animation-direction&quot;, &quot;animation-duration&quot;, &quot;animation-iteration-count&quot;,
-    &quot;animation-name&quot;, &quot;animation-play-state&quot;, &quot;animation-timing-function&quot;,
-    &quot;appearance&quot;, &quot;azimuth&quot;, &quot;backface-visibility&quot;, &quot;background&quot;,
-    &quot;background-attachment&quot;, &quot;background-clip&quot;, &quot;background-color&quot;,
</del><ins>+    &quot;animation-direction&quot;, &quot;animation-duration&quot;, &quot;animation-fill-mode&quot;,
+    &quot;animation-iteration-count&quot;, &quot;animation-name&quot;, &quot;animation-play-state&quot;,
+    &quot;animation-timing-function&quot;, &quot;appearance&quot;, &quot;azimuth&quot;, &quot;backface-visibility&quot;,
+    &quot;background&quot;, &quot;background-attachment&quot;, &quot;background-clip&quot;, &quot;background-color&quot;,
</ins><span class="cx">     &quot;background-image&quot;, &quot;background-origin&quot;, &quot;background-position&quot;,
</span><span class="cx">     &quot;background-repeat&quot;, &quot;background-size&quot;, &quot;baseline-shift&quot;, &quot;binding&quot;,
</span><span class="cx">     &quot;bleed&quot;, &quot;bookmark-label&quot;, &quot;bookmark-level&quot;, &quot;bookmark-state&quot;,
</span><span class="lines">@@ -374,10 +408,11 @@
</span><span class="cx">     &quot;font-stretch&quot;, &quot;font-style&quot;, &quot;font-synthesis&quot;, &quot;font-variant&quot;,
</span><span class="cx">     &quot;font-variant-alternates&quot;, &quot;font-variant-caps&quot;, &quot;font-variant-east-asian&quot;,
</span><span class="cx">     &quot;font-variant-ligatures&quot;, &quot;font-variant-numeric&quot;, &quot;font-variant-position&quot;,
</span><del>-    &quot;font-weight&quot;, &quot;grid-cell&quot;, &quot;grid-column&quot;, &quot;grid-column-align&quot;,
-    &quot;grid-column-sizing&quot;, &quot;grid-column-span&quot;, &quot;grid-columns&quot;, &quot;grid-flow&quot;,
-    &quot;grid-row&quot;, &quot;grid-row-align&quot;, &quot;grid-row-sizing&quot;, &quot;grid-row-span&quot;,
-    &quot;grid-rows&quot;, &quot;grid-template-areas&quot;, &quot;hanging-punctuation&quot;, &quot;height&quot;, &quot;hyphens&quot;,
</del><ins>+    &quot;font-weight&quot;, &quot;grid&quot;, &quot;grid-area&quot;, &quot;grid-auto-columns&quot;, &quot;grid-auto-flow&quot;,
+    &quot;grid-auto-position&quot;, &quot;grid-auto-rows&quot;, &quot;grid-column&quot;, &quot;grid-column-end&quot;,
+    &quot;grid-column-start&quot;, &quot;grid-row&quot;, &quot;grid-row-end&quot;, &quot;grid-row-start&quot;,
+    &quot;grid-template&quot;, &quot;grid-template-areas&quot;, &quot;grid-template-columns&quot;,
+    &quot;grid-template-rows&quot;, &quot;hanging-punctuation&quot;, &quot;height&quot;, &quot;hyphens&quot;,
</ins><span class="cx">     &quot;icon&quot;, &quot;image-orientation&quot;, &quot;image-rendering&quot;, &quot;image-resolution&quot;,
</span><span class="cx">     &quot;inline-box-align&quot;, &quot;justify-content&quot;, &quot;left&quot;, &quot;letter-spacing&quot;,
</span><span class="cx">     &quot;line-break&quot;, &quot;line-height&quot;, &quot;line-stacking&quot;, &quot;line-stacking-ruby&quot;,
</span><span class="lines">@@ -414,7 +449,7 @@
</span><span class="cx">     &quot;vertical-align&quot;, &quot;visibility&quot;, &quot;voice-balance&quot;, &quot;voice-duration&quot;,
</span><span class="cx">     &quot;voice-family&quot;, &quot;voice-pitch&quot;, &quot;voice-range&quot;, &quot;voice-rate&quot;, &quot;voice-stress&quot;,
</span><span class="cx">     &quot;voice-volume&quot;, &quot;volume&quot;, &quot;white-space&quot;, &quot;widows&quot;, &quot;width&quot;, &quot;word-break&quot;,
</span><del>-    &quot;word-spacing&quot;, &quot;word-wrap&quot;, &quot;z-index&quot;, &quot;zoom&quot;,
</del><ins>+    &quot;word-spacing&quot;, &quot;word-wrap&quot;, &quot;z-index&quot;,
</ins><span class="cx">     // SVG-specific
</span><span class="cx">     &quot;clip-path&quot;, &quot;clip-rule&quot;, &quot;mask&quot;, &quot;enable-background&quot;, &quot;filter&quot;, &quot;flood-color&quot;,
</span><span class="cx">     &quot;flood-opacity&quot;, &quot;lighting-color&quot;, &quot;stop-color&quot;, &quot;stop-opacity&quot;, &quot;pointer-events&quot;,
</span><span class="lines">@@ -425,9 +460,17 @@
</span><span class="cx">     &quot;stroke-miterlimit&quot;, &quot;stroke-opacity&quot;, &quot;stroke-width&quot;, &quot;text-rendering&quot;,
</span><span class="cx">     &quot;baseline-shift&quot;, &quot;dominant-baseline&quot;, &quot;glyph-orientation-horizontal&quot;,
</span><span class="cx">     &quot;glyph-orientation-vertical&quot;, &quot;kerning&quot;, &quot;text-anchor&quot;, &quot;writing-mode&quot;
</span><del>-  ]);
</del><ins>+  ], propertyKeywords = keySet(propertyKeywords_);
</ins><span class="cx"> 
</span><del>-  var colorKeywords = keySet([
</del><ins>+  var nonStandardPropertyKeywords = [
+    &quot;scrollbar-arrow-color&quot;, &quot;scrollbar-base-color&quot;, &quot;scrollbar-dark-shadow-color&quot;,
+    &quot;scrollbar-face-color&quot;, &quot;scrollbar-highlight-color&quot;, &quot;scrollbar-shadow-color&quot;,
+    &quot;scrollbar-3d-light-color&quot;, &quot;scrollbar-track-color&quot;, &quot;shape-inside&quot;,
+    &quot;searchfield-cancel-button&quot;, &quot;searchfield-decoration&quot;, &quot;searchfield-results-button&quot;,
+    &quot;searchfield-results-decoration&quot;, &quot;zoom&quot;
+  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords);
+
+  var colorKeywords_ = [
</ins><span class="cx">     &quot;aliceblue&quot;, &quot;antiquewhite&quot;, &quot;aqua&quot;, &quot;aquamarine&quot;, &quot;azure&quot;, &quot;beige&quot;,
</span><span class="cx">     &quot;bisque&quot;, &quot;black&quot;, &quot;blanchedalmond&quot;, &quot;blue&quot;, &quot;blueviolet&quot;, &quot;brown&quot;,
</span><span class="cx">     &quot;burlywood&quot;, &quot;cadetblue&quot;, &quot;chartreuse&quot;, &quot;chocolate&quot;, &quot;coral&quot;, &quot;cornflowerblue&quot;,
</span><span class="lines">@@ -454,9 +497,9 @@
</span><span class="cx">     &quot;slateblue&quot;, &quot;slategray&quot;, &quot;snow&quot;, &quot;springgreen&quot;, &quot;steelblue&quot;, &quot;tan&quot;,
</span><span class="cx">     &quot;teal&quot;, &quot;thistle&quot;, &quot;tomato&quot;, &quot;turquoise&quot;, &quot;violet&quot;, &quot;wheat&quot;, &quot;white&quot;,
</span><span class="cx">     &quot;whitesmoke&quot;, &quot;yellow&quot;, &quot;yellowgreen&quot;
</span><del>-  ]);
</del><ins>+  ], colorKeywords = keySet(colorKeywords_);
</ins><span class="cx"> 
</span><del>-  var valueKeywords = keySet([
</del><ins>+  var valueKeywords_ = [
</ins><span class="cx">     &quot;above&quot;, &quot;absolute&quot;, &quot;activeborder&quot;, &quot;activecaption&quot;, &quot;afar&quot;,
</span><span class="cx">     &quot;after-white-space&quot;, &quot;ahead&quot;, &quot;alias&quot;, &quot;all&quot;, &quot;all-scroll&quot;, &quot;alternate&quot;,
</span><span class="cx">     &quot;always&quot;, &quot;amharic&quot;, &quot;amharic-abegede&quot;, &quot;antialiased&quot;, &quot;appworkspace&quot;,
</span><span class="lines">@@ -539,8 +582,17 @@
</span><span class="cx">     &quot;visibleStroke&quot;, &quot;visual&quot;, &quot;w-resize&quot;, &quot;wait&quot;, &quot;wave&quot;, &quot;wider&quot;,
</span><span class="cx">     &quot;window&quot;, &quot;windowframe&quot;, &quot;windowtext&quot;, &quot;x-large&quot;, &quot;x-small&quot;, &quot;xor&quot;,
</span><span class="cx">     &quot;xx-large&quot;, &quot;xx-small&quot;
</span><del>-  ]);
</del><ins>+  ], valueKeywords = keySet(valueKeywords_);
</ins><span class="cx"> 
</span><ins>+  var fontProperties_ = [
+    &quot;font-family&quot;, &quot;src&quot;, &quot;unicode-range&quot;, &quot;font-variant&quot;, &quot;font-feature-settings&quot;,
+    &quot;font-stretch&quot;, &quot;font-weight&quot;, &quot;font-style&quot;
+  ], fontProperties = keySet(fontProperties_);
+
+  var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_)
+    .concat(nonStandardPropertyKeywords).concat(colorKeywords_).concat(valueKeywords_);
+  CodeMirror.registerHelper(&quot;hintWords&quot;, &quot;css&quot;, allWords);
+
</ins><span class="cx">   function tokenCComment(stream, state) {
</span><span class="cx">     var maybeEnd = false, ch;
</span><span class="cx">     while ((ch = stream.next()) != null) {
</span><span class="lines">@@ -553,67 +605,90 @@
</span><span class="cx">     return [&quot;comment&quot;, &quot;comment&quot;];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function tokenSGMLComment(stream, state) {
+    if (stream.skipTo(&quot;--&gt;&quot;)) {
+      stream.match(&quot;--&gt;&quot;);
+      state.tokenize = null;
+    } else {
+      stream.skipToEnd();
+    }
+    return [&quot;comment&quot;, &quot;comment&quot;];
+  }
+
</ins><span class="cx">   CodeMirror.defineMIME(&quot;text/css&quot;, {
</span><del>-    atMediaTypes: atMediaTypes,
-    atMediaFeatures: atMediaFeatures,
</del><ins>+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
</ins><span class="cx">     propertyKeywords: propertyKeywords,
</span><ins>+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
</ins><span class="cx">     colorKeywords: colorKeywords,
</span><span class="cx">     valueKeywords: valueKeywords,
</span><del>-    hooks: {
</del><ins>+    fontProperties: fontProperties,
+    tokenHooks: {
</ins><span class="cx">       &quot;&lt;&quot;: function(stream, state) {
</span><del>-        function tokenSGMLComment(stream, state) {
-          var dashes = 0, ch;
-          while ((ch = stream.next()) != null) {
-            if (dashes &gt;= 2 &amp;&amp; ch == &quot;&gt;&quot;) {
-              state.tokenize = null;
-              break;
-            }
-            dashes = (ch == &quot;-&quot;) ? dashes + 1 : 0;
-          }
-          return [&quot;comment&quot;, &quot;comment&quot;];
-        }
-        if (stream.eat(&quot;!&quot;)) {
-          state.tokenize = tokenSGMLComment;
-          return tokenSGMLComment(stream, state);
-        }
</del><ins>+        if (!stream.match(&quot;!--&quot;)) return false;
+        state.tokenize = tokenSGMLComment;
+        return tokenSGMLComment(stream, state);
</ins><span class="cx">       },
</span><span class="cx">       &quot;/&quot;: function(stream, state) {
</span><del>-        if (stream.eat(&quot;*&quot;)) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        }
-        return false;
</del><ins>+        if (!stream.eat(&quot;*&quot;)) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
</ins><span class="cx">       }
</span><span class="cx">     },
</span><span class="cx">     name: &quot;css&quot;
</span><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/x-scss&quot;, {
</span><del>-    atMediaTypes: atMediaTypes,
-    atMediaFeatures: atMediaFeatures,
</del><ins>+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
</ins><span class="cx">     propertyKeywords: propertyKeywords,
</span><ins>+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
</ins><span class="cx">     colorKeywords: colorKeywords,
</span><span class="cx">     valueKeywords: valueKeywords,
</span><ins>+    fontProperties: fontProperties,
</ins><span class="cx">     allowNested: true,
</span><del>-    hooks: {
</del><ins>+    tokenHooks: {
+      &quot;/&quot;: function(stream, state) {
+        if (stream.eat(&quot;/&quot;)) {
+          stream.skipToEnd();
+          return [&quot;comment&quot;, &quot;comment&quot;];
+        } else if (stream.eat(&quot;*&quot;)) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return [&quot;operator&quot;, &quot;operator&quot;];
+        }
+      },
</ins><span class="cx">       &quot;:&quot;: function(stream) {
</span><del>-        if (stream.match(/\s*{/)) {
</del><ins>+        if (stream.match(/\s*{/))
</ins><span class="cx">           return [null, &quot;{&quot;];
</span><del>-        }
</del><span class="cx">         return false;
</span><span class="cx">       },
</span><span class="cx">       &quot;$&quot;: function(stream) {
</span><span class="cx">         stream.match(/^[\w-]+/);
</span><del>-        if (stream.peek() == &quot;:&quot;) {
-          return [&quot;variable&quot;, &quot;variable-definition&quot;];
-        }
-        return [&quot;variable&quot;, &quot;variable&quot;];
</del><ins>+        if (stream.match(/^\s*:/, false))
+          return [&quot;variable-2&quot;, &quot;variable-definition&quot;];
+        return [&quot;variable-2&quot;, &quot;variable&quot;];
</ins><span class="cx">       },
</span><del>-      &quot;,&quot;: function(stream, state) {
-        if (state.stack[state.stack.length - 1] == &quot;propertyValue&quot; &amp;&amp; stream.match(/^ *\$/, false)) {
-          return [&quot;operator&quot;, &quot;;&quot;];
-        }
-      },
</del><ins>+      &quot;#&quot;: function(stream) {
+        if (!stream.eat(&quot;{&quot;)) return false;
+        return [null, &quot;interpolation&quot;];
+      }
+    },
+    name: &quot;css&quot;,
+    helperType: &quot;scss&quot;
+  });
+
+  CodeMirror.defineMIME(&quot;text/x-less&quot;, {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    tokenHooks: {
</ins><span class="cx">       &quot;/&quot;: function(stream, state) {
</span><span class="cx">         if (stream.eat(&quot;/&quot;)) {
</span><span class="cx">           stream.skipToEnd();
</span><span class="lines">@@ -625,15 +700,19 @@
</span><span class="cx">           return [&quot;operator&quot;, &quot;operator&quot;];
</span><span class="cx">         }
</span><span class="cx">       },
</span><del>-      &quot;#&quot;: function(stream) {
-        if (stream.eat(&quot;{&quot;)) {
-          return [&quot;operator&quot;, &quot;interpolation&quot;];
-        } else {
-          stream.eatWhile(/[\w\\\-]/);
-          return [&quot;atom&quot;, &quot;hash&quot;];
-        }
</del><ins>+      &quot;@&quot;: function(stream) {
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return [&quot;variable-2&quot;, &quot;variable-definition&quot;];
+        return [&quot;variable-2&quot;, &quot;variable&quot;];
+      },
+      &quot;&amp;&quot;: function() {
+        return [&quot;atom&quot;, &quot;atom&quot;];
</ins><span class="cx">       }
</span><span class="cx">     },
</span><del>-    name: &quot;css&quot;
</del><ins>+    name: &quot;css&quot;,
+    helperType: &quot;less&quot;
</ins><span class="cx">   });
</span><del>-})();
</del><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIToolsPrettyPrintingjavascriptjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/javascript.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/javascript.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/javascript.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,9 +1,20 @@
</span><span class="cx"> // TODO actually recognize syntax of TypeScript constructs
</span><span class="cx"> 
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;javascript&quot;, function(config, parserConfig) {
</span><span class="cx">   var indentUnit = config.indentUnit;
</span><span class="cx">   var statementIndent = parserConfig.statementIndent;
</span><del>-  var jsonMode = parserConfig.json;
</del><ins>+  var jsonldMode = parserConfig.jsonld;
+  var jsonMode = parserConfig.json || jsonldMode;
</ins><span class="cx">   var isTS = parserConfig.typescript;
</span><span class="cx"> 
</span><span class="cx">   // Tokenizer
</span><span class="lines">@@ -15,7 +26,7 @@
</span><span class="cx"> 
</span><span class="cx">     var jsKeywords = {
</span><span class="cx">       &quot;if&quot;: kw(&quot;if&quot;), &quot;while&quot;: A, &quot;with&quot;: A, &quot;else&quot;: B, &quot;do&quot;: B, &quot;try&quot;: B, &quot;finally&quot;: B,
</span><del>-      &quot;return&quot;: C, &quot;break&quot;: C, &quot;continue&quot;: C, &quot;new&quot;: C, &quot;delete&quot;: C, &quot;throw&quot;: C,
</del><ins>+      &quot;return&quot;: C, &quot;break&quot;: C, &quot;continue&quot;: C, &quot;new&quot;: C, &quot;delete&quot;: C, &quot;throw&quot;: C, &quot;debugger&quot;: C,
</ins><span class="cx">       &quot;var&quot;: kw(&quot;var&quot;), &quot;const&quot;: kw(&quot;var&quot;), &quot;let&quot;: kw(&quot;var&quot;),
</span><span class="cx">       &quot;function&quot;: kw(&quot;function&quot;), &quot;catch&quot;: kw(&quot;catch&quot;),
</span><span class="cx">       &quot;for&quot;: kw(&quot;for&quot;), &quot;switch&quot;: kw(&quot;switch&quot;), &quot;case&quot;: kw(&quot;case&quot;), &quot;default&quot;: kw(&quot;default&quot;),
</span><span class="lines">@@ -53,15 +64,18 @@
</span><span class="cx">   }();
</span><span class="cx"> 
</span><span class="cx">   var isOperatorChar = /[+\-*&amp;%=&lt;&gt;!?|~^]/;
</span><ins>+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)&quot;/;
</ins><span class="cx"> 
</span><del>-  function nextUntilUnescaped(stream, end) {
-    var escaped = false, next;
</del><ins>+  function readRegexp(stream) {
+    var escaped = false, next, inSet = false;
</ins><span class="cx">     while ((next = stream.next()) != null) {
</span><del>-      if (next == end &amp;&amp; !escaped)
-        return false;
</del><ins>+      if (!escaped) {
+        if (next == &quot;/&quot; &amp;&amp; !inSet) return;
+        if (next == &quot;[&quot;) inSet = true;
+        else if (inSet &amp;&amp; next == &quot;]&quot;) inSet = false;
+      }
</ins><span class="cx">       escaped = !escaped &amp;&amp; next == &quot;\\&quot;;
</span><span class="cx">     }
</span><del>-    return escaped;
</del><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Used as scratch variables to communicate multiple values without
</span><span class="lines">@@ -83,7 +97,7 @@
</span><span class="cx">     } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
</span><span class="cx">       return ret(ch);
</span><span class="cx">     } else if (ch == &quot;=&quot; &amp;&amp; stream.eat(&quot;&gt;&quot;)) {
</span><del>-      return ret(&quot;=&gt;&quot;);
</del><ins>+      return ret(&quot;=&gt;&quot;, &quot;operator&quot;);
</ins><span class="cx">     } else if (ch == &quot;0&quot; &amp;&amp; stream.eat(/x/i)) {
</span><span class="cx">       stream.eatWhile(/[\da-f]/i);
</span><span class="cx">       return ret(&quot;number&quot;, &quot;number&quot;);
</span><span class="lines">@@ -99,12 +113,12 @@
</span><span class="cx">         return ret(&quot;comment&quot;, &quot;comment&quot;);
</span><span class="cx">       } else if (state.lastType == &quot;operator&quot; || state.lastType == &quot;keyword c&quot; ||
</span><span class="cx">                state.lastType == &quot;sof&quot; || /^[\[{}\(,;:]$/.test(state.lastType)) {
</span><del>-        nextUntilUnescaped(stream, &quot;/&quot;);
</del><ins>+        readRegexp(stream);
</ins><span class="cx">         stream.eatWhile(/[gimy]/); // 'y' is &quot;sticky&quot; option in Mozilla
</span><span class="cx">         return ret(&quot;regexp&quot;, &quot;string-2&quot;);
</span><span class="cx">       } else {
</span><span class="cx">         stream.eatWhile(isOperatorChar);
</span><del>-        return ret(&quot;operator&quot;, null, stream.current());
</del><ins>+        return ret(&quot;operator&quot;, &quot;operator&quot;, stream.current());
</ins><span class="cx">       }
</span><span class="cx">     } else if (ch == &quot;`&quot;) {
</span><span class="cx">       state.tokenize = tokenQuasi;
</span><span class="lines">@@ -114,7 +128,7 @@
</span><span class="cx">       return ret(&quot;error&quot;, &quot;error&quot;);
</span><span class="cx">     } else if (isOperatorChar.test(ch)) {
</span><span class="cx">       stream.eatWhile(isOperatorChar);
</span><del>-      return ret(&quot;operator&quot;, null, stream.current());
</del><ins>+      return ret(&quot;operator&quot;, &quot;operator&quot;, stream.current());
</ins><span class="cx">     } else {
</span><span class="cx">       stream.eatWhile(/[\w\$_]/);
</span><span class="cx">       var word = stream.current(), known = keywords.propertyIsEnumerable(word) &amp;&amp; keywords[word];
</span><span class="lines">@@ -125,8 +139,16 @@
</span><span class="cx"> 
</span><span class="cx">   function tokenString(quote) {
</span><span class="cx">     return function(stream, state) {
</span><del>-      if (!nextUntilUnescaped(stream, quote))
</del><ins>+      var escaped = false, next;
+      if (jsonldMode &amp;&amp; stream.peek() == &quot;@&quot; &amp;&amp; stream.match(isJsonldKeyword)){
</ins><span class="cx">         state.tokenize = tokenBase;
</span><ins>+        return ret(&quot;jsonld-keyword&quot;, &quot;meta&quot;);
+      }
+      while ((next = stream.next()) != null) {
+        if (next == quote &amp;&amp; !escaped) break;
+        escaped = !escaped &amp;&amp; next == &quot;\\&quot;;
+      }
+      if (!escaped) state.tokenize = tokenBase;
</ins><span class="cx">       return ret(&quot;string&quot;, &quot;string&quot;);
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="lines">@@ -189,7 +211,7 @@
</span><span class="cx"> 
</span><span class="cx">   // Parser
</span><span class="cx"> 
</span><del>-  var atomicTypes = {&quot;atom&quot;: true, &quot;number&quot;: true, &quot;variable&quot;: true, &quot;string&quot;: true, &quot;regexp&quot;: true, &quot;this&quot;: true};
</del><ins>+  var atomicTypes = {&quot;atom&quot;: true, &quot;number&quot;: true, &quot;variable&quot;: true, &quot;string&quot;: true, &quot;regexp&quot;: true, &quot;this&quot;: true, &quot;jsonld-keyword&quot;: true};
</ins><span class="cx"> 
</span><span class="cx">   function JSLexical(indented, column, type, align, prev, info) {
</span><span class="cx">     this.indented = indented;
</span><span class="lines">@@ -289,11 +311,12 @@
</span><span class="cx">   poplex.lex = true;
</span><span class="cx"> 
</span><span class="cx">   function expect(wanted) {
</span><del>-    return function(type) {
</del><ins>+    function exp(type) {
</ins><span class="cx">       if (type == wanted) return cont();
</span><span class="cx">       else if (wanted == &quot;;&quot;) return pass();
</span><del>-      else return cont(arguments.callee);
</del><ins>+      else return cont(exp);
</ins><span class="cx">     };
</span><ins>+    return exp;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function statement(type, value) {
</span><span class="lines">@@ -302,9 +325,13 @@
</span><span class="cx">     if (type == &quot;keyword b&quot;) return cont(pushlex(&quot;form&quot;), statement, poplex);
</span><span class="cx">     if (type == &quot;{&quot;) return cont(pushlex(&quot;}&quot;), block, poplex);
</span><span class="cx">     if (type == &quot;;&quot;) return cont();
</span><del>-    if (type == &quot;if&quot;) return cont(pushlex(&quot;form&quot;), expression, statement, poplex, maybeelse);
</del><ins>+    if (type == &quot;if&quot;) {
+      if (cx.state.lexical.info == &quot;else&quot; &amp;&amp; cx.state.cc[cx.state.cc.length - 1] == poplex)
+        cx.state.cc.pop()();
+      return cont(pushlex(&quot;form&quot;), expression, statement, poplex, maybeelse);
+    }
</ins><span class="cx">     if (type == &quot;function&quot;) return cont(functiondef);
</span><del>-    if (type == &quot;for&quot;) return cont(pushlex(&quot;form&quot;), forspec, poplex, statement, poplex);
</del><ins>+    if (type == &quot;for&quot;) return cont(pushlex(&quot;form&quot;), forspec, statement, poplex);
</ins><span class="cx">     if (type == &quot;variable&quot;) return cont(pushlex(&quot;stat&quot;), maybelabel);
</span><span class="cx">     if (type == &quot;switch&quot;) return cont(pushlex(&quot;form&quot;), expression, pushlex(&quot;}&quot;, &quot;switch&quot;), expect(&quot;{&quot;),
</span><span class="cx">                                       block, poplex, poplex);
</span><span class="lines">@@ -327,18 +354,18 @@
</span><span class="cx">   function expressionInner(type, noComma) {
</span><span class="cx">     if (cx.state.fatArrowAt == cx.stream.start) {
</span><span class="cx">       var body = noComma ? arrowBodyNoComma : arrowBody;
</span><del>-      if (type == &quot;(&quot;) return cont(pushcontext, commasep(pattern, &quot;)&quot;), expect(&quot;=&gt;&quot;), body, popcontext);
</del><ins>+      if (type == &quot;(&quot;) return cont(pushcontext, pushlex(&quot;)&quot;), commasep(pattern, &quot;)&quot;), poplex, expect(&quot;=&gt;&quot;), body, popcontext);
</ins><span class="cx">       else if (type == &quot;variable&quot;) return pass(pushcontext, pattern, expect(&quot;=&gt;&quot;), body, popcontext);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
</span><span class="cx">     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
</span><del>-    if (type == &quot;function&quot;) return cont(functiondef);
</del><ins>+    if (type == &quot;function&quot;) return cont(functiondef, maybeop);
</ins><span class="cx">     if (type == &quot;keyword c&quot;) return cont(noComma ? maybeexpressionNoComma : maybeexpression);
</span><span class="cx">     if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), maybeexpression, comprehension, expect(&quot;)&quot;), poplex, maybeop);
</span><span class="cx">     if (type == &quot;operator&quot; || type == &quot;spread&quot;) return cont(noComma ? expressionNoComma : expression);
</span><del>-    if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), expressionNoComma, maybeArrayComprehension, poplex, maybeop);
-    if (type == &quot;{&quot;) return cont(commasep(objprop, &quot;}&quot;), maybeop);
</del><ins>+    if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), arrayLiteral, poplex, maybeop);
+    if (type == &quot;{&quot;) return contCommasep(objprop, &quot;}&quot;, null, maybeop);
</ins><span class="cx">     return cont();
</span><span class="cx">   }
</span><span class="cx">   function maybeexpression(type) {
</span><span class="lines">@@ -365,12 +392,11 @@
</span><span class="cx">     }
</span><span class="cx">     if (type == &quot;quasi&quot;) { cx.cc.push(me); return quasi(value); }
</span><span class="cx">     if (type == &quot;;&quot;) return;
</span><del>-    if (type == &quot;(&quot;) return cont(commasep(expressionNoComma, &quot;)&quot;, &quot;call&quot;), me);
</del><ins>+    if (type == &quot;(&quot;) return contCommasep(expressionNoComma, &quot;)&quot;, &quot;call&quot;, me);
</ins><span class="cx">     if (type == &quot;.&quot;) return cont(property, me);
</span><span class="cx">     if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), maybeexpression, expect(&quot;]&quot;), poplex, me);
</span><span class="cx">   }
</span><span class="cx">   function quasi(value) {
</span><del>-    if (!value) debugger;
</del><span class="cx">     if (value.slice(value.length - 2) != &quot;${&quot;) return cont();
</span><span class="cx">     return cont(expression, continueQuasi);
</span><span class="cx">   }
</span><span class="lines">@@ -403,7 +429,7 @@
</span><span class="cx">       cx.marked = &quot;property&quot;;
</span><span class="cx">       if (value == &quot;get&quot; || value == &quot;set&quot;) return cont(getterSetter);
</span><span class="cx">     } else if (type == &quot;number&quot; || type == &quot;string&quot;) {
</span><del>-      cx.marked = type + &quot; property&quot;;
</del><ins>+      cx.marked = jsonldMode ? &quot;property&quot; : (type + &quot; property&quot;);
</ins><span class="cx">     } else if (type == &quot;[&quot;) {
</span><span class="cx">       return cont(expression, expect(&quot;]&quot;), afterprop);
</span><span class="cx">     }
</span><span class="lines">@@ -418,7 +444,7 @@
</span><span class="cx">     if (type == &quot;:&quot;) return cont(expressionNoComma);
</span><span class="cx">     if (type == &quot;(&quot;) return pass(functiondef);
</span><span class="cx">   }
</span><del>-  function commasep(what, end, info) {
</del><ins>+  function commasep(what, end) {
</ins><span class="cx">     function proceed(type) {
</span><span class="cx">       if (type == &quot;,&quot;) {
</span><span class="cx">         var lex = cx.state.lexical;
</span><span class="lines">@@ -430,10 +456,14 @@
</span><span class="cx">     }
</span><span class="cx">     return function(type) {
</span><span class="cx">       if (type == end) return cont();
</span><del>-      if (info === false) return pass(what, proceed);
-      return pass(pushlex(end, info), what, proceed, poplex);
</del><ins>+      return pass(what, proceed);
</ins><span class="cx">     };
</span><span class="cx">   }
</span><ins>+  function contCommasep(what, end, info) {
+    for (var i = 3; i &lt; arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
</ins><span class="cx">   function block(type) {
</span><span class="cx">     if (type == &quot;}&quot;) return cont();
</span><span class="cx">     return pass(statement, block);
</span><span class="lines">@@ -449,8 +479,8 @@
</span><span class="cx">   }
</span><span class="cx">   function pattern(type, value) {
</span><span class="cx">     if (type == &quot;variable&quot;) { register(value); return cont(); }
</span><del>-    if (type == &quot;[&quot;) return cont(commasep(pattern, &quot;]&quot;));
-    if (type == &quot;{&quot;) return cont(commasep(proppattern, &quot;}&quot;));
</del><ins>+    if (type == &quot;[&quot;) return contCommasep(pattern, &quot;]&quot;);
+    if (type == &quot;{&quot;) return contCommasep(proppattern, &quot;}&quot;);
</ins><span class="cx">   }
</span><span class="cx">   function proppattern(type, value) {
</span><span class="cx">     if (type == &quot;variable&quot; &amp;&amp; !cx.stream.match(/^\s*:/, false)) {
</span><span class="lines">@@ -467,10 +497,10 @@
</span><span class="cx">     if (type == &quot;,&quot;) return cont(vardef);
</span><span class="cx">   }
</span><span class="cx">   function maybeelse(type, value) {
</span><del>-    if (type == &quot;keyword b&quot; &amp;&amp; value == &quot;else&quot;) return cont(pushlex(&quot;form&quot;), statement, poplex);
</del><ins>+    if (type == &quot;keyword b&quot; &amp;&amp; value == &quot;else&quot;) return cont(pushlex(&quot;form&quot;, &quot;else&quot;), statement, poplex);
</ins><span class="cx">   }
</span><span class="cx">   function forspec(type) {
</span><del>-    if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), forspec1, expect(&quot;)&quot;));
</del><ins>+    if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), forspec1, expect(&quot;)&quot;), poplex);
</ins><span class="cx">   }
</span><span class="cx">   function forspec1(type) {
</span><span class="cx">     if (type == &quot;var&quot;) return cont(vardef, expect(&quot;;&quot;), forspec2);
</span><span class="lines">@@ -493,7 +523,7 @@
</span><span class="cx">   function functiondef(type, value) {
</span><span class="cx">     if (value == &quot;*&quot;) {cx.marked = &quot;keyword&quot;; return cont(functiondef);}
</span><span class="cx">     if (type == &quot;variable&quot;) {register(value); return cont(functiondef);}
</span><del>-    if (type == &quot;(&quot;) return cont(pushcontext, commasep(funarg, &quot;)&quot;), statement, popcontext);
</del><ins>+    if (type == &quot;(&quot;) return cont(pushcontext, pushlex(&quot;)&quot;), commasep(funarg, &quot;)&quot;), poplex, statement, popcontext);
</ins><span class="cx">   }
</span><span class="cx">   function funarg(type) {
</span><span class="cx">     if (type == &quot;spread&quot;) return cont(funarg);
</span><span class="lines">@@ -506,7 +536,7 @@
</span><span class="cx">     if (value == &quot;extends&quot;) return cont(expression);
</span><span class="cx">   }
</span><span class="cx">   function objlit(type) {
</span><del>-    if (type == &quot;{&quot;) return cont(commasep(objprop, &quot;}&quot;));
</del><ins>+    if (type == &quot;{&quot;) return contCommasep(objprop, &quot;}&quot;);
</ins><span class="cx">   }
</span><span class="cx">   function afterModule(type, value) {
</span><span class="cx">     if (type == &quot;string&quot;) return cont(statement);
</span><span class="lines">@@ -522,17 +552,21 @@
</span><span class="cx">     return pass(importSpec, maybeFrom);
</span><span class="cx">   }
</span><span class="cx">   function importSpec(type, value) {
</span><del>-    if (type == &quot;{&quot;) return cont(commasep(importSpec, &quot;}&quot;));
</del><ins>+    if (type == &quot;{&quot;) return contCommasep(importSpec, &quot;}&quot;);
</ins><span class="cx">     if (type == &quot;variable&quot;) register(value);
</span><span class="cx">     return cont();
</span><span class="cx">   }
</span><span class="cx">   function maybeFrom(_type, value) {
</span><span class="cx">     if (value == &quot;from&quot;) { cx.marked = &quot;keyword&quot;; return cont(expression); }
</span><span class="cx">   }
</span><ins>+  function arrayLiteral(type) {
+    if (type == &quot;]&quot;) return cont();
+    return pass(expressionNoComma, maybeArrayComprehension);
+  }
</ins><span class="cx">   function maybeArrayComprehension(type) {
</span><del>-    if (type == &quot;for&quot;) return pass(comprehension);
-    if (type == &quot;,&quot;) return cont(commasep(expressionNoComma, &quot;]&quot;, false));
-    return pass(commasep(expressionNoComma, &quot;]&quot;, false));
</del><ins>+    if (type == &quot;for&quot;) return pass(comprehension, expect(&quot;]&quot;));
+    if (type == &quot;,&quot;) return cont(commasep(expressionNoComma, &quot;]&quot;));
+    return pass(commasep(expressionNoComma, &quot;]&quot;));
</ins><span class="cx">   }
</span><span class="cx">   function comprehension(type) {
</span><span class="cx">     if (type == &quot;for&quot;) return cont(forspec, comprehension);
</span><span class="lines">@@ -552,7 +586,8 @@
</span><span class="cx">         context: parserConfig.localVars &amp;&amp; {vars: parserConfig.localVars},
</span><span class="cx">         indented: 0
</span><span class="cx">       };
</span><del>-      if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars;
</del><ins>+      if (parserConfig.globalVars &amp;&amp; typeof parserConfig.globalVars == &quot;object&quot;)
+        state.globalVars = parserConfig.globalVars;
</ins><span class="cx">       return state;
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -575,7 +610,7 @@
</span><span class="cx">       if (state.tokenize != tokenBase) return 0;
</span><span class="cx">       var firstChar = textAfter &amp;&amp; textAfter.charAt(0), lexical = state.lexical;
</span><span class="cx">       // Kludge to prevent 'maybelse' from blocking lexical scope pops
</span><del>-      for (var i = state.cc.length - 1; i &gt;= 0; --i) {
</del><ins>+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i &gt;= 0; --i) {
</ins><span class="cx">         var c = state.cc[i];
</span><span class="cx">         if (c == poplex) lexical = lexical.prev;
</span><span class="cx">         else if (c != maybeelse) break;
</span><span class="lines">@@ -603,6 +638,7 @@
</span><span class="cx">     fold: &quot;brace&quot;,
</span><span class="cx"> 
</span><span class="cx">     helperType: jsonMode ? &quot;json&quot; : &quot;javascript&quot;,
</span><ins>+    jsonldMode: jsonldMode,
</ins><span class="cx">     jsonMode: jsonMode
</span><span class="cx">   };
</span><span class="cx"> });
</span><span class="lines">@@ -613,5 +649,8 @@
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/ecmascript&quot;, &quot;javascript&quot;);
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/json&quot;, {name: &quot;javascript&quot;, json: true});
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/x-json&quot;, {name: &quot;javascript&quot;, json: true});
</span><ins>+CodeMirror.defineMIME(&quot;application/ld+json&quot;, {name: &quot;javascript&quot;, jsonld: true});
</ins><span class="cx"> CodeMirror.defineMIME(&quot;text/typescript&quot;, { name: &quot;javascript&quot;, typescript: true });
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/typescript&quot;, { name: &quot;javascript&quot;, typescript: true });
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorclojurejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/clojure.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/clojure.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/clojure.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -2,10 +2,22 @@
</span><span class="cx">  * Author: Hans Engel
</span><span class="cx">  * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
</span><span class="cx">  */
</span><del>-CodeMirror.defineMode(&quot;clojure&quot;, function () {
</del><ins>+
+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
+CodeMirror.defineMode(&quot;clojure&quot;, function (options) {
</ins><span class="cx">     var BUILTIN = &quot;builtin&quot;, COMMENT = &quot;comment&quot;, STRING = &quot;string&quot;, CHARACTER = &quot;string-2&quot;,
</span><span class="cx">         ATOM = &quot;atom&quot;, NUMBER = &quot;number&quot;, BRACKET = &quot;bracket&quot;, KEYWORD = &quot;keyword&quot;;
</span><del>-    var INDENT_WORD_SKIP = 2;
</del><ins>+    var INDENT_WORD_SKIP = options.indentUnit || 2;
+    var NORMAL_INDENT_UNIT = options.indentUnit || 2;
</ins><span class="cx"> 
</span><span class="cx">     function makeKeywords(str) {
</span><span class="cx">         var obj = {}, words = str.split(&quot; &quot;);
</span><span class="lines">@@ -44,7 +56,7 @@
</span><span class="cx">         sign: /[+-]/,
</span><span class="cx">         exponent: /e/i,
</span><span class="cx">         keyword_char: /[^\s\(\[\;\)\]]/,
</span><del>-        symbol: /[\w*+!\-\._?:\/]/
</del><ins>+        symbol: /[\w*+!\-\._?:&lt;&gt;\/]/
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     function stateStack(indent, type, prev) { // represents a state stack object
</span><span class="lines">@@ -179,8 +191,8 @@
</span><span class="cx">                             stream.eatSpace();
</span><span class="cx">                             if (stream.eol() || stream.peek() == &quot;;&quot;) {
</span><span class="cx">                                 // nothing significant after
</span><del>-                                // we restart indentation 1 space after
-                                pushStack(state, indentTemp + 1, ch);
</del><ins>+                                // we restart indentation the user defined spaces after
+                                pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
</ins><span class="cx">                             } else {
</span><span class="cx">                                 pushStack(state, indentTemp + stream.current().length, ch); // else we match
</span><span class="cx">                             }
</span><span class="lines">@@ -222,3 +234,5 @@
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> CodeMirror.defineMIME(&quot;text/x-clojure&quot;, &quot;clojure&quot;);
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorclosebracketsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/closebrackets.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/closebrackets.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/closebrackets.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,8 +1,17 @@
</span><del>-(function() {
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
</ins><span class="cx">   var DEFAULT_BRACKETS = &quot;()[]{}''\&quot;\&quot;&quot;;
</span><span class="cx">   var DEFAULT_EXPLODE_ON_ENTER = &quot;[]{}&quot;;
</span><span class="cx">   var SPACE_CHAR_REGEX = /\s/;
</span><span class="cx"> 
</span><ins>+  var Pos = CodeMirror.Pos;
+
</ins><span class="cx">   CodeMirror.defineOption(&quot;autoCloseBrackets&quot;, false, function(cm, val, old) {
</span><span class="cx">     if (old != CodeMirror.Init &amp;&amp; old)
</span><span class="cx">       cm.removeKeyMap(&quot;autoCloseBrackets&quot;);
</span><span class="lines">@@ -19,8 +28,8 @@
</span><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">   function charsAround(cm, pos) {
</span><del>-    var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1),
-                          CodeMirror.Pos(pos.line, pos.ch + 1));
</del><ins>+    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+                          Pos(pos.line, pos.ch + 1));
</ins><span class="cx">     return str.length == 2 ? str : null;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -28,55 +37,104 @@
</span><span class="cx">     var map = {
</span><span class="cx">       name : &quot;autoCloseBrackets&quot;,
</span><span class="cx">       Backspace: function(cm) {
</span><del>-        if (cm.somethingSelected()) return CodeMirror.Pass;
-        var cur = cm.getCursor(), around = charsAround(cm, cur);
-        if (around &amp;&amp; pairs.indexOf(around) % 2 == 0)
-          cm.replaceRange(&quot;&quot;, CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
-        else
-          return CodeMirror.Pass;
</del><ins>+        if (cm.getOption(&quot;disableInput&quot;)) return CodeMirror.Pass;
+        var ranges = cm.listSelections();
+        for (var i = 0; i &lt; ranges.length; i++) {
+          if (!ranges[i].empty()) return CodeMirror.Pass;
+          var around = charsAround(cm, ranges[i].head);
+          if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+        }
+        for (var i = ranges.length - 1; i &gt;= 0; i--) {
+          var cur = ranges[i].head;
+          cm.replaceRange(&quot;&quot;, Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+        }
</ins><span class="cx">       }
</span><span class="cx">     };
</span><span class="cx">     var closingBrackets = &quot;&quot;;
</span><span class="cx">     for (var i = 0; i &lt; pairs.length; i += 2) (function(left, right) {
</span><span class="cx">       if (left != right) closingBrackets += right;
</span><del>-      function surround(cm) {
-        var selection = cm.getSelection();
-        cm.replaceSelection(left + selection + right);
-      }
-      function maybeOverwrite(cm) {
-        var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
-        if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass;
-        else cm.execCommand(&quot;goCharRight&quot;);
-      }
</del><span class="cx">       map[&quot;'&quot; + left + &quot;'&quot;] = function(cm) {
</span><del>-        if (left == &quot;'&quot; &amp;&amp; cm.getTokenAt(cm.getCursor()).type == &quot;comment&quot;)
-          return CodeMirror.Pass;
-        if (cm.somethingSelected()) return surround(cm);
-        if (left == right &amp;&amp; maybeOverwrite(cm) != CodeMirror.Pass) return;
-        var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
-        var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch &gt; 0 ? line.charAt(cur.ch - 1) : &quot;&quot;;
-        if (left == right &amp;&amp; CodeMirror.isWordChar(curChar))
-          return CodeMirror.Pass;
-        if (line.length == cur.ch || closingBrackets.indexOf(nextChar) &gt;= 0 || SPACE_CHAR_REGEX.test(nextChar))
-          cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
-        else
-          return CodeMirror.Pass;
</del><ins>+        if (cm.getOption(&quot;disableInput&quot;)) return CodeMirror.Pass;
+        var ranges = cm.listSelections(), type, next;
+        for (var i = 0; i &lt; ranges.length; i++) {
+          var range = ranges[i], cur = range.head, curType;
+          if (left == &quot;'&quot; &amp;&amp; cm.getTokenTypeAt(cur) == &quot;comment&quot;)
+            return CodeMirror.Pass;
+          var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+          if (!range.empty())
+            curType = &quot;surround&quot;;
+          else if (left == right &amp;&amp; next == right) {
+            if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left)
+              curType = &quot;skipThree&quot;;
+            else
+              curType = &quot;skip&quot;;
+          } else if (left == right &amp;&amp; cur.ch &gt; 1 &amp;&amp;
+                     cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left &amp;&amp;
+                     (cur.ch &lt;= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left))
+            curType = &quot;addFour&quot;;
+          else if (left == right &amp;&amp; CodeMirror.isWordChar(next))
+            return CodeMirror.Pass;
+          else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) &gt;= 0 || SPACE_CHAR_REGEX.test(next))
+            curType = &quot;both&quot;;
+          else
+            return CodeMirror.Pass;
+          if (!type) type = curType;
+          else if (type != curType) return CodeMirror.Pass;
+        }
+
+        cm.operation(function() {
+          if (type == &quot;skip&quot;) {
+            cm.execCommand(&quot;goCharRight&quot;);
+          } else if (type == &quot;skipThree&quot;) {
+            for (var i = 0; i &lt; 3; i++)
+              cm.execCommand(&quot;goCharRight&quot;);
+          } else if (type == &quot;surround&quot;) {
+            var sels = cm.getSelections();
+            for (var i = 0; i &lt; sels.length; i++)
+              sels[i] = left + sels[i] + right;
+            cm.replaceSelections(sels, &quot;around&quot;);
+          } else if (type == &quot;both&quot;) {
+            cm.replaceSelection(left + right, null);
+            cm.execCommand(&quot;goCharLeft&quot;);
+          } else if (type == &quot;addFour&quot;) {
+            cm.replaceSelection(left + left + left + left, &quot;before&quot;);
+            cm.execCommand(&quot;goCharRight&quot;);
+          }
+        });
</ins><span class="cx">       };
</span><del>-      if (left != right) map[&quot;'&quot; + right + &quot;'&quot;] = maybeOverwrite;
</del><ins>+      if (left != right) map[&quot;'&quot; + right + &quot;'&quot;] = function(cm) {
+        var ranges = cm.listSelections();
+        for (var i = 0; i &lt; ranges.length; i++) {
+          var range = ranges[i];
+          if (!range.empty() ||
+              cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right)
+            return CodeMirror.Pass;
+        }
+        cm.execCommand(&quot;goCharRight&quot;);
+      };
</ins><span class="cx">     })(pairs.charAt(i), pairs.charAt(i + 1));
</span><span class="cx">     return map;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function buildExplodeHandler(pairs) {
</span><span class="cx">     return function(cm) {
</span><del>-      var cur = cm.getCursor(), around = charsAround(cm, cur);
-      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
</del><ins>+      if (cm.getOption(&quot;disableInput&quot;)) return CodeMirror.Pass;
+      var ranges = cm.listSelections();
+      for (var i = 0; i &lt; ranges.length; i++) {
+        if (!ranges[i].empty()) return CodeMirror.Pass;
+        var around = charsAround(cm, ranges[i].head);
+        if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+      }
</ins><span class="cx">       cm.operation(function() {
</span><del>-        var newPos = CodeMirror.Pos(cur.line + 1, 0);
-        cm.replaceSelection(&quot;\n\n&quot;, {anchor: newPos, head: newPos}, &quot;+input&quot;);
-        cm.indentLine(cur.line + 1, null, true);
-        cm.indentLine(cur.line + 2, null, true);
</del><ins>+        cm.replaceSelection(&quot;\n\n&quot;, null);
+        cm.execCommand(&quot;goCharLeft&quot;);
+        ranges = cm.listSelections();
+        for (var i = 0; i &lt; ranges.length; i++) {
+          var line = ranges[i].head.line;
+          cm.indentLine(line, null, true);
+          cm.indentLine(line + 1, null, true);
+        }
</ins><span class="cx">       });
</span><span class="cx">     };
</span><span class="cx">   }
</span><del>-})();
</del><ins>+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcodemirrorcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.css (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.css        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.css        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -36,13 +36,14 @@
</span><span class="cx">   min-width: 20px;
</span><span class="cx">   text-align: right;
</span><span class="cx">   color: #999;
</span><ins>+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /* CURSOR */
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror div.CodeMirror-cursor {
</span><span class="cx">   border-left: 1px solid black;
</span><del>-  z-index: 3;
</del><span class="cx"> }
</span><span class="cx"> /* Shown when moving in bi-directional text */
</span><span class="cx"> .CodeMirror div.CodeMirror-secondarycursor {
</span><span class="lines">@@ -52,24 +53,29 @@
</span><span class="cx">   width: auto;
</span><span class="cx">   border: 0;
</span><span class="cx">   background: #7e7;
</span><del>-  z-index: 1;
</del><span class="cx"> }
</span><span class="cx"> /* Can style cursor different in overwrite (non-insert) mode */
</span><del>-.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
</del><ins>+div.CodeMirror-overwrite div.CodeMirror-cursor {}
</ins><span class="cx"> 
</span><span class="cx"> .cm-tab { display: inline-block; }
</span><span class="cx"> 
</span><ins>+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  position: absolute;
+}
+
</ins><span class="cx"> /* DEFAULT THEME */
</span><span class="cx"> 
</span><span class="cx"> .cm-s-default .cm-keyword {color: #708;}
</span><span class="cx"> .cm-s-default .cm-atom {color: #219;}
</span><span class="cx"> .cm-s-default .cm-number {color: #164;}
</span><span class="cx"> .cm-s-default .cm-def {color: #00f;}
</span><del>-.cm-s-default .cm-variable {color: black;}
</del><ins>+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
</ins><span class="cx"> .cm-s-default .cm-variable-2 {color: #05a;}
</span><span class="cx"> .cm-s-default .cm-variable-3 {color: #085;}
</span><del>-.cm-s-default .cm-property {color: black;}
-.cm-s-default .cm-operator {color: black;}
</del><span class="cx"> .cm-s-default .cm-comment {color: #a50;}
</span><span class="cx"> .cm-s-default .cm-string {color: #a11;}
</span><span class="cx"> .cm-s-default .cm-string-2 {color: #f50;}
</span><span class="lines">@@ -114,7 +120,7 @@
</span><span class="cx">   /* 30px is the magic margin used to hide the element's real scrollbars */
</span><span class="cx">   /* See overflow: hidden in .CodeMirror */
</span><span class="cx">   margin-bottom: -30px; margin-right: -30px;
</span><del>-  padding-bottom: 30px; padding-right: 30px;
</del><ins>+  padding-bottom: 30px;
</ins><span class="cx">   height: 100%;
</span><span class="cx">   outline: none; /* Prevent dragging from highlighting the element */
</span><span class="cx">   position: relative;
</span><span class="lines">@@ -123,6 +129,9 @@
</span><span class="cx"> }
</span><span class="cx"> .CodeMirror-sizer {
</span><span class="cx">   position: relative;
</span><ins>+  border-right: 30px solid transparent;
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /* The fake, visible scrollbars. Used to force redraw during scrolling
</span><span class="lines">@@ -197,16 +206,7 @@
</span><span class="cx">   white-space: pre-wrap;
</span><span class="cx">   word-break: normal;
</span><span class="cx"> }
</span><del>-.CodeMirror-code pre {
-  border-right: 30px solid transparent;
-  width: -webkit-fit-content;
-  width: -moz-fit-content;
-  width: fit-content;
-}
-.CodeMirror-wrap .CodeMirror-code pre {
-  border-right: none;
-  width: auto;
-}
</del><ins>+
</ins><span class="cx"> .CodeMirror-linebackground {
</span><span class="cx">   position: absolute;
</span><span class="cx">   left: 0; right: 0; top: 0; bottom: 0;
</span><span class="lines">@@ -236,16 +236,22 @@
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror div.CodeMirror-cursor {
</span><span class="cx">   position: absolute;
</span><del>-  visibility: hidden;
</del><span class="cx">   border-right: none;
</span><span class="cx">   width: 0;
</span><span class="cx"> }
</span><del>-.CodeMirror-focused div.CodeMirror-cursor {
</del><ins>+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 1;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
</ins><span class="cx">   visibility: visible;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .CodeMirror-selected { background: #d9d9d9; }
</span><span class="cx"> .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
</span><ins>+.CodeMirror-crosshair { cursor: crosshair; }
</ins><span class="cx"> 
</span><span class="cx"> .cm-searching {
</span><span class="cx">   background: #ffa;
</span><span class="lines">@@ -255,9 +261,12 @@
</span><span class="cx"> /* IE7 hack to prevent it from returning funny offsetTops on the spans */
</span><span class="cx"> .CodeMirror span { *vertical-align: text-bottom; }
</span><span class="cx"> 
</span><ins>+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
</ins><span class="cx"> @media print {
</span><span class="cx">   /* Hide the cursor when printing */
</span><del>-  .CodeMirror div.CodeMirror-cursor {
</del><ins>+  .CodeMirror div.CodeMirror-cursors {
</ins><span class="cx">     visibility: hidden;
</span><span class="cx">   }
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcodemirrorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/codemirror.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,23 +1,36 @@
</span><del>-// CodeMirror is the only global var we claim
-window.CodeMirror = (function() {
</del><ins>+// This is CodeMirror (http://codemirror.net), a code editor
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
+
+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    module.exports = mod();
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    return define([], mod);
+  else // Plain browser env
+    this.CodeMirror = mod();
+})(function() {
</ins><span class="cx">   &quot;use strict&quot;;
</span><span class="cx"> 
</span><span class="cx">   // BROWSER SNIFFING
</span><span class="cx"> 
</span><del>-  // Crude, but necessary to handle a number of hard-to-feature-detect
-  // bugs and behavior differences.
</del><ins>+  // Kludges for bugs and behavior differences that can't be feature
+  // detected are enabled based on userAgent etc sniffing.
+
</ins><span class="cx">   var gecko = /gecko\/\d/i.test(navigator.userAgent);
</span><del>-  // IE11 currently doesn't count as 'ie', since it has almost none of
-  // the same bugs as earlier versions. Use ie_gt10 to handle
-  // incompatibilities in that version.
-  var ie = /MSIE \d/.test(navigator.userAgent);
-  var ie_lt8 = ie &amp;&amp; (document.documentMode == null || document.documentMode &lt; 8);
-  var ie_lt9 = ie &amp;&amp; (document.documentMode == null || document.documentMode &lt; 9);
-  var ie_gt10 = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
</del><ins>+  // ie_uptoN means Internet Explorer version N or lower
+  var ie_upto10 = /MSIE \d/.test(navigator.userAgent);
+  var ie_upto7 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 8);
+  var ie_upto8 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 9);
+  var ie_upto9 = ie_upto10 &amp;&amp; (document.documentMode == null || document.documentMode &lt; 10);
+  var ie_11up = /Trident\/([7-9]|\d{2,})\./.test(navigator.userAgent);
+  var ie = ie_upto10 || ie_11up;
</ins><span class="cx">   var webkit = /WebKit\//.test(navigator.userAgent);
</span><span class="cx">   var qtwebkit = webkit &amp;&amp; /Qt\/\d+\.\d+/.test(navigator.userAgent);
</span><span class="cx">   var chrome = /Chrome\//.test(navigator.userAgent);
</span><del>-  var opera = /Opera\//.test(navigator.userAgent);
</del><ins>+  var presto = /Opera\//.test(navigator.userAgent);
</ins><span class="cx">   var safari = /Apple Computer/.test(navigator.vendor);
</span><span class="cx">   var khtml = /KHTML\//.test(navigator.userAgent);
</span><span class="cx">   var mac_geLion = /Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent);
</span><span class="lines">@@ -30,151 +43,181 @@
</span><span class="cx">   var mac = ios || /Mac/.test(navigator.platform);
</span><span class="cx">   var windows = /win/i.test(navigator.platform);
</span><span class="cx"> 
</span><del>-  var opera_version = opera &amp;&amp; navigator.userAgent.match(/Version\/(\d*\.\d*)/);
-  if (opera_version) opera_version = Number(opera_version[1]);
-  if (opera_version &amp;&amp; opera_version &gt;= 15) { opera = false; webkit = true; }
</del><ins>+  var presto_version = presto &amp;&amp; navigator.userAgent.match(/Version\/(\d*\.\d*)/);
+  if (presto_version) presto_version = Number(presto_version[1]);
+  if (presto_version &amp;&amp; presto_version &gt;= 15) { presto = false; webkit = true; }
</ins><span class="cx">   // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
</span><del>-  var flipCtrlCmd = mac &amp;&amp; (qtwebkit || opera &amp;&amp; (opera_version == null || opera_version &lt; 12.11));
-  var captureMiddleClick = gecko || (ie &amp;&amp; !ie_lt9);
</del><ins>+  var flipCtrlCmd = mac &amp;&amp; (qtwebkit || presto &amp;&amp; (presto_version == null || presto_version &lt; 12.11));
+  var captureRightClick = gecko || (ie &amp;&amp; !ie_upto8);
</ins><span class="cx"> 
</span><del>-  // Optimize some code when these features are not used
</del><ins>+  // Optimize some code when these features are not used.
</ins><span class="cx">   var sawReadOnlySpans = false, sawCollapsedSpans = false;
</span><span class="cx"> 
</span><del>-  // CONSTRUCTOR
</del><ins>+  // EDITOR CONSTRUCTOR
</ins><span class="cx"> 
</span><ins>+  // A CodeMirror instance represents an editor. This is the object
+  // that user code is usually dealing with.
+
</ins><span class="cx">   function CodeMirror(place, options) {
</span><span class="cx">     if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
</span><span class="cx"> 
</span><span class="cx">     this.options = options = options || {};
</span><span class="cx">     // Determine effective options based on given values and defaults.
</span><del>-    for (var opt in defaults) if (!options.hasOwnProperty(opt) &amp;&amp; defaults.hasOwnProperty(opt))
-      options[opt] = defaults[opt];
</del><ins>+    copyObj(defaults, options, false);
</ins><span class="cx">     setGuttersForLineNumbers(options);
</span><span class="cx"> 
</span><del>-    var docStart = typeof options.value == &quot;string&quot; ? 0 : options.value.first;
-    var display = this.display = makeDisplay(place, docStart);
</del><ins>+    var doc = options.value;
+    if (typeof doc == &quot;string&quot;) doc = new Doc(doc, options.mode);
+    this.doc = doc;
+
+    var display = this.display = new Display(place, doc);
</ins><span class="cx">     display.wrapper.CodeMirror = this;
</span><span class="cx">     updateGutters(this);
</span><del>-    if (options.autofocus &amp;&amp; !mobile) focusInput(this);
-
-    this.state = {keyMaps: [],
-                  overlays: [],
-                  modeGen: 0,
-                  overwrite: false, focused: false,
-                  suppressEdits: false, pasteIncoming: false,
-                  draggingText: false,
-                  highlight: new Delayed()};
-
</del><span class="cx">     themeChanged(this);
</span><span class="cx">     if (options.lineWrapping)
</span><span class="cx">       this.display.wrapper.className += &quot; CodeMirror-wrap&quot;;
</span><ins>+    if (options.autofocus &amp;&amp; !mobile) focusInput(this);
</ins><span class="cx"> 
</span><del>-    var doc = options.value;
-    if (typeof doc == &quot;string&quot;) doc = new Doc(options.value, options.mode);
-    operation(this, attachDoc)(this, doc);
</del><ins>+    this.state = {
+      keyMaps: [],  // stores maps added by addKeyMap
+      overlays: [], // highlighting overlays, as added by addOverlay
+      modeGen: 0,   // bumped when mode/overlay changes, used to invalidate highlighting info
+      overwrite: false, focused: false,
+      suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
+      pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
+      draggingText: false,
+      highlight: new Delayed() // stores highlight worker timeout
+    };
</ins><span class="cx"> 
</span><span class="cx">     // Override magic textarea content restore that IE sometimes does
</span><span class="cx">     // on our hidden textarea on reload
</span><del>-    if (ie) setTimeout(bind(resetInput, this, true), 20);
</del><ins>+    if (ie_upto10) setTimeout(bind(resetInput, this, true), 20);
</ins><span class="cx"> 
</span><span class="cx">     registerEventHandlers(this);
</span><del>-    // IE throws unspecified error in certain cases, when
-    // trying to access activeElement before onload
-    var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { }
-    if (hasFocus || (options.autofocus &amp;&amp; !mobile)) setTimeout(bind(onFocus, this), 20);
-    else onBlur(this);
</del><span class="cx"> 
</span><del>-    operation(this, function() {
-      for (var opt in optionHandlers)
-        if (optionHandlers.propertyIsEnumerable(opt))
-          optionHandlers[opt](this, options[opt], Init);
-      for (var i = 0; i &lt; initHooks.length; ++i) initHooks[i](this);
-    })();
</del><ins>+    var cm = this;
+    runInOp(this, function() {
+      cm.curOp.forceUpdate = true;
+      attachDoc(cm, doc);
+
+      if ((options.autofocus &amp;&amp; !mobile) || activeElt() == display.input)
+        setTimeout(bind(onFocus, cm), 20);
+      else
+        onBlur(cm);
+
+      for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
+        optionHandlers[opt](cm, options[opt], Init);
+      for (var i = 0; i &lt; initHooks.length; ++i) initHooks[i](cm);
+    });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DISPLAY CONSTRUCTOR
</span><span class="cx"> 
</span><del>-  function makeDisplay(place, docStart) {
-    var d = {};
</del><ins>+  // The display handles the DOM integration, both for input reading
+  // and content drawing. It holds references to DOM nodes and
+  // display-related state.
</ins><span class="cx"> 
</span><del>-    var input = d.input = elt(&quot;textarea&quot;, null, null, &quot;position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;&quot;);
</del><ins>+  function Display(place, doc) {
+    var d = this;
+
+    // The semihidden textarea that is focused when the editor is
+    // focused, and receives input.
+    var input = d.input = elt(&quot;textarea&quot;, null, null, &quot;position: absolute; padding: 0; width: 1px; height: 1em; outline: none&quot;);
+    // The textarea is kept positioned near the cursor to prevent the
+    // fact that it'll be scrolled into view on input from scrolling
+    // our fake cursor out of view. On webkit, when wrap=off, paste is
+    // very slow. So make the area wide instead.
</ins><span class="cx">     if (webkit) input.style.width = &quot;1000px&quot;;
</span><span class="cx">     else input.setAttribute(&quot;wrap&quot;, &quot;off&quot;);
</span><del>-    // if border: 0; -- iOS fails to open keyboard (issue #1287)
</del><ins>+    // If border: 0; -- iOS fails to open keyboard (issue #1287)
</ins><span class="cx">     if (ios) input.style.border = &quot;1px solid black&quot;;
</span><span class="cx">     input.setAttribute(&quot;autocorrect&quot;, &quot;off&quot;); input.setAttribute(&quot;autocapitalize&quot;, &quot;off&quot;); input.setAttribute(&quot;spellcheck&quot;, &quot;false&quot;);
</span><span class="cx"> 
</span><span class="cx">     // Wraps and hides input textarea
</span><span class="cx">     d.inputDiv = elt(&quot;div&quot;, [input], null, &quot;overflow: hidden; position: relative; width: 3px; height: 0px;&quot;);
</span><del>-    // The actual fake scrollbars.
-    d.scrollbarH = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;height: 1px&quot;)], &quot;CodeMirror-hscrollbar&quot;);
-    d.scrollbarV = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;width: 1px&quot;)], &quot;CodeMirror-vscrollbar&quot;);
</del><ins>+    // The fake scrollbar elements.
+    d.scrollbarH = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;height: 100%; min-height: 1px&quot;)], &quot;CodeMirror-hscrollbar&quot;);
+    d.scrollbarV = elt(&quot;div&quot;, [elt(&quot;div&quot;, null, null, &quot;min-width: 1px&quot;)], &quot;CodeMirror-vscrollbar&quot;);
+    // Covers bottom-right square when both scrollbars are present.
</ins><span class="cx">     d.scrollbarFiller = elt(&quot;div&quot;, null, &quot;CodeMirror-scrollbar-filler&quot;);
</span><ins>+    // Covers bottom of gutter when coverGutterNextToScrollbar is on
+    // and h scrollbar is present.
</ins><span class="cx">     d.gutterFiller = elt(&quot;div&quot;, null, &quot;CodeMirror-gutter-filler&quot;);
</span><del>-    // DIVs containing the selection and the actual code
</del><ins>+    // Will contain the actual code, positioned to cover the viewport.
</ins><span class="cx">     d.lineDiv = elt(&quot;div&quot;, null, &quot;CodeMirror-code&quot;);
</span><ins>+    // Elements are added to these to represent selection and cursors.
</ins><span class="cx">     d.selectionDiv = elt(&quot;div&quot;, null, null, &quot;position: relative; z-index: 1&quot;);
</span><del>-    // Blinky cursor, and element used to ensure cursor fits at the end of a line
-    d.cursor = elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor&quot;);
-    // Secondary cursor, shown when on a 'jump' in bi-directional text
-    d.otherCursor = elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor CodeMirror-secondarycursor&quot;);
-    // Used to measure text size
</del><ins>+    d.cursorDiv = elt(&quot;div&quot;, null, &quot;CodeMirror-cursors&quot;);
+    // A visibility: hidden element used to find the size of things.
</ins><span class="cx">     d.measure = elt(&quot;div&quot;, null, &quot;CodeMirror-measure&quot;);
</span><ins>+    // When lines outside of the viewport are measured, they are drawn in this.
+    d.lineMeasure = elt(&quot;div&quot;, null, &quot;CodeMirror-measure&quot;);
</ins><span class="cx">     // Wraps everything that needs to exist inside the vertically-padded coordinate system
</span><del>-    d.lineSpace = elt(&quot;div&quot;, [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor],
-                         null, &quot;position: relative; outline: none&quot;);
-    // Moved around its parent to cover visible view
</del><ins>+    d.lineSpace = elt(&quot;div&quot;, [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+                      null, &quot;position: relative; outline: none&quot;);
+    // Moved around its parent to cover visible view.
</ins><span class="cx">     d.mover = elt(&quot;div&quot;, [elt(&quot;div&quot;, [d.lineSpace], &quot;CodeMirror-lines&quot;)], null, &quot;position: relative&quot;);
</span><del>-    // Set to the height of the text, causes scrolling
</del><ins>+    // Set to the height of the document, allowing scrolling.
</ins><span class="cx">     d.sizer = elt(&quot;div&quot;, [d.mover], &quot;CodeMirror-sizer&quot;);
</span><del>-    // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
</del><ins>+    // Behavior of elts with overflow: auto and padding is
+    // inconsistent across browsers. This is used to ensure the
+    // scrollable area is big enough.
</ins><span class="cx">     d.heightForcer = elt(&quot;div&quot;, null, null, &quot;position: absolute; height: &quot; + scrollerCutOff + &quot;px; width: 1px;&quot;);
</span><del>-    // Will contain the gutters, if any
</del><ins>+    // Will contain the gutters, if any.
</ins><span class="cx">     d.gutters = elt(&quot;div&quot;, null, &quot;CodeMirror-gutters&quot;);
</span><span class="cx">     d.lineGutter = null;
</span><del>-    // Provides scrolling
</del><ins>+    // Actual scrollable element.
</ins><span class="cx">     d.scroller = elt(&quot;div&quot;, [d.sizer, d.heightForcer, d.gutters], &quot;CodeMirror-scroll&quot;);
</span><span class="cx">     d.scroller.setAttribute(&quot;tabIndex&quot;, &quot;-1&quot;);
</span><span class="cx">     // The element in which the editor lives.
</span><span class="cx">     d.wrapper = elt(&quot;div&quot;, [d.inputDiv, d.scrollbarH, d.scrollbarV,
</span><span class="cx">                             d.scrollbarFiller, d.gutterFiller, d.scroller], &quot;CodeMirror&quot;);
</span><del>-    // Work around IE7 z-index bug
-    if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
-    if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
</del><span class="cx"> 
</span><ins>+    // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
+    if (ie_upto7) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
</ins><span class="cx">     // Needed to hide big blue blinking cursor on Mobile Safari
</span><span class="cx">     if (ios) input.style.width = &quot;0px&quot;;
</span><span class="cx">     if (!webkit) d.scroller.draggable = true;
</span><span class="cx">     // Needed to handle Tab key in KHTML
</span><span class="cx">     if (khtml) { d.inputDiv.style.height = &quot;1px&quot;; d.inputDiv.style.position = &quot;absolute&quot;; }
</span><span class="cx">     // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
</span><del>-    else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = &quot;18px&quot;;
</del><ins>+    if (ie_upto7) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = &quot;18px&quot;;
</ins><span class="cx"> 
</span><del>-    // Current visible range (may be bigger than the view window).
-    d.viewOffset = d.lastSizeC = 0;
-    d.showingFrom = d.showingTo = docStart;
</del><ins>+    if (place.appendChild) place.appendChild(d.wrapper);
+    else place(d.wrapper);
</ins><span class="cx"> 
</span><ins>+    // Current rendered range (may be bigger than the view window).
+    d.viewFrom = d.viewTo = doc.first;
+    // Information about the rendered lines.
+    d.view = [];
+    // Holds info about a single rendered line when it was rendered
+    // for measurement, while not in view.
+    d.externalMeasured = null;
+    // Empty space (in pixels) above the view
+    d.viewOffset = 0;
+    d.lastSizeC = 0;
+    d.updateLineNumbers = null;
+
</ins><span class="cx">     // Used to only resize the line number gutter when necessary (when
</span><span class="cx">     // the amount of lines crosses a boundary that makes its width change)
</span><span class="cx">     d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
</span><span class="cx">     // See readInput and resetInput
</span><span class="cx">     d.prevInput = &quot;&quot;;
</span><del>-    // Set to true when a non-horizontal-scrolling widget is added. As
-    // an optimization, widget aligning is skipped when d is false.
</del><ins>+    // Set to true when a non-horizontal-scrolling line widget is
+    // added. As an optimization, line widget aligning is skipped when
+    // this is false.
</ins><span class="cx">     d.alignWidgets = false;
</span><del>-    // Flag that indicates whether we currently expect input to appear
-    // (after some event like 'keypress' or 'input') and are polling
-    // intensively.
</del><ins>+    // Flag that indicates whether we expect input to appear real soon
+    // now (after some event like 'keypress' or 'input') and are
+    // polling intensively.
</ins><span class="cx">     d.pollingFast = false;
</span><span class="cx">     // Self-resetting timeout for the poller
</span><span class="cx">     d.poll = new Delayed();
</span><span class="cx"> 
</span><del>-    d.cachedCharWidth = d.cachedTextHeight = null;
-    d.measureLineCache = [];
-    d.measureLineCachePos = 0;
</del><ins>+    d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
</ins><span class="cx"> 
</span><span class="cx">     // Tracks when resetInput has punted to just putting a short
</span><del>-    // string instead of the (large) selection.
</del><ins>+    // string into the textarea instead of the full selection.
</ins><span class="cx">     d.inaccurateSelection = false;
</span><span class="cx"> 
</span><span class="cx">     // Tracks the maximum line length so that the horizontal scrollbar
</span><span class="lines">@@ -186,7 +229,8 @@
</span><span class="cx">     // Used for measuring wheel scrolling granularity
</span><span class="cx">     d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
</span><span class="cx"> 
</span><del>-    return d;
</del><ins>+    // True when shift is held down.
+    d.shift = false;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // STATE UPDATES
</span><span class="lines">@@ -195,6 +239,10 @@
</span><span class="cx"> 
</span><span class="cx">   function loadMode(cm) {
</span><span class="cx">     cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
</span><ins>+    resetModeState(cm);
+  }
+
+  function resetModeState(cm) {
</ins><span class="cx">     cm.doc.iter(function(line) {
</span><span class="cx">       if (line.stateAfter) line.stateAfter = null;
</span><span class="cx">       if (line.styles) line.styles = null;
</span><span class="lines">@@ -207,11 +255,11 @@
</span><span class="cx"> 
</span><span class="cx">   function wrappingChanged(cm) {
</span><span class="cx">     if (cm.options.lineWrapping) {
</span><del>-      cm.display.wrapper.className += &quot; CodeMirror-wrap&quot;;
</del><ins>+      addClass(cm.display.wrapper, &quot;CodeMirror-wrap&quot;);
</ins><span class="cx">       cm.display.sizer.style.minWidth = &quot;&quot;;
</span><span class="cx">     } else {
</span><del>-      cm.display.wrapper.className = cm.display.wrapper.className.replace(&quot; CodeMirror-wrap&quot;, &quot;&quot;);
-      computeMaxLength(cm);
</del><ins>+      rmClass(cm.display.wrapper, &quot;CodeMirror-wrap&quot;);
+      findMaxLine(cm);
</ins><span class="cx">     }
</span><span class="cx">     estimateLineHeights(cm);
</span><span class="cx">     regChange(cm);
</span><span class="lines">@@ -219,16 +267,24 @@
</span><span class="cx">     setTimeout(function(){updateScrollbars(cm);}, 100);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Returns a function that estimates the height of a line, to use as
+  // first approximation until the line becomes visible (and is thus
+  // properly measurable).
</ins><span class="cx">   function estimateHeight(cm) {
</span><span class="cx">     var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
</span><span class="cx">     var perLine = wrapping &amp;&amp; Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
</span><span class="cx">     return function(line) {
</span><del>-      if (lineIsHidden(cm.doc, line))
-        return 0;
-      else if (wrapping)
-        return (Math.ceil(line.text.length / perLine) || 1) * th;
</del><ins>+      if (lineIsHidden(cm.doc, line)) return 0;
+
+      var widgetsHeight = 0;
+      if (line.widgets) for (var i = 0; i &lt; line.widgets.length; i++) {
+        if (line.widgets[i].height) widgetsHeight += line.widgets[i].height;
+      }
+
+      if (wrapping)
+        return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th;
</ins><span class="cx">       else
</span><del>-        return th;
</del><ins>+        return widgetsHeight + th;
</ins><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -244,7 +300,6 @@
</span><span class="cx">     var map = keyMap[cm.options.keyMap], style = map.style;
</span><span class="cx">     cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, &quot;&quot;) +
</span><span class="cx">       (style ? &quot; cm-keymap-&quot; + style : &quot;&quot;);
</span><del>-    cm.state.disableInput = map.disableInput;
</del><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function themeChanged(cm) {
</span><span class="lines">@@ -259,6 +314,8 @@
</span><span class="cx">     setTimeout(function(){alignHorizontally(cm);}, 20);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Rebuild the gutter elements, ensure the margin to the left of the
+  // code matches their width.
</ins><span class="cx">   function updateGutters(cm) {
</span><span class="cx">     var gutters = cm.display.gutters, specs = cm.options.gutters;
</span><span class="cx">     removeChildren(gutters);
</span><span class="lines">@@ -271,33 +328,44 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     gutters.style.display = i ? &quot;&quot; : &quot;none&quot;;
</span><ins>+    updateGutterSpace(cm);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function lineLength(doc, line) {
</del><ins>+  function updateGutterSpace(cm) {
+    var width = cm.display.gutters.offsetWidth;
+    cm.display.sizer.style.marginLeft = width + &quot;px&quot;;
+    cm.display.scrollbarH.style.left = cm.options.fixedGutter ? width + &quot;px&quot; : 0;
+  }
+
+  // Compute the character length of a line, taking into account
+  // collapsed ranges (see markText) that might hide parts, and join
+  // other lines onto it.
+  function lineLength(line) {
</ins><span class="cx">     if (line.height == 0) return 0;
</span><span class="cx">     var len = line.text.length, merged, cur = line;
</span><span class="cx">     while (merged = collapsedSpanAtStart(cur)) {
</span><del>-      var found = merged.find();
-      cur = getLine(doc, found.from.line);
</del><ins>+      var found = merged.find(0, true);
+      cur = found.from.line;
</ins><span class="cx">       len += found.from.ch - found.to.ch;
</span><span class="cx">     }
</span><span class="cx">     cur = line;
</span><span class="cx">     while (merged = collapsedSpanAtEnd(cur)) {
</span><del>-      var found = merged.find();
</del><ins>+      var found = merged.find(0, true);
</ins><span class="cx">       len -= cur.text.length - found.from.ch;
</span><del>-      cur = getLine(doc, found.to.line);
</del><ins>+      cur = found.to.line;
</ins><span class="cx">       len += cur.text.length - found.to.ch;
</span><span class="cx">     }
</span><span class="cx">     return len;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function computeMaxLength(cm) {
</del><ins>+  // Find the longest line in the document.
+  function findMaxLine(cm) {
</ins><span class="cx">     var d = cm.display, doc = cm.doc;
</span><span class="cx">     d.maxLine = getLine(doc, doc.first);
</span><del>-    d.maxLineLength = lineLength(doc, d.maxLine);
</del><ins>+    d.maxLineLength = lineLength(d.maxLine);
</ins><span class="cx">     d.maxLineChanged = true;
</span><span class="cx">     doc.iter(function(line) {
</span><del>-      var len = lineLength(doc, line);
</del><ins>+      var len = lineLength(line);
</ins><span class="cx">       if (len &gt; d.maxLineLength) {
</span><span class="cx">         d.maxLineLength = len;
</span><span class="cx">         d.maxLine = line;
</span><span class="lines">@@ -319,21 +387,33 @@
</span><span class="cx"> 
</span><span class="cx">   // SCROLLBARS
</span><span class="cx"> 
</span><ins>+  // Prepare DOM reads needed to update the scrollbars. Done in one
+  // shot to minimize update/measure roundtrips.
+  function measureForScrollbars(cm) {
+    var scroll = cm.display.scroller;
+    return {
+      clientHeight: scroll.clientHeight,
+      barHeight: cm.display.scrollbarV.clientHeight,
+      scrollWidth: scroll.scrollWidth, clientWidth: scroll.clientWidth,
+      barWidth: cm.display.scrollbarH.clientWidth,
+      docHeight: Math.round(cm.doc.height + paddingVert(cm.display))
+    };
+  }
+
</ins><span class="cx">   // Re-synchronize the fake scrollbars with the actual size of the
</span><del>-  // content. Optionally force a scrollTop.
-  function updateScrollbars(cm) {
-    var d = cm.display, docHeight = cm.doc.height;
-    var totalHeight = docHeight + paddingVert(d);
-    d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + &quot;px&quot;;
-    d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + &quot;px&quot;;
-    var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
-    var needsH = d.scroller.scrollWidth &gt; (d.scroller.clientWidth + 1);
-    var needsV = scrollHeight &gt; (d.scroller.clientHeight + 1);
</del><ins>+  // content.
+  function updateScrollbars(cm, measure) {
+    if (!measure) measure = measureForScrollbars(cm);
+    var d = cm.display;
+    var scrollHeight = measure.docHeight + scrollerCutOff;
+    var needsH = measure.scrollWidth &gt; measure.clientWidth;
+    var needsV = scrollHeight &gt; measure.clientHeight;
</ins><span class="cx">     if (needsV) {
</span><span class="cx">       d.scrollbarV.style.display = &quot;block&quot;;
</span><span class="cx">       d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + &quot;px&quot; : &quot;0&quot;;
</span><ins>+      // A bug in IE8 can cause this value to be negative, so guard it.
</ins><span class="cx">       d.scrollbarV.firstChild.style.height =
</span><del>-        (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + &quot;px&quot;;
</del><ins>+        Math.max(0, scrollHeight - measure.clientHeight + (measure.barHeight || d.scrollbarV.clientHeight)) + &quot;px&quot;;
</ins><span class="cx">     } else {
</span><span class="cx">       d.scrollbarV.style.display = &quot;&quot;;
</span><span class="cx">       d.scrollbarV.firstChild.style.height = &quot;0&quot;;
</span><span class="lines">@@ -342,7 +422,7 @@
</span><span class="cx">       d.scrollbarH.style.display = &quot;block&quot;;
</span><span class="cx">       d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + &quot;px&quot; : &quot;0&quot;;
</span><span class="cx">       d.scrollbarH.firstChild.style.width =
</span><del>-        (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + &quot;px&quot;;
</del><ins>+        (measure.scrollWidth - measure.clientWidth + (measure.barWidth || d.scrollbarH.clientWidth)) + &quot;px&quot;;
</ins><span class="cx">     } else {
</span><span class="cx">       d.scrollbarH.style.display = &quot;&quot;;
</span><span class="cx">       d.scrollbarH.firstChild.style.width = &quot;0&quot;;
</span><span class="lines">@@ -359,33 +439,61 @@
</span><span class="cx"> 
</span><span class="cx">     if (mac_geLion &amp;&amp; scrollbarWidth(d.measure) === 0) {
</span><span class="cx">       d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? &quot;18px&quot; : &quot;12px&quot;;
</span><del>-      d.scrollbarV.style.pointerEvents = d.scrollbarH.style.pointerEvents = &quot;none&quot;;
</del><ins>+      var barMouseDown = function(e) {
+        if (e_target(e) != d.scrollbarV &amp;&amp; e_target(e) != d.scrollbarH)
+          operation(cm, onMouseDown)(e);
+      };
+      on(d.scrollbarV, &quot;mousedown&quot;, barMouseDown);
+      on(d.scrollbarH, &quot;mousedown&quot;, barMouseDown);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute the lines that are visible in a given viewport (defaults
+  // the the current scroll position). viewPort may contain top,
+  // height, and ensure (see op.scrollToPos) properties.
</ins><span class="cx">   function visibleLines(display, doc, viewPort) {
</span><del>-    var top = display.scroller.scrollTop, height = display.wrapper.clientHeight;
-    if (typeof viewPort == &quot;number&quot;) top = viewPort;
-    else if (viewPort) {top = viewPort.top; height = viewPort.bottom - viewPort.top;}
</del><ins>+    var top = viewPort &amp;&amp; viewPort.top != null ? viewPort.top : display.scroller.scrollTop;
</ins><span class="cx">     top = Math.floor(top - paddingTop(display));
</span><del>-    var bottom = Math.ceil(top + height);
-    return {from: lineAtHeight(doc, top), to: lineAtHeight(doc, bottom)};
</del><ins>+    var bottom = viewPort &amp;&amp; viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight;
+
+    var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
+    // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+    // forces those lines into the viewport (if possible).
+    if (viewPort &amp;&amp; viewPort.ensure) {
+      var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line;
+      if (ensureFrom &lt; from)
+        return {from: ensureFrom,
+                to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
+      if (Math.min(ensureTo, doc.lastLine()) &gt;= to)
+        return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight),
+                to: ensureTo};
+    }
+    return {from: from, to: to};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // LINE NUMBERS
</span><span class="cx"> 
</span><ins>+  // Re-align line numbers and gutter marks to compensate for
+  // horizontal scrolling.
</ins><span class="cx">   function alignHorizontally(cm) {
</span><del>-    var display = cm.display;
</del><ins>+    var display = cm.display, view = display.view;
</ins><span class="cx">     if (!display.alignWidgets &amp;&amp; (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
</span><span class="cx">     var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
</span><del>-    var gutterW = display.gutters.offsetWidth, l = comp + &quot;px&quot;;
-    for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
-      for (var i = 0, a = n.alignable; i &lt; a.length; ++i) a[i].style.left = l;
</del><ins>+    var gutterW = display.gutters.offsetWidth, left = comp + &quot;px&quot;;
+    for (var i = 0; i &lt; view.length; i++) if (!view[i].hidden) {
+      if (cm.options.fixedGutter &amp;&amp; view[i].gutter)
+        view[i].gutter.style.left = left;
+      var align = view[i].alignable;
+      if (align) for (var j = 0; j &lt; align.length; j++)
+        align[j].style.left = left;
</ins><span class="cx">     }
</span><span class="cx">     if (cm.options.fixedGutter)
</span><span class="cx">       display.gutters.style.left = (comp + gutterW) + &quot;px&quot;;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to ensure that the line number gutter is still the right
+  // size for the current document size. Returns true when an update
+  // is needed.
</ins><span class="cx">   function maybeUpdateLineNumberWidth(cm) {
</span><span class="cx">     if (!cm.options.lineNumbers) return false;
</span><span class="cx">     var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
</span><span class="lines">@@ -398,6 +506,7 @@
</span><span class="cx">       display.lineNumWidth = display.lineNumInnerWidth + padding;
</span><span class="cx">       display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
</span><span class="cx">       display.lineGutter.style.width = display.lineNumWidth + &quot;px&quot;;
</span><ins>+      updateGutterSpace(cm);
</ins><span class="cx">       return true;
</span><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="lines">@@ -406,191 +515,193 @@
</span><span class="cx">   function lineNumberFor(options, i) {
</span><span class="cx">     return String(options.lineNumberFormatter(i + options.firstLineNumber));
</span><span class="cx">   }
</span><ins>+
+  // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
+  // but using getBoundingClientRect to get a sub-pixel-accurate
+  // result.
</ins><span class="cx">   function compensateForHScroll(display) {
</span><del>-    return getRect(display.scroller).left - getRect(display.sizer).left;
</del><ins>+    return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DISPLAY DRAWING
</span><span class="cx"> 
</span><del>-  function updateDisplay(cm, changes, viewPort, forced) {
-    var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
</del><ins>+  // Updates the display, selection, and scrollbars, using the
+  // information in display.view to find out which nodes are no longer
+  // up-to-date. Tries to bail out early when no changes are needed,
+  // unless forced is true.
+  // Returns true if an actual update happened, false otherwise.
+  function updateDisplay(cm, viewPort, forced) {
+    var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated;
</ins><span class="cx">     var visible = visibleLines(cm.display, cm.doc, viewPort);
</span><span class="cx">     for (var first = true;; first = false) {
</span><span class="cx">       var oldWidth = cm.display.scroller.clientWidth;
</span><del>-      if (!updateDisplayInner(cm, changes, visible, forced)) break;
</del><ins>+      if (!updateDisplayInner(cm, visible, forced)) break;
</ins><span class="cx">       updated = true;
</span><del>-      changes = [];
</del><ins>+
+      // If the max line changed since it was last measured, measure it,
+      // and ensure the document's width matches it.
+      if (cm.display.maxLineChanged &amp;&amp; !cm.options.lineWrapping)
+        adjustContentWidth(cm);
+
+      var barMeasure = measureForScrollbars(cm);
</ins><span class="cx">       updateSelection(cm);
</span><del>-      updateScrollbars(cm);
</del><ins>+      setDocumentHeight(cm, barMeasure);
+      updateScrollbars(cm, barMeasure);
+      if (webkit &amp;&amp; cm.options.lineWrapping)
+        checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420)
</ins><span class="cx">       if (first &amp;&amp; cm.options.lineWrapping &amp;&amp; oldWidth != cm.display.scroller.clientWidth) {
</span><span class="cx">         forced = true;
</span><span class="cx">         continue;
</span><span class="cx">       }
</span><span class="cx">       forced = false;
</span><span class="cx"> 
</span><del>-      // Clip forced viewport to actual scrollable area
-      if (viewPort)
-        viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight,
-                            typeof viewPort == &quot;number&quot; ? viewPort : viewPort.top);
</del><ins>+      // Clip forced viewport to actual scrollable area.
+      if (viewPort &amp;&amp; viewPort.top != null)
+        viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)};
+      // Updated line heights might result in the drawn area not
+      // actually covering the viewport. Keep looping until it does.
</ins><span class="cx">       visible = visibleLines(cm.display, cm.doc, viewPort);
</span><del>-      if (visible.from &gt;= cm.display.showingFrom &amp;&amp; visible.to &lt;= cm.display.showingTo)
</del><ins>+      if (visible.from &gt;= cm.display.viewFrom &amp;&amp; visible.to &lt;= cm.display.viewTo)
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    cm.display.updateLineNumbers = null;
</ins><span class="cx">     if (updated) {
</span><span class="cx">       signalLater(cm, &quot;update&quot;, cm);
</span><del>-      if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
-        signalLater(cm, &quot;viewportChange&quot;, cm, cm.display.showingFrom, cm.display.showingTo);
</del><ins>+      if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
+        signalLater(cm, &quot;viewportChange&quot;, cm, cm.display.viewFrom, cm.display.viewTo);
</ins><span class="cx">     }
</span><span class="cx">     return updated;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Uses a set of changes plus the current scroll position to
-  // determine which DOM updates have to be made, and makes the
-  // updates.
-  function updateDisplayInner(cm, changes, visible, forced) {
</del><ins>+  // Does the actual updating of the line display. Bails out
+  // (returning false) when there is nothing to be done and forced is
+  // false.
+  function updateDisplayInner(cm, visible, forced) {
</ins><span class="cx">     var display = cm.display, doc = cm.doc;
</span><del>-    if (!display.wrapper.clientWidth) {
-      display.showingFrom = display.showingTo = doc.first;
-      display.viewOffset = 0;
</del><ins>+    if (!display.wrapper.offsetWidth) {
+      resetView(cm);
</ins><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Bail out if the visible area is already rendered and nothing changed.
</span><del>-    if (!forced &amp;&amp; changes.length == 0 &amp;&amp;
-        visible.from &gt; display.showingFrom &amp;&amp; visible.to &lt; display.showingTo)
</del><ins>+    if (!forced &amp;&amp; visible.from &gt;= display.viewFrom &amp;&amp; visible.to &lt;= display.viewTo &amp;&amp;
+        countDirtyView(cm) == 0)
</ins><span class="cx">       return;
</span><span class="cx"> 
</span><span class="cx">     if (maybeUpdateLineNumberWidth(cm))
</span><del>-      changes = [{from: doc.first, to: doc.first + doc.size}];
-    var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + &quot;px&quot;;
-    display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : &quot;0&quot;;
</del><ins>+      resetView(cm);
+    var dims = getDimensions(cm);
</ins><span class="cx"> 
</span><del>-    // Used to determine which lines need their line numbers updated
-    var positionsChangedFrom = Infinity;
-    if (cm.options.lineNumbers)
-      for (var i = 0; i &lt; changes.length; ++i)
-        if (changes[i].diff &amp;&amp; changes[i].from &lt; positionsChangedFrom) { positionsChangedFrom = changes[i].from; }
-
</del><ins>+    // Compute a suitable new viewport (from &amp; to)
</ins><span class="cx">     var end = doc.first + doc.size;
</span><span class="cx">     var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
</span><span class="cx">     var to = Math.min(end, visible.to + cm.options.viewportMargin);
</span><del>-    if (display.showingFrom &lt; from &amp;&amp; from - display.showingFrom &lt; 20) from = Math.max(doc.first, display.showingFrom);
-    if (display.showingTo &gt; to &amp;&amp; display.showingTo - to &lt; 20) to = Math.min(end, display.showingTo);
</del><ins>+    if (display.viewFrom &lt; from &amp;&amp; from - display.viewFrom &lt; 20) from = Math.max(doc.first, display.viewFrom);
+    if (display.viewTo &gt; to &amp;&amp; display.viewTo - to &lt; 20) to = Math.min(end, display.viewTo);
</ins><span class="cx">     if (sawCollapsedSpans) {
</span><del>-      from = lineNo(visualLine(doc, getLine(doc, from)));
-      while (to &lt; end &amp;&amp; lineIsHidden(doc, getLine(doc, to))) ++to;
</del><ins>+      from = visualLineNo(cm.doc, from);
+      to = visualLineEndNo(cm.doc, to);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Create a range of theoretically intact lines, and punch holes
-    // in that using the change info.
-    var intact = [{from: Math.max(display.showingFrom, doc.first),
-                   to: Math.min(display.showingTo, end)}];
-    if (intact[0].from &gt;= intact[0].to) intact = [];
-    else intact = computeIntact(intact, changes);
-    // When merged lines are present, we might have to reduce the
-    // intact ranges because changes in continued fragments of the
-    // intact lines do require the lines to be redrawn.
-    if (sawCollapsedSpans)
-      for (var i = 0; i &lt; intact.length; ++i) {
-        var range = intact[i], merged;
-        while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
-          var newTo = merged.find().from.line;
-          if (newTo &gt; range.from) range.to = newTo;
-          else { intact.splice(i--, 1); break; }
-        }
-      }
</del><ins>+    var different = from != display.viewFrom || to != display.viewTo ||
+      display.lastSizeC != display.wrapper.clientHeight;
+    adjustView(cm, from, to);
</ins><span class="cx"> 
</span><del>-    // Clip off the parts that won't be visible
-    var intactLines = 0;
-    for (var i = 0; i &lt; intact.length; ++i) {
-      var range = intact[i];
-      if (range.from &lt; from) range.from = from;
-      if (range.to &gt; to) range.to = to;
-      if (range.from &gt;= range.to) intact.splice(i--, 1);
-      else intactLines += range.to - range.from;
-    }
-    if (!forced &amp;&amp; intactLines == to - from &amp;&amp; from == display.showingFrom &amp;&amp; to == display.showingTo) {
-      updateViewOffset(cm);
-      return;
-    }
-    intact.sort(function(a, b) {return a.from - b.from;});
</del><ins>+    display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
+    // Position the mover div to align with the current scroll position
+    cm.display.mover.style.top = display.viewOffset + &quot;px&quot;;
</ins><span class="cx"> 
</span><del>-    // Avoid crashing on IE's &quot;unspecified error&quot; when in iframes
-    try {
-      var focused = document.activeElement;
-    } catch(e) {}
-    if (intactLines &lt; (to - from) * .7) display.lineDiv.style.display = &quot;none&quot;;
-    patchDisplay(cm, from, to, intact, positionsChangedFrom);
-    display.lineDiv.style.display = &quot;&quot;;
-    if (focused &amp;&amp; document.activeElement != focused &amp;&amp; focused.offsetHeight) focused.focus();
</del><ins>+    var toUpdate = countDirtyView(cm);
+    if (!different &amp;&amp; toUpdate == 0 &amp;&amp; !forced) return;
</ins><span class="cx"> 
</span><del>-    var different = from != display.showingFrom || to != display.showingTo ||
-      display.lastSizeC != display.wrapper.clientHeight;
-    // This is just a bogus formula that detects when the editor is
-    // resized or the font size changes.
</del><ins>+    // For big changes, we hide the enclosing element during the
+    // update, since that speeds up the operations on most browsers.
+    var focused = activeElt();
+    if (toUpdate &gt; 4) display.lineDiv.style.display = &quot;none&quot;;
+    patchDisplay(cm, display.updateLineNumbers, dims);
+    if (toUpdate &gt; 4) display.lineDiv.style.display = &quot;&quot;;
+    // There might have been a widget with a focused element that got
+    // hidden or updated, if so re-focus it.
+    if (focused &amp;&amp; activeElt() != focused &amp;&amp; focused.offsetHeight) focused.focus();
+
+    // Prevent selection and cursors from interfering with the scroll
+    // width.
+    removeChildren(display.cursorDiv);
+    removeChildren(display.selectionDiv);
+
</ins><span class="cx">     if (different) {
</span><span class="cx">       display.lastSizeC = display.wrapper.clientHeight;
</span><span class="cx">       startWorker(cm, 400);
</span><span class="cx">     }
</span><del>-    display.showingFrom = from; display.showingTo = to;
</del><span class="cx"> 
</span><span class="cx">     updateHeightsInViewport(cm);
</span><del>-    updateViewOffset(cm);
</del><span class="cx"> 
</span><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function adjustContentWidth(cm) {
+    var display = cm.display;
+    var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
+    display.maxLineChanged = false;
+    var minWidth = Math.max(0, width + 3);
+    var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth);
+    display.sizer.style.minWidth = minWidth + &quot;px&quot;;
+    if (maxScrollLeft &lt; cm.doc.scrollLeft)
+      setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
+  }
+
+  function setDocumentHeight(cm, measure) {
+    cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measure.docHeight + &quot;px&quot;;
+    cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + &quot;px&quot;;
+  }
+
+
+  function checkForWebkitWidthBug(cm, measure) {
+    // Work around Webkit bug where it sometimes reserves space for a
+    // non-existing phantom scrollbar in the scroller (Issue #2420)
+    if (cm.display.sizer.offsetWidth + cm.display.gutters.offsetWidth &lt; cm.display.scroller.clientWidth - 1) {
+      cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = &quot;0px&quot;;
+      cm.display.gutters.style.height = measure.docHeight + &quot;px&quot;;
+    }
+  }
+
+  // Read the actual heights of the rendered lines, and update their
+  // stored heights to match.
</ins><span class="cx">   function updateHeightsInViewport(cm) {
</span><span class="cx">     var display = cm.display;
</span><span class="cx">     var prevBottom = display.lineDiv.offsetTop;
</span><del>-    for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) {
-      if (ie_lt8) {
-        var bot = node.offsetTop + node.offsetHeight;
</del><ins>+    for (var i = 0; i &lt; display.view.length; i++) {
+      var cur = display.view[i], height;
+      if (cur.hidden) continue;
+      if (ie_upto7) {
+        var bot = cur.node.offsetTop + cur.node.offsetHeight;
</ins><span class="cx">         height = bot - prevBottom;
</span><span class="cx">         prevBottom = bot;
</span><span class="cx">       } else {
</span><del>-        var box = getRect(node);
</del><ins>+        var box = cur.node.getBoundingClientRect();
</ins><span class="cx">         height = box.bottom - box.top;
</span><span class="cx">       }
</span><del>-      var diff = node.lineObj.height - height;
</del><ins>+      var diff = cur.line.height - height;
</ins><span class="cx">       if (height &lt; 2) height = textHeight(display);
</span><span class="cx">       if (diff &gt; .001 || diff &lt; -.001) {
</span><del>-        updateLineHeight(node.lineObj, height);
-        var widgets = node.lineObj.widgets;
-        if (widgets) for (var i = 0; i &lt; widgets.length; ++i)
-          widgets[i].height = widgets[i].node.offsetHeight;
</del><ins>+        updateLineHeight(cur.line, height);
+        updateWidgetHeight(cur.line);
+        if (cur.rest) for (var j = 0; j &lt; cur.rest.length; j++)
+          updateWidgetHeight(cur.rest[j]);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function updateViewOffset(cm) {
-    var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
-    // Position the mover div to align with the current virtual scroll position
-    cm.display.mover.style.top = off + &quot;px&quot;;
</del><ins>+  // Read and store the height of line widgets associated with the
+  // given line.
+  function updateWidgetHeight(line) {
+    if (line.widgets) for (var i = 0; i &lt; line.widgets.length; ++i)
+      line.widgets[i].height = line.widgets[i].node.offsetHeight;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function computeIntact(intact, changes) {
-    for (var i = 0, l = changes.length || 0; i &lt; l; ++i) {
-      var change = changes[i], intact2 = [], diff = change.diff || 0;
-      for (var j = 0, l2 = intact.length; j &lt; l2; ++j) {
-        var range = intact[j];
-        if (change.to &lt;= range.from &amp;&amp; change.diff) {
-          intact2.push({from: range.from + diff, to: range.to + diff});
-        } else if (change.to &lt;= range.from || change.from &gt;= range.to) {
-          intact2.push(range);
-        } else {
-          if (change.from &gt; range.from)
-            intact2.push({from: range.from, to: change.from});
-          if (change.to &lt; range.to)
-            intact2.push({from: change.to + diff, to: range.to + diff});
-        }
-      }
-      intact = intact2;
-    }
-    return intact;
-  }
-
</del><ins>+  // Do a bulk-read of the DOM positions and sizes needed to draw the
+  // view, so that we don't interleave reading and writing to the DOM.
</ins><span class="cx">   function getDimensions(cm) {
</span><span class="cx">     var d = cm.display, left = {}, width = {};
</span><span class="cx">     for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
</span><span class="lines">@@ -604,154 +715,207 @@
</span><span class="cx">             wrapperWidth: d.wrapper.clientWidth};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
-    var dims = getDimensions(cm);
</del><ins>+  // Sync the actual display DOM structure with display.view, removing
+  // nodes for lines that are no longer in view, and creating the ones
+  // that are not there yet, and updating the ones that are out of
+  // date.
+  function patchDisplay(cm, updateNumbersFrom, dims) {
</ins><span class="cx">     var display = cm.display, lineNumbers = cm.options.lineNumbers;
</span><del>-    if (!intact.length &amp;&amp; (!webkit || !cm.display.currentWheelTarget))
-      removeChildren(display.lineDiv);
</del><span class="cx">     var container = display.lineDiv, cur = container.firstChild;
</span><span class="cx"> 
</span><span class="cx">     function rm(node) {
</span><span class="cx">       var next = node.nextSibling;
</span><del>-      if (webkit &amp;&amp; mac &amp;&amp; cm.display.currentWheelTarget == node) {
</del><ins>+      // Works around a throw-scroll bug in OS X Webkit
+      if (webkit &amp;&amp; mac &amp;&amp; cm.display.currentWheelTarget == node)
</ins><span class="cx">         node.style.display = &quot;none&quot;;
</span><del>-        node.lineObj = null;
-      } else {
</del><ins>+      else
</ins><span class="cx">         node.parentNode.removeChild(node);
</span><del>-      }
</del><span class="cx">       return next;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var nextIntact = intact.shift(), lineN = from;
-    cm.doc.iter(from, to, function(line) {
-      if (nextIntact &amp;&amp; nextIntact.to == lineN) nextIntact = intact.shift();
-      if (lineIsHidden(cm.doc, line)) {
-        if (line.height != 0) updateLineHeight(line, 0);
-        if (line.widgets &amp;&amp; cur &amp;&amp; cur.previousSibling) for (var i = 0; i &lt; line.widgets.length; ++i) {
-          var w = line.widgets[i];
-          if (w.showIfHidden) {
-            var prev = cur.previousSibling;
-            if (/pre/i.test(prev.nodeName)) {
-              var wrap = elt(&quot;div&quot;, null, null, &quot;position: relative&quot;);
-              prev.parentNode.replaceChild(wrap, prev);
-              wrap.appendChild(prev);
-              prev = wrap;
-            }
-            var wnode = prev.appendChild(elt(&quot;div&quot;, [w.node], &quot;CodeMirror-linewidget&quot;));
-            if (!w.handleMouseEvents) wnode.ignoreEvents = true;
-            positionLineWidget(w, wnode, prev, dims);
-          }
</del><ins>+    var view = display.view, lineN = display.viewFrom;
+    // Loop over the elements in the view, syncing cur (the DOM nodes
+    // in display.lineDiv) with the view as we go.
+    for (var i = 0; i &lt; view.length; i++) {
+      var lineView = view[i];
+      if (lineView.hidden) {
+      } else if (!lineView.node) { // Not drawn yet
+        var node = buildLineElement(cm, lineView, lineN, dims);
+        container.insertBefore(node, cur);
+      } else { // Already drawn
+        while (cur != lineView.node) cur = rm(cur);
+        var updateNumber = lineNumbers &amp;&amp; updateNumbersFrom != null &amp;&amp;
+          updateNumbersFrom &lt;= lineN &amp;&amp; lineView.lineNumber;
+        if (lineView.changes) {
+          if (indexOf(lineView.changes, &quot;gutter&quot;) &gt; -1) updateNumber = false;
+          updateLineForChanges(cm, lineView, lineN, dims);
</ins><span class="cx">         }
</span><del>-      } else if (nextIntact &amp;&amp; nextIntact.from &lt;= lineN &amp;&amp; nextIntact.to &gt; lineN) {
-        // This line is intact. Skip to the actual node. Update its
-        // line number if needed.
-        while (cur.lineObj != line) cur = rm(cur);
-        if (lineNumbers &amp;&amp; updateNumbersFrom &lt;= lineN &amp;&amp; cur.lineNumber)
-          setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
-        cur = cur.nextSibling;
-      } else {
-        // For lines with widgets, make an attempt to find and reuse
-        // the existing element, so that widgets aren't needlessly
-        // removed and re-inserted into the dom
-        if (line.widgets) for (var j = 0, search = cur, reuse; search &amp;&amp; j &lt; 20; ++j, search = search.nextSibling)
-          if (search.lineObj == line &amp;&amp; /div/i.test(search.nodeName)) { reuse = search; break; }
-        // This line needs to be generated.
-        var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
-        if (lineNode != reuse) {
-          container.insertBefore(lineNode, cur);
-        } else {
-          while (cur != reuse) cur = rm(cur);
-          cur = cur.nextSibling;
</del><ins>+        if (updateNumber) {
+          removeChildren(lineView.lineNumber);
+          lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
</ins><span class="cx">         }
</span><del>-
-        lineNode.lineObj = line;
</del><ins>+        cur = lineView.node.nextSibling;
</ins><span class="cx">       }
</span><del>-      ++lineN;
-    });
</del><ins>+      lineN += lineView.size;
+    }
</ins><span class="cx">     while (cur) cur = rm(cur);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildLineElement(cm, line, lineNo, dims, reuse) {
-    var built = buildLineContent(cm, line), lineElement = built.pre;
-    var markers = line.gutterMarkers, display = cm.display, wrap;
</del><ins>+  // When an aspect of a line changes, a string is added to
+  // lineView.changes. This updates the relevant part of the line's
+  // DOM structure.
+  function updateLineForChanges(cm, lineView, lineN, dims) {
+    for (var j = 0; j &lt; lineView.changes.length; j++) {
+      var type = lineView.changes[j];
+      if (type == &quot;text&quot;) updateLineText(cm, lineView);
+      else if (type == &quot;gutter&quot;) updateLineGutter(cm, lineView, lineN, dims);
+      else if (type == &quot;class&quot;) updateLineClasses(lineView);
+      else if (type == &quot;widget&quot;) updateLineWidgets(lineView, dims);
+    }
+    lineView.changes = null;
+  }
</ins><span class="cx"> 
</span><del>-    var bgClass = built.bgClass ? built.bgClass + &quot; &quot; + (line.bgClass || &quot;&quot;) : line.bgClass;
-    if (!cm.options.lineNumbers &amp;&amp; !markers &amp;&amp; !bgClass &amp;&amp; !line.wrapClass &amp;&amp; !line.widgets)
-      return lineElement;
</del><ins>+  // Lines with gutter elements, widgets or a background class need to
+  // be wrapped, and have the extra elements added to the wrapper div
+  function ensureLineWrapped(lineView) {
+    if (lineView.node == lineView.text) {
+      lineView.node = elt(&quot;div&quot;, null, null, &quot;position: relative&quot;);
+      if (lineView.text.parentNode)
+        lineView.text.parentNode.replaceChild(lineView.node, lineView.text);
+      lineView.node.appendChild(lineView.text);
+      if (ie_upto7) lineView.node.style.zIndex = 2;
+    }
+    return lineView.node;
+  }
</ins><span class="cx"> 
</span><del>-    // Lines with gutter elements, widgets or a background class need
-    // to be wrapped again, and have the extra elements added to the
-    // wrapper div
</del><ins>+  function updateLineBackground(lineView) {
+    var cls = lineView.bgClass ? lineView.bgClass + &quot; &quot; + (lineView.line.bgClass || &quot;&quot;) : lineView.line.bgClass;
+    if (cls) cls += &quot; CodeMirror-linebackground&quot;;
+    if (lineView.background) {
+      if (cls) lineView.background.className = cls;
+      else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
+    } else if (cls) {
+      var wrap = ensureLineWrapped(lineView);
+      lineView.background = wrap.insertBefore(elt(&quot;div&quot;, null, cls), wrap.firstChild);
+    }
+  }
</ins><span class="cx"> 
</span><del>-    if (reuse) {
-      reuse.alignable = null;
-      var isOk = true, widgetsSeen = 0, insertBefore = null;
-      for (var n = reuse.firstChild, next; n; n = next) {
-        next = n.nextSibling;
-        if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
-          reuse.removeChild(n);
-        } else {
-          for (var i = 0; i &lt; line.widgets.length; ++i) {
-            var widget = line.widgets[i];
-            if (widget.node == n.firstChild) {
-              if (!widget.above &amp;&amp; !insertBefore) insertBefore = n;
-              positionLineWidget(widget, n, reuse, dims);
-              ++widgetsSeen;
-              break;
-            }
-          }
-          if (i == line.widgets.length) { isOk = false; break; }
-        }
-      }
-      reuse.insertBefore(lineElement, insertBefore);
-      if (isOk &amp;&amp; widgetsSeen == line.widgets.length) {
-        wrap = reuse;
-        reuse.className = line.wrapClass || &quot;&quot;;
-      }
</del><ins>+  // Wrapper around buildLineContent which will reuse the structure
+  // in display.externalMeasured when possible.
+  function getLineContent(cm, lineView) {
+    var ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; ext.line == lineView.line) {
+      cm.display.externalMeasured = null;
+      lineView.measure = ext.measure;
+      return ext.built;
</ins><span class="cx">     }
</span><del>-    if (!wrap) {
-      wrap = elt(&quot;div&quot;, null, line.wrapClass, &quot;position: relative&quot;);
-      wrap.appendChild(lineElement);
</del><ins>+    return buildLineContent(cm, lineView);
+  }
+
+  // Redraw the line's text. Interacts with the background and text
+  // classes because the mode may output tokens that influence these
+  // classes.
+  function updateLineText(cm, lineView) {
+    var cls = lineView.text.className;
+    var built = getLineContent(cm, lineView);
+    if (lineView.text == lineView.node) lineView.node = built.pre;
+    lineView.text.parentNode.replaceChild(built.pre, lineView.text);
+    lineView.text = built.pre;
+    if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+      lineView.bgClass = built.bgClass;
+      lineView.textClass = built.textClass;
+      updateLineClasses(lineView);
+    } else if (cls) {
+      lineView.text.className = cls;
</ins><span class="cx">     }
</span><del>-    // Kludge to make sure the styled element lies behind the selection (by z-index)
-    if (bgClass)
-      wrap.insertBefore(elt(&quot;div&quot;, null, bgClass + &quot; CodeMirror-linebackground&quot;), wrap.firstChild);
</del><ins>+  }
+
+  function updateLineClasses(lineView) {
+    updateLineBackground(lineView);
+    if (lineView.line.wrapClass)
+      ensureLineWrapped(lineView).className = lineView.line.wrapClass;
+    else if (lineView.node != lineView.text)
+      lineView.node.className = &quot;&quot;;
+    var textClass = lineView.textClass ? lineView.textClass + &quot; &quot; + (lineView.line.textClass || &quot;&quot;) : lineView.line.textClass;
+    lineView.text.className = textClass || &quot;&quot;;
+  }
+
+  function updateLineGutter(cm, lineView, lineN, dims) {
+    if (lineView.gutter) {
+      lineView.node.removeChild(lineView.gutter);
+      lineView.gutter = null;
+    }
+    var markers = lineView.line.gutterMarkers;
</ins><span class="cx">     if (cm.options.lineNumbers || markers) {
</span><del>-      var gutterWrap = wrap.insertBefore(elt(&quot;div&quot;, null, null, &quot;position: absolute; left: &quot; +
-                                             (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + &quot;px&quot;),
-                                         wrap.firstChild);
-      if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
</del><ins>+      var wrap = ensureLineWrapped(lineView);
+      var gutterWrap = lineView.gutter =
+        wrap.insertBefore(elt(&quot;div&quot;, null, &quot;CodeMirror-gutter-wrapper&quot;, &quot;position: absolute; left: &quot; +
+                              (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + &quot;px&quot;),
+                          lineView.text);
</ins><span class="cx">       if (cm.options.lineNumbers &amp;&amp; (!markers || !markers[&quot;CodeMirror-linenumbers&quot;]))
</span><del>-        wrap.lineNumber = gutterWrap.appendChild(
-          elt(&quot;div&quot;, lineNumberFor(cm.options, lineNo),
</del><ins>+        lineView.lineNumber = gutterWrap.appendChild(
+          elt(&quot;div&quot;, lineNumberFor(cm.options, lineN),
</ins><span class="cx">               &quot;CodeMirror-linenumber CodeMirror-gutter-elt&quot;,
</span><span class="cx">               &quot;left: &quot; + dims.gutterLeft[&quot;CodeMirror-linenumbers&quot;] + &quot;px; width: &quot;
</span><del>-              + display.lineNumInnerWidth + &quot;px&quot;));
-      if (markers)
-        for (var k = 0; k &lt; cm.options.gutters.length; ++k) {
-          var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) &amp;&amp; markers[id];
-          if (found)
-            gutterWrap.appendChild(elt(&quot;div&quot;, [found], &quot;CodeMirror-gutter-elt&quot;, &quot;left: &quot; +
-                                       dims.gutterLeft[id] + &quot;px; width: &quot; + dims.gutterWidth[id] + &quot;px&quot;));
-        }
</del><ins>+              + cm.display.lineNumInnerWidth + &quot;px&quot;));
+      if (markers) for (var k = 0; k &lt; cm.options.gutters.length; ++k) {
+        var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) &amp;&amp; markers[id];
+        if (found)
+          gutterWrap.appendChild(elt(&quot;div&quot;, [found], &quot;CodeMirror-gutter-elt&quot;, &quot;left: &quot; +
+                                     dims.gutterLeft[id] + &quot;px; width: &quot; + dims.gutterWidth[id] + &quot;px&quot;));
+      }
</ins><span class="cx">     }
</span><del>-    if (ie_lt8) wrap.style.zIndex = 2;
-    if (line.widgets &amp;&amp; wrap != reuse) for (var i = 0, ws = line.widgets; i &lt; ws.length; ++i) {
</del><ins>+  }
+
+  function updateLineWidgets(lineView, dims) {
+    if (lineView.alignable) lineView.alignable = null;
+    for (var node = lineView.node.firstChild, next; node; node = next) {
+      var next = node.nextSibling;
+      if (node.className == &quot;CodeMirror-linewidget&quot;)
+        lineView.node.removeChild(node);
+    }
+    insertLineWidgets(lineView, dims);
+  }
+
+  // Build a line's DOM representation from scratch
+  function buildLineElement(cm, lineView, lineN, dims) {
+    var built = getLineContent(cm, lineView);
+    lineView.text = lineView.node = built.pre;
+    if (built.bgClass) lineView.bgClass = built.bgClass;
+    if (built.textClass) lineView.textClass = built.textClass;
+
+    updateLineClasses(lineView);
+    updateLineGutter(cm, lineView, lineN, dims);
+    insertLineWidgets(lineView, dims);
+    return lineView.node;
+  }
+
+  // A lineView may contain multiple logical lines (when merged by
+  // collapsed spans). The widgets for all of them need to be drawn.
+  function insertLineWidgets(lineView, dims) {
+    insertLineWidgetsFor(lineView.line, lineView, dims, true);
+    if (lineView.rest) for (var i = 0; i &lt; lineView.rest.length; i++)
+      insertLineWidgetsFor(lineView.rest[i], lineView, dims, false);
+  }
+
+  function insertLineWidgetsFor(line, lineView, dims, allowAbove) {
+    if (!line.widgets) return;
+    var wrap = ensureLineWrapped(lineView);
+    for (var i = 0, ws = line.widgets; i &lt; ws.length; ++i) {
</ins><span class="cx">       var widget = ws[i], node = elt(&quot;div&quot;, [widget.node], &quot;CodeMirror-linewidget&quot;);
</span><span class="cx">       if (!widget.handleMouseEvents) node.ignoreEvents = true;
</span><del>-      positionLineWidget(widget, node, wrap, dims);
-      if (widget.above)
-        wrap.insertBefore(node, cm.options.lineNumbers &amp;&amp; line.height != 0 ? gutterWrap : lineElement);
</del><ins>+      positionLineWidget(widget, node, lineView, dims);
+      if (allowAbove &amp;&amp; widget.above)
+        wrap.insertBefore(node, lineView.gutter || lineView.text);
</ins><span class="cx">       else
</span><span class="cx">         wrap.appendChild(node);
</span><span class="cx">       signalLater(widget, &quot;redraw&quot;);
</span><span class="cx">     }
</span><del>-    return wrap;
</del><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function positionLineWidget(widget, node, wrap, dims) {
</del><ins>+  function positionLineWidget(widget, node, lineView, dims) {
</ins><span class="cx">     if (widget.noHScroll) {
</span><del>-      (wrap.alignable || (wrap.alignable = [])).push(node);
</del><ins>+      (lineView.alignable || (lineView.alignable = [])).push(node);
</ins><span class="cx">       var width = dims.wrapperWidth;
</span><span class="cx">       node.style.left = dims.fixedPos + &quot;px&quot;;
</span><span class="cx">       if (!widget.coverGutter) {
</span><span class="lines">@@ -767,57 +931,370 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // POSITION OBJECT
+
+  // A Pos instance represents a position within the text.
+  var Pos = CodeMirror.Pos = function(line, ch) {
+    if (!(this instanceof Pos)) return new Pos(line, ch);
+    this.line = line; this.ch = ch;
+  };
+
+  // Compare two positions, return 0 if they are the same, a negative
+  // number when a is less, and a positive number otherwise.
+  var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; };
+
+  function copyPos(x) {return Pos(x.line, x.ch);}
+  function maxPos(a, b) { return cmp(a, b) &lt; 0 ? b : a; }
+  function minPos(a, b) { return cmp(a, b) &lt; 0 ? a : b; }
+
</ins><span class="cx">   // SELECTION / CURSOR
</span><span class="cx"> 
</span><ins>+  // Selection objects are immutable. A new one is created every time
+  // the selection changes. A selection is one or more non-overlapping
+  // (and non-touching) ranges, sorted, and an integer that indicates
+  // which one is the primary selection (the one that's scrolled into
+  // view, that getCursor returns, etc).
+  function Selection(ranges, primIndex) {
+    this.ranges = ranges;
+    this.primIndex = primIndex;
+  }
+
+  Selection.prototype = {
+    primary: function() { return this.ranges[this.primIndex]; },
+    equals: function(other) {
+      if (other == this) return true;
+      if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false;
+      for (var i = 0; i &lt; this.ranges.length; i++) {
+        var here = this.ranges[i], there = other.ranges[i];
+        if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false;
+      }
+      return true;
+    },
+    deepCopy: function() {
+      for (var out = [], i = 0; i &lt; this.ranges.length; i++)
+        out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head));
+      return new Selection(out, this.primIndex);
+    },
+    somethingSelected: function() {
+      for (var i = 0; i &lt; this.ranges.length; i++)
+        if (!this.ranges[i].empty()) return true;
+      return false;
+    },
+    contains: function(pos, end) {
+      if (!end) end = pos;
+      for (var i = 0; i &lt; this.ranges.length; i++) {
+        var range = this.ranges[i];
+        if (cmp(end, range.from()) &gt;= 0 &amp;&amp; cmp(pos, range.to()) &lt;= 0)
+          return i;
+      }
+      return -1;
+    }
+  };
+
+  function Range(anchor, head) {
+    this.anchor = anchor; this.head = head;
+  }
+
+  Range.prototype = {
+    from: function() { return minPos(this.anchor, this.head); },
+    to: function() { return maxPos(this.anchor, this.head); },
+    empty: function() {
+      return this.head.line == this.anchor.line &amp;&amp; this.head.ch == this.anchor.ch;
+    }
+  };
+
+  // Take an unsorted, potentially overlapping set of ranges, and
+  // build a selection out of it. 'Consumes' ranges array (modifying
+  // it).
+  function normalizeSelection(ranges, primIndex) {
+    var prim = ranges[primIndex];
+    ranges.sort(function(a, b) { return cmp(a.from(), b.from()); });
+    primIndex = indexOf(ranges, prim);
+    for (var i = 1; i &lt; ranges.length; i++) {
+      var cur = ranges[i], prev = ranges[i - 1];
+      if (cmp(prev.to(), cur.from()) &gt;= 0) {
+        var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
+        var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
+        if (i &lt;= primIndex) --primIndex;
+        ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
+      }
+    }
+    return new Selection(ranges, primIndex);
+  }
+
+  function simpleSelection(anchor, head) {
+    return new Selection([new Range(anchor, head || anchor)], 0);
+  }
+
+  // Most of the external API clips given positions to make sure they
+  // actually exist within the document.
+  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
+  function clipPos(doc, pos) {
+    if (pos.line &lt; doc.first) return Pos(doc.first, 0);
+    var last = doc.first + doc.size - 1;
+    if (pos.line &gt; last) return Pos(last, getLine(doc, last).text.length);
+    return clipToLen(pos, getLine(doc, pos.line).text.length);
+  }
+  function clipToLen(pos, linelen) {
+    var ch = pos.ch;
+    if (ch == null || ch &gt; linelen) return Pos(pos.line, linelen);
+    else if (ch &lt; 0) return Pos(pos.line, 0);
+    else return pos;
+  }
+  function isLine(doc, l) {return l &gt;= doc.first &amp;&amp; l &lt; doc.first + doc.size;}
+  function clipPosArray(doc, array) {
+    for (var out = [], i = 0; i &lt; array.length; i++) out[i] = clipPos(doc, array[i]);
+    return out;
+  }
+
+  // SELECTION UPDATES
+
+  // The 'scroll' parameter given to many of these indicated whether
+  // the new cursor position should be scrolled into view after
+  // modifying the selection.
+
+  // If shift is held or the extend flag is set, extends a range to
+  // include a given position (and optionally a second position).
+  // Otherwise, simply returns the range between the given positions.
+  // Used for cursor motion and such.
+  function extendRange(doc, range, head, other) {
+    if (doc.cm &amp;&amp; doc.cm.display.shift || doc.extend) {
+      var anchor = range.anchor;
+      if (other) {
+        var posBefore = cmp(head, anchor) &lt; 0;
+        if (posBefore != (cmp(other, anchor) &lt; 0)) {
+          anchor = head;
+          head = other;
+        } else if (posBefore != (cmp(head, other) &lt; 0)) {
+          head = other;
+        }
+      }
+      return new Range(anchor, head);
+    } else {
+      return new Range(other || head, head);
+    }
+  }
+
+  // Extend the primary selection range, discard the rest.
+  function extendSelection(doc, head, other, options) {
+    setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options);
+  }
+
+  // Extend all selections (pos is an array of selections with length
+  // equal the number of selections)
+  function extendSelections(doc, heads, options) {
+    for (var out = [], i = 0; i &lt; doc.sel.ranges.length; i++)
+      out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null);
+    var newSel = normalizeSelection(out, doc.sel.primIndex);
+    setSelection(doc, newSel, options);
+  }
+
+  // Updates a single range in the selection.
+  function replaceOneSelection(doc, i, range, options) {
+    var ranges = doc.sel.ranges.slice(0);
+    ranges[i] = range;
+    setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options);
+  }
+
+  // Reset the selection to a single range.
+  function setSimpleSelection(doc, anchor, head, options) {
+    setSelection(doc, simpleSelection(anchor, head), options);
+  }
+
+  // Give beforeSelectionChange handlers a change to influence a
+  // selection update.
+  function filterSelectionChange(doc, sel) {
+    var obj = {
+      ranges: sel.ranges,
+      update: function(ranges) {
+        this.ranges = [];
+        for (var i = 0; i &lt; ranges.length; i++)
+          this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
+                                     clipPos(doc, ranges[i].head));
+      }
+    };
+    signal(doc, &quot;beforeSelectionChange&quot;, doc, obj);
+    if (doc.cm) signal(doc.cm, &quot;beforeSelectionChange&quot;, doc.cm, obj);
+    if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1);
+    else return sel;
+  }
+
+  function setSelectionReplaceHistory(doc, sel, options) {
+    var done = doc.history.done, last = lst(done);
+    if (last &amp;&amp; last.ranges) {
+      done[done.length - 1] = sel;
+      setSelectionNoUndo(doc, sel, options);
+    } else {
+      setSelection(doc, sel, options);
+    }
+  }
+
+  // Set a new selection.
+  function setSelection(doc, sel, options) {
+    if (options &amp;&amp; options.origin &amp;&amp; doc.cm) doc.cm.curOp.origin = options.origin;
+    setSelectionNoUndo(doc, sel, options);
+    addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
+  }
+
+  function setSelectionNoUndo(doc, sel, options) {
+    if (hasHandler(doc, &quot;beforeSelectionChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeSelectionChange&quot;))
+      sel = filterSelectionChange(doc, sel);
+
+    var bias = cmp(sel.primary().head, doc.sel.primary().head) &lt; 0 ? -1 : 1;
+    setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
+
+    if (!(options &amp;&amp; options.scroll === false) &amp;&amp; doc.cm)
+      ensureCursorVisible(doc.cm);
+  }
+
+  function setSelectionInner(doc, sel) {
+    if (sel.equals(doc.sel)) return;
+
+    doc.sel = sel;
+
+    if (doc.cm)
+      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
+        doc.cm.curOp.cursorActivity = true;
+    signalLater(doc, &quot;cursorActivity&quot;, doc);
+  }
+
+  // Verify that the selection does not partially select any atomic
+  // marked ranges.
+  function reCheckSelection(doc) {
+    setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll);
+  }
+
+  // Return a selection that does not partially select any atomic
+  // ranges.
+  function skipAtomicInSelection(doc, sel, bias, mayClear) {
+    var out;
+    for (var i = 0; i &lt; sel.ranges.length; i++) {
+      var range = sel.ranges[i];
+      var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear);
+      var newHead = skipAtomic(doc, range.head, bias, mayClear);
+      if (out || newAnchor != range.anchor || newHead != range.head) {
+        if (!out) out = sel.ranges.slice(0, i);
+        out[i] = new Range(newAnchor, newHead);
+      }
+    }
+    return out ? normalizeSelection(out, sel.primIndex) : sel;
+  }
+
+  // Ensure a given position is not inside an atomic range.
+  function skipAtomic(doc, pos, bias, mayClear) {
+    var flipped = false, curPos = pos;
+    var dir = bias || 1;
+    doc.cantEdit = false;
+    search: for (;;) {
+      var line = getLine(doc, curPos.line);
+      if (line.markedSpans) {
+        for (var i = 0; i &lt; line.markedSpans.length; ++i) {
+          var sp = line.markedSpans[i], m = sp.marker;
+          if ((sp.from == null || (m.inclusiveLeft ? sp.from &lt;= curPos.ch : sp.from &lt; curPos.ch)) &amp;&amp;
+              (sp.to == null || (m.inclusiveRight ? sp.to &gt;= curPos.ch : sp.to &gt; curPos.ch))) {
+            if (mayClear) {
+              signal(m, &quot;beforeCursorEnter&quot;);
+              if (m.explicitlyCleared) {
+                if (!line.markedSpans) break;
+                else {--i; continue;}
+              }
+            }
+            if (!m.atomic) continue;
+            var newPos = m.find(dir &lt; 0 ? -1 : 1);
+            if (cmp(newPos, curPos) == 0) {
+              newPos.ch += dir;
+              if (newPos.ch &lt; 0) {
+                if (newPos.line &gt; doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
+                else newPos = null;
+              } else if (newPos.ch &gt; line.text.length) {
+                if (newPos.line &lt; doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
+                else newPos = null;
+              }
+              if (!newPos) {
+                if (flipped) {
+                  // Driven in a corner -- no valid cursor position found at all
+                  // -- try again *with* clearing, if we didn't already
+                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
+                  // Otherwise, turn off editing until further notice, and return the start of the doc
+                  doc.cantEdit = true;
+                  return Pos(doc.first, 0);
+                }
+                flipped = true; newPos = pos; dir = -dir;
+              }
+            }
+            curPos = newPos;
+            continue search;
+          }
+        }
+      }
+      return curPos;
+    }
+  }
+
+  // SELECTION DRAWING
+
+  // Redraw the selection and/or cursor
</ins><span class="cx">   function updateSelection(cm) {
</span><del>-    var display = cm.display;
-    var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
-    if (collapsed || cm.options.showCursorWhenSelecting)
-      updateSelectionCursor(cm);
-    else
-      display.cursor.style.display = display.otherCursor.style.display = &quot;none&quot;;
-    if (!collapsed)
-      updateSelectionRange(cm);
-    else
-      display.selectionDiv.style.display = &quot;none&quot;;
</del><ins>+    var display = cm.display, doc = cm.doc;
+    var curFragment = document.createDocumentFragment();
+    var selFragment = document.createDocumentFragment();
</ins><span class="cx"> 
</span><ins>+    for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+      var range = doc.sel.ranges[i];
+      var collapsed = range.empty();
+      if (collapsed || cm.options.showCursorWhenSelecting)
+        drawSelectionCursor(cm, range, curFragment);
+      if (!collapsed)
+        drawSelectionRange(cm, range, selFragment);
+    }
+
</ins><span class="cx">     // Move the hidden textarea near the cursor to prevent scrolling artifacts
</span><span class="cx">     if (cm.options.moveInputWithCursor) {
</span><del>-      var headPos = cursorCoords(cm, cm.doc.sel.head, &quot;div&quot;);
-      var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
-      display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
-                                                        headPos.top + lineOff.top - wrapOff.top)) + &quot;px&quot;;
-      display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
-                                                         headPos.left + lineOff.left - wrapOff.left)) + &quot;px&quot;;
</del><ins>+      var headPos = cursorCoords(cm, doc.sel.primary().head, &quot;div&quot;);
+      var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
+      var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+                                     headPos.top + lineOff.top - wrapOff.top));
+      var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+                                      headPos.left + lineOff.left - wrapOff.left));
+      display.inputDiv.style.top = top + &quot;px&quot;;
+      display.inputDiv.style.left = left + &quot;px&quot;;
</ins><span class="cx">     }
</span><ins>+
+    removeChildrenAndAdd(display.cursorDiv, curFragment);
+    removeChildrenAndAdd(display.selectionDiv, selFragment);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // No selection, plain cursor
-  function updateSelectionCursor(cm) {
-    var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, &quot;div&quot;);
-    display.cursor.style.left = pos.left + &quot;px&quot;;
-    display.cursor.style.top = pos.top + &quot;px&quot;;
-    display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + &quot;px&quot;;
-    display.cursor.style.display = &quot;&quot;;
</del><ins>+  // Draws a cursor for the given range
+  function drawSelectionCursor(cm, range, output) {
+    var pos = cursorCoords(cm, range.head, &quot;div&quot;);
</ins><span class="cx"> 
</span><ins>+    var cursor = output.appendChild(elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor&quot;));
+    cursor.style.left = pos.left + &quot;px&quot;;
+    cursor.style.top = pos.top + &quot;px&quot;;
+    cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + &quot;px&quot;;
+
</ins><span class="cx">     if (pos.other) {
</span><del>-      display.otherCursor.style.display = &quot;&quot;;
-      display.otherCursor.style.left = pos.other.left + &quot;px&quot;;
-      display.otherCursor.style.top = pos.other.top + &quot;px&quot;;
-      display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + &quot;px&quot;;
-    } else { display.otherCursor.style.display = &quot;none&quot;; }
</del><ins>+      // Secondary cursor, shown when on a 'jump' in bi-directional text
+      var otherCursor = output.appendChild(elt(&quot;div&quot;, &quot;\u00a0&quot;, &quot;CodeMirror-cursor CodeMirror-secondarycursor&quot;));
+      otherCursor.style.display = &quot;&quot;;
+      otherCursor.style.left = pos.other.left + &quot;px&quot;;
+      otherCursor.style.top = pos.other.top + &quot;px&quot;;
+      otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + &quot;px&quot;;
+    }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Highlight selection
-  function updateSelectionRange(cm) {
-    var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
</del><ins>+  // Draws the given range as a highlighted selection
+  function drawSelectionRange(cm, range, output) {
+    var display = cm.display, doc = cm.doc;
</ins><span class="cx">     var fragment = document.createDocumentFragment();
</span><del>-    var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
</del><ins>+    var padding = paddingH(cm.display), leftSide = padding.left, rightSide = display.lineSpace.offsetWidth - padding.right;
</ins><span class="cx"> 
</span><span class="cx">     function add(left, top, width, bottom) {
</span><span class="cx">       if (top &lt; 0) top = 0;
</span><ins>+      top = Math.round(top);
+      bottom = Math.round(bottom);
</ins><span class="cx">       fragment.appendChild(elt(&quot;div&quot;, null, &quot;CodeMirror-selected&quot;, &quot;position: absolute; left: &quot; + left +
</span><del>-                               &quot;px; top: &quot; + top + &quot;px; width: &quot; + (width == null ? clientWidth - left : width) +
</del><ins>+                               &quot;px; top: &quot; + top + &quot;px; width: &quot; + (width == null ? rightSide - left : width) +
</ins><span class="cx">                                &quot;px; height: &quot; + (bottom - top) + &quot;px&quot;));
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -840,44 +1317,44 @@
</span><span class="cx">           left = leftPos.left;
</span><span class="cx">           right = rightPos.right;
</span><span class="cx">         }
</span><del>-        if (fromArg == null &amp;&amp; from == 0) left = pl;
</del><ins>+        if (fromArg == null &amp;&amp; from == 0) left = leftSide;
</ins><span class="cx">         if (rightPos.top - leftPos.top &gt; 3) { // Different lines, draw top part
</span><span class="cx">           add(left, leftPos.top, null, leftPos.bottom);
</span><del>-          left = pl;
</del><ins>+          left = leftSide;
</ins><span class="cx">           if (leftPos.bottom &lt; rightPos.top) add(left, leftPos.bottom, null, rightPos.top);
</span><span class="cx">         }
</span><del>-        if (toArg == null &amp;&amp; to == lineLen) right = clientWidth;
</del><ins>+        if (toArg == null &amp;&amp; to == lineLen) right = rightSide;
</ins><span class="cx">         if (!start || leftPos.top &lt; start.top || leftPos.top == start.top &amp;&amp; leftPos.left &lt; start.left)
</span><span class="cx">           start = leftPos;
</span><span class="cx">         if (!end || rightPos.bottom &gt; end.bottom || rightPos.bottom == end.bottom &amp;&amp; rightPos.right &gt; end.right)
</span><span class="cx">           end = rightPos;
</span><del>-        if (left &lt; pl + 1) left = pl;
</del><ins>+        if (left &lt; leftSide + 1) left = leftSide;
</ins><span class="cx">         add(left, rightPos.top, right - left, rightPos.bottom);
</span><span class="cx">       });
</span><span class="cx">       return {start: start, end: end};
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (sel.from.line == sel.to.line) {
-      drawForLine(sel.from.line, sel.from.ch, sel.to.ch);
</del><ins>+    var sFrom = range.from(), sTo = range.to();
+    if (sFrom.line == sTo.line) {
+      drawForLine(sFrom.line, sFrom.ch, sTo.ch);
</ins><span class="cx">     } else {
</span><del>-      var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line);
-      var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine);
-      var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end;
-      var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start;
</del><ins>+      var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
+      var singleVLine = visualLine(fromLine) == visualLine(toLine);
+      var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
+      var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
</ins><span class="cx">       if (singleVLine) {
</span><span class="cx">         if (leftEnd.top &lt; rightStart.top - 2) {
</span><span class="cx">           add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
</span><del>-          add(pl, rightStart.top, rightStart.left, rightStart.bottom);
</del><ins>+          add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
</ins><span class="cx">         } else {
</span><span class="cx">           add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">       if (leftEnd.bottom &lt; rightStart.top)
</span><del>-        add(pl, leftEnd.bottom, null, rightStart.top);
</del><ins>+        add(leftSide, leftEnd.bottom, null, rightStart.top);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    removeChildrenAndAdd(display.selectionDiv, fragment);
-    display.selectionDiv.style.display = &quot;&quot;;
</del><ins>+    output.appendChild(fragment);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Cursor-blinking
</span><span class="lines">@@ -886,37 +1363,38 @@
</span><span class="cx">     var display = cm.display;
</span><span class="cx">     clearInterval(display.blinker);
</span><span class="cx">     var on = true;
</span><del>-    display.cursor.style.visibility = display.otherCursor.style.visibility = &quot;&quot;;
</del><ins>+    display.cursorDiv.style.visibility = &quot;&quot;;
</ins><span class="cx">     if (cm.options.cursorBlinkRate &gt; 0)
</span><span class="cx">       display.blinker = setInterval(function() {
</span><del>-        display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? &quot;&quot; : &quot;hidden&quot;;
</del><ins>+        display.cursorDiv.style.visibility = (on = !on) ? &quot;&quot; : &quot;hidden&quot;;
</ins><span class="cx">       }, cm.options.cursorBlinkRate);
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // HIGHLIGHT WORKER
</span><span class="cx"> 
</span><span class="cx">   function startWorker(cm, time) {
</span><del>-    if (cm.doc.mode.startState &amp;&amp; cm.doc.frontier &lt; cm.display.showingTo)
</del><ins>+    if (cm.doc.mode.startState &amp;&amp; cm.doc.frontier &lt; cm.display.viewTo)
</ins><span class="cx">       cm.state.highlight.set(time, bind(highlightWorker, cm));
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function highlightWorker(cm) {
</span><span class="cx">     var doc = cm.doc;
</span><span class="cx">     if (doc.frontier &lt; doc.first) doc.frontier = doc.first;
</span><del>-    if (doc.frontier &gt;= cm.display.showingTo) return;
</del><ins>+    if (doc.frontier &gt;= cm.display.viewTo) return;
</ins><span class="cx">     var end = +new Date + cm.options.workTime;
</span><span class="cx">     var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
</span><del>-    var changed = [], prevChange;
-    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
-      if (doc.frontier &gt;= cm.display.showingFrom) { // Visible
</del><ins>+
+    runInOp(cm, function() {
+    doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
+      if (doc.frontier &gt;= cm.display.viewFrom) { // Visible
</ins><span class="cx">         var oldStyles = line.styles;
</span><del>-        line.styles = highlightLine(cm, line, state, true);
</del><ins>+        var highlighted = highlightLine(cm, line, state, true);
+        line.styles = highlighted.styles;
+        if (highlighted.classes) line.styleClasses = highlighted.classes;
+        else if (line.styleClasses) line.styleClasses = null;
</ins><span class="cx">         var ischange = !oldStyles || oldStyles.length != line.styles.length;
</span><span class="cx">         for (var i = 0; !ischange &amp;&amp; i &lt; oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
</span><del>-        if (ischange) {
-          if (prevChange &amp;&amp; prevChange.end == doc.frontier) prevChange.end++;
-          else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
-        }
</del><ins>+        if (ischange) regLineChange(cm, doc.frontier, &quot;text&quot;);
</ins><span class="cx">         line.stateAfter = copyState(doc.mode, state);
</span><span class="cx">       } else {
</span><span class="cx">         processLine(cm, line.text, state);
</span><span class="lines">@@ -928,11 +1406,7 @@
</span><span class="cx">         return true;
</span><span class="cx">       }
</span><span class="cx">     });
</span><del>-    if (changed.length)
-      operation(cm, function() {
-        for (var i = 0; i &lt; changed.length; ++i)
-          regChange(this, changed[i].start, changed[i].end);
-      })();
</del><ins>+    });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Finds the line to start with when starting a parse. Tries to
</span><span class="lines">@@ -964,7 +1438,7 @@
</span><span class="cx">     else state = copyState(doc.mode, state);
</span><span class="cx">     doc.iter(pos, n, function(line) {
</span><span class="cx">       processLine(cm, line.text, state);
</span><del>-      var save = pos == n - 1 || pos % 5 == 0 || pos &gt;= display.showingFrom &amp;&amp; pos &lt; display.showingTo;
</del><ins>+      var save = pos == n - 1 || pos % 5 == 0 || pos &gt;= display.viewFrom &amp;&amp; pos &lt; display.viewTo;
</ins><span class="cx">       line.stateAfter = save ? copyState(doc.mode, state) : null;
</span><span class="cx">       ++pos;
</span><span class="cx">     });
</span><span class="lines">@@ -976,183 +1450,222 @@
</span><span class="cx"> 
</span><span class="cx">   function paddingTop(display) {return display.lineSpace.offsetTop;}
</span><span class="cx">   function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
</span><del>-  function paddingLeft(display) {
-    var e = removeChildrenAndAdd(display.measure, elt(&quot;pre&quot;, null, null, &quot;text-align: left&quot;)).appendChild(elt(&quot;span&quot;, &quot;x&quot;));
-    return e.offsetLeft;
</del><ins>+  function paddingH(display) {
+    if (display.cachedPaddingH) return display.cachedPaddingH;
+    var e = removeChildrenAndAdd(display.measure, elt(&quot;pre&quot;, &quot;x&quot;));
+    var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
+    return display.cachedPaddingH = {left: parseInt(style.paddingLeft),
+                                     right: parseInt(style.paddingRight)};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureChar(cm, line, ch, data, bias) {
-    var dir = -1;
-    data = data || measureLine(cm, line);
-    if (data.crude) {
-      var left = data.left + ch * data.width;
-      return {left: left, right: left + data.width, top: data.top, bottom: data.bottom};
</del><ins>+  // Ensure the lineView.wrapping.heights array is populated. This is
+  // an array of bottom offsets for the lines that make up a drawn
+  // line. When lineWrapping is on, there might be more than one
+  // height.
+  function ensureLineHeights(cm, lineView, rect) {
+    var wrapping = cm.options.lineWrapping;
+    var curWidth = wrapping &amp;&amp; cm.display.scroller.clientWidth;
+    if (!lineView.measure.heights || wrapping &amp;&amp; lineView.measure.width != curWidth) {
+      var heights = lineView.measure.heights = [];
+      if (wrapping) {
+        lineView.measure.width = curWidth;
+        var rects = lineView.text.firstChild.getClientRects();
+        for (var i = 0; i &lt; rects.length - 1; i++) {
+          var cur = rects[i], next = rects[i + 1];
+          if (Math.abs(cur.bottom - next.bottom) &gt; 2)
+            heights.push((cur.bottom + next.top) / 2 - rect.top);
+        }
+      }
+      heights.push(rect.bottom - rect.top);
</ins><span class="cx">     }
</span><ins>+  }
</ins><span class="cx"> 
</span><del>-    for (var pos = ch;; pos += dir) {
-      var r = data[pos];
-      if (r) break;
-      if (dir &lt; 0 &amp;&amp; pos == 0) dir = 1;
-    }
-    bias = pos &gt; ch ? &quot;left&quot; : pos &lt; ch ? &quot;right&quot; : bias;
-    if (bias == &quot;left&quot; &amp;&amp; r.leftSide) r = r.leftSide;
-    else if (bias == &quot;right&quot; &amp;&amp; r.rightSide) r = r.rightSide;
-    return {left: pos &lt; ch ? r.right : r.left,
-            right: pos &gt; ch ? r.left : r.right,
-            top: r.top,
-            bottom: r.bottom};
</del><ins>+  // Find a line map (mapping character offsets to text nodes) and a
+  // measurement cache for the given line number. (A line view might
+  // contain multiple lines when collapsed ranges are present.)
+  function mapFromLineView(lineView, line, lineN) {
+    if (lineView.line == line)
+      return {map: lineView.measure.map, cache: lineView.measure.cache};
+    for (var i = 0; i &lt; lineView.rest.length; i++)
+      if (lineView.rest[i] == line)
+        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]};
+    for (var i = 0; i &lt; lineView.rest.length; i++)
+      if (lineNo(lineView.rest[i]) &gt; lineN)
+        return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function findCachedMeasurement(cm, line) {
-    var cache = cm.display.measureLineCache;
-    for (var i = 0; i &lt; cache.length; ++i) {
-      var memo = cache[i];
-      if (memo.text == line.text &amp;&amp; memo.markedSpans == line.markedSpans &amp;&amp;
-          cm.display.scroller.clientWidth == memo.width &amp;&amp;
-          memo.classes == line.textClass + &quot;|&quot; + line.wrapClass)
-        return memo;
-    }
</del><ins>+  // Render a line into the hidden node display.externalMeasured. Used
+  // when measurement is needed for a line that's not in the viewport.
+  function updateExternalMeasurement(cm, line) {
+    line = visualLine(line);
+    var lineN = lineNo(line);
+    var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
+    view.lineN = lineN;
+    var built = view.built = buildLineContent(cm, view);
+    view.text = built.pre;
+    removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
+    return view;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function clearCachedMeasurement(cm, line) {
-    var exists = findCachedMeasurement(cm, line);
-    if (exists) exists.text = exists.measure = exists.markedSpans = null;
</del><ins>+  // Get a {top, bottom, left, right} box (in line-local coordinates)
+  // for a given character.
+  function measureChar(cm, line, ch, bias) {
+    return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLine(cm, line) {
-    // First look in the cache
-    var cached = findCachedMeasurement(cm, line);
-    if (cached) return cached.measure;
-
-    // Failing that, recompute and store result in cache
-    var measure = measureLineInner(cm, line);
-    var cache = cm.display.measureLineCache;
-    var memo = {text: line.text, width: cm.display.scroller.clientWidth,
-                markedSpans: line.markedSpans, measure: measure,
-                classes: line.textClass + &quot;|&quot; + line.wrapClass};
-    if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
-    else cache.push(memo);
-    return measure;
</del><ins>+  // Find a line view that corresponds to the given line number.
+  function findViewForLine(cm, lineN) {
+    if (lineN &gt;= cm.display.viewFrom &amp;&amp; lineN &lt; cm.display.viewTo)
+      return cm.display.view[findViewIndex(cm, lineN)];
+    var ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; lineN &gt;= ext.lineN &amp;&amp; lineN &lt; ext.lineN + ext.size)
+      return ext;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLineInner(cm, line) {
-    if (!cm.options.lineWrapping &amp;&amp; line.text.length &gt;= cm.options.crudeMeasuringFrom)
-      return crudelyMeasureLine(cm, line);
</del><ins>+  // Measurement can be split in two steps, the set-up work that
+  // applies to the whole line, and the measurement of the actual
+  // character. Functions like coordsChar, that need to do a lot of
+  // measurements in a row, can thus ensure that the set-up work is
+  // only done once.
+  function prepareMeasureForLine(cm, line) {
+    var lineN = lineNo(line);
+    var view = findViewForLine(cm, lineN);
+    if (view &amp;&amp; !view.text)
+      view = null;
+    else if (view &amp;&amp; view.changes)
+      updateLineForChanges(cm, view, lineN, getDimensions(cm));
+    if (!view)
+      view = updateExternalMeasurement(cm, line);
</ins><span class="cx"> 
</span><del>-    var display = cm.display, measure = emptyArray(line.text.length);
-    var pre = buildLineContent(cm, line, measure, true).pre;
</del><ins>+    var info = mapFromLineView(view, line, lineN);
+    return {
+      line: line, view: view, rect: null,
+      map: info.map, cache: info.cache, before: info.before,
+      hasHeights: false
+    };
+  }
</ins><span class="cx"> 
</span><del>-    // IE does not cache element positions of inline elements between
-    // calls to getBoundingClientRect. This makes the loop below,
-    // which gathers the positions of all the characters on the line,
-    // do an amount of layout work quadratic to the number of
-    // characters. When line wrapping is off, we try to improve things
-    // by first subdividing the line into a bunch of inline blocks, so
-    // that IE can reuse most of the layout information from caches
-    // for those blocks. This does interfere with line wrapping, so it
-    // doesn't work when wrapping is on, but in that case the
-    // situation is slightly better, since IE does cache line-wrapping
-    // information and only recomputes per-line.
-    if (ie &amp;&amp; !ie_lt8 &amp;&amp; !cm.options.lineWrapping &amp;&amp; pre.childNodes.length &gt; 100) {
-      var fragment = document.createDocumentFragment();
-      var chunk = 10, n = pre.childNodes.length;
-      for (var i = 0, chunks = Math.ceil(n / chunk); i &lt; chunks; ++i) {
-        var wrap = elt(&quot;div&quot;, null, null, &quot;display: inline-block&quot;);
-        for (var j = 0; j &lt; chunk &amp;&amp; n; ++j) {
-          wrap.appendChild(pre.firstChild);
-          --n;
-        }
-        fragment.appendChild(wrap);
</del><ins>+  // Given a prepared measurement object, measures the position of an
+  // actual character (or fetches it from the cache).
+  function measureCharPrepared(cm, prepared, ch, bias) {
+    if (prepared.before) ch = -1;
+    var key = ch + (bias || &quot;&quot;), found;
+    if (prepared.cache.hasOwnProperty(key)) {
+      found = prepared.cache[key];
+    } else {
+      if (!prepared.rect)
+        prepared.rect = prepared.view.text.getBoundingClientRect();
+      if (!prepared.hasHeights) {
+        ensureLineHeights(cm, prepared.view, prepared.rect);
+        prepared.hasHeights = true;
</ins><span class="cx">       }
</span><del>-      pre.appendChild(fragment);
</del><ins>+      found = measureCharInner(cm, prepared, ch, bias);
+      if (!found.bogus) prepared.cache[key] = found;
</ins><span class="cx">     }
</span><ins>+    return {left: found.left, right: found.right, top: found.top, bottom: found.bottom};
+  }
</ins><span class="cx"> 
</span><del>-    removeChildrenAndAdd(display.measure, pre);
</del><ins>+  var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
</ins><span class="cx"> 
</span><del>-    var outer = getRect(display.lineDiv);
-    var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
-    // Work around an IE7/8 bug where it will sometimes have randomly
-    // replaced our pre with a clone at this point.
-    if (ie_lt9 &amp;&amp; display.measure.first != pre)
-      removeChildrenAndAdd(display.measure, pre);
</del><ins>+  function measureCharInner(cm, prepared, ch, bias) {
+    var map = prepared.map;
</ins><span class="cx"> 
</span><del>-    function measureRect(rect) {
-      var top = rect.top - outer.top, bot = rect.bottom - outer.top;
-      if (bot &gt; maxBot) bot = maxBot;
-      if (top &lt; 0) top = 0;
-      for (var i = vranges.length - 2; i &gt;= 0; i -= 2) {
-        var rtop = vranges[i], rbot = vranges[i+1];
-        if (rtop &gt; bot || rbot &lt; top) continue;
-        if (rtop &lt;= top &amp;&amp; rbot &gt;= bot ||
-            top &lt;= rtop &amp;&amp; bot &gt;= rbot ||
-            Math.min(bot, rbot) - Math.max(top, rtop) &gt;= (bot - top) &gt;&gt; 1) {
-          vranges[i] = Math.min(top, rtop);
-          vranges[i+1] = Math.max(bot, rbot);
-          break;
-        }
</del><ins>+    var node, start, end, collapse;
+    // First, search the line map for the text node corresponding to,
+    // or closest to, the target character.
+    for (var i = 0; i &lt; map.length; i += 3) {
+      var mStart = map[i], mEnd = map[i + 1];
+      if (ch &lt; mStart) {
+        start = 0; end = 1;
+        collapse = &quot;left&quot;;
+      } else if (ch &lt; mEnd) {
+        start = ch - mStart;
+        end = start + 1;
+      } else if (i == map.length - 3 || ch == mEnd &amp;&amp; map[i + 3] &gt; ch) {
+        end = mEnd - mStart;
+        start = end - 1;
+        if (ch &gt;= mEnd) collapse = &quot;right&quot;;
</ins><span class="cx">       }
</span><del>-      if (i &lt; 0) { i = vranges.length; vranges.push(top, bot); }
-      return {left: rect.left - outer.left,
-              right: rect.right - outer.left,
-              top: i, bottom: null};
</del><ins>+      if (start != null) {
+        node = map[i + 2];
+        if (mStart == mEnd &amp;&amp; bias == (node.insertLeft ? &quot;left&quot; : &quot;right&quot;))
+          collapse = bias;
+        if (bias == &quot;left&quot; &amp;&amp; start == 0)
+          while (i &amp;&amp; map[i - 2] == map[i - 3] &amp;&amp; map[i - 1].insertLeft) {
+            node = map[(i -= 3) + 2];
+            collapse = &quot;left&quot;;
+          }
+        if (bias == &quot;right&quot; &amp;&amp; start == mEnd - mStart)
+          while (i &lt; map.length - 3 &amp;&amp; map[i + 3] == map[i + 4] &amp;&amp; !map[i + 5].insertLeft) {
+            node = map[(i += 3) + 2];
+            collapse = &quot;right&quot;;
+          }
+        break;
+      }
</ins><span class="cx">     }
</span><del>-    function finishRect(rect) {
-      rect.bottom = vranges[rect.top+1];
-      rect.top = vranges[rect.top];
-    }
</del><span class="cx"> 
</span><del>-    for (var i = 0, cur; i &lt; measure.length; ++i) if (cur = measure[i]) {
-      var node = cur, rect = null;
-      // A widget might wrap, needs special care
-      if (/\bCodeMirror-widget\b/.test(cur.className) &amp;&amp; cur.getClientRects) {
-        if (cur.firstChild.nodeType == 1) node = cur.firstChild;
-        var rects = node.getClientRects();
-        if (rects.length &gt; 1) {
-          rect = data[i] = measureRect(rects[0]);
-          rect.rightSide = measureRect(rects[rects.length - 1]);
-        }
</del><ins>+    var rect;
+    if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
+      while (start &amp;&amp; isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
+      while (mStart + end &lt; mEnd &amp;&amp; isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
+      if (ie_upto8 &amp;&amp; start == 0 &amp;&amp; end == mEnd - mStart) {
+        rect = node.parentNode.getBoundingClientRect();
+      } else if (ie &amp;&amp; cm.options.lineWrapping) {
+        var rects = range(node, start, end).getClientRects();
+        if (rects.length)
+          rect = rects[bias == &quot;right&quot; ? rects.length - 1 : 0];
+        else
+          rect = nullRect;
+      } else {
+        rect = range(node, start, end).getBoundingClientRect();
</ins><span class="cx">       }
</span><del>-      if (!rect) rect = data[i] = measureRect(getRect(node));
-      if (cur.measureRight) rect.right = getRect(cur.measureRight).left;
-      if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide));
</del><ins>+    } else { // If it is a widget, simply get the box for the whole widget.
+      if (start &gt; 0) collapse = bias = &quot;right&quot;;
+      var rects;
+      if (cm.options.lineWrapping &amp;&amp; (rects = node.getClientRects()).length &gt; 1)
+        rect = rects[bias == &quot;right&quot; ? rects.length - 1 : 0];
+      else
+        rect = node.getBoundingClientRect();
</ins><span class="cx">     }
</span><del>-    removeChildren(cm.display.measure);
-    for (var i = 0, cur; i &lt; data.length; ++i) if (cur = data[i]) {
-      finishRect(cur);
-      if (cur.leftSide) finishRect(cur.leftSide);
-      if (cur.rightSide) finishRect(cur.rightSide);
</del><ins>+    if (ie_upto8 &amp;&amp; !start &amp;&amp; (!rect || !rect.left &amp;&amp; !rect.right)) {
+      var rSpan = node.parentNode.getClientRects()[0];
+      if (rSpan)
+        rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom};
+      else
+        rect = nullRect;
</ins><span class="cx">     }
</span><del>-    return data;
-  }
</del><span class="cx"> 
</span><del>-  function crudelyMeasureLine(cm, line) {
-    var copy = new Line(line.text.slice(0, 100), null);
-    if (line.textClass) copy.textClass = line.textClass;
-    var measure = measureLineInner(cm, copy);
-    var left = measureChar(cm, copy, 0, measure, &quot;left&quot;);
-    var right = measureChar(cm, copy, 99, measure, &quot;right&quot;);
-    return {crude: true, top: left.top, left: left.left, bottom: left.bottom, width: (right.right - left.left) / 100};
</del><ins>+    var top, bot = (rect.bottom + rect.top) / 2 - prepared.rect.top;
+    var heights = prepared.view.measure.heights;
+    for (var i = 0; i &lt; heights.length - 1; i++)
+      if (bot &lt; heights[i]) break;
+    top = i ? heights[i - 1] : 0; bot = heights[i];
+    var result = {left: (collapse == &quot;right&quot; ? rect.right : rect.left) - prepared.rect.left,
+                  right: (collapse == &quot;left&quot; ? rect.left : rect.right) - prepared.rect.left,
+                  top: top, bottom: bot};
+    if (!rect.left &amp;&amp; !rect.right) result.bogus = true;
+    return result;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function measureLineWidth(cm, line) {
-    var hasBadSpan = false;
-    if (line.markedSpans) for (var i = 0; i &lt; line.markedSpans; ++i) {
-      var sp = line.markedSpans[i];
-      if (sp.collapsed &amp;&amp; (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
</del><ins>+  function clearLineMeasurementCacheFor(lineView) {
+    if (lineView.measure) {
+      lineView.measure.cache = {};
+      lineView.measure.heights = null;
+      if (lineView.rest) for (var i = 0; i &lt; lineView.rest.length; i++)
+        lineView.measure.caches[i] = {};
</ins><span class="cx">     }
</span><del>-    var cached = !hasBadSpan &amp;&amp; findCachedMeasurement(cm, line);
-    if (cached || line.text.length &gt;= cm.options.crudeMeasuringFrom)
-      return measureChar(cm, line, line.text.length, cached &amp;&amp; cached.measure, &quot;right&quot;).right;
</del><ins>+  }
</ins><span class="cx"> 
</span><del>-    var pre = buildLineContent(cm, line, null, true).pre;
-    var end = pre.appendChild(zeroWidthElement(cm.display.measure));
-    removeChildrenAndAdd(cm.display.measure, pre);
-    return getRect(end).right - getRect(cm.display.lineDiv).left;
</del><ins>+  function clearLineMeasurementCache(cm) {
+    cm.display.externalMeasure = null;
+    removeChildren(cm.display.lineMeasure);
+    for (var i = 0; i &lt; cm.display.view.length; i++)
+      clearLineMeasurementCacheFor(cm.display.view[i]);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function clearCaches(cm) {
</span><del>-    cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
-    cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
</del><ins>+    clearLineMeasurementCache(cm);
+    cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
</ins><span class="cx">     if (!cm.options.lineWrapping) cm.display.maxLineChanged = true;
</span><span class="cx">     cm.display.lineNumChars = null;
</span><span class="cx">   }
</span><span class="lines">@@ -1160,7 +1673,9 @@
</span><span class="cx">   function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
</span><span class="cx">   function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
</span><span class="cx"> 
</span><del>-  // Context is one of &quot;line&quot;, &quot;div&quot; (display.lineDiv), &quot;local&quot;/null (editor), or &quot;page&quot;
</del><ins>+  // Converts a {top, bottom, left, right} box from line-local
+  // coordinates into another coordinate system. Context may be one of
+  // &quot;line&quot;, &quot;div&quot; (display.lineDiv), &quot;local&quot;/null (editor), or &quot;page&quot;.
</ins><span class="cx">   function intoCoordSystem(cm, lineObj, rect, context) {
</span><span class="cx">     if (lineObj.widgets) for (var i = 0; i &lt; lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
</span><span class="cx">       var size = widgetHeight(lineObj.widgets[i]);
</span><span class="lines">@@ -1168,11 +1683,11 @@
</span><span class="cx">     }
</span><span class="cx">     if (context == &quot;line&quot;) return rect;
</span><span class="cx">     if (!context) context = &quot;local&quot;;
</span><del>-    var yOff = heightAtLine(cm, lineObj);
</del><ins>+    var yOff = heightAtLine(lineObj);
</ins><span class="cx">     if (context == &quot;local&quot;) yOff += paddingTop(cm.display);
</span><span class="cx">     else yOff -= cm.display.viewOffset;
</span><span class="cx">     if (context == &quot;page&quot; || context == &quot;window&quot;) {
</span><del>-      var lOff = getRect(cm.display.lineSpace);
</del><ins>+      var lOff = cm.display.lineSpace.getBoundingClientRect();
</ins><span class="cx">       yOff += lOff.top + (context == &quot;window&quot; ? 0 : pageScrollY());
</span><span class="cx">       var xOff = lOff.left + (context == &quot;window&quot; ? 0 : pageScrollX());
</span><span class="cx">       rect.left += xOff; rect.right += xOff;
</span><span class="lines">@@ -1181,8 +1696,8 @@
</span><span class="cx">     return rect;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Context may be &quot;window&quot;, &quot;page&quot;, &quot;div&quot;, or &quot;local&quot;/null
-  // Result is in &quot;div&quot; coords
</del><ins>+  // Coverts a box from &quot;div&quot; coords to another coordinate system.
+  // Context may be &quot;window&quot;, &quot;page&quot;, &quot;div&quot;, or &quot;local&quot;/null.
</ins><span class="cx">   function fromCoordSystem(cm, coords, context) {
</span><span class="cx">     if (context == &quot;div&quot;) return coords;
</span><span class="cx">     var left = coords.left, top = coords.top;
</span><span class="lines">@@ -1191,25 +1706,28 @@
</span><span class="cx">       left -= pageScrollX();
</span><span class="cx">       top -= pageScrollY();
</span><span class="cx">     } else if (context == &quot;local&quot; || !context) {
</span><del>-      var localBox = getRect(cm.display.sizer);
</del><ins>+      var localBox = cm.display.sizer.getBoundingClientRect();
</ins><span class="cx">       left += localBox.left;
</span><span class="cx">       top += localBox.top;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var lineSpaceBox = getRect(cm.display.lineSpace);
</del><ins>+    var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
</ins><span class="cx">     return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top};
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function charCoords(cm, pos, context, lineObj, bias) {
</span><span class="cx">     if (!lineObj) lineObj = getLine(cm.doc, pos.line);
</span><del>-    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context);
</del><ins>+    return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function cursorCoords(cm, pos, context, lineObj, measurement) {
</del><ins>+  // Returns a box for a given cursor position, which may have an
+  // 'other' property containing the position of the secondary cursor
+  // on a bidi boundary.
+  function cursorCoords(cm, pos, context, lineObj, preparedMeasure) {
</ins><span class="cx">     lineObj = lineObj || getLine(cm.doc, pos.line);
</span><del>-    if (!measurement) measurement = measureLine(cm, lineObj);
</del><ins>+    if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj);
</ins><span class="cx">     function get(ch, right) {
</span><del>-      var m = measureChar(cm, lineObj, ch, measurement, right ? &quot;right&quot; : &quot;left&quot;);
</del><ins>+      var m = measureCharPrepared(cm, preparedMeasure, ch, right ? &quot;right&quot; : &quot;left&quot;);
</ins><span class="cx">       if (right) m.left = m.right; else m.right = m.left;
</span><span class="cx">       return intoCoordSystem(cm, lineObj, m, context);
</span><span class="cx">     }
</span><span class="lines">@@ -1235,43 +1753,59 @@
</span><span class="cx">     return val;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to cheaply estimate the coordinates for a position. Used for
+  // intermediate scroll updates.
+  function estimateCoords(cm, pos) {
+    var left = 0, pos = clipPos(cm.doc, pos);
+    if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch;
+    var lineObj = getLine(cm.doc, pos.line);
+    var top = heightAtLine(lineObj) + paddingTop(cm.display);
+    return {left: left, right: left, top: top, bottom: top + lineObj.height};
+  }
+
+  // Positions returned by coordsChar contain some extra information.
+  // xRel is the relative x position of the input coordinates compared
+  // to the found position (so xRel &gt; 0 means the coordinates are to
+  // the right of the character position, for example). When outside
+  // is true, that means the coordinates lie outside the line's
+  // vertical range.
</ins><span class="cx">   function PosWithInfo(line, ch, outside, xRel) {
</span><del>-    var pos = new Pos(line, ch);
</del><ins>+    var pos = Pos(line, ch);
</ins><span class="cx">     pos.xRel = xRel;
</span><span class="cx">     if (outside) pos.outside = true;
</span><span class="cx">     return pos;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Coords must be lineSpace-local
</del><ins>+  // Compute the character position closest to the given coordinates.
+  // Input must be lineSpace-local (&quot;div&quot; coordinate system).
</ins><span class="cx">   function coordsChar(cm, x, y) {
</span><span class="cx">     var doc = cm.doc;
</span><span class="cx">     y += cm.display.viewOffset;
</span><span class="cx">     if (y &lt; 0) return PosWithInfo(doc.first, 0, true, -1);
</span><del>-    var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
-    if (lineNo &gt; last)
</del><ins>+    var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+    if (lineN &gt; last)
</ins><span class="cx">       return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1);
</span><span class="cx">     if (x &lt; 0) x = 0;
</span><span class="cx"> 
</span><ins>+    var lineObj = getLine(doc, lineN);
</ins><span class="cx">     for (;;) {
</span><del>-      var lineObj = getLine(doc, lineNo);
-      var found = coordsCharInner(cm, lineObj, lineNo, x, y);
</del><ins>+      var found = coordsCharInner(cm, lineObj, lineN, x, y);
</ins><span class="cx">       var merged = collapsedSpanAtEnd(lineObj);
</span><del>-      var mergedPos = merged &amp;&amp; merged.find();
</del><ins>+      var mergedPos = merged &amp;&amp; merged.find(0, true);
</ins><span class="cx">       if (merged &amp;&amp; (found.ch &gt; mergedPos.from.ch || found.ch == mergedPos.from.ch &amp;&amp; found.xRel &gt; 0))
</span><del>-        lineNo = mergedPos.to.line;
</del><ins>+        lineN = lineNo(lineObj = mergedPos.to.line);
</ins><span class="cx">       else
</span><span class="cx">         return found;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function coordsCharInner(cm, lineObj, lineNo, x, y) {
</span><del>-    var innerOff = y - heightAtLine(cm, lineObj);
</del><ins>+    var innerOff = y - heightAtLine(lineObj);
</ins><span class="cx">     var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
</span><del>-    var measurement = measureLine(cm, lineObj);
</del><ins>+    var preparedMeasure = prepareMeasureForLine(cm, lineObj);
</ins><span class="cx"> 
</span><span class="cx">     function getX(ch) {
</span><del>-      var sp = cursorCoords(cm, Pos(lineNo, ch), &quot;line&quot;,
-                            lineObj, measurement);
</del><ins>+      var sp = cursorCoords(cm, Pos(lineNo, ch), &quot;line&quot;, lineObj, preparedMeasure);
</ins><span class="cx">       wrongLine = true;
</span><span class="cx">       if (innerOff &gt; sp.bottom) return sp.left - adjust;
</span><span class="cx">       else if (innerOff &lt; sp.top) return sp.left + adjust;
</span><span class="lines">@@ -1289,9 +1823,9 @@
</span><span class="cx">       if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from &lt;= 1) {
</span><span class="cx">         var ch = x &lt; fromX || x - fromX &lt;= toX - x ? from : to;
</span><span class="cx">         var xDiff = x - (ch == from ? fromX : toX);
</span><del>-        while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
</del><ins>+        while (isExtendingChar(lineObj.text.charAt(ch))) ++ch;
</ins><span class="cx">         var pos = PosWithInfo(lineNo, ch, ch == from ? fromOutside : toOutside,
</span><del>-                              xDiff &lt; 0 ? -1 : xDiff ? 1 : 0);
</del><ins>+                              xDiff &lt; -1 ? -1 : xDiff &gt; 1 ? 1 : 0);
</ins><span class="cx">         return pos;
</span><span class="cx">       }
</span><span class="cx">       var step = Math.ceil(dist / 2), middle = from + step;
</span><span class="lines">@@ -1306,6 +1840,7 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   var measureText;
</span><ins>+  // Compute the default text height.
</ins><span class="cx">   function textHeight(display) {
</span><span class="cx">     if (display.cachedTextHeight != null) return display.cachedTextHeight;
</span><span class="cx">     if (measureText == null) {
</span><span class="lines">@@ -1325,84 +1860,89 @@
</span><span class="cx">     return height || 1;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute the default character width.
</ins><span class="cx">   function charWidth(display) {
</span><span class="cx">     if (display.cachedCharWidth != null) return display.cachedCharWidth;
</span><del>-    var anchor = elt(&quot;span&quot;, &quot;x&quot;);
</del><ins>+    var anchor = elt(&quot;span&quot;, &quot;xxxxxxxxxx&quot;);
</ins><span class="cx">     var pre = elt(&quot;pre&quot;, [anchor]);
</span><span class="cx">     removeChildrenAndAdd(display.measure, pre);
</span><del>-    var width = anchor.offsetWidth;
</del><ins>+    var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
</ins><span class="cx">     if (width &gt; 2) display.cachedCharWidth = width;
</span><span class="cx">     return width || 10;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // OPERATIONS
</span><span class="cx"> 
</span><del>-  // Operations are used to wrap changes in such a way that each
-  // change won't have to update the cursor and display (which would
-  // be awkward, slow, and error-prone), but instead updates are
-  // batched and then all combined and executed at once.
</del><ins>+  // Operations are used to wrap a series of changes to the editor
+  // state in such a way that each change won't have to update the
+  // cursor and display (which would be awkward, slow, and
+  // error-prone). Instead, display updates are batched and then all
+  // combined and executed at once.
</ins><span class="cx"> 
</span><span class="cx">   var nextOpId = 0;
</span><ins>+  // Start a new operation.
</ins><span class="cx">   function startOperation(cm) {
</span><span class="cx">     cm.curOp = {
</span><del>-      // An array of ranges of lines that have to be updated. See
-      // updateDisplay.
-      changes: [],
-      forceUpdate: false,
-      updateInput: null,
-      userSelChange: null,
-      textChanged: null,
-      selectionChanged: false,
-      cursorActivity: false,
-      updateMaxLine: false,
-      updateScrollPos: false,
-      id: ++nextOpId
</del><ins>+      viewChanged: false,      // Flag that indicates that lines might need to be redrawn
+      startHeight: cm.doc.height, // Used to detect need to update scrollbar
+      forceUpdate: false,      // Used to force a redraw
+      updateInput: null,       // Whether to reset the input textarea
+      typing: false,           // Whether this reset should be careful to leave existing text (for compositing)
+      changeObjs: null,        // Accumulated changes, for firing change events
+      origin: null,            // Selection's origin
+      cursorActivity: false,   // Whether to fire a cursorActivity event
+      selectionChanged: false, // Whether the selection needs to be redrawn
+      updateMaxLine: false,    // Set when the widest line needs to be determined anew
+      scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+      scrollToPos: null,       // Used to scroll to a specific position
+      id: ++nextOpId           // Unique ID
</ins><span class="cx">     };
</span><span class="cx">     if (!delayedCallbackDepth++) delayedCallbacks = [];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Finish an operation, updating the display and signalling delayed events
</ins><span class="cx">   function endOperation(cm) {
</span><span class="cx">     var op = cm.curOp, doc = cm.doc, display = cm.display;
</span><span class="cx">     cm.curOp = null;
</span><span class="cx"> 
</span><del>-    if (op.updateMaxLine) computeMaxLength(cm);
-    if (display.maxLineChanged &amp;&amp; !cm.options.lineWrapping &amp;&amp; display.maxLine) {
-      var width = measureLineWidth(cm, display.maxLine);
-      display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + &quot;px&quot;;
-      display.maxLineChanged = false;
-      var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
-      if (maxScrollLeft &lt; doc.scrollLeft &amp;&amp; !op.updateScrollPos)
-        setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
-    }
-    var newScrollPos, updated;
-    if (op.updateScrollPos) {
-      newScrollPos = op.updateScrollPos;
-    } else if (op.selectionChanged &amp;&amp; display.scroller.clientHeight) { // don't rescroll if not visible
-      var coords = cursorCoords(cm, doc.sel.head);
-      newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
-    }
-    if (op.changes.length || op.forceUpdate || newScrollPos &amp;&amp; newScrollPos.scrollTop != null) {
-      updated = updateDisplay(cm, op.changes, newScrollPos &amp;&amp; newScrollPos.scrollTop, op.forceUpdate);
</del><ins>+    if (op.updateMaxLine) findMaxLine(cm);
+
+    // If it looks like an update might be needed, call updateDisplay
+    if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
+        op.scrollToPos &amp;&amp; (op.scrollToPos.from.line &lt; display.viewFrom ||
+                           op.scrollToPos.to.line &gt;= display.viewTo) ||
+        display.maxLineChanged &amp;&amp; cm.options.lineWrapping) {
+      var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
</ins><span class="cx">       if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
</span><span class="cx">     }
</span><ins>+    // If no update was run, but the selection changed, redraw that.
</ins><span class="cx">     if (!updated &amp;&amp; op.selectionChanged) updateSelection(cm);
</span><del>-    if (op.updateScrollPos) {
-      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop));
-      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft));
</del><ins>+    if (!updated &amp;&amp; op.startHeight != cm.doc.height) updateScrollbars(cm);
+
+    // Propagate the scroll position to the actual DOM scroller
+    if (op.scrollTop != null &amp;&amp; display.scroller.scrollTop != op.scrollTop) {
+      var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
</ins><span class="cx">       display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
</span><ins>+    }
+    if (op.scrollLeft != null &amp;&amp; display.scroller.scrollLeft != op.scrollLeft) {
+      var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
</ins><span class="cx">       display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
</span><span class="cx">       alignHorizontally(cm);
</span><del>-      if (op.scrollToPos)
-        scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
-                          clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
-    } else if (newScrollPos) {
-      scrollCursorIntoView(cm);
</del><span class="cx">     }
</span><ins>+    // If we need to scroll a specific position into view, do so.
+    if (op.scrollToPos) {
+      var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
+                                     clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
+      if (op.scrollToPos.isCursor &amp;&amp; cm.state.focused) maybeScrollWindow(cm, coords);
+    }
+
</ins><span class="cx">     if (op.selectionChanged) restartBlink(cm);
</span><span class="cx"> 
</span><span class="cx">     if (cm.state.focused &amp;&amp; op.updateInput)
</span><del>-      resetInput(cm, op.userSelChange);
</del><ins>+      resetInput(cm, op.typing);
</ins><span class="cx"> 
</span><ins>+    // Fire events for markers that are hidden/unidden by editing or
+    // undoing
</ins><span class="cx">     var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
</span><span class="cx">     if (hidden) for (var i = 0; i &lt; hidden.length; ++i)
</span><span class="cx">       if (!hidden[i].lines.length) signal(hidden[i], &quot;hide&quot;);
</span><span class="lines">@@ -1414,47 +1954,242 @@
</span><span class="cx">       delayed = delayedCallbacks;
</span><span class="cx">       delayedCallbacks = null;
</span><span class="cx">     }
</span><del>-    if (op.textChanged)
-      signal(cm, &quot;change&quot;, cm, op.textChanged);
-    if (op.cursorActivity) signal(cm, &quot;cursorActivity&quot;, cm);
</del><ins>+    // Fire change events, and delayed event handlers
+    if (op.changeObjs) {
+      for (var i = 0; i &lt; op.changeObjs.length; i++)
+        signal(cm, &quot;change&quot;, cm, op.changeObjs[i]);
+      signal(cm, &quot;changes&quot;, cm, op.changeObjs);
+    }
+    if (op.cursorActivity) signal(cm, &quot;cursorActivity&quot;, cm, op.origin);
</ins><span class="cx">     if (delayed) for (var i = 0; i &lt; delayed.length; ++i) delayed[i]();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Run the given function in an operation
+  function runInOp(cm, f) {
+    if (cm.curOp) return f();
+    startOperation(cm);
+    try { return f(); }
+    finally { endOperation(cm); }
+  }
</ins><span class="cx">   // Wraps a function in an operation. Returns the wrapped function.
</span><del>-  function operation(cm1, f) {
</del><ins>+  function operation(cm, f) {
</ins><span class="cx">     return function() {
</span><del>-      var cm = cm1 || this, withOp = !cm.curOp;
-      if (withOp) startOperation(cm);
-      try { var result = f.apply(cm, arguments); }
-      finally { if (withOp) endOperation(cm); }
-      return result;
</del><ins>+      if (cm.curOp) return f.apply(cm, arguments);
+      startOperation(cm);
+      try { return f.apply(cm, arguments); }
+      finally { endOperation(cm); }
</ins><span class="cx">     };
</span><span class="cx">   }
</span><del>-  function docOperation(f) {
</del><ins>+  // Used to add methods to editor and doc instances, wrapping them in
+  // operations.
+  function methodOp(f) {
</ins><span class="cx">     return function() {
</span><del>-      var withOp = this.cm &amp;&amp; !this.cm.curOp, result;
-      if (withOp) startOperation(this.cm);
-      try { result = f.apply(this, arguments); }
-      finally { if (withOp) endOperation(this.cm); }
-      return result;
</del><ins>+      if (this.curOp) return f.apply(this, arguments);
+      startOperation(this);
+      try { return f.apply(this, arguments); }
+      finally { endOperation(this); }
</ins><span class="cx">     };
</span><span class="cx">   }
</span><del>-  function runInOp(cm, f) {
-    var withOp = !cm.curOp, result;
-    if (withOp) startOperation(cm);
-    try { result = f(); }
-    finally { if (withOp) endOperation(cm); }
-    return result;
</del><ins>+  function docMethodOp(f) {
+    return function() {
+      var cm = this.cm;
+      if (!cm || cm.curOp) return f.apply(this, arguments);
+      startOperation(cm);
+      try { return f.apply(this, arguments); }
+      finally { endOperation(cm); }
+    };
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // VIEW TRACKING
+
+  // These objects are used to represent the visible (currently drawn)
+  // part of the document. A LineView may correspond to multiple
+  // logical lines, if those are connected by collapsed ranges.
+  function LineView(doc, line, lineN) {
+    // The starting line
+    this.line = line;
+    // Continuing lines, if any
+    this.rest = visualLineContinued(line);
+    // Number of logical lines in this visual line
+    this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
+    this.node = this.text = null;
+    this.hidden = lineIsHidden(doc, line);
+  }
+
+  // Create a range of LineView objects for the given lines.
+  function buildViewArray(cm, from, to) {
+    var array = [], nextPos;
+    for (var pos = from; pos &lt; to; pos = nextPos) {
+      var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
+      nextPos = pos + view.size;
+      array.push(view);
+    }
+    return array;
+  }
+
+  // Updates the display.view data structure for a given change to the
+  // document. From and to are in pre-change coordinates. Lendiff is
+  // the amount of lines added or subtracted by the change. This is
+  // used for changes that span multiple lines, or change the way
+  // lines are divided into visual lines. regLineChange (below)
+  // registers single-line changes.
</ins><span class="cx">   function regChange(cm, from, to, lendiff) {
</span><span class="cx">     if (from == null) from = cm.doc.first;
</span><span class="cx">     if (to == null) to = cm.doc.first + cm.doc.size;
</span><del>-    cm.curOp.changes.push({from: from, to: to, diff: lendiff});
</del><ins>+    if (!lendiff) lendiff = 0;
+
+    var display = cm.display;
+    if (lendiff &amp;&amp; to &lt; display.viewTo &amp;&amp;
+        (display.updateLineNumbers == null || display.updateLineNumbers &gt; from))
+      display.updateLineNumbers = from;
+
+    cm.curOp.viewChanged = true;
+
+    if (from &gt;= display.viewTo) { // Change after
+      if (sawCollapsedSpans &amp;&amp; visualLineNo(cm.doc, from) &lt; display.viewTo)
+        resetView(cm);
+    } else if (to &lt;= display.viewFrom) { // Change before
+      if (sawCollapsedSpans &amp;&amp; visualLineEndNo(cm.doc, to + lendiff) &gt; display.viewFrom) {
+        resetView(cm);
+      } else {
+        display.viewFrom += lendiff;
+        display.viewTo += lendiff;
+      }
+    } else if (from &lt;= display.viewFrom &amp;&amp; to &gt;= display.viewTo) { // Full overlap
+      resetView(cm);
+    } else if (from &lt;= display.viewFrom) { // Top overlap
+      var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cut) {
+        display.view = display.view.slice(cut.index);
+        display.viewFrom = cut.lineN;
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    } else if (to &gt;= display.viewTo) { // Bottom overlap
+      var cut = viewCuttingPoint(cm, from, from, -1);
+      if (cut) {
+        display.view = display.view.slice(0, cut.index);
+        display.viewTo = cut.lineN;
+      } else {
+        resetView(cm);
+      }
+    } else { // Gap in the middle
+      var cutTop = viewCuttingPoint(cm, from, from, -1);
+      var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
+      if (cutTop &amp;&amp; cutBot) {
+        display.view = display.view.slice(0, cutTop.index)
+          .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+          .concat(display.view.slice(cutBot.index));
+        display.viewTo += lendiff;
+      } else {
+        resetView(cm);
+      }
+    }
+
+    var ext = display.externalMeasured;
+    if (ext) {
+      if (to &lt; ext.lineN)
+        ext.lineN += lendiff;
+      else if (from &lt; ext.lineN + ext.size)
+        display.externalMeasured = null;
+    }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Register a change to a single line. Type must be one of &quot;text&quot;,
+  // &quot;gutter&quot;, &quot;class&quot;, &quot;widget&quot;
+  function regLineChange(cm, line, type) {
+    cm.curOp.viewChanged = true;
+    var display = cm.display, ext = cm.display.externalMeasured;
+    if (ext &amp;&amp; line &gt;= ext.lineN &amp;&amp; line &lt; ext.lineN + ext.size)
+      display.externalMeasured = null;
+
+    if (line &lt; display.viewFrom || line &gt;= display.viewTo) return;
+    var lineView = display.view[findViewIndex(cm, line)];
+    if (lineView.node == null) return;
+    var arr = lineView.changes || (lineView.changes = []);
+    if (indexOf(arr, type) == -1) arr.push(type);
+  }
+
+  // Clear the view.
+  function resetView(cm) {
+    cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
+    cm.display.view = [];
+    cm.display.viewOffset = 0;
+  }
+
+  // Find the view element corresponding to a given line. Return null
+  // when the line isn't visible.
+  function findViewIndex(cm, n) {
+    if (n &gt;= cm.display.viewTo) return null;
+    n -= cm.display.viewFrom;
+    if (n &lt; 0) return null;
+    var view = cm.display.view;
+    for (var i = 0; i &lt; view.length; i++) {
+      n -= view[i].size;
+      if (n &lt; 0) return i;
+    }
+  }
+
+  function viewCuttingPoint(cm, oldN, newN, dir) {
+    var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
+    if (!sawCollapsedSpans) return {index: index, lineN: newN};
+    for (var i = 0, n = cm.display.viewFrom; i &lt; index; i++)
+      n += view[i].size;
+    if (n != oldN) {
+      if (dir &gt; 0) {
+        if (index == view.length - 1) return null;
+        diff = (n + view[index].size) - oldN;
+        index++;
+      } else {
+        diff = n - oldN;
+      }
+      oldN += diff; newN += diff;
+    }
+    while (visualLineNo(cm.doc, newN) != newN) {
+      if (index == (dir &lt; 0 ? 0 : view.length - 1)) return null;
+      newN += dir * view[index - (dir &lt; 0 ? 1 : 0)].size;
+      index += dir;
+    }
+    return {index: index, lineN: newN};
+  }
+
+  // Force the view to cover a given range, adding empty view element
+  // or clipping off existing ones as needed.
+  function adjustView(cm, from, to) {
+    var display = cm.display, view = display.view;
+    if (view.length == 0 || from &gt;= display.viewTo || to &lt;= display.viewFrom) {
+      display.view = buildViewArray(cm, from, to);
+      display.viewFrom = from;
+    } else {
+      if (display.viewFrom &gt; from)
+        display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view);
+      else if (display.viewFrom &lt; from)
+        display.view = display.view.slice(findViewIndex(cm, from));
+      display.viewFrom = from;
+      if (display.viewTo &lt; to)
+        display.view = display.view.concat(buildViewArray(cm, display.viewTo, to));
+      else if (display.viewTo &gt; to)
+        display.view = display.view.slice(0, findViewIndex(cm, to));
+    }
+    display.viewTo = to;
+  }
+
+  // Count the number of lines in the view whose DOM representation is
+  // out of date (or nonexistent).
+  function countDirtyView(cm) {
+    var view = cm.display.view, dirty = 0;
+    for (var i = 0; i &lt; view.length; i++) {
+      var lineView = view[i];
+      if (!lineView.hidden &amp;&amp; (!lineView.node || lineView.changes)) ++dirty;
+    }
+    return dirty;
+  }
+
</ins><span class="cx">   // INPUT HANDLING
</span><span class="cx"> 
</span><ins>+  // Poll for input changes, using the normal rate of polling. This
+  // runs as long as the editor is focused.
</ins><span class="cx">   function slowPoll(cm) {
</span><span class="cx">     if (cm.display.pollingFast) return;
</span><span class="cx">     cm.display.poll.set(cm.options.pollInterval, function() {
</span><span class="lines">@@ -1463,6 +2198,9 @@
</span><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // When an event has just come in that is likely to add or change
+  // something in the input textarea, we poll faster, to ensure that
+  // the change appears on the screen quickly.
</ins><span class="cx">   function fastPoll(cm) {
</span><span class="cx">     var missed = false;
</span><span class="cx">     cm.display.pollingFast = true;
</span><span class="lines">@@ -1474,100 +2212,147 @@
</span><span class="cx">     cm.display.poll.set(20, p);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // prevInput is a hack to work with IME. If we reset the textarea
-  // on every change, that breaks IME. So we look for changes
-  // compared to the previous content instead. (Modern browsers have
-  // events that indicate IME taking place, but these are not widely
-  // supported or compatible enough yet to rely on.)
</del><ins>+  // Read input from the textarea, and update the document to match.
+  // When something is selected, it is present in the textarea, and
+  // selected (unless it is huge, in which case a placeholder is
+  // used). When nothing is selected, the cursor sits after previously
+  // seen text (can be empty), which is stored in prevInput (we must
+  // not reset the textarea when typing, because that breaks IME).
</ins><span class="cx">   function readInput(cm) {
</span><del>-    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
-    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false;
-    if (cm.state.pasteIncoming &amp;&amp; cm.state.fakedLastChar) {
-      input.value = input.value.substring(0, input.value.length - 1);
-      cm.state.fakedLastChar = false;
-    }
</del><ins>+    var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc;
+    // Since this is called a *lot*, try to bail out as cheaply as
+    // possible when it is clear that nothing happened. hasSelection
+    // will be the case when there is a lot of text in the textarea,
+    // in which case reading its value would be expensive.
+    if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false;
</ins><span class="cx">     var text = input.value;
</span><del>-    if (text == prevInput &amp;&amp; posEq(sel.from, sel.to)) return false;
-    if (ie &amp;&amp; !ie_lt9 &amp;&amp; cm.display.inputHasSelection === text) {
-      resetInput(cm, true);
</del><ins>+    // If nothing changed, bail.
+    if (text == prevInput &amp;&amp; !cm.somethingSelected()) return false;
+    // Work around nonsensical selection resetting in IE9/10
+    if (ie &amp;&amp; !ie_upto8 &amp;&amp; cm.display.inputHasSelection === text) {
+      resetInput(cm);
</ins><span class="cx">       return false;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var withOp = !cm.curOp;
</span><span class="cx">     if (withOp) startOperation(cm);
</span><del>-    sel.shift = false;
</del><ins>+    cm.display.shift = false;
+
+    // Find the part of the input that is actually new
</ins><span class="cx">     var same = 0, l = Math.min(prevInput.length, text.length);
</span><span class="cx">     while (same &lt; l &amp;&amp; prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same;
</span><del>-    var from = sel.from, to = sel.to;
-    if (same &lt; prevInput.length)
-      from = Pos(from.line, from.ch - (prevInput.length - same));
-    else if (cm.state.overwrite &amp;&amp; posEq(from, to) &amp;&amp; !cm.state.pasteIncoming)
-      to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
</del><ins>+    var inserted = text.slice(same), textLines = splitLines(inserted);
</ins><span class="cx"> 
</span><del>-    var updateInput = cm.curOp.updateInput;
-    var changeEvent = {from: from, to: to, text: splitLines(text.slice(same)),
-                       origin: cm.state.pasteIncoming ? &quot;paste&quot; : &quot;+input&quot;};
-    makeChange(cm.doc, changeEvent, &quot;end&quot;);
</del><ins>+    // When pasing N lines into N selections, insert one line per selection
+    var multiPaste = cm.state.pasteIncoming &amp;&amp; textLines.length &gt; 1 &amp;&amp; doc.sel.ranges.length == textLines.length;
+
+    // Normal behavior is to insert the new text into every selection
+    for (var i = doc.sel.ranges.length - 1; i &gt;= 0; i--) {
+      var range = doc.sel.ranges[i];
+      var from = range.from(), to = range.to();
+      // Handle deletion
+      if (same &lt; prevInput.length)
+        from = Pos(from.line, from.ch - (prevInput.length - same));
+      // Handle overwrite
+      else if (cm.state.overwrite &amp;&amp; range.empty() &amp;&amp; !cm.state.pasteIncoming)
+        to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
+      var updateInput = cm.curOp.updateInput;
+      var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines,
+                         origin: cm.state.pasteIncoming ? &quot;paste&quot; : cm.state.cutIncoming ? &quot;cut&quot; : &quot;+input&quot;};
+      makeChange(cm.doc, changeEvent);
+      signalLater(cm, &quot;inputRead&quot;, cm, changeEvent);
+      // When an 'electric' character is inserted, immediately trigger a reindent
+      if (inserted &amp;&amp; !cm.state.pasteIncoming &amp;&amp; cm.options.electricChars &amp;&amp;
+          cm.options.smartIndent &amp;&amp; range.head.ch &lt; 100 &amp;&amp;
+          (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
+        var mode = cm.getModeAt(range.head);
+        if (mode.electricChars) {
+          for (var j = 0; j &lt; mode.electricChars.length; j++)
+            if (inserted.indexOf(mode.electricChars.charAt(j)) &gt; -1) {
+              indentLine(cm, range.head.line, &quot;smart&quot;);
+              break;
+            }
+        } else if (mode.electricInput) {
+          var end = changeEnd(changeEvent);
+          if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
+            indentLine(cm, range.head.line, &quot;smart&quot;);
+        }
+      }
+    }
+    ensureCursorVisible(cm);
</ins><span class="cx">     cm.curOp.updateInput = updateInput;
</span><del>-    signalLater(cm, &quot;inputRead&quot;, cm, changeEvent);
</del><ins>+    cm.curOp.typing = true;
</ins><span class="cx"> 
</span><ins>+    // Don't leave long text in the textarea, since it makes further polling slow
</ins><span class="cx">     if (text.length &gt; 1000 || text.indexOf(&quot;\n&quot;) &gt; -1) input.value = cm.display.prevInput = &quot;&quot;;
</span><span class="cx">     else cm.display.prevInput = text;
</span><span class="cx">     if (withOp) endOperation(cm);
</span><del>-    cm.state.pasteIncoming = false;
</del><ins>+    cm.state.pasteIncoming = cm.state.cutIncoming = false;
</ins><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function resetInput(cm, user) {
</del><ins>+  // Reset the input to correspond to the selection (or to be empty,
+  // when not typing and nothing is selected)
+  function resetInput(cm, typing) {
</ins><span class="cx">     var minimal, selected, doc = cm.doc;
</span><del>-    if (!posEq(doc.sel.from, doc.sel.to)) {
</del><ins>+    if (cm.somethingSelected()) {
</ins><span class="cx">       cm.display.prevInput = &quot;&quot;;
</span><ins>+      var range = doc.sel.primary();
</ins><span class="cx">       minimal = hasCopyEvent &amp;&amp;
</span><del>-        (doc.sel.to.line - doc.sel.from.line &gt; 100 || (selected = cm.getSelection()).length &gt; 1000);
</del><ins>+        (range.to().line - range.from().line &gt; 100 || (selected = cm.getSelection()).length &gt; 1000);
</ins><span class="cx">       var content = minimal ? &quot;-&quot; : selected || cm.getSelection();
</span><span class="cx">       cm.display.input.value = content;
</span><span class="cx">       if (cm.state.focused) selectInput(cm.display.input);
</span><del>-      if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = content;
-    } else if (user) {
</del><ins>+      if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = content;
+    } else if (!typing) {
</ins><span class="cx">       cm.display.prevInput = cm.display.input.value = &quot;&quot;;
</span><del>-      if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = null;
</del><ins>+      if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = null;
</ins><span class="cx">     }
</span><span class="cx">     cm.display.inaccurateSelection = minimal;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function focusInput(cm) {
</span><del>-    if (cm.options.readOnly != &quot;nocursor&quot; &amp;&amp; (!mobile || document.activeElement != cm.display.input))
</del><ins>+    if (cm.options.readOnly != &quot;nocursor&quot; &amp;&amp; (!mobile || activeElt() != cm.display.input))
</ins><span class="cx">       cm.display.input.focus();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function ensureFocus(cm) {
+    if (!cm.state.focused) { focusInput(cm); onFocus(cm); }
+  }
+
</ins><span class="cx">   function isReadOnly(cm) {
</span><span class="cx">     return cm.options.readOnly || cm.doc.cantEdit;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // EVENT HANDLERS
</span><span class="cx"> 
</span><ins>+  // Attach the necessary event handlers when initializing the editor
</ins><span class="cx">   function registerEventHandlers(cm) {
</span><span class="cx">     var d = cm.display;
</span><span class="cx">     on(d.scroller, &quot;mousedown&quot;, operation(cm, onMouseDown));
</span><del>-    if (ie)
</del><ins>+    // Older IE's will not fire a second mousedown for a double click
+    if (ie_upto10)
</ins><span class="cx">       on(d.scroller, &quot;dblclick&quot;, operation(cm, function(e) {
</span><span class="cx">         if (signalDOMEvent(cm, e)) return;
</span><span class="cx">         var pos = posFromMouse(cm, e);
</span><span class="cx">         if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
</span><span class="cx">         e_preventDefault(e);
</span><del>-        var word = findWordAt(getLine(cm.doc, pos.line).text, pos);
-        extendSelection(cm.doc, word.from, word.to);
</del><ins>+        var word = findWordAt(cm.doc, pos);
+        extendSelection(cm.doc, word.anchor, word.head);
</ins><span class="cx">       }));
</span><span class="cx">     else
</span><span class="cx">       on(d.scroller, &quot;dblclick&quot;, function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); });
</span><ins>+    // Prevent normal selection in the editor (we handle our own)
</ins><span class="cx">     on(d.lineSpace, &quot;selectstart&quot;, function(e) {
</span><span class="cx">       if (!eventInWidget(d, e)) e_preventDefault(e);
</span><span class="cx">     });
</span><del>-    // Gecko browsers fire contextmenu *after* opening the menu, at
</del><ins>+    // Some browsers fire contextmenu *after* opening the menu, at
</ins><span class="cx">     // which point we can't mess with it anymore. Context menu is
</span><del>-    // handled in onMouseDown for Gecko.
-    if (!captureMiddleClick) on(d.scroller, &quot;contextmenu&quot;, function(e) {onContextMenu(cm, e);});
</del><ins>+    // handled in onMouseDown for these browsers.
+    if (!captureRightClick) on(d.scroller, &quot;contextmenu&quot;, function(e) {onContextMenu(cm, e);});
</ins><span class="cx"> 
</span><ins>+    // Sync scrolling between fake scrollbars and real scrollable
+    // area, ensure viewport is updated when scrolling.
</ins><span class="cx">     on(d.scroller, &quot;scroll&quot;, function() {
</span><span class="cx">       if (d.scroller.clientHeight) {
</span><span class="cx">         setScrollTop(cm, d.scroller.scrollTop);
</span><span class="lines">@@ -1582,42 +2367,40 @@
</span><span class="cx">       if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
</span><span class="cx">     });
</span><span class="cx"> 
</span><ins>+    // Listen to wheel events in order to try and update the viewport on time.
</ins><span class="cx">     on(d.scroller, &quot;mousewheel&quot;, function(e){onScrollWheel(cm, e);});
</span><span class="cx">     on(d.scroller, &quot;DOMMouseScroll&quot;, function(e){onScrollWheel(cm, e);});
</span><span class="cx"> 
</span><ins>+    // Prevent clicks in the scrollbars from killing focus
</ins><span class="cx">     function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
</span><span class="cx">     on(d.scrollbarH, &quot;mousedown&quot;, reFocus);
</span><span class="cx">     on(d.scrollbarV, &quot;mousedown&quot;, reFocus);
</span><span class="cx">     // Prevent wrapper from ever scrolling
</span><span class="cx">     on(d.wrapper, &quot;scroll&quot;, function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
</span><span class="cx"> 
</span><ins>+    // When the window resizes, we need to refresh active editors.
</ins><span class="cx">     var resizeTimer;
</span><span class="cx">     function onResize() {
</span><span class="cx">       if (resizeTimer == null) resizeTimer = setTimeout(function() {
</span><span class="cx">         resizeTimer = null;
</span><span class="cx">         // Might be a text scaling operation, clear size caches.
</span><del>-        d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null;
-        clearCaches(cm);
-        runInOp(cm, bind(regChange, cm));
</del><ins>+        d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = knownScrollbarWidth = null;
+        cm.setSize();
</ins><span class="cx">       }, 100);
</span><span class="cx">     }
</span><span class="cx">     on(window, &quot;resize&quot;, onResize);
</span><del>-    // Above handler holds on to the editor and its data structures.
-    // Here we poll to unregister it when the editor is no longer in
-    // the document, so that it can be garbage-collected.
</del><ins>+    // The above handler holds on to the editor and its data
+    // structures. Here we poll to unregister it when the editor is no
+    // longer in the document, so that it can be garbage-collected.
</ins><span class="cx">     function unregister() {
</span><del>-      for (var p = d.wrapper.parentNode; p &amp;&amp; p != document.body; p = p.parentNode) {}
-      if (p) setTimeout(unregister, 5000);
</del><ins>+      if (contains(document.body, d.wrapper)) setTimeout(unregister, 5000);
</ins><span class="cx">       else off(window, &quot;resize&quot;, onResize);
</span><span class="cx">     }
</span><span class="cx">     setTimeout(unregister, 5000);
</span><span class="cx"> 
</span><del>-    on(d.input, &quot;keyup&quot;, operation(cm, function(e) {
-      if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
-      if (e.keyCode == 16) cm.doc.sel.shift = false;
-    }));
</del><ins>+    on(d.input, &quot;keyup&quot;, operation(cm, onKeyUp));
</ins><span class="cx">     on(d.input, &quot;input&quot;, function() {
</span><del>-      if (ie &amp;&amp; !ie_lt9 &amp;&amp; cm.display.inputHasSelection) cm.display.inputHasSelection = null;
</del><ins>+      if (ie &amp;&amp; !ie_upto8 &amp;&amp; cm.display.inputHasSelection) cm.display.inputHasSelection = null;
</ins><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx">     on(d.input, &quot;keydown&quot;, operation(cm, onKeyDown));
</span><span class="lines">@@ -1626,8 +2409,7 @@
</span><span class="cx">     on(d.input, &quot;blur&quot;, bind(onBlur, cm));
</span><span class="cx"> 
</span><span class="cx">     function drag_(e) {
</span><del>-      if (signalDOMEvent(cm, e) || cm.options.onDragEvent &amp;&amp; cm.options.onDragEvent(cm, addStop(e))) return;
-      e_stop(e);
</del><ins>+      if (!signalDOMEvent(cm, e)) e_stop(e);
</ins><span class="cx">     }
</span><span class="cx">     if (cm.options.dragDrop) {
</span><span class="cx">       on(d.scroller, &quot;dragstart&quot;, function(e){onDragStart(cm, e);});
</span><span class="lines">@@ -1637,70 +2419,98 @@
</span><span class="cx">     }
</span><span class="cx">     on(d.scroller, &quot;paste&quot;, function(e) {
</span><span class="cx">       if (eventInWidget(d, e)) return;
</span><ins>+      cm.state.pasteIncoming = true;
</ins><span class="cx">       focusInput(cm);
</span><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx">     on(d.input, &quot;paste&quot;, function() {
</span><del>-      // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
-      // Add a char to the end of textarea before paste occur so that
-      // selection doesn't span to the end of textarea.
-      if (webkit &amp;&amp; !cm.state.fakedLastChar &amp;&amp; !(new Date - cm.state.lastMiddleDown &lt; 200)) {
-        var start = d.input.selectionStart, end = d.input.selectionEnd;
-        d.input.value += &quot;$&quot;;
-        d.input.selectionStart = start;
-        d.input.selectionEnd = end;
-        cm.state.fakedLastChar = true;
-      }
</del><span class="cx">       cm.state.pasteIncoming = true;
</span><span class="cx">       fastPoll(cm);
</span><span class="cx">     });
</span><span class="cx"> 
</span><del>-    function prepareCopy() {
-      if (d.inaccurateSelection) {
-        d.prevInput = &quot;&quot;;
-        d.inaccurateSelection = false;
-        d.input.value = cm.getSelection();
-        selectInput(d.input);
</del><ins>+    function prepareCopyCut(e) {
+      if (cm.somethingSelected()) {
+        if (d.inaccurateSelection) {
+          d.prevInput = &quot;&quot;;
+          d.inaccurateSelection = false;
+          d.input.value = cm.getSelection();
+          selectInput(d.input);
+        }
+      } else {
+        var text = &quot;&quot;, ranges = [];
+        for (var i = 0; i &lt; cm.doc.sel.ranges.length; i++) {
+          var line = cm.doc.sel.ranges[i].head.line;
+          var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
+          ranges.push(lineRange);
+          text += cm.getRange(lineRange.anchor, lineRange.head);
+        }
+        if (e.type == &quot;cut&quot;) {
+          cm.setSelections(ranges, null, sel_dontScroll);
+        } else {
+          d.prevInput = &quot;&quot;;
+          d.input.value = text;
+          selectInput(d.input);
+        }
</ins><span class="cx">       }
</span><ins>+      if (e.type == &quot;cut&quot;) cm.state.cutIncoming = true;
</ins><span class="cx">     }
</span><del>-    on(d.input, &quot;cut&quot;, prepareCopy);
-    on(d.input, &quot;copy&quot;, prepareCopy);
</del><ins>+    on(d.input, &quot;cut&quot;, prepareCopyCut);
+    on(d.input, &quot;copy&quot;, prepareCopyCut);
</ins><span class="cx"> 
</span><span class="cx">     // Needed to handle Tab key in KHTML
</span><span class="cx">     if (khtml) on(d.sizer, &quot;mouseup&quot;, function() {
</span><del>-        if (document.activeElement == d.input) d.input.blur();
-        focusInput(cm);
</del><ins>+      if (activeElt() == d.input) d.input.blur();
+      focusInput(cm);
</ins><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // MOUSE EVENTS
+
+  // Return true when the given mouse event happened in a widget
</ins><span class="cx">   function eventInWidget(display, e) {
</span><span class="cx">     for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
</span><span class="cx">       if (!n || n.ignoreEvents || n.parentNode == display.sizer &amp;&amp; n != display.mover) return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function posFromMouse(cm, e, liberal) {
</del><ins>+  // Given a mouse event, find the corresponding position. If liberal
+  // is false, it checks whether a gutter or scrollbar was clicked,
+  // and returns null if it was. forRect is used by rectangular
+  // selections, and tries to estimate a character position even for
+  // coordinates beyond the right of the text.
+  function posFromMouse(cm, e, liberal, forRect) {
</ins><span class="cx">     var display = cm.display;
</span><span class="cx">     if (!liberal) {
</span><span class="cx">       var target = e_target(e);
</span><del>-      if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
-          target == display.scrollbarV || target == display.scrollbarV.firstChild ||
</del><ins>+      if (target == display.scrollbarH || target == display.scrollbarV ||
</ins><span class="cx">           target == display.scrollbarFiller || target == display.gutterFiller) return null;
</span><span class="cx">     }
</span><del>-    var x, y, space = getRect(display.lineSpace);
</del><ins>+    var x, y, space = display.lineSpace.getBoundingClientRect();
</ins><span class="cx">     // Fails unpredictably on IE[67] when mouse is dragged around quickly.
</span><del>-    try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
-    return coordsChar(cm, x - space.left, y - space.top);
</del><ins>+    try { x = e.clientX - space.left; y = e.clientY - space.top; }
+    catch (e) { return null; }
+    var coords = coordsChar(cm, x, y), line;
+    if (forRect &amp;&amp; coords.xRel == 1 &amp;&amp; (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
+      var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
+      coords = Pos(coords.line, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff);
+    }
+    return coords;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var lastClick, lastDoubleClick;
</del><ins>+  // A mouse down can be a single click, double click, triple click,
+  // start of selection drag, start of text drag, new cursor
+  // (ctrl-click), rectangle drag (alt-drag), or xwin
+  // middle-click-paste. Or it might be a click on something we should
+  // not interfere with, such as a scrollbar or widget.
</ins><span class="cx">   function onMouseDown(e) {
</span><span class="cx">     if (signalDOMEvent(this, e)) return;
</span><del>-    var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
-    sel.shift = e.shiftKey;
</del><ins>+    var cm = this, display = cm.display;
+    display.shift = e.shiftKey;
</ins><span class="cx"> 
</span><span class="cx">     if (eventInWidget(display, e)) {
</span><span class="cx">       if (!webkit) {
</span><ins>+        // Briefly turn off draggability, to allow widgets to do
+        // normal dragging things.
</ins><span class="cx">         display.scroller.draggable = false;
</span><span class="cx">         setTimeout(function(){display.scroller.draggable = true;}, 100);
</span><span class="cx">       }
</span><span class="lines">@@ -1708,89 +2518,168 @@
</span><span class="cx">     }
</span><span class="cx">     if (clickInGutter(cm, e)) return;
</span><span class="cx">     var start = posFromMouse(cm, e);
</span><ins>+    window.focus();
</ins><span class="cx"> 
</span><span class="cx">     switch (e_button(e)) {
</span><del>-    case 3:
-      if (captureMiddleClick) onContextMenu.call(cm, cm, e);
-      return;
</del><ins>+    case 1:
+      if (start)
+        leftButtonDown(cm, e, start);
+      else if (e_target(e) == display.scroller)
+        e_preventDefault(e);
+      break;
</ins><span class="cx">     case 2:
</span><span class="cx">       if (webkit) cm.state.lastMiddleDown = +new Date;
</span><span class="cx">       if (start) extendSelection(cm.doc, start);
</span><span class="cx">       setTimeout(bind(focusInput, cm), 20);
</span><span class="cx">       e_preventDefault(e);
</span><del>-      return;
</del><ins>+      break;
+    case 3:
+      if (captureRightClick) onContextMenu(cm, e);
+      break;
</ins><span class="cx">     }
</span><del>-    // For button 1, if it was clicked inside the editor
-    // (posFromMouse returning non-null), we have to adjust the
-    // selection.
-    if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
</del><ins>+  }
</ins><span class="cx"> 
</span><del>-    if (!cm.state.focused) onFocus(cm);
</del><ins>+  var lastClick, lastDoubleClick;
+  function leftButtonDown(cm, e, start) {
+    setTimeout(bind(ensureFocus, cm), 0);
</ins><span class="cx"> 
</span><del>-    var now = +new Date, type = &quot;single&quot;;
-    if (lastDoubleClick &amp;&amp; lastDoubleClick.time &gt; now - 400 &amp;&amp; posEq(lastDoubleClick.pos, start)) {
</del><ins>+    var now = +new Date, type;
+    if (lastDoubleClick &amp;&amp; lastDoubleClick.time &gt; now - 400 &amp;&amp; cmp(lastDoubleClick.pos, start) == 0) {
</ins><span class="cx">       type = &quot;triple&quot;;
</span><del>-      e_preventDefault(e);
-      setTimeout(bind(focusInput, cm), 20);
-      selectLine(cm, start.line);
-    } else if (lastClick &amp;&amp; lastClick.time &gt; now - 400 &amp;&amp; posEq(lastClick.pos, start)) {
</del><ins>+    } else if (lastClick &amp;&amp; lastClick.time &gt; now - 400 &amp;&amp; cmp(lastClick.pos, start) == 0) {
</ins><span class="cx">       type = &quot;double&quot;;
</span><span class="cx">       lastDoubleClick = {time: now, pos: start};
</span><del>-      e_preventDefault(e);
-      var word = findWordAt(getLine(doc, start.line).text, start);
-      extendSelection(cm.doc, word.from, word.to);
-    } else { lastClick = {time: now, pos: start}; }
</del><ins>+    } else {
+      type = &quot;single&quot;;
+      lastClick = {time: now, pos: start};
+    }
</ins><span class="cx"> 
</span><del>-    var last = start;
-    if (cm.options.dragDrop &amp;&amp; dragAndDrop &amp;&amp; !isReadOnly(cm) &amp;&amp; !posEq(sel.from, sel.to) &amp;&amp;
-        !posLess(start, sel.from) &amp;&amp; !posLess(sel.to, start) &amp;&amp; type == &quot;single&quot;) {
-      var dragEnd = operation(cm, function(e2) {
-        if (webkit) display.scroller.draggable = false;
-        cm.state.draggingText = false;
-        off(document, &quot;mouseup&quot;, dragEnd);
-        off(display.scroller, &quot;drop&quot;, dragEnd);
-        if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) &lt; 10) {
-          e_preventDefault(e2);
-          extendSelection(cm.doc, start);
-          focusInput(cm);
-        }
-      });
-      // Let the drag handler handle this.
-      if (webkit) display.scroller.draggable = true;
-      cm.state.draggingText = dragEnd;
-      // IE's approach to draggable
-      if (display.scroller.dragDrop) display.scroller.dragDrop();
-      on(document, &quot;mouseup&quot;, dragEnd);
-      on(display.scroller, &quot;drop&quot;, dragEnd);
-      return;
-    }
</del><ins>+    var sel = cm.doc.sel, addNew = mac ? e.metaKey : e.ctrlKey;
+    if (cm.options.dragDrop &amp;&amp; dragAndDrop &amp;&amp; !addNew &amp;&amp; !isReadOnly(cm) &amp;&amp;
+        type == &quot;single&quot; &amp;&amp; sel.contains(start) &gt; -1 &amp;&amp; sel.somethingSelected())
+      leftButtonStartDrag(cm, e, start);
+    else
+      leftButtonSelect(cm, e, start, type, addNew);
+  }
+
+  // Start a text drag. When it ends, see if any dragging actually
+  // happen, and treat as a click if it didn't.
+  function leftButtonStartDrag(cm, e, start) {
+    var display = cm.display;
+    var dragEnd = operation(cm, function(e2) {
+      if (webkit) display.scroller.draggable = false;
+      cm.state.draggingText = false;
+      off(document, &quot;mouseup&quot;, dragEnd);
+      off(display.scroller, &quot;drop&quot;, dragEnd);
+      if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) &lt; 10) {
+        e_preventDefault(e2);
+        extendSelection(cm.doc, start);
+        focusInput(cm);
+        // Work around unexplainable focus problem in IE9 (#2127)
+        if (ie_upto10 &amp;&amp; !ie_upto8)
+          setTimeout(function() {document.body.focus(); focusInput(cm);}, 20);
+      }
+    });
+    // Let the drag handler handle this.
+    if (webkit) display.scroller.draggable = true;
+    cm.state.draggingText = dragEnd;
+    // IE's approach to draggable
+    if (display.scroller.dragDrop) display.scroller.dragDrop();
+    on(document, &quot;mouseup&quot;, dragEnd);
+    on(display.scroller, &quot;drop&quot;, dragEnd);
+  }
+
+  // Normal selection, as opposed to text dragging.
+  function leftButtonSelect(cm, e, start, type, addNew) {
+    var display = cm.display, doc = cm.doc;
</ins><span class="cx">     e_preventDefault(e);
</span><del>-    if (type == &quot;single&quot;) extendSelection(cm.doc, clipPos(doc, start));
</del><span class="cx"> 
</span><del>-    var startstart = sel.from, startend = sel.to, lastPos = start;
</del><ins>+    var ourRange, ourIndex, startSel = doc.sel;
+    if (addNew) {
+      ourIndex = doc.sel.contains(start);
+      if (ourIndex &gt; -1)
+        ourRange = doc.sel.ranges[ourIndex];
+      else
+        ourRange = new Range(start, start);
+    } else {
+      ourRange = doc.sel.primary();
+    }
</ins><span class="cx"> 
</span><del>-    function doSelect(cur) {
-      if (posEq(lastPos, cur)) return;
-      lastPos = cur;
</del><ins>+    if (e.altKey) {
+      type = &quot;rect&quot;;
+      if (!addNew) ourRange = new Range(start, start);
+      start = posFromMouse(cm, e, true, true);
+      ourIndex = -1;
+    } else if (type == &quot;double&quot;) {
+      var word = findWordAt(doc, start);
+      if (cm.display.shift || doc.extend)
+        ourRange = extendRange(doc, ourRange, word.anchor, word.head);
+      else
+        ourRange = word;
+    } else if (type == &quot;triple&quot;) {
+      var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0)));
+      if (cm.display.shift || doc.extend)
+        ourRange = extendRange(doc, ourRange, line.anchor, line.head);
+      else
+        ourRange = line;
+    } else {
+      ourRange = extendRange(doc, ourRange, start);
+    }
</ins><span class="cx"> 
</span><del>-      if (type == &quot;single&quot;) {
-        extendSelection(cm.doc, clipPos(doc, start), cur);
-        return;
-      }
</del><ins>+    if (!addNew) {
+      ourIndex = 0;
+      setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+    } else if (ourIndex &gt; -1) {
+      replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
+    } else {
+      ourIndex = doc.sel.ranges.length;
+      setSelection(doc, normalizeSelection(doc.sel.ranges.concat([ourRange]), ourIndex),
+                   {scroll: false, origin: &quot;*mouse&quot;});
+    }
</ins><span class="cx"> 
</span><del>-      startstart = clipPos(doc, startstart);
-      startend = clipPos(doc, startend);
-      if (type == &quot;double&quot;) {
-        var word = findWordAt(getLine(doc, cur.line).text, cur);
-        if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
-        else extendSelection(cm.doc, startstart, word.to);
-      } else if (type == &quot;triple&quot;) {
-        if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
-        else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
</del><ins>+    var lastPos = start;
+    function extendTo(pos) {
+      if (cmp(lastPos, pos) == 0) return;
+      lastPos = pos;
+
+      if (type == &quot;rect&quot;) {
+        var ranges = [], tabSize = cm.options.tabSize;
+        var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
+        var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
+        var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
+        for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
+             line &lt;= end; line++) {
+          var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
+          if (left == right)
+            ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos)));
+          else if (text.length &gt; leftPos)
+            ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize))));
+        }
+        if (!ranges.length) ranges.push(new Range(start, start));
+        setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), sel_mouse);
+      } else {
+        var oldRange = ourRange;
+        var anchor = oldRange.anchor, head = pos;
+        if (type != &quot;single&quot;) {
+          if (type == &quot;double&quot;)
+            var range = findWordAt(doc, pos);
+          else
+            var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
+          if (cmp(range.anchor, anchor) &gt; 0) {
+            head = range.head;
+            anchor = minPos(oldRange.from(), range.anchor);
+          } else {
+            head = range.anchor;
+            anchor = maxPos(oldRange.to(), range.head);
+          }
+        }
+        var ranges = startSel.ranges.slice(0);
+        ranges[ourIndex] = new Range(clipPos(doc, anchor), head);
+        setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var editorSize = getRect(display.wrapper);
</del><ins>+    var editorSize = display.wrapper.getBoundingClientRect();
</ins><span class="cx">     // Used to ensure timeout re-tries don't fire when another extend
</span><span class="cx">     // happened in the meantime (clearTimeout isn't reliable -- at
</span><span class="cx">     // least on Chrome, the timeouts still happen even when cleared,
</span><span class="lines">@@ -1799,12 +2688,11 @@
</span><span class="cx"> 
</span><span class="cx">     function extend(e) {
</span><span class="cx">       var curCount = ++counter;
</span><del>-      var cur = posFromMouse(cm, e, true);
</del><ins>+      var cur = posFromMouse(cm, e, true, type == &quot;rect&quot;);
</ins><span class="cx">       if (!cur) return;
</span><del>-      if (!posEq(cur, last)) {
-        if (!cm.state.focused) onFocus(cm);
-        last = cur;
-        doSelect(cur);
</del><ins>+      if (cmp(cur, lastPos) != 0) {
+        ensureFocus(cm);
+        extendTo(cur);
</ins><span class="cx">         var visible = visibleLines(display, doc);
</span><span class="cx">         if (cur.line &gt;= visible.to || cur.line &lt; visible.from)
</span><span class="cx">           setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150);
</span><span class="lines">@@ -1824,10 +2712,11 @@
</span><span class="cx">       focusInput(cm);
</span><span class="cx">       off(document, &quot;mousemove&quot;, move);
</span><span class="cx">       off(document, &quot;mouseup&quot;, up);
</span><ins>+      doc.history.lastSelOrigin = null;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var move = operation(cm, function(e) {
</span><del>-      if (!ie &amp;&amp; !e_button(e)) done(e);
</del><ins>+      if ((ie &amp;&amp; !ie_upto9) ?  !e.buttons : !e_button(e)) done(e);
</ins><span class="cx">       else extend(e);
</span><span class="cx">     });
</span><span class="cx">     var up = operation(cm, done);
</span><span class="lines">@@ -1835,21 +2724,23 @@
</span><span class="cx">     on(document, &quot;mouseup&quot;, up);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Determines whether an event happened in the gutter, and fires the
+  // handlers for the corresponding event.
</ins><span class="cx">   function gutterEvent(cm, e, type, prevent, signalfn) {
</span><span class="cx">     try { var mX = e.clientX, mY = e.clientY; }
</span><span class="cx">     catch(e) { return false; }
</span><del>-    if (mX &gt;= Math.floor(getRect(cm.display.gutters).right)) return false;
</del><ins>+    if (mX &gt;= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false;
</ins><span class="cx">     if (prevent) e_preventDefault(e);
</span><span class="cx"> 
</span><span class="cx">     var display = cm.display;
</span><del>-    var lineBox = getRect(display.lineDiv);
</del><ins>+    var lineBox = display.lineDiv.getBoundingClientRect();
</ins><span class="cx"> 
</span><span class="cx">     if (mY &gt; lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e);
</span><span class="cx">     mY -= lineBox.top - display.viewOffset;
</span><span class="cx"> 
</span><span class="cx">     for (var i = 0; i &lt; cm.options.gutters.length; ++i) {
</span><span class="cx">       var g = display.gutters.childNodes[i];
</span><del>-      if (g &amp;&amp; getRect(g).right &gt;= mX) {
</del><ins>+      if (g &amp;&amp; g.getBoundingClientRect().right &gt;= mX) {
</ins><span class="cx">         var line = lineAtHeight(cm.doc, mY);
</span><span class="cx">         var gutter = cm.options.gutters[i];
</span><span class="cx">         signalfn(cm, type, cm, line, gutter, e);
</span><span class="lines">@@ -1858,11 +2749,6 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function contextMenuInGutter(cm, e) {
-    if (!hasHandler(cm, &quot;gutterContextMenu&quot;)) return false;
-    return gutterEvent(cm, e, &quot;gutterContextMenu&quot;, false, signal);
-  }
-
</del><span class="cx">   function clickInGutter(cm, e) {
</span><span class="cx">     return gutterEvent(cm, e, &quot;gutterClick&quot;, true, signalLater);
</span><span class="cx">   }
</span><span class="lines">@@ -1873,29 +2759,33 @@
</span><span class="cx"> 
</span><span class="cx">   function onDrop(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent &amp;&amp; cm.options.onDragEvent(cm, addStop(e))))
</del><ins>+    if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
</ins><span class="cx">       return;
</span><span class="cx">     e_preventDefault(e);
</span><del>-    if (ie) lastDrop = +new Date;
</del><ins>+    if (ie_upto10) lastDrop = +new Date;
</ins><span class="cx">     var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
</span><span class="cx">     if (!pos || isReadOnly(cm)) return;
</span><ins>+    // Might be a file drop, in which case we simply extract the text
+    // and insert it.
</ins><span class="cx">     if (files &amp;&amp; files.length &amp;&amp; window.FileReader &amp;&amp; window.File) {
</span><span class="cx">       var n = files.length, text = Array(n), read = 0;
</span><span class="cx">       var loadFile = function(file, i) {
</span><span class="cx">         var reader = new FileReader;
</span><del>-        reader.onload = function() {
</del><ins>+        reader.onload = operation(cm, function() {
</ins><span class="cx">           text[i] = reader.result;
</span><span class="cx">           if (++read == n) {
</span><span class="cx">             pos = clipPos(cm.doc, pos);
</span><del>-            makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join(&quot;\n&quot;)), origin: &quot;paste&quot;}, &quot;around&quot;);
</del><ins>+            var change = {from: pos, to: pos, text: splitLines(text.join(&quot;\n&quot;)), origin: &quot;paste&quot;};
+            makeChange(cm.doc, change);
+            setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change)));
</ins><span class="cx">           }
</span><del>-        };
</del><ins>+        });
</ins><span class="cx">         reader.readAsText(file);
</span><span class="cx">       };
</span><span class="cx">       for (var i = 0; i &lt; n; ++i) loadFile(files[i], i);
</span><del>-    } else {
</del><ins>+    } else { // Normal drop
</ins><span class="cx">       // Don't do a replace if the drop happened inside of the selected text.
</span><del>-      if (cm.state.draggingText &amp;&amp; !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
</del><ins>+      if (cm.state.draggingText &amp;&amp; cm.doc.sel.contains(pos) &gt; -1) {
</ins><span class="cx">         cm.state.draggingText(e);
</span><span class="cx">         // Ensure the editor is re-focused
</span><span class="cx">         setTimeout(bind(focusInput, cm), 20);
</span><span class="lines">@@ -1904,10 +2794,11 @@
</span><span class="cx">       try {
</span><span class="cx">         var text = e.dataTransfer.getData(&quot;Text&quot;);
</span><span class="cx">         if (text) {
</span><del>-          var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
-          setSelection(cm.doc, pos, pos);
-          if (cm.state.draggingText) replaceRange(cm.doc, &quot;&quot;, curFrom, curTo, &quot;paste&quot;);
-          cm.replaceSelection(text, null, &quot;paste&quot;);
</del><ins>+          var selected = cm.state.draggingText &amp;&amp; cm.listSelections();
+          setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+          if (selected) for (var i = 0; i &lt; selected.length; ++i)
+            replaceRange(cm.doc, &quot;&quot;, selected[i].anchor, selected[i].head, &quot;drag&quot;);
+          cm.replaceSelection(text, &quot;around&quot;, &quot;paste&quot;);
</ins><span class="cx">           focusInput(cm);
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="lines">@@ -1916,37 +2807,42 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function onDragStart(cm, e) {
</span><del>-    if (ie &amp;&amp; (!cm.state.draggingText || +new Date - lastDrop &lt; 100)) { e_stop(e); return; }
</del><ins>+    if (ie_upto10 &amp;&amp; (!cm.state.draggingText || +new Date - lastDrop &lt; 100)) { e_stop(e); return; }
</ins><span class="cx">     if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return;
</span><span class="cx"> 
</span><del>-    var txt = cm.getSelection();
-    e.dataTransfer.setData(&quot;Text&quot;, txt);
</del><ins>+    e.dataTransfer.setData(&quot;Text&quot;, cm.getSelection());
</ins><span class="cx"> 
</span><span class="cx">     // Use dummy image instead of default browsers image.
</span><span class="cx">     // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
</span><span class="cx">     if (e.dataTransfer.setDragImage &amp;&amp; !safari) {
</span><span class="cx">       var img = elt(&quot;img&quot;, null, null, &quot;position: fixed; left: 0; top: 0;&quot;);
</span><span class="cx">       img.src = &quot;data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==&quot;;
</span><del>-      if (opera) {
</del><ins>+      if (presto) {
</ins><span class="cx">         img.width = img.height = 1;
</span><span class="cx">         cm.display.wrapper.appendChild(img);
</span><span class="cx">         // Force a relayout, or Opera won't use our image for some obscure reason
</span><span class="cx">         img._top = img.offsetTop;
</span><span class="cx">       }
</span><span class="cx">       e.dataTransfer.setDragImage(img, 0, 0);
</span><del>-      if (opera) img.parentNode.removeChild(img);
</del><ins>+      if (presto) img.parentNode.removeChild(img);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // SCROLL EVENTS
+
+  // Sync the scrollable area and scrollbars, ensure the viewport
+  // covers the visible area.
</ins><span class="cx">   function setScrollTop(cm, val) {
</span><span class="cx">     if (Math.abs(cm.doc.scrollTop - val) &lt; 2) return;
</span><span class="cx">     cm.doc.scrollTop = val;
</span><del>-    if (!gecko) updateDisplay(cm, [], val);
</del><ins>+    if (!gecko) updateDisplay(cm, {top: val});
</ins><span class="cx">     if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
</span><span class="cx">     if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
</span><del>-    if (gecko) updateDisplay(cm, []);
</del><ins>+    if (gecko) updateDisplay(cm);
</ins><span class="cx">     startWorker(cm, 100);
</span><span class="cx">   }
</span><ins>+  // Sync scroller and scrollbar, ensure the gutter elements are
+  // aligned.
</ins><span class="cx">   function setScrollLeft(cm, val, isScroller) {
</span><span class="cx">     if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) &lt; 2) return;
</span><span class="cx">     val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
</span><span class="lines">@@ -1993,10 +2889,12 @@
</span><span class="cx">     // This hack (see related code in patchDisplay) makes sure the
</span><span class="cx">     // element is kept around.
</span><span class="cx">     if (dy &amp;&amp; mac &amp;&amp; webkit) {
</span><del>-      for (var cur = e.target; cur != scroll; cur = cur.parentNode) {
-        if (cur.lineObj) {
-          cm.display.currentWheelTarget = cur;
-          break;
</del><ins>+      outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
+        for (var i = 0; i &lt; view.length; i++) {
+          if (view[i].node == cur) {
+            cm.display.currentWheelTarget = cur;
+            break outer;
+          }
</ins><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="lines">@@ -2007,7 +2905,7 @@
</span><span class="cx">     // estimated pixels/delta value, we just handle horizontal
</span><span class="cx">     // scrolling entirely here. It'll be slightly off from native, but
</span><span class="cx">     // better than glitching out.
</span><del>-    if (dx &amp;&amp; !gecko &amp;&amp; !opera &amp;&amp; wheelPixelsPerUnit != null) {
</del><ins>+    if (dx &amp;&amp; !gecko &amp;&amp; !presto &amp;&amp; wheelPixelsPerUnit != null) {
</ins><span class="cx">       if (dy)
</span><span class="cx">         setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
</span><span class="cx">       setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
</span><span class="lines">@@ -2016,12 +2914,14 @@
</span><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    // 'Project' the visible viewport to cover the area that is being
+    // scrolled into view (if we know enough to estimate it).
</ins><span class="cx">     if (dy &amp;&amp; wheelPixelsPerUnit != null) {
</span><span class="cx">       var pixels = dy * wheelPixelsPerUnit;
</span><span class="cx">       var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
</span><span class="cx">       if (pixels &lt; 0) top = Math.max(0, top + pixels - 50);
</span><span class="cx">       else bot = Math.min(cm.doc.height, bot + pixels + 50);
</span><del>-      updateDisplay(cm, [], {top: top, bottom: bot});
</del><ins>+      updateDisplay(cm, {top: top, bottom: bot});
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (wheelSamples &lt; 20) {
</span><span class="lines">@@ -2045,6 +2945,9 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // KEY EVENTS
+
+  // Run a handler that was bound to a key.
</ins><span class="cx">   function doHandleBinding(cm, bound, dropShift) {
</span><span class="cx">     if (typeof bound == &quot;string&quot;) {
</span><span class="cx">       bound = commands[bound];
</span><span class="lines">@@ -2053,18 +2956,19 @@
</span><span class="cx">     // Ensure previous input has been read, so that the handler sees a
</span><span class="cx">     // consistent view of the document
</span><span class="cx">     if (cm.display.pollingFast &amp;&amp; readInput(cm)) cm.display.pollingFast = false;
</span><del>-    var doc = cm.doc, prevShift = doc.sel.shift, done = false;
</del><ins>+    var prevShift = cm.display.shift, done = false;
</ins><span class="cx">     try {
</span><span class="cx">       if (isReadOnly(cm)) cm.state.suppressEdits = true;
</span><del>-      if (dropShift) doc.sel.shift = false;
</del><ins>+      if (dropShift) cm.display.shift = false;
</ins><span class="cx">       done = bound(cm) != Pass;
</span><span class="cx">     } finally {
</span><del>-      doc.sel.shift = prevShift;
</del><ins>+      cm.display.shift = prevShift;
</ins><span class="cx">       cm.state.suppressEdits = false;
</span><span class="cx">     }
</span><span class="cx">     return done;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Collect the currently active keymaps.
</ins><span class="cx">   function allKeyMaps(cm) {
</span><span class="cx">     var maps = cm.state.keyMaps.slice(0);
</span><span class="cx">     if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
</span><span class="lines">@@ -2073,8 +2977,9 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   var maybeTransition;
</span><ins>+  // Handle a key from the keydown event.
</ins><span class="cx">   function handleKeyBinding(cm, e) {
</span><del>-    // Handle auto keymap transitions
</del><ins>+    // Handle automatic keymap transitions
</ins><span class="cx">     var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
</span><span class="cx">     clearTimeout(maybeTransition);
</span><span class="cx">     if (next &amp;&amp; !isModifierKey(e)) maybeTransition = setTimeout(function() {
</span><span class="lines">@@ -2104,12 +3009,12 @@
</span><span class="cx">     if (handled) {
</span><span class="cx">       e_preventDefault(e);
</span><span class="cx">       restartBlink(cm);
</span><del>-      if (ie_lt9) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
</del><span class="cx">       signalLater(cm, &quot;keyHandled&quot;, cm, name, e);
</span><span class="cx">     }
</span><span class="cx">     return handled;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Handle a key from the keypress event
</ins><span class="cx">   function handleCharBinding(cm, e, ch) {
</span><span class="cx">     var handled = lookupKey(&quot;'&quot; + ch + &quot;'&quot;, allKeyMaps(cm),
</span><span class="cx">                             function(b) { return doHandleBinding(cm, b, true); });
</span><span class="lines">@@ -2124,47 +3029,67 @@
</span><span class="cx">   var lastStoppedKey = null;
</span><span class="cx">   function onKeyDown(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (!cm.state.focused) onFocus(cm);
-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
-    if (ie &amp;&amp; e.keyCode == 27) e.returnValue = false;
</del><ins>+    ensureFocus(cm);
+    if (signalDOMEvent(cm, e)) return;
+    // IE does strange things with escape.
+    if (ie_upto10 &amp;&amp; e.keyCode == 27) e.returnValue = false;
</ins><span class="cx">     var code = e.keyCode;
</span><del>-    // IE does strange things with escape.
-    cm.doc.sel.shift = code == 16 || e.shiftKey;
-    // First give onKeyEvent option a chance to handle this.
</del><ins>+    cm.display.shift = code == 16 || e.shiftKey;
</ins><span class="cx">     var handled = handleKeyBinding(cm, e);
</span><del>-    if (opera) {
</del><ins>+    if (presto) {
</ins><span class="cx">       lastStoppedKey = handled ? code : null;
</span><span class="cx">       // Opera has no cut event... we try to at least catch the key combo
</span><span class="cx">       if (!handled &amp;&amp; code == 88 &amp;&amp; !hasCopyEvent &amp;&amp; (mac ? e.metaKey : e.ctrlKey))
</span><del>-        cm.replaceSelection(&quot;&quot;);
</del><ins>+        cm.replaceSelection(&quot;&quot;, null, &quot;cut&quot;);
</ins><span class="cx">     }
</span><ins>+
+    // Turn mouse into crosshair when Alt is held on Mac.
+    if (code == 18 &amp;&amp; !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+      showCrossHair(cm);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function showCrossHair(cm) {
+    var lineDiv = cm.display.lineDiv;
+    addClass(lineDiv, &quot;CodeMirror-crosshair&quot;);
+
+    function up(e) {
+      if (e.keyCode == 18 || !e.altKey) {
+        rmClass(lineDiv, &quot;CodeMirror-crosshair&quot;);
+        off(document, &quot;keyup&quot;, up);
+        off(document, &quot;mouseover&quot;, up);
+      }
+    }
+    on(document, &quot;keyup&quot;, up);
+    on(document, &quot;mouseover&quot;, up);
+  }
+
+  function onKeyUp(e) {
+    if (signalDOMEvent(this, e)) return;
+    if (e.keyCode == 16) this.doc.sel.shift = false;
+  }
+
</ins><span class="cx">   function onKeyPress(e) {
</span><span class="cx">     var cm = this;
</span><del>-    if (signalDOMEvent(cm, e) || cm.options.onKeyEvent &amp;&amp; cm.options.onKeyEvent(cm, addStop(e))) return;
</del><ins>+    if (signalDOMEvent(cm, e)) return;
</ins><span class="cx">     var keyCode = e.keyCode, charCode = e.charCode;
</span><del>-    if (opera &amp;&amp; keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
-    if (((opera &amp;&amp; (!e.which || e.which &lt; 10)) || khtml) &amp;&amp; handleKeyBinding(cm, e)) return;
</del><ins>+    if (presto &amp;&amp; keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
+    if (((presto &amp;&amp; (!e.which || e.which &lt; 10)) || khtml) &amp;&amp; handleKeyBinding(cm, e)) return;
</ins><span class="cx">     var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
</span><del>-    if (this.options.electricChars &amp;&amp; this.doc.mode.electricChars &amp;&amp;
-        this.options.smartIndent &amp;&amp; !isReadOnly(this) &amp;&amp;
-        this.doc.mode.electricChars.indexOf(ch) &gt; -1)
-      setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, &quot;smart&quot;);}), 75);
</del><span class="cx">     if (handleCharBinding(cm, e, ch)) return;
</span><del>-    if (ie &amp;&amp; !ie_lt9) cm.display.inputHasSelection = null;
</del><ins>+    if (ie &amp;&amp; !ie_upto8) cm.display.inputHasSelection = null;
</ins><span class="cx">     fastPoll(cm);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // FOCUS/BLUR EVENTS
+
</ins><span class="cx">   function onFocus(cm) {
</span><span class="cx">     if (cm.options.readOnly == &quot;nocursor&quot;) return;
</span><span class="cx">     if (!cm.state.focused) {
</span><span class="cx">       signal(cm, &quot;focus&quot;, cm);
</span><span class="cx">       cm.state.focused = true;
</span><del>-      if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
-        cm.display.wrapper.className += &quot; CodeMirror-focused&quot;;
</del><ins>+      addClass(cm.display.wrapper, &quot;CodeMirror-focused&quot;);
</ins><span class="cx">       if (!cm.curOp) {
</span><del>-        resetInput(cm, true);
</del><ins>+        resetInput(cm);
</ins><span class="cx">         if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="lines">@@ -2175,40 +3100,49 @@
</span><span class="cx">     if (cm.state.focused) {
</span><span class="cx">       signal(cm, &quot;blur&quot;, cm);
</span><span class="cx">       cm.state.focused = false;
</span><del>-      cm.display.wrapper.className = cm.display.wrapper.className.replace(&quot; CodeMirror-focused&quot;, &quot;&quot;);
</del><ins>+      rmClass(cm.display.wrapper, &quot;CodeMirror-focused&quot;);
</ins><span class="cx">     }
</span><span class="cx">     clearInterval(cm.display.blinker);
</span><del>-    setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
</del><ins>+    setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // CONTEXT MENU HANDLING
+
</ins><span class="cx">   var detectingSelectAll;
</span><ins>+  // To make the context menu work, we need to briefly unhide the
+  // textarea (making it as unobtrusive as possible) to let the
+  // right-click take effect on it.
</ins><span class="cx">   function onContextMenu(cm, e) {
</span><span class="cx">     if (signalDOMEvent(cm, e, &quot;contextmenu&quot;)) return;
</span><del>-    var display = cm.display, sel = cm.doc.sel;
</del><ins>+    var display = cm.display;
</ins><span class="cx">     if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return;
</span><span class="cx"> 
</span><span class="cx">     var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
</span><del>-    if (!pos || opera) return; // Opera is difficult.
</del><ins>+    if (!pos || presto) return; // Opera is difficult.
</ins><span class="cx"> 
</span><span class="cx">     // Reset the current text selection only if the click is done outside of the selection
</span><span class="cx">     // and 'resetSelectionOnContextMenu' option is true.
</span><span class="cx">     var reset = cm.options.resetSelectionOnContextMenu;
</span><del>-    if (reset &amp;&amp; (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)))
-      operation(cm, setSelection)(cm.doc, pos, pos);
</del><ins>+    if (reset &amp;&amp; cm.doc.sel.contains(pos) == -1)
+      operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
</ins><span class="cx"> 
</span><span class="cx">     var oldCSS = display.input.style.cssText;
</span><span class="cx">     display.inputDiv.style.position = &quot;absolute&quot;;
</span><span class="cx">     display.input.style.cssText = &quot;position: fixed; width: 30px; height: 30px; top: &quot; + (e.clientY - 5) +
</span><del>-      &quot;px; left: &quot; + (e.clientX - 5) + &quot;px; z-index: 1000; background: white; outline: none;&quot; +
-      &quot;border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);&quot;;
</del><ins>+      &quot;px; left: &quot; + (e.clientX - 5) + &quot;px; z-index: 1000; background: &quot; +
+      (ie ? &quot;rgba(255, 255, 255, .05)&quot; : &quot;transparent&quot;) +
+      &quot;; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);&quot;;
</ins><span class="cx">     focusInput(cm);
</span><del>-    resetInput(cm, true);
</del><ins>+    resetInput(cm);
</ins><span class="cx">     // Adds &quot;Select all&quot; to context menu in FF
</span><del>-    if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = &quot; &quot;;
</del><ins>+    if (!cm.somethingSelected()) display.input.value = display.prevInput = &quot; &quot;;
</ins><span class="cx"> 
</span><ins>+    // Select-all will be greyed out if there's nothing to select, so
+    // this adds a zero-width space so that we can later check whether
+    // it got selected.
</ins><span class="cx">     function prepareSelectAllHack() {
</span><span class="cx">       if (display.input.selectionStart != null) {
</span><del>-        var extval = display.input.value = &quot;\u200b&quot; + (posEq(sel.from, sel.to) ? &quot;&quot; : display.input.value);
</del><ins>+        var extval = display.input.value = &quot;\u200b&quot; + (cm.somethingSelected() ? display.input.value : &quot;&quot;);
</ins><span class="cx">         display.prevInput = &quot;\u200b&quot;;
</span><span class="cx">         display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
</span><span class="cx">       }
</span><span class="lines">@@ -2216,15 +3150,15 @@
</span><span class="cx">     function rehide() {
</span><span class="cx">       display.inputDiv.style.position = &quot;relative&quot;;
</span><span class="cx">       display.input.style.cssText = oldCSS;
</span><del>-      if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
</del><ins>+      if (ie_upto8) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
</ins><span class="cx">       slowPoll(cm);
</span><span class="cx"> 
</span><span class="cx">       // Try to detect the user choosing select-all
</span><span class="cx">       if (display.input.selectionStart != null) {
</span><del>-        if (!ie || ie_lt9) prepareSelectAllHack();
</del><ins>+        if (!ie || ie_upto8) prepareSelectAllHack();
</ins><span class="cx">         clearTimeout(detectingSelectAll);
</span><span class="cx">         var i = 0, poll = function(){
</span><del>-          if (display.prevInput == &quot; &quot; &amp;&amp; display.input.selectionStart == 0)
</del><ins>+          if (display.prevInput == &quot;\u200b&quot; &amp;&amp; display.input.selectionStart == 0)
</ins><span class="cx">             operation(cm, commands.selectAll)(cm);
</span><span class="cx">           else if (i++ &lt; 10) detectingSelectAll = setTimeout(poll, 500);
</span><span class="cx">           else resetInput(cm);
</span><span class="lines">@@ -2233,8 +3167,8 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (ie &amp;&amp; !ie_lt9) prepareSelectAllHack();
-    if (captureMiddleClick) {
</del><ins>+    if (ie &amp;&amp; !ie_upto8) prepareSelectAllHack();
+    if (captureRightClick) {
</ins><span class="cx">       e_stop(e);
</span><span class="cx">       var mouseup = function() {
</span><span class="cx">         off(window, &quot;mouseup&quot;, mouseup);
</span><span class="lines">@@ -2246,54 +3180,71 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function contextMenuInGutter(cm, e) {
+    if (!hasHandler(cm, &quot;gutterContextMenu&quot;)) return false;
+    return gutterEvent(cm, e, &quot;gutterContextMenu&quot;, false, signal);
+  }
+
</ins><span class="cx">   // UPDATING
</span><span class="cx"> 
</span><ins>+  // Compute the position of the end of a change (its 'to' property
+  // refers to the pre-change end).
</ins><span class="cx">   var changeEnd = CodeMirror.changeEnd = function(change) {
</span><span class="cx">     if (!change.text) return change.to;
</span><span class="cx">     return Pos(change.from.line + change.text.length - 1,
</span><span class="cx">                lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
</span><span class="cx">   };
</span><span class="cx"> 
</span><del>-  // Make sure a position will be valid after the given change.
-  function clipPostChange(doc, change, pos) {
-    if (!posLess(change.from, pos)) return clipPos(doc, pos);
-    var diff = (change.text.length - 1) - (change.to.line - change.from.line);
-    if (pos.line &gt; change.to.line + diff) {
-      var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
-      if (preLine &gt; lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
-      return clipToLen(pos, getLine(doc, preLine).text.length);
</del><ins>+  // Adjust a position to refer to the post-change position of the
+  // same text, or the end of the change if the change covers it.
+  function adjustForChange(pos, change) {
+    if (cmp(pos, change.from) &lt; 0) return pos;
+    if (cmp(pos, change.to) &lt;= 0) return changeEnd(change);
+
+    var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+    if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch;
+    return Pos(line, ch);
+  }
+
+  function computeSelAfterChange(doc, change) {
+    var out = [];
+    for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+      var range = doc.sel.ranges[i];
+      out.push(new Range(adjustForChange(range.anchor, change),
+                         adjustForChange(range.head, change)));
</ins><span class="cx">     }
</span><del>-    if (pos.line == change.to.line + diff)
-      return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
-                       getLine(doc, change.to.line).text.length - change.to.ch);
-    var inside = pos.line - change.from.line;
-    return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
</del><ins>+    return normalizeSelection(out, doc.sel.primIndex);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Hint can be null|&quot;end&quot;|&quot;start&quot;|&quot;around&quot;|{anchor,head}
-  function computeSelAfterChange(doc, change, hint) {
-    if (hint &amp;&amp; typeof hint == &quot;object&quot;) // Assumed to be {anchor, head} object
-      return {anchor: clipPostChange(doc, change, hint.anchor),
-              head: clipPostChange(doc, change, hint.head)};
</del><ins>+  function offsetPos(pos, old, nw) {
+    if (pos.line == old.line)
+      return Pos(nw.line, pos.ch - old.ch + nw.ch);
+    else
+      return Pos(nw.line + (pos.line - old.line), pos.ch);
+  }
</ins><span class="cx"> 
</span><del>-    if (hint == &quot;start&quot;) return {anchor: change.from, head: change.from};
-
-    var end = changeEnd(change);
-    if (hint == &quot;around&quot;) return {anchor: change.from, head: end};
-    if (hint == &quot;end&quot;) return {anchor: end, head: end};
-
-    // hint is null, leave the selection alone as much as possible
-    var adjustPos = function(pos) {
-      if (posLess(pos, change.from)) return pos;
-      if (!posLess(change.to, pos)) return end;
-
-      var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
-      if (pos.line == change.to.line) ch += end.ch - change.to.ch;
-      return Pos(line, ch);
-    };
-    return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
</del><ins>+  // Used by replaceSelections to allow moving the selection to the
+  // start or around the replaced test. Hint may be &quot;start&quot; or &quot;around&quot;.
+  function computeReplacedSel(doc, changes, hint) {
+    var out = [];
+    var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
+    for (var i = 0; i &lt; changes.length; i++) {
+      var change = changes[i];
+      var from = offsetPos(change.from, oldPrev, newPrev);
+      var to = offsetPos(changeEnd(change), oldPrev, newPrev);
+      oldPrev = change.to;
+      newPrev = to;
+      if (hint == &quot;around&quot;) {
+        var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) &lt; 0;
+        out[i] = new Range(inv ? to : from, inv ? from : to);
+      } else {
+        out[i] = new Range(from, from);
+      }
+    }
+    return new Selection(out, doc.sel.primIndex);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Allow &quot;beforeChange&quot; event handlers to influence a change
</ins><span class="cx">   function filterChange(doc, change, update) {
</span><span class="cx">     var obj = {
</span><span class="cx">       canceled: false,
</span><span class="lines">@@ -2316,11 +3267,11 @@
</span><span class="cx">     return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Replace the range from from to to by the strings in replacement.
-  // change is a {from, to, text [, origin]} object
-  function makeChange(doc, change, selUpdate, ignoreReadOnly) {
</del><ins>+  // Apply a change to a document, and add it to the document's
+  // history, and propagating it to all linked documents.
+  function makeChange(doc, change, ignoreReadOnly) {
</ins><span class="cx">     if (doc.cm) {
</span><del>-      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
</del><ins>+      if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly);
</ins><span class="cx">       if (doc.cm.state.suppressEdits) return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -2333,19 +3284,17 @@
</span><span class="cx">     // of read-only spans in its range.
</span><span class="cx">     var split = sawReadOnlySpans &amp;&amp; !ignoreReadOnly &amp;&amp; removeReadOnlyRanges(doc, change.from, change.to);
</span><span class="cx">     if (split) {
</span><del>-      for (var i = split.length - 1; i &gt;= 1; --i)
-        makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [&quot;&quot;]});
-      if (split.length)
-        makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
</del><ins>+      for (var i = split.length - 1; i &gt;= 0; --i)
+        makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [&quot;&quot;] : change.text});
</ins><span class="cx">     } else {
</span><del>-      makeChangeNoReadonly(doc, change, selUpdate);
</del><ins>+      makeChangeInner(doc, change);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeNoReadonly(doc, change, selUpdate) {
-    if (change.text.length == 1 &amp;&amp; change.text[0] == &quot;&quot; &amp;&amp; posEq(change.from, change.to)) return;
-    var selAfter = computeSelAfterChange(doc, change, selUpdate);
-    addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
</del><ins>+  function makeChangeInner(doc, change) {
+    if (change.text.length == 1 &amp;&amp; change.text[0] == &quot;&quot; &amp;&amp; cmp(change.from, change.to) == 0) return;
+    var selAfter = computeSelAfterChange(doc, change);
+    addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
</ins><span class="cx"> 
</span><span class="cx">     makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
</span><span class="cx">     var rebased = [];
</span><span class="lines">@@ -2359,17 +3308,41 @@
</span><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeFromHistory(doc, type) {
</del><ins>+  // Revert a change stored in a document's history.
+  function makeChangeFromHistory(doc, type, allowSelectionOnly) {
</ins><span class="cx">     if (doc.cm &amp;&amp; doc.cm.state.suppressEdits) return;
</span><span class="cx"> 
</span><del>-    var hist = doc.history;
-    var event = (type == &quot;undo&quot; ? hist.done : hist.undone).pop();
-    if (!event) return;
</del><ins>+    var hist = doc.history, event, selAfter = doc.sel;
+    var source = type == &quot;undo&quot; ? hist.done : hist.undone, dest = type == &quot;undo&quot; ? hist.undone : hist.done;
</ins><span class="cx"> 
</span><del>-    var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
-                anchorAfter: event.anchorBefore, headAfter: event.headBefore,
-                generation: hist.generation};
-    (type == &quot;undo&quot; ? hist.undone : hist.done).push(anti);
</del><ins>+    // Verify that there is a useable event (so that ctrl-z won't
+    // needlessly clear selection events)
+    for (var i = 0; i &lt; source.length; i++) {
+      event = source[i];
+      if (allowSelectionOnly ? event.ranges &amp;&amp; !event.equals(doc.sel) : !event.ranges)
+        break;
+    }
+    if (i == source.length) return;
+    hist.lastOrigin = hist.lastSelOrigin = null;
+
+    for (;;) {
+      event = source.pop();
+      if (event.ranges) {
+        pushSelectionToHistory(event, dest);
+        if (allowSelectionOnly &amp;&amp; !event.equals(doc.sel)) {
+          setSelection(doc, event, {clearRedo: false});
+          return;
+        }
+        selAfter = event;
+      }
+      else break;
+    }
+
+    // Build up a reverse change object to add to the opposite history
+    // stack (redo when undoing, and vice versa).
+    var antiChanges = [];
+    pushSelectionToHistory(selAfter, dest);
+    dest.push({changes: antiChanges, generation: hist.generation});
</ins><span class="cx">     hist.generation = event.generation || ++hist.maxGeneration;
</span><span class="cx"> 
</span><span class="cx">     var filter = hasHandler(doc, &quot;beforeChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeChange&quot;);
</span><span class="lines">@@ -2378,17 +3351,18 @@
</span><span class="cx">       var change = event.changes[i];
</span><span class="cx">       change.origin = type;
</span><span class="cx">       if (filter &amp;&amp; !filterChange(doc, change, false)) {
</span><del>-        (type == &quot;undo&quot; ? hist.done : hist.undone).length = 0;
</del><ins>+        source.length = 0;
</ins><span class="cx">         return;
</span><span class="cx">       }
</span><span class="cx"> 
</span><del>-      anti.changes.push(historyChangeFromChange(doc, change));
</del><ins>+      antiChanges.push(historyChangeFromChange(doc, change));
</ins><span class="cx"> 
</span><del>-      var after = i ? computeSelAfterChange(doc, change, null)
-                    : {anchor: event.anchorBefore, head: event.headBefore};
</del><ins>+      var after = i ? computeSelAfterChange(doc, change, null) : lst(source);
</ins><span class="cx">       makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
</span><ins>+      if (doc.cm) ensureCursorVisible(doc.cm);
</ins><span class="cx">       var rebased = [];
</span><span class="cx"> 
</span><ins>+      // Propagate to the linked documents
</ins><span class="cx">       linkedDocs(doc, function(doc, sharedHist) {
</span><span class="cx">         if (!sharedHist &amp;&amp; indexOf(rebased, doc.history) == -1) {
</span><span class="cx">           rebaseHist(doc.history, change);
</span><span class="lines">@@ -2399,14 +3373,19 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Sub-views need their line numbers shifted when text is added
+  // above or below them in the parent document.
</ins><span class="cx">   function shiftDoc(doc, distance) {
</span><del>-    function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
</del><span class="cx">     doc.first += distance;
</span><del>-    if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
-    doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
-    doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
</del><ins>+    doc.sel = new Selection(map(doc.sel.ranges, function(range) {
+      return new Range(Pos(range.anchor.line + distance, range.anchor.ch),
+                       Pos(range.head.line + distance, range.head.ch));
+    }), doc.sel.primIndex);
+    if (doc.cm) regChange(doc.cm, doc.first, doc.first - distance, distance);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // More lower-level change function, handling only a single document
+  // (not linked ones).
</ins><span class="cx">   function makeChangeSingleDoc(doc, change, selAfter, spans) {
</span><span class="cx">     if (doc.cm &amp;&amp; !doc.cm.curOp)
</span><span class="cx">       return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
</span><span class="lines">@@ -2433,16 +3412,19 @@
</span><span class="cx">     change.removed = getBetween(doc, change.from, change.to);
</span><span class="cx"> 
</span><span class="cx">     if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
</span><del>-    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
-    else updateDoc(doc, change, spans, selAfter);
</del><ins>+    if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
+    else updateDoc(doc, change, spans);
+    setSelectionNoUndo(doc, selAfter, sel_dontScroll);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
</del><ins>+  // Handle the interaction of a change to a document with the editor
+  // that this document is part of.
+  function makeChangeSingleDocInEditor(cm, change, spans) {
</ins><span class="cx">     var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
</span><span class="cx"> 
</span><span class="cx">     var recomputeMaxLength = false, checkWidthStart = from.line;
</span><span class="cx">     if (!cm.options.lineWrapping) {
</span><del>-      checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
</del><ins>+      checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
</ins><span class="cx">       doc.iter(checkWidthStart, to.line + 1, function(line) {
</span><span class="cx">         if (line == display.maxLine) {
</span><span class="cx">           recomputeMaxLength = true;
</span><span class="lines">@@ -2451,14 +3433,14 @@
</span><span class="cx">       });
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!posLess(doc.sel.head, change.from) &amp;&amp; !posLess(change.to, doc.sel.head))
</del><ins>+    if (doc.sel.contains(change.from, change.to) &gt; -1)
</ins><span class="cx">       cm.curOp.cursorActivity = true;
</span><span class="cx"> 
</span><del>-    updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
</del><ins>+    updateDoc(doc, change, spans, estimateHeight(cm));
</ins><span class="cx"> 
</span><span class="cx">     if (!cm.options.lineWrapping) {
</span><span class="cx">       doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
</span><del>-        var len = lineLength(doc, line);
</del><ins>+        var len = lineLength(line);
</ins><span class="cx">         if (len &gt; display.maxLineLength) {
</span><span class="cx">           display.maxLine = line;
</span><span class="cx">           display.maxLineLength = len;
</span><span class="lines">@@ -2475,192 +3457,49 @@
</span><span class="cx"> 
</span><span class="cx">     var lendiff = change.text.length - (to.line - from.line) - 1;
</span><span class="cx">     // Remember that these lines changed, for updating the display
</span><del>-    regChange(cm, from.line, to.line + 1, lendiff);
</del><ins>+    if (from.line == to.line &amp;&amp; change.text.length == 1 &amp;&amp; !isWholeLineUpdate(cm.doc, change))
+      regLineChange(cm, from.line, &quot;text&quot;);
+    else
+      regChange(cm, from.line, to.line + 1, lendiff);
</ins><span class="cx"> 
</span><del>-    if (hasHandler(cm, &quot;change&quot;)) {
-      var changeObj = {from: from, to: to,
-                       text: change.text,
-                       removed: change.removed,
-                       origin: change.origin};
-      if (cm.curOp.textChanged) {
-        for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
-        cur.next = changeObj;
-      } else cm.curOp.textChanged = changeObj;
-    }
</del><ins>+    if (hasHandler(cm, &quot;change&quot;) || hasHandler(cm, &quot;changes&quot;))
+      (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push({
+        from: from, to: to,
+        text: change.text,
+        removed: change.removed,
+        origin: change.origin
+      });
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function replaceRange(doc, code, from, to, origin) {
</span><span class="cx">     if (!to) to = from;
</span><del>-    if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
</del><ins>+    if (cmp(to, from) &lt; 0) { var tmp = to; to = from; from = tmp; }
</ins><span class="cx">     if (typeof code == &quot;string&quot;) code = splitLines(code);
</span><del>-    makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
</del><ins>+    makeChange(doc, {from: from, to: to, text: code, origin: origin});
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // POSITION OBJECT
</del><ins>+  // SCROLLING THINGS INTO VIEW
</ins><span class="cx"> 
</span><del>-  function Pos(line, ch) {
-    if (!(this instanceof Pos)) return new Pos(line, ch);
-    this.line = line; this.ch = ch;
-  }
-  CodeMirror.Pos = Pos;
-
-  function posEq(a, b) {return a.line == b.line &amp;&amp; a.ch == b.ch;}
-  function posLess(a, b) {return a.line &lt; b.line || (a.line == b.line &amp;&amp; a.ch &lt; b.ch);}
-  function copyPos(x) {return Pos(x.line, x.ch);}
-
-  // SELECTION
-
-  function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
-  function clipPos(doc, pos) {
-    if (pos.line &lt; doc.first) return Pos(doc.first, 0);
-    var last = doc.first + doc.size - 1;
-    if (pos.line &gt; last) return Pos(last, getLine(doc, last).text.length);
-    return clipToLen(pos, getLine(doc, pos.line).text.length);
-  }
-  function clipToLen(pos, linelen) {
-    var ch = pos.ch;
-    if (ch == null || ch &gt; linelen) return Pos(pos.line, linelen);
-    else if (ch &lt; 0) return Pos(pos.line, 0);
-    else return pos;
-  }
-  function isLine(doc, l) {return l &gt;= doc.first &amp;&amp; l &lt; doc.first + doc.size;}
-
-  // If shift is held, this will move the selection anchor. Otherwise,
-  // it'll set the whole selection.
-  function extendSelection(doc, pos, other, bias) {
-    if (doc.sel.shift || doc.sel.extend) {
-      var anchor = doc.sel.anchor;
-      if (other) {
-        var posBefore = posLess(pos, anchor);
-        if (posBefore != posLess(other, anchor)) {
-          anchor = pos;
-          pos = other;
-        } else if (posBefore != posLess(pos, other)) {
-          pos = other;
-        }
-      }
-      setSelection(doc, anchor, pos, bias);
-    } else {
-      setSelection(doc, pos, other || pos, bias);
-    }
-    if (doc.cm) doc.cm.curOp.userSelChange = true;
-  }
-
-  function filterSelectionChange(doc, anchor, head) {
-    var obj = {anchor: anchor, head: head};
-    signal(doc, &quot;beforeSelectionChange&quot;, doc, obj);
-    if (doc.cm) signal(doc.cm, &quot;beforeSelectionChange&quot;, doc.cm, obj);
-    obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
-    return obj;
-  }
-
-  // Update the selection. Last two args are only used by
-  // updateDoc, since they have to be expressed in the line
-  // numbers before the update.
-  function setSelection(doc, anchor, head, bias, checkAtomic) {
-    if (!checkAtomic &amp;&amp; hasHandler(doc, &quot;beforeSelectionChange&quot;) || doc.cm &amp;&amp; hasHandler(doc.cm, &quot;beforeSelectionChange&quot;)) {
-      var filtered = filterSelectionChange(doc, anchor, head);
-      head = filtered.head;
-      anchor = filtered.anchor;
-    }
-
-    var sel = doc.sel;
-    sel.goalColumn = null;
-    if (bias == null) bias = posLess(head, sel.head) ? -1 : 1;
-    // Skip over atomic spans.
-    if (checkAtomic || !posEq(anchor, sel.anchor))
-      anchor = skipAtomic(doc, anchor, bias, checkAtomic != &quot;push&quot;);
-    if (checkAtomic || !posEq(head, sel.head))
-      head = skipAtomic(doc, head, bias, checkAtomic != &quot;push&quot;);
-
-    if (posEq(sel.anchor, anchor) &amp;&amp; posEq(sel.head, head)) return;
-
-    sel.anchor = anchor; sel.head = head;
-    var inv = posLess(head, anchor);
-    sel.from = inv ? head : anchor;
-    sel.to = inv ? anchor : head;
-
-    if (doc.cm)
-      doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged =
-        doc.cm.curOp.cursorActivity = true;
-
-    signalLater(doc, &quot;cursorActivity&quot;, doc);
-  }
-
-  function reCheckSelection(cm) {
-    setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, &quot;push&quot;);
-  }
-
-  function skipAtomic(doc, pos, bias, mayClear) {
-    var flipped = false, curPos = pos;
-    var dir = bias || 1;
-    doc.cantEdit = false;
-    search: for (;;) {
-      var line = getLine(doc, curPos.line);
-      if (line.markedSpans) {
-        for (var i = 0; i &lt; line.markedSpans.length; ++i) {
-          var sp = line.markedSpans[i], m = sp.marker;
-          if ((sp.from == null || (m.inclusiveLeft ? sp.from &lt;= curPos.ch : sp.from &lt; curPos.ch)) &amp;&amp;
-              (sp.to == null || (m.inclusiveRight ? sp.to &gt;= curPos.ch : sp.to &gt; curPos.ch))) {
-            if (mayClear) {
-              signal(m, &quot;beforeCursorEnter&quot;);
-              if (m.explicitlyCleared) {
-                if (!line.markedSpans) break;
-                else {--i; continue;}
-              }
-            }
-            if (!m.atomic) continue;
-            var newPos = m.find()[dir &lt; 0 ? &quot;from&quot; : &quot;to&quot;];
-            if (posEq(newPos, curPos)) {
-              newPos.ch += dir;
-              if (newPos.ch &lt; 0) {
-                if (newPos.line &gt; doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
-                else newPos = null;
-              } else if (newPos.ch &gt; line.text.length) {
-                if (newPos.line &lt; doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
-                else newPos = null;
-              }
-              if (!newPos) {
-                if (flipped) {
-                  // Driven in a corner -- no valid cursor position found at all
-                  // -- try again *with* clearing, if we didn't already
-                  if (!mayClear) return skipAtomic(doc, pos, bias, true);
-                  // Otherwise, turn off editing until further notice, and return the start of the doc
-                  doc.cantEdit = true;
-                  return Pos(doc.first, 0);
-                }
-                flipped = true; newPos = pos; dir = -dir;
-              }
-            }
-            curPos = newPos;
-            continue search;
-          }
-        }
-      }
-      return curPos;
-    }
-  }
-
-  // SCROLLING
-
-  function scrollCursorIntoView(cm) {
-    var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin);
-    if (!cm.state.focused) return;
-    var display = cm.display, box = getRect(display.sizer), doScroll = null;
</del><ins>+  // If an editor sits on the top or bottom of the window, partially
+  // scrolled out of view, this ensures that the cursor is visible.
+  function maybeScrollWindow(cm, coords) {
+    var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
</ins><span class="cx">     if (coords.top + box.top &lt; 0) doScroll = true;
</span><span class="cx">     else if (coords.bottom + box.top &gt; (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
</span><span class="cx">     if (doScroll != null &amp;&amp; !phantom) {
</span><del>-      var hidden = display.cursor.style.display == &quot;none&quot;;
-      if (hidden) {
-        display.cursor.style.display = &quot;&quot;;
-        display.cursor.style.left = coords.left + &quot;px&quot;;
-        display.cursor.style.top = (coords.top - display.viewOffset) + &quot;px&quot;;
-      }
-      display.cursor.scrollIntoView(doScroll);
-      if (hidden) display.cursor.style.display = &quot;none&quot;;
</del><ins>+      var scrollNode = elt(&quot;div&quot;, &quot;\u200b&quot;, null, &quot;position: absolute; top: &quot; +
+                           (coords.top - display.viewOffset - paddingTop(cm.display)) + &quot;px; height: &quot; +
+                           (coords.bottom - coords.top + scrollerCutOff) + &quot;px; left: &quot; +
+                           coords.left + &quot;px; width: 2px;&quot;);
+      cm.display.lineSpace.appendChild(scrollNode);
+      scrollNode.scrollIntoView(doScroll);
+      cm.display.lineSpace.removeChild(scrollNode);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Scroll a given position into view (immediately), verifying that
+  // it actually became visible (as line heights are accurately
+  // measured, the position of something may 'drift' during drawing).
</ins><span class="cx">   function scrollPosIntoView(cm, pos, end, margin) {
</span><span class="cx">     if (margin == null) margin = 0;
</span><span class="cx">     for (;;) {
</span><span class="lines">@@ -2683,16 +3522,22 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Scroll a given set of coordinates into view (immediately).
</ins><span class="cx">   function scrollIntoView(cm, x1, y1, x2, y2) {
</span><span class="cx">     var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2);
</span><span class="cx">     if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop);
</span><span class="cx">     if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Calculate a new scroll position needed to scroll the given
+  // rectangle into view. Returns an object with scrollTop and
+  // scrollLeft properties. When these are undefined, the
+  // vertical/horizontal position does not need to be adjusted.
</ins><span class="cx">   function calculateScrollPos(cm, x1, y1, x2, y2) {
</span><span class="cx">     var display = cm.display, snapMargin = textHeight(cm.display);
</span><span class="cx">     if (y1 &lt; 0) y1 = 0;
</span><del>-    var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
</del><ins>+    var screentop = cm.curOp &amp;&amp; cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+    var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
</ins><span class="cx">     var docBottom = cm.doc.height + paddingVert(display);
</span><span class="cx">     var atTop = y1 &lt; snapMargin, atBottom = y2 &gt; docBottom - snapMargin;
</span><span class="cx">     if (y1 &lt; screentop) {
</span><span class="lines">@@ -2702,7 +3547,8 @@
</span><span class="cx">       if (newTop != screentop) result.scrollTop = newTop;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft;
</del><ins>+    var screenleft = cm.curOp &amp;&amp; cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
+    var screenw = display.scroller.clientWidth - scrollerCutOff;
</ins><span class="cx">     x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth;
</span><span class="cx">     var gutterw = display.gutters.offsetWidth;
</span><span class="cx">     var atLeft = x1 &lt; gutterw + 10;
</span><span class="lines">@@ -2715,32 +3561,70 @@
</span><span class="cx">     return result;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function updateScrollPos(cm, left, top) {
-    cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left,
-                                scrollTop: top == null ? cm.doc.scrollTop : top};
</del><ins>+  // Store a relative adjustment to the scroll position in the current
+  // operation (to be applied when the operation finishes).
+  function addToScrollPos(cm, left, top) {
+    if (left != null || top != null) resolveScrollToPos(cm);
+    if (left != null)
+      cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left;
+    if (top != null)
+      cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function addToScrollPos(cm, left, top) {
-    var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
-    var scroll = cm.display.scroller;
-    pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
-    pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
</del><ins>+  // Make sure that at the end of the operation the current cursor is
+  // shown.
+  function ensureCursorVisible(cm) {
+    resolveScrollToPos(cm);
+    var cur = cm.getCursor(), from = cur, to = cur;
+    if (!cm.options.lineWrapping) {
+      from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur;
+      to = Pos(cur.line, cur.ch + 1);
+    }
+    cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // When an operation has its scrollToPos property set, and another
+  // scroll action is applied before the end of the operation, this
+  // 'simulates' scrolling that position into view in a cheap way, so
+  // that the effect of intermediate scroll commands is not ignored.
+  function resolveScrollToPos(cm) {
+    var range = cm.curOp.scrollToPos;
+    if (range) {
+      cm.curOp.scrollToPos = null;
+      var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
+      var sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
+                                    Math.min(from.top, to.top) - range.margin,
+                                    Math.max(from.right, to.right),
+                                    Math.max(from.bottom, to.bottom) + range.margin);
+      cm.scrollTo(sPos.scrollLeft, sPos.scrollTop);
+    }
+  }
+
</ins><span class="cx">   // API UTILITIES
</span><span class="cx"> 
</span><ins>+  // Indent the given line. The how parameter can be &quot;smart&quot;,
+  // &quot;add&quot;/null, &quot;subtract&quot;, or &quot;prev&quot;. When aggressive is false
+  // (typically set to true for forced single-line indents), empty
+  // lines are not indented, and places where the mode returns Pass
+  // are left alone.
</ins><span class="cx">   function indentLine(cm, n, how, aggressive) {
</span><del>-    var doc = cm.doc;
</del><ins>+    var doc = cm.doc, state;
</ins><span class="cx">     if (how == null) how = &quot;add&quot;;
</span><span class="cx">     if (how == &quot;smart&quot;) {
</span><ins>+      // Fall back to &quot;prev&quot; when the mode doesn't have an indentation
+      // method.
</ins><span class="cx">       if (!cm.doc.mode.indent) how = &quot;prev&quot;;
</span><del>-      else var state = getStateBefore(cm, n);
</del><ins>+      else state = getStateBefore(cm, n);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var tabSize = cm.options.tabSize;
</span><span class="cx">     var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
</span><ins>+    if (line.stateAfter) line.stateAfter = null;
</ins><span class="cx">     var curSpaceString = line.text.match(/^\s*/)[0], indentation;
</span><del>-    if (how == &quot;smart&quot;) {
</del><ins>+    if (!aggressive &amp;&amp; !/\S/.test(line.text)) {
+      indentation = 0;
+      how = &quot;not&quot;;
+    } else if (how == &quot;smart&quot;) {
</ins><span class="cx">       indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
</span><span class="cx">       if (indentation == Pass) {
</span><span class="cx">         if (!aggressive) return;
</span><span class="lines">@@ -2764,23 +3648,69 @@
</span><span class="cx">       for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += &quot;\t&quot;;}
</span><span class="cx">     if (pos &lt; indentation) indentString += spaceStr(indentation - pos);
</span><span class="cx"> 
</span><del>-    if (indentString != curSpaceString)
</del><ins>+    if (indentString != curSpaceString) {
</ins><span class="cx">       replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), &quot;+input&quot;);
</span><del>-    else if (doc.sel.head.line == n &amp;&amp; doc.sel.head.ch &lt; curSpaceString.length)
-      setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1);
</del><ins>+    } else {
+      // Ensure that, if the cursor was in the whitespace at the start
+      // of the line, it is moved to the end of that space.
+      for (var i = 0; i &lt; doc.sel.ranges.length; i++) {
+        var range = doc.sel.ranges[i];
+        if (range.head.line == n &amp;&amp; range.head.ch &lt; curSpaceString.length) {
+          var pos = Pos(n, curSpaceString.length);
+          replaceOneSelection(doc, i, new Range(pos, pos));
+          break;
+        }
+      }
+    }
</ins><span class="cx">     line.stateAfter = null;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function changeLine(cm, handle, op) {
</del><ins>+  // Utility for applying a change to a line by handle or number,
+  // returning the number and optionally registering the line as
+  // changed.
+  function changeLine(cm, handle, changeType, op) {
</ins><span class="cx">     var no = handle, line = handle, doc = cm.doc;
</span><span class="cx">     if (typeof handle == &quot;number&quot;) line = getLine(doc, clipLine(doc, handle));
</span><span class="cx">     else no = lineNo(handle);
</span><span class="cx">     if (no == null) return null;
</span><del>-    if (op(line, no)) regChange(cm, no, no + 1);
-    else return null;
</del><ins>+    if (op(line, no)) regLineChange(cm, no, changeType);
</ins><span class="cx">     return line;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Helper for deleting text near the selection(s), used to implement
+  // backspace, delete, and similar functionality.
+  function deleteNearSelection(cm, compute) {
+    var ranges = cm.doc.sel.ranges, kill = [];
+    // Build up a set of ranges to kill first, merging overlapping
+    // ranges.
+    for (var i = 0; i &lt; ranges.length; i++) {
+      var toKill = compute(ranges[i]);
+      while (kill.length &amp;&amp; cmp(toKill.from, lst(kill).to) &lt;= 0) {
+        var replaced = kill.pop();
+        if (cmp(replaced.from, toKill.from) &lt; 0) {
+          toKill.from = replaced.from;
+          break;
+        }
+      }
+      kill.push(toKill);
+    }
+    // Next, remove those actual ranges.
+    runInOp(cm, function() {
+      for (var i = kill.length - 1; i &gt;= 0; i--)
+        replaceRange(cm.doc, &quot;&quot;, kill[i].from, kill[i].to, &quot;+delete&quot;);
+      ensureCursorVisible(cm);
+    });
+  }
+
+  // Used for horizontal relative motion. Dir is -1 or 1 (left or
+  // right), unit can be &quot;char&quot;, &quot;column&quot; (like char, but doesn't
+  // cross line boundaries), &quot;word&quot; (across next word), or &quot;group&quot; (to
+  // the start of next group of word or non-word-non-whitespace
+  // chars). The visually param controls whether, in right-to-left
+  // text, direction 1 means to move towards the next index in the
+  // string, or towards the character to the right of the current
+  // position. The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
</ins><span class="cx">   function findPosH(doc, pos, dir, unit, visually) {
</span><span class="cx">     var line = pos.line, ch = pos.ch, origDir = dir;
</span><span class="cx">     var lineObj = getLine(doc, line);
</span><span class="lines">@@ -2810,13 +3740,15 @@
</span><span class="cx">         if (dir &lt; 0 &amp;&amp; !moveOnce(!first)) break;
</span><span class="cx">         var cur = lineObj.text.charAt(ch) || &quot;\n&quot;;
</span><span class="cx">         var type = isWordChar(cur) ? &quot;w&quot;
</span><del>-          : !group ? null
-          : /\s/.test(cur) ? null
</del><ins>+          : group &amp;&amp; cur == &quot;\n&quot; ? &quot;n&quot;
+          : !group || /\s/.test(cur) ? null
</ins><span class="cx">           : &quot;p&quot;;
</span><ins>+        if (group &amp;&amp; !first &amp;&amp; !type) type = &quot;s&quot;;
</ins><span class="cx">         if (sawType &amp;&amp; sawType != type) {
</span><span class="cx">           if (dir &lt; 0) {dir = 1; moveOnce();}
</span><span class="cx">           break;
</span><span class="cx">         }
</span><ins>+
</ins><span class="cx">         if (type) sawType = type;
</span><span class="cx">         if (dir &gt; 0 &amp;&amp; !moveOnce(!first)) break;
</span><span class="cx">       }
</span><span class="lines">@@ -2826,6 +3758,9 @@
</span><span class="cx">     return result;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // For relative vertical movement. Dir may be -1 or 1. Unit can be
+  // &quot;page&quot; or &quot;line&quot;. The resulting position will have a hitSide=true
+  // property if it reached the end of the document.
</ins><span class="cx">   function findPosV(cm, pos, dir, unit) {
</span><span class="cx">     var doc = cm.doc, x = pos.left, y;
</span><span class="cx">     if (unit == &quot;page&quot;) {
</span><span class="lines">@@ -2843,7 +3778,9 @@
</span><span class="cx">     return target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function findWordAt(line, pos) {
</del><ins>+  // Find the word at the given position (as returned by coordsChar).
+  function findWordAt(doc, pos) {
+    var line = getLine(doc, pos.line).text;
</ins><span class="cx">     var start = pos.ch, end = pos.ch;
</span><span class="cx">     if (line) {
</span><span class="cx">       if ((pos.xRel &lt; 0 || end == line.length) &amp;&amp; start) --start; else ++end;
</span><span class="lines">@@ -2854,17 +3791,18 @@
</span><span class="cx">       while (start &gt; 0 &amp;&amp; check(line.charAt(start - 1))) --start;
</span><span class="cx">       while (end &lt; line.length &amp;&amp; check(line.charAt(end))) ++end;
</span><span class="cx">     }
</span><del>-    return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
</del><ins>+    return new Range(Pos(pos.line, start), Pos(pos.line, end));
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function selectLine(cm, line) {
-    extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
-  }
</del><ins>+  // EDITOR METHODS
</ins><span class="cx"> 
</span><del>-  // PROTOTYPE
</del><ins>+  // The publicly visible API. Note that methodOp(f) means
+  // 'wrap f in an operation, performed on its `this` parameter'.
</ins><span class="cx"> 
</span><del>-  // The publicly visible API. Note that operation(null, f) means
-  // 'wrap f in an operation, performed on its `this` parameter'
</del><ins>+  // This is not the complete set of editor methods. Most of the
+  // methods defined on the Doc type are also injected into
+  // CodeMirror.prototype, for backwards compatibility and
+  // convenience.
</ins><span class="cx"> 
</span><span class="cx">   CodeMirror.prototype = {
</span><span class="cx">     constructor: CodeMirror,
</span><span class="lines">@@ -2893,14 +3831,14 @@
</span><span class="cx">         }
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    addOverlay: operation(null, function(spec, options) {
</del><ins>+    addOverlay: methodOp(function(spec, options) {
</ins><span class="cx">       var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
</span><span class="cx">       if (mode.startState) throw new Error(&quot;Overlays may not be stateful.&quot;);
</span><span class="cx">       this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options &amp;&amp; options.opaque});
</span><span class="cx">       this.state.modeGen++;
</span><span class="cx">       regChange(this);
</span><span class="cx">     }),
</span><del>-    removeOverlay: operation(null, function(spec) {
</del><ins>+    removeOverlay: methodOp(function(spec) {
</ins><span class="cx">       var overlays = this.state.overlays;
</span><span class="cx">       for (var i = 0; i &lt; overlays.length; ++i) {
</span><span class="cx">         var cur = overlays[i].modeSpec;
</span><span class="lines">@@ -2913,18 +3851,29 @@
</span><span class="cx">       }
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    indentLine: operation(null, function(n, dir, aggressive) {
</del><ins>+    indentLine: methodOp(function(n, dir, aggressive) {
</ins><span class="cx">       if (typeof dir != &quot;string&quot; &amp;&amp; typeof dir != &quot;number&quot;) {
</span><span class="cx">         if (dir == null) dir = this.options.smartIndent ? &quot;smart&quot; : &quot;prev&quot;;
</span><span class="cx">         else dir = dir ? &quot;add&quot; : &quot;subtract&quot;;
</span><span class="cx">       }
</span><span class="cx">       if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
</span><span class="cx">     }),
</span><del>-    indentSelection: operation(null, function(how) {
-      var sel = this.doc.sel;
-      if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
-      var e = sel.to.line - (sel.to.ch ? 0 : 1);
-      for (var i = sel.from.line; i &lt;= e; ++i) indentLine(this, i, how);
</del><ins>+    indentSelection: methodOp(function(how) {
+      var ranges = this.doc.sel.ranges, end = -1;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var range = ranges[i];
+        if (!range.empty()) {
+          var start = Math.max(end, range.from().line);
+          var to = range.to();
+          end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
+          for (var j = start; j &lt; end; ++j)
+            indentLine(this, j, how);
+        } else if (range.head.line &gt; end) {
+          indentLine(this, range.head.line, how, true);
+          end = range.head.line;
+          if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
+        }
+      }
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     // Fetch the parser token for a given character. Useful for hacks
</span><span class="lines">@@ -2937,12 +3886,11 @@
</span><span class="cx">       var stream = new StringStream(line.text, this.options.tabSize);
</span><span class="cx">       while (stream.pos &lt; pos.ch &amp;&amp; !stream.eol()) {
</span><span class="cx">         stream.start = stream.pos;
</span><del>-        var style = mode.token(stream, state);
</del><ins>+        var style = readToken(mode, stream, state);
</ins><span class="cx">       }
</span><span class="cx">       return {start: stream.start,
</span><span class="cx">               end: stream.pos,
</span><span class="cx">               string: stream.current(),
</span><del>-              className: style || null, // Deprecated, use 'type' instead
</del><span class="cx">               type: style || null,
</span><span class="cx">               state: state};
</span><span class="cx">     },
</span><span class="lines">@@ -2967,11 +3915,31 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getHelper: function(pos, type) {
</span><del>-      if (!helpers.hasOwnProperty(type)) return;
</del><ins>+      return this.getHelpers(pos, type)[0];
+    },
+
+    getHelpers: function(pos, type) {
+      var found = [];
+      if (!helpers.hasOwnProperty(type)) return helpers;
</ins><span class="cx">       var help = helpers[type], mode = this.getModeAt(pos);
</span><del>-      return mode[type] &amp;&amp; help[mode[type]] ||
-        mode.helperType &amp;&amp; help[mode.helperType] ||
-        help[mode.name];
</del><ins>+      if (typeof mode[type] == &quot;string&quot;) {
+        if (help[mode[type]]) found.push(help[mode[type]]);
+      } else if (mode[type]) {
+        for (var i = 0; i &lt; mode[type].length; i++) {
+          var val = help[mode[type][i]];
+          if (val) found.push(val);
+        }
+      } else if (mode.helperType &amp;&amp; help[mode.helperType]) {
+        found.push(help[mode.helperType]);
+      } else if (help[mode.name]) {
+        found.push(help[mode.name]);
+      }
+      for (var i = 0; i &lt; help._global.length; i++) {
+        var cur = help._global[i];
+        if (cur.pred(mode, this) &amp;&amp; indexOf(found, cur.val) == -1)
+          found.push(cur.val);
+      }
+      return found;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getStateAfter: function(line, precise) {
</span><span class="lines">@@ -2981,10 +3949,10 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     cursorCoords: function(start, mode) {
</span><del>-      var pos, sel = this.doc.sel;
-      if (start == null) pos = sel.head;
</del><ins>+      var pos, range = this.doc.sel.primary();
+      if (start == null) pos = range.head;
</ins><span class="cx">       else if (typeof start == &quot;object&quot;) pos = clipPos(this.doc, start);
</span><del>-      else pos = start ? sel.from : sel.to;
</del><ins>+      else pos = start ? range.from() : range.to();
</ins><span class="cx">       return cursorCoords(this, pos, mode || &quot;page&quot;);
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -3006,15 +3974,15 @@
</span><span class="cx">       if (line &lt; this.doc.first) line = this.doc.first;
</span><span class="cx">       else if (line &gt; last) { line = last; end = true; }
</span><span class="cx">       var lineObj = getLine(this.doc, line);
</span><del>-      return intoCoordSystem(this, getLine(this.doc, line), {top: 0, left: 0}, mode || &quot;page&quot;).top +
-        (end ? lineObj.height : 0);
</del><ins>+      return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || &quot;page&quot;).top +
+        (end ? this.doc.height - heightAtLine(lineObj) : 0);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     defaultTextHeight: function() { return textHeight(this.display); },
</span><span class="cx">     defaultCharWidth: function() { return charWidth(this.display); },
</span><span class="cx"> 
</span><del>-    setGutterMarker: operation(null, function(line, gutterID, value) {
-      return changeLine(this, line, function(line) {
</del><ins>+    setGutterMarker: methodOp(function(line, gutterID, value) {
+      return changeLine(this, line, &quot;gutter&quot;, function(line) {
</ins><span class="cx">         var markers = line.gutterMarkers || (line.gutterMarkers = {});
</span><span class="cx">         markers[gutterID] = value;
</span><span class="cx">         if (!value &amp;&amp; isEmpty(markers)) line.gutterMarkers = null;
</span><span class="lines">@@ -3022,20 +3990,20 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    clearGutter: operation(null, function(gutterID) {
</del><ins>+    clearGutter: methodOp(function(gutterID) {
</ins><span class="cx">       var cm = this, doc = cm.doc, i = doc.first;
</span><span class="cx">       doc.iter(function(line) {
</span><span class="cx">         if (line.gutterMarkers &amp;&amp; line.gutterMarkers[gutterID]) {
</span><span class="cx">           line.gutterMarkers[gutterID] = null;
</span><del>-          regChange(cm, i, i + 1);
</del><ins>+          regLineChange(cm, i, &quot;gutter&quot;);
</ins><span class="cx">           if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null;
</span><span class="cx">         }
</span><span class="cx">         ++i;
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    addLineClass: operation(null, function(handle, where, cls) {
-      return changeLine(this, handle, function(line) {
</del><ins>+    addLineClass: methodOp(function(handle, where, cls) {
+      return changeLine(this, handle, &quot;class&quot;, function(line) {
</ins><span class="cx">         var prop = where == &quot;text&quot; ? &quot;textClass&quot; : where == &quot;background&quot; ? &quot;bgClass&quot; : &quot;wrapClass&quot;;
</span><span class="cx">         if (!line[prop]) line[prop] = cls;
</span><span class="cx">         else if (new RegExp(&quot;(?:^|\\s)&quot; + cls + &quot;(?:$|\\s)&quot;).test(line[prop])) return false;
</span><span class="lines">@@ -3044,8 +4012,8 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    removeLineClass: operation(null, function(handle, where, cls) {
-      return changeLine(this, handle, function(line) {
</del><ins>+    removeLineClass: methodOp(function(handle, where, cls) {
+      return changeLine(this, handle, &quot;class&quot;, function(line) {
</ins><span class="cx">         var prop = where == &quot;text&quot; ? &quot;textClass&quot; : where == &quot;background&quot; ? &quot;bgClass&quot; : &quot;wrapClass&quot;;
</span><span class="cx">         var cur = line[prop];
</span><span class="cx">         if (!cur) return false;
</span><span class="lines">@@ -3060,7 +4028,7 @@
</span><span class="cx">       });
</span><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    addLineWidget: operation(null, function(handle, node, options) {
</del><ins>+    addLineWidget: methodOp(function(handle, node, options) {
</ins><span class="cx">       return addLineWidget(this, handle, node, options);
</span><span class="cx">     }),
</span><span class="cx"> 
</span><span class="lines">@@ -3081,7 +4049,7 @@
</span><span class="cx">               widgets: line.widgets};
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};},
</del><ins>+    getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};},
</ins><span class="cx"> 
</span><span class="cx">     addWidget: function(pos, node, scroll, vert, horiz) {
</span><span class="cx">       var display = this.display;
</span><span class="lines">@@ -3116,9 +4084,14 @@
</span><span class="cx">         scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    triggerOnKeyDown: operation(null, onKeyDown),
</del><ins>+    triggerOnKeyDown: methodOp(onKeyDown),
+    triggerOnKeyPress: methodOp(onKeyPress),
+    triggerOnKeyUp: methodOp(onKeyUp),
</ins><span class="cx"> 
</span><del>-    execCommand: function(cmd) {return commands[cmd](this);},
</del><ins>+    execCommand: function(cmd) {
+      if (commands.hasOwnProperty(cmd))
+        return commands[cmd](this);
+    },
</ins><span class="cx"> 
</span><span class="cx">     findPosH: function(from, amount, unit, visually) {
</span><span class="cx">       var dir = 1;
</span><span class="lines">@@ -3130,20 +4103,25 @@
</span><span class="cx">       return cur;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    moveH: operation(null, function(dir, unit) {
-      var sel = this.doc.sel, pos;
-      if (sel.shift || sel.extend || posEq(sel.from, sel.to))
-        pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
-      else
-        pos = dir &lt; 0 ? sel.from : sel.to;
-      extendSelection(this.doc, pos, pos, dir);
</del><ins>+    moveH: methodOp(function(dir, unit) {
+      var cm = this;
+      cm.extendSelectionsBy(function(range) {
+        if (cm.display.shift || cm.doc.extend || range.empty())
+          return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually);
+        else
+          return dir &lt; 0 ? range.from() : range.to();
+      }, sel_move);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    deleteH: operation(null, function(dir, unit) {
-      var sel = this.doc.sel;
-      if (!posEq(sel.from, sel.to)) replaceRange(this.doc, &quot;&quot;, sel.from, sel.to, &quot;+delete&quot;);
-      else replaceRange(this.doc, &quot;&quot;, sel.from, findPosH(this.doc, sel.head, dir, unit, false), &quot;+delete&quot;);
-      this.curOp.userSelChange = true;
</del><ins>+    deleteH: methodOp(function(dir, unit) {
+      var sel = this.doc.sel, doc = this.doc;
+      if (sel.somethingSelected())
+        doc.replaceSelection(&quot;&quot;, null, &quot;+delete&quot;);
+      else
+        deleteNearSelection(this, function(range) {
+          var other = findPosH(doc, range.head, dir, unit, false);
+          return dir &lt; 0 ? {from: other, to: range.head} : {from: range.head, to: other};
+        });
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     findPosV: function(from, amount, unit, goalColumn) {
</span><span class="lines">@@ -3159,28 +4137,39 @@
</span><span class="cx">       return cur;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    moveV: operation(null, function(dir, unit) {
-      var sel = this.doc.sel;
-      var pos = cursorCoords(this, sel.head, &quot;div&quot;);
-      if (sel.goalColumn != null) pos.left = sel.goalColumn;
-      var target = findPosV(this, pos, dir, unit);
-
-      if (unit == &quot;page&quot;) addToScrollPos(this, 0, charCoords(this, target, &quot;div&quot;).top - pos.top);
-      extendSelection(this.doc, target, target, dir);
-      sel.goalColumn = pos.left;
</del><ins>+    moveV: methodOp(function(dir, unit) {
+      var cm = this, doc = this.doc, goals = [];
+      var collapse = !cm.display.shift &amp;&amp; !doc.extend &amp;&amp; doc.sel.somethingSelected();
+      doc.extendSelectionsBy(function(range) {
+        if (collapse)
+          return dir &lt; 0 ? range.from() : range.to();
+        var headPos = cursorCoords(cm, range.head, &quot;div&quot;);
+        if (range.goalColumn != null) headPos.left = range.goalColumn;
+        goals.push(headPos.left);
+        var pos = findPosV(cm, headPos, dir, unit);
+        if (unit == &quot;page&quot; &amp;&amp; range == doc.sel.primary())
+          addToScrollPos(cm, null, charCoords(cm, pos, &quot;div&quot;).top - headPos.top);
+        return pos;
+      }, sel_move);
+      if (goals.length) for (var i = 0; i &lt; doc.sel.ranges.length; i++)
+        doc.sel.ranges[i].goalColumn = goals[i];
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     toggleOverwrite: function(value) {
</span><span class="cx">       if (value != null &amp;&amp; value == this.state.overwrite) return;
</span><span class="cx">       if (this.state.overwrite = !this.state.overwrite)
</span><del>-        this.display.cursor.className += &quot; CodeMirror-overwrite&quot;;
</del><ins>+        addClass(this.display.cursorDiv, &quot;CodeMirror-overwrite&quot;);
</ins><span class="cx">       else
</span><del>-        this.display.cursor.className = this.display.cursor.className.replace(&quot; CodeMirror-overwrite&quot;, &quot;&quot;);
</del><ins>+        rmClass(this.display.cursorDiv, &quot;CodeMirror-overwrite&quot;);
+
+      signal(this, &quot;overwriteToggle&quot;, this, this.state.overwrite);
</ins><span class="cx">     },
</span><del>-    hasFocus: function() { return this.state.focused; },
</del><ins>+    hasFocus: function() { return activeElt() == this.display.input; },
</ins><span class="cx"> 
</span><del>-    scrollTo: operation(null, function(x, y) {
-      updateScrollPos(this, x, y);
</del><ins>+    scrollTo: methodOp(function(x, y) {
+      if (x != null || y != null) resolveScrollToPos(this);
+      if (x != null) this.curOp.scrollLeft = x;
+      if (y != null) this.curOp.scrollTop = y;
</ins><span class="cx">     }),
</span><span class="cx">     getScrollInfo: function() {
</span><span class="cx">       var scroller = this.display.scroller, co = scrollerCutOff;
</span><span class="lines">@@ -3189,54 +4178,62 @@
</span><span class="cx">               clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    scrollIntoView: operation(null, function(range, margin) {
-      if (range == null) range = {from: this.doc.sel.head, to: null};
-      else if (typeof range == &quot;number&quot;) range = {from: Pos(range, 0), to: null};
-      else if (range.from == null) range = {from: range, to: null};
</del><ins>+    scrollIntoView: methodOp(function(range, margin) {
+      if (range == null) {
+        range = {from: this.doc.sel.primary().head, to: null};
+        if (margin == null) margin = this.options.cursorScrollMargin;
+      } else if (typeof range == &quot;number&quot;) {
+        range = {from: Pos(range, 0), to: null};
+      } else if (range.from == null) {
+        range = {from: range, to: null};
+      }
</ins><span class="cx">       if (!range.to) range.to = range.from;
</span><del>-      if (!margin) margin = 0;
</del><ins>+      range.margin = margin || 0;
</ins><span class="cx"> 
</span><del>-      var coords = range;
</del><span class="cx">       if (range.from.line != null) {
</span><del>-        this.curOp.scrollToPos = {from: range.from, to: range.to, margin: margin};
-        coords = {from: cursorCoords(this, range.from),
-                  to: cursorCoords(this, range.to)};
</del><ins>+        resolveScrollToPos(this);
+        this.curOp.scrollToPos = range;
+      } else {
+        var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
+                                      Math.min(range.from.top, range.to.top) - range.margin,
+                                      Math.max(range.from.right, range.to.right),
+                                      Math.max(range.from.bottom, range.to.bottom) + range.margin);
+        this.scrollTo(sPos.scrollLeft, sPos.scrollTop);
</ins><span class="cx">       }
</span><del>-      var sPos = calculateScrollPos(this, Math.min(coords.from.left, coords.to.left),
-                                    Math.min(coords.from.top, coords.to.top) - margin,
-                                    Math.max(coords.from.right, coords.to.right),
-                                    Math.max(coords.from.bottom, coords.to.bottom) + margin);
-      updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
</del><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    setSize: operation(null, function(width, height) {
</del><ins>+    setSize: methodOp(function(width, height) {
</ins><span class="cx">       function interpret(val) {
</span><span class="cx">         return typeof val == &quot;number&quot; || /^\d+$/.test(String(val)) ? val + &quot;px&quot; : val;
</span><span class="cx">       }
</span><span class="cx">       if (width != null) this.display.wrapper.style.width = interpret(width);
</span><span class="cx">       if (height != null) this.display.wrapper.style.height = interpret(height);
</span><del>-      if (this.options.lineWrapping)
-        this.display.measureLineCache.length = this.display.measureLineCachePos = 0;
</del><ins>+      if (this.options.lineWrapping) clearLineMeasurementCache(this);
</ins><span class="cx">       this.curOp.forceUpdate = true;
</span><ins>+      signal(this, &quot;refresh&quot;, this);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><span class="cx">     operation: function(f){return runInOp(this, f);},
</span><span class="cx"> 
</span><del>-    refresh: operation(null, function() {
-      var badHeight = this.display.cachedTextHeight == null;
</del><ins>+    refresh: methodOp(function() {
+      var oldHeight = this.display.cachedTextHeight;
+      regChange(this);
+      this.curOp.forceUpdate = true;
</ins><span class="cx">       clearCaches(this);
</span><del>-      updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
-      regChange(this);
-      if (badHeight) estimateLineHeights(this);
</del><ins>+      this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop);
+      updateGutterSpace(this);
+      if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) &gt; .5)
+        estimateLineHeights(this);
+      signal(this, &quot;refresh&quot;, this);
</ins><span class="cx">     }),
</span><span class="cx"> 
</span><del>-    swapDoc: operation(null, function(doc) {
</del><ins>+    swapDoc: methodOp(function(doc) {
</ins><span class="cx">       var old = this.doc;
</span><span class="cx">       old.cm = null;
</span><span class="cx">       attachDoc(this, doc);
</span><span class="cx">       clearCaches(this);
</span><del>-      resetInput(this, true);
-      updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
</del><ins>+      resetInput(this);
+      this.scrollTo(doc.scrollLeft, doc.scrollTop);
</ins><span class="cx">       signalLater(this, &quot;swapDoc&quot;, this, old);
</span><span class="cx">       return old;
</span><span class="cx">     }),
</span><span class="lines">@@ -3250,10 +4247,10 @@
</span><span class="cx"> 
</span><span class="cx">   // OPTION DEFAULTS
</span><span class="cx"> 
</span><del>-  var optionHandlers = CodeMirror.optionHandlers = {};
-
</del><span class="cx">   // The default configuration options.
</span><span class="cx">   var defaults = CodeMirror.defaults = {};
</span><ins>+  // Functions to run when options are changed.
+  var optionHandlers = CodeMirror.optionHandlers = {};
</ins><span class="cx"> 
</span><span class="cx">   function option(name, deflt, handle, notOnInit) {
</span><span class="cx">     CodeMirror.defaults[name] = deflt;
</span><span class="lines">@@ -3261,6 +4258,7 @@
</span><span class="cx">       notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Passed to option handlers when there is no old value.
</ins><span class="cx">   var Init = CodeMirror.Init = {toString: function(){return &quot;CodeMirror.Init&quot;;}};
</span><span class="cx"> 
</span><span class="cx">   // These two are, on init, called from the constructor because they
</span><span class="lines">@@ -3277,7 +4275,7 @@
</span><span class="cx">   option(&quot;indentWithTabs&quot;, false);
</span><span class="cx">   option(&quot;smartIndent&quot;, true);
</span><span class="cx">   option(&quot;tabSize&quot;, 4, function(cm) {
</span><del>-    loadMode(cm);
</del><ins>+    resetModeState(cm);
</ins><span class="cx">     clearCaches(cm);
</span><span class="cx">     regChange(cm);
</span><span class="cx">   }, true);
</span><span class="lines">@@ -3297,9 +4295,6 @@
</span><span class="cx">   option(&quot;keyMap&quot;, &quot;default&quot;, keyMapChanged);
</span><span class="cx">   option(&quot;extraKeys&quot;, null);
</span><span class="cx"> 
</span><del>-  option(&quot;onKeyEvent&quot;, null);
-  option(&quot;onDragEvent&quot;, null);
-
</del><span class="cx">   option(&quot;lineWrapping&quot;, false, wrappingChanged, true);
</span><span class="cx">   option(&quot;gutters&quot;, [], function(cm) {
</span><span class="cx">     setGuttersForLineNumbers(cm.options);
</span><span class="lines">@@ -3327,9 +4322,10 @@
</span><span class="cx">       cm.display.disabled = true;
</span><span class="cx">     } else {
</span><span class="cx">       cm.display.disabled = false;
</span><del>-      if (!val) resetInput(cm, true);
</del><ins>+      if (!val) resetInput(cm);
</ins><span class="cx">     }
</span><span class="cx">   });
</span><ins>+  option(&quot;disableInput&quot;, false, function(cm, val) {if (!val) resetInput(cm);}, true);
</ins><span class="cx">   option(&quot;dragDrop&quot;, true);
</span><span class="cx"> 
</span><span class="cx">   option(&quot;cursorBlinkRate&quot;, 530);
</span><span class="lines">@@ -3337,13 +4333,13 @@
</span><span class="cx">   option(&quot;cursorHeight&quot;, 1);
</span><span class="cx">   option(&quot;workTime&quot;, 100);
</span><span class="cx">   option(&quot;workDelay&quot;, 100);
</span><del>-  option(&quot;flattenSpans&quot;, true);
</del><ins>+  option(&quot;flattenSpans&quot;, true, resetModeState, true);
+  option(&quot;addModeClass&quot;, false, resetModeState, true);
</ins><span class="cx">   option(&quot;pollInterval&quot;, 100);
</span><del>-  option(&quot;undoDepth&quot;, 40, function(cm, val){cm.doc.history.undoDepth = val;});
-  option(&quot;historyEventDelay&quot;, 500);
</del><ins>+  option(&quot;undoDepth&quot;, 200, function(cm, val){cm.doc.history.undoDepth = val;});
+  option(&quot;historyEventDelay&quot;, 1250);
</ins><span class="cx">   option(&quot;viewportMargin&quot;, 10, function(cm){cm.refresh();}, true);
</span><del>-  option(&quot;maxHighlightLength&quot;, 10000, function(cm){loadMode(cm); cm.refresh();}, true);
-  option(&quot;crudeMeasuringFrom&quot;, 10000);
</del><ins>+  option(&quot;maxHighlightLength&quot;, 10000, resetModeState, true);
</ins><span class="cx">   option(&quot;moveInputWithCursor&quot;, true, function(cm, val) {
</span><span class="cx">     if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0;
</span><span class="cx">   });
</span><span class="lines">@@ -3358,6 +4354,9 @@
</span><span class="cx">   // Known modes, by name and by MIME
</span><span class="cx">   var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
</span><span class="cx"> 
</span><ins>+  // Extra arguments are stored as the mode's dependencies, which is
+  // used by (legacy) mechanisms like loadmode.js to automatically
+  // load a mode. (Preferred mechanism is the require/define calls.)
</ins><span class="cx">   CodeMirror.defineMode = function(name, mode) {
</span><span class="cx">     if (!CodeMirror.defaults.mode &amp;&amp; name != &quot;null&quot;) CodeMirror.defaults.mode = name;
</span><span class="cx">     if (arguments.length &gt; 2) {
</span><span class="lines">@@ -3371,11 +4370,14 @@
</span><span class="cx">     mimeModes[mime] = spec;
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Given a MIME type, a {name, ...options} config object, or a name
+  // string, return a mode config object.
</ins><span class="cx">   CodeMirror.resolveMode = function(spec) {
</span><span class="cx">     if (typeof spec == &quot;string&quot; &amp;&amp; mimeModes.hasOwnProperty(spec)) {
</span><span class="cx">       spec = mimeModes[spec];
</span><span class="cx">     } else if (spec &amp;&amp; typeof spec.name == &quot;string&quot; &amp;&amp; mimeModes.hasOwnProperty(spec.name)) {
</span><span class="cx">       var found = mimeModes[spec.name];
</span><ins>+      if (typeof found == &quot;string&quot;) found = {name: found};
</ins><span class="cx">       spec = createObj(found, spec);
</span><span class="cx">       spec.name = found.name;
</span><span class="cx">     } else if (typeof spec == &quot;string&quot; &amp;&amp; /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
</span><span class="lines">@@ -3385,6 +4387,8 @@
</span><span class="cx">     else return spec || {name: &quot;null&quot;};
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Given a mode spec (anything that resolveMode accepts), find and
+  // initialize an actual mode object.
</ins><span class="cx">   CodeMirror.getMode = function(options, spec) {
</span><span class="cx">     var spec = CodeMirror.resolveMode(spec);
</span><span class="cx">     var mfactory = modes[spec.name];
</span><span class="lines">@@ -3399,15 +4403,21 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     modeObj.name = spec.name;
</span><ins>+    if (spec.helperType) modeObj.helperType = spec.helperType;
+    if (spec.modeProps) for (var prop in spec.modeProps)
+      modeObj[prop] = spec.modeProps[prop];
</ins><span class="cx"> 
</span><span class="cx">     return modeObj;
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Minimal default mode.
</ins><span class="cx">   CodeMirror.defineMode(&quot;null&quot;, function() {
</span><span class="cx">     return {token: function(stream) {stream.skipToEnd();}};
</span><span class="cx">   });
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/plain&quot;, &quot;null&quot;);
</span><span class="cx"> 
</span><ins>+  // This can be used to attach properties to mode objects from
+  // outside the actual mode definition.
</ins><span class="cx">   var modeExtensions = CodeMirror.modeExtensions = {};
</span><span class="cx">   CodeMirror.extendMode = function(mode, properties) {
</span><span class="cx">     var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
</span><span class="lines">@@ -3429,19 +4439,20 @@
</span><span class="cx"> 
</span><span class="cx">   var helpers = CodeMirror.helpers = {};
</span><span class="cx">   CodeMirror.registerHelper = function(type, name, value) {
</span><del>-    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {};
</del><ins>+    if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []};
</ins><span class="cx">     helpers[type][name] = value;
</span><span class="cx">   };
</span><ins>+  CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
+    CodeMirror.registerHelper(type, name, value);
+    helpers[type]._global.push({pred: predicate, val: value});
+  };
</ins><span class="cx"> 
</span><del>-  // UTILITIES
</del><ins>+  // MODE STATE HANDLING
</ins><span class="cx"> 
</span><del>-  CodeMirror.isWordChar = isWordChar;
</del><ins>+  // Utility functions for working with state. Exported because nested
+  // modes need to do this for their inner modes.
</ins><span class="cx"> 
</span><del>-  // MODE STATE HANDLING
-
-  // Utility functions for working with state. Exported because modes
-  // sometimes need to do this.
-  function copyState(mode, state) {
</del><ins>+  var copyState = CodeMirror.copyState = function(mode, state) {
</ins><span class="cx">     if (state === true) return state;
</span><span class="cx">     if (mode.copyState) return mode.copyState(state);
</span><span class="cx">     var nstate = {};
</span><span class="lines">@@ -3451,14 +4462,14 @@
</span><span class="cx">       nstate[n] = val;
</span><span class="cx">     }
</span><span class="cx">     return nstate;
</span><del>-  }
-  CodeMirror.copyState = copyState;
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function startState(mode, a1, a2) {
</del><ins>+  var startState = CodeMirror.startState = function(mode, a1, a2) {
</ins><span class="cx">     return mode.startState ? mode.startState(a1, a2) : true;
</span><del>-  }
-  CodeMirror.startState = startState;
</del><ins>+  };
</ins><span class="cx"> 
</span><ins>+  // Given a mode and a state (for that mode), find the inner mode and
+  // state at the position that the state refers to.
</ins><span class="cx">   CodeMirror.innerMode = function(mode, state) {
</span><span class="cx">     while (mode.innerMode) {
</span><span class="cx">       var info = mode.innerMode(state);
</span><span class="lines">@@ -3471,49 +4482,73 @@
</span><span class="cx"> 
</span><span class="cx">   // STANDARD COMMANDS
</span><span class="cx"> 
</span><ins>+  // Commands are parameter-less actions that can be performed on an
+  // editor, mostly used for keybindings.
</ins><span class="cx">   var commands = CodeMirror.commands = {
</span><del>-    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
</del><ins>+    selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);},
+    singleSelection: function(cm) {
+      cm.setSelection(cm.getCursor(&quot;anchor&quot;), cm.getCursor(&quot;head&quot;), sel_dontScroll);
+    },
</ins><span class="cx">     killLine: function(cm) {
</span><del>-      var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
-      if (!sel &amp;&amp; cm.getLine(from.line).length == from.ch)
-        cm.replaceRange(&quot;&quot;, from, Pos(from.line + 1, 0), &quot;+delete&quot;);
-      else cm.replaceRange(&quot;&quot;, from, sel ? to : Pos(from.line), &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        if (range.empty()) {
+          var len = getLine(cm.doc, range.head.line).text.length;
+          if (range.head.ch == len &amp;&amp; range.head.line &lt; cm.lastLine())
+            return {from: range.head, to: Pos(range.head.line + 1, 0)};
+          else
+            return {from: range.head, to: Pos(range.head.line, len)};
+        } else {
+          return {from: range.from(), to: range.to()};
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     deleteLine: function(cm) {
</span><del>-      var l = cm.getCursor().line;
-      cm.replaceRange(&quot;&quot;, Pos(l, 0), Pos(l), &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        return {from: Pos(range.from().line, 0),
+                to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
+      });
</ins><span class="cx">     },
</span><span class="cx">     delLineLeft: function(cm) {
</span><del>-      var cur = cm.getCursor();
-      cm.replaceRange(&quot;&quot;, Pos(cur.line, 0), cur, &quot;+delete&quot;);
</del><ins>+      deleteNearSelection(cm, function(range) {
+        return {from: Pos(range.from().line, 0), to: range.from()};
+      });
</ins><span class="cx">     },
</span><span class="cx">     undo: function(cm) {cm.undo();},
</span><span class="cx">     redo: function(cm) {cm.redo();},
</span><ins>+    undoSelection: function(cm) {cm.undoSelection();},
+    redoSelection: function(cm) {cm.redoSelection();},
</ins><span class="cx">     goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
</span><span class="cx">     goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
</span><span class="cx">     goLineStart: function(cm) {
</span><del>-      cm.extendSelection(lineStart(cm, cm.getCursor().line));
</del><ins>+      cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineStartSmart: function(cm) {
</span><del>-      var cur = cm.getCursor(), start = lineStart(cm, cur.line);
-      var line = cm.getLineHandle(start.line);
-      var order = getOrder(line);
-      if (!order || order[0].level == 0) {
-        var firstNonWS = Math.max(0, line.text.search(/\S/));
-        var inWS = cur.line == start.line &amp;&amp; cur.ch &lt;= firstNonWS &amp;&amp; cur.ch;
-        cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
-      } else cm.extendSelection(start);
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var start = lineStart(cm, range.head.line);
+        var line = cm.getLineHandle(start.line);
+        var order = getOrder(line);
+        if (!order || order[0].level == 0) {
+          var firstNonWS = Math.max(0, line.text.search(/\S/));
+          var inWS = range.head.line == start.line &amp;&amp; range.head.ch &lt;= firstNonWS &amp;&amp; range.head.ch;
+          return Pos(start.line, inWS ? 0 : firstNonWS);
+        }
+        return start;
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineEnd: function(cm) {
</span><del>-      cm.extendSelection(lineEnd(cm, cm.getCursor().line));
</del><ins>+      cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineRight: function(cm) {
</span><del>-      var top = cm.charCoords(cm.getCursor(), &quot;div&quot;).top + 5;
-      cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, &quot;div&quot;));
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var top = cm.charCoords(range.head, &quot;div&quot;).top + 5;
+        return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, &quot;div&quot;);
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineLeft: function(cm) {
</span><del>-      var top = cm.charCoords(cm.getCursor(), &quot;div&quot;).top + 5;
-      cm.extendSelection(cm.coordsChar({left: 0, top: top}, &quot;div&quot;));
</del><ins>+      cm.extendSelectionsBy(function(range) {
+        var top = cm.charCoords(range.head, &quot;div&quot;).top + 5;
+        return cm.coordsChar({left: 0, top: top}, &quot;div&quot;);
+      }, sel_move);
</ins><span class="cx">     },
</span><span class="cx">     goLineUp: function(cm) {cm.moveV(-1, &quot;line&quot;);},
</span><span class="cx">     goLineDown: function(cm) {cm.moveV(1, &quot;line&quot;);},
</span><span class="lines">@@ -3536,22 +4571,41 @@
</span><span class="cx">     indentAuto: function(cm) {cm.indentSelection(&quot;smart&quot;);},
</span><span class="cx">     indentMore: function(cm) {cm.indentSelection(&quot;add&quot;);},
</span><span class="cx">     indentLess: function(cm) {cm.indentSelection(&quot;subtract&quot;);},
</span><del>-    insertTab: function(cm) {cm.replaceSelection(&quot;\t&quot;, &quot;end&quot;, &quot;+input&quot;);},
</del><ins>+    insertTab: function(cm) {cm.replaceSelection(&quot;\t&quot;);},
+    insertSoftTab: function(cm) {
+      var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var pos = ranges[i].from();
+        var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
+        spaces.push(new Array(tabSize - col % tabSize + 1).join(&quot; &quot;));
+      }
+      cm.replaceSelections(spaces);
+    },
</ins><span class="cx">     defaultTab: function(cm) {
</span><span class="cx">       if (cm.somethingSelected()) cm.indentSelection(&quot;add&quot;);
</span><del>-      else cm.replaceSelection(&quot;\t&quot;, &quot;end&quot;, &quot;+input&quot;);
</del><ins>+      else cm.execCommand(&quot;insertTab&quot;);
</ins><span class="cx">     },
</span><span class="cx">     transposeChars: function(cm) {
</span><del>-      var cur = cm.getCursor(), line = cm.getLine(cur.line);
-      if (cur.ch &gt; 0 &amp;&amp; cur.ch &lt; line.length - 1)
-        cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
-                        Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
</del><ins>+      runInOp(cm, function() {
+        var ranges = cm.listSelections();
+        for (var i = 0; i &lt; ranges.length; i++) {
+          var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
+          if (cur.ch &gt; 0 &amp;&amp; cur.ch &lt; line.length - 1)
+            cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
+                            Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     newlineAndIndent: function(cm) {
</span><del>-      operation(cm, function() {
-        cm.replaceSelection(&quot;\n&quot;, &quot;end&quot;, &quot;+input&quot;);
-        cm.indentLine(cm.getCursor().line, null, true);
-      })();
</del><ins>+      runInOp(cm, function() {
+        var len = cm.listSelections().length;
+        for (var i = 0; i &lt; len; i++) {
+          var range = cm.listSelections()[i];
+          cm.replaceRange(&quot;\n&quot;, range.anchor, range.head, &quot;+input&quot;);
+          cm.indentLine(range.from().line + 1, null, true);
+          ensureCursorVisible(cm);
+        }
+      });
</ins><span class="cx">     },
</span><span class="cx">     toggleOverwrite: function(cm) {cm.toggleOverwrite();}
</span><span class="cx">   };
</span><span class="lines">@@ -3564,17 +4618,20 @@
</span><span class="cx">     &quot;End&quot;: &quot;goLineEnd&quot;, &quot;Home&quot;: &quot;goLineStartSmart&quot;, &quot;PageUp&quot;: &quot;goPageUp&quot;, &quot;PageDown&quot;: &quot;goPageDown&quot;,
</span><span class="cx">     &quot;Delete&quot;: &quot;delCharAfter&quot;, &quot;Backspace&quot;: &quot;delCharBefore&quot;, &quot;Shift-Backspace&quot;: &quot;delCharBefore&quot;,
</span><span class="cx">     &quot;Tab&quot;: &quot;defaultTab&quot;, &quot;Shift-Tab&quot;: &quot;indentAuto&quot;,
</span><del>-    &quot;Enter&quot;: &quot;newlineAndIndent&quot;, &quot;Insert&quot;: &quot;toggleOverwrite&quot;
</del><ins>+    &quot;Enter&quot;: &quot;newlineAndIndent&quot;, &quot;Insert&quot;: &quot;toggleOverwrite&quot;,
+    &quot;Esc&quot;: &quot;singleSelection&quot;
</ins><span class="cx">   };
</span><span class="cx">   // Note that the save and find-related commands aren't defined by
</span><del>-  // default. Unknown commands are simply ignored.
</del><ins>+  // default. User code or addons can define them. Unknown commands
+  // are simply ignored.
</ins><span class="cx">   keyMap.pcDefault = {
</span><span class="cx">     &quot;Ctrl-A&quot;: &quot;selectAll&quot;, &quot;Ctrl-D&quot;: &quot;deleteLine&quot;, &quot;Ctrl-Z&quot;: &quot;undo&quot;, &quot;Shift-Ctrl-Z&quot;: &quot;redo&quot;, &quot;Ctrl-Y&quot;: &quot;redo&quot;,
</span><del>-    &quot;Ctrl-Home&quot;: &quot;goDocStart&quot;, &quot;Alt-Up&quot;: &quot;goDocStart&quot;, &quot;Ctrl-End&quot;: &quot;goDocEnd&quot;, &quot;Ctrl-Down&quot;: &quot;goDocEnd&quot;,
</del><ins>+    &quot;Ctrl-Home&quot;: &quot;goDocStart&quot;, &quot;Ctrl-Up&quot;: &quot;goDocStart&quot;, &quot;Ctrl-End&quot;: &quot;goDocEnd&quot;, &quot;Ctrl-Down&quot;: &quot;goDocEnd&quot;,
</ins><span class="cx">     &quot;Ctrl-Left&quot;: &quot;goGroupLeft&quot;, &quot;Ctrl-Right&quot;: &quot;goGroupRight&quot;, &quot;Alt-Left&quot;: &quot;goLineStart&quot;, &quot;Alt-Right&quot;: &quot;goLineEnd&quot;,
</span><span class="cx">     &quot;Ctrl-Backspace&quot;: &quot;delGroupBefore&quot;, &quot;Ctrl-Delete&quot;: &quot;delGroupAfter&quot;, &quot;Ctrl-S&quot;: &quot;save&quot;, &quot;Ctrl-F&quot;: &quot;find&quot;,
</span><span class="cx">     &quot;Ctrl-G&quot;: &quot;findNext&quot;, &quot;Shift-Ctrl-G&quot;: &quot;findPrev&quot;, &quot;Shift-Ctrl-F&quot;: &quot;replace&quot;, &quot;Shift-Ctrl-R&quot;: &quot;replaceAll&quot;,
</span><span class="cx">     &quot;Ctrl-[&quot;: &quot;indentLess&quot;, &quot;Ctrl-]&quot;: &quot;indentMore&quot;,
</span><ins>+    &quot;Ctrl-U&quot;: &quot;undoSelection&quot;, &quot;Shift-Ctrl-U&quot;: &quot;redoSelection&quot;, &quot;Alt-U&quot;: &quot;redoSelection&quot;,
</ins><span class="cx">     fallthrough: &quot;basic&quot;
</span><span class="cx">   };
</span><span class="cx">   keyMap.macDefault = {
</span><span class="lines">@@ -3584,15 +4641,17 @@
</span><span class="cx">     &quot;Ctrl-Alt-Backspace&quot;: &quot;delGroupAfter&quot;, &quot;Alt-Delete&quot;: &quot;delGroupAfter&quot;, &quot;Cmd-S&quot;: &quot;save&quot;, &quot;Cmd-F&quot;: &quot;find&quot;,
</span><span class="cx">     &quot;Cmd-G&quot;: &quot;findNext&quot;, &quot;Shift-Cmd-G&quot;: &quot;findPrev&quot;, &quot;Cmd-Alt-F&quot;: &quot;replace&quot;, &quot;Shift-Cmd-Alt-F&quot;: &quot;replaceAll&quot;,
</span><span class="cx">     &quot;Cmd-[&quot;: &quot;indentLess&quot;, &quot;Cmd-]&quot;: &quot;indentMore&quot;, &quot;Cmd-Backspace&quot;: &quot;delLineLeft&quot;,
</span><ins>+    &quot;Cmd-U&quot;: &quot;undoSelection&quot;, &quot;Shift-Cmd-U&quot;: &quot;redoSelection&quot;,
</ins><span class="cx">     fallthrough: [&quot;basic&quot;, &quot;emacsy&quot;]
</span><span class="cx">   };
</span><del>-  keyMap[&quot;default&quot;] = mac ? keyMap.macDefault : keyMap.pcDefault;
</del><ins>+  // Very basic readline/emacs-style bindings, which are standard on Mac.
</ins><span class="cx">   keyMap.emacsy = {
</span><span class="cx">     &quot;Ctrl-F&quot;: &quot;goCharRight&quot;, &quot;Ctrl-B&quot;: &quot;goCharLeft&quot;, &quot;Ctrl-P&quot;: &quot;goLineUp&quot;, &quot;Ctrl-N&quot;: &quot;goLineDown&quot;,
</span><span class="cx">     &quot;Alt-F&quot;: &quot;goWordRight&quot;, &quot;Alt-B&quot;: &quot;goWordLeft&quot;, &quot;Ctrl-A&quot;: &quot;goLineStart&quot;, &quot;Ctrl-E&quot;: &quot;goLineEnd&quot;,
</span><span class="cx">     &quot;Ctrl-V&quot;: &quot;goPageDown&quot;, &quot;Shift-Ctrl-V&quot;: &quot;goPageUp&quot;, &quot;Ctrl-D&quot;: &quot;delCharAfter&quot;, &quot;Ctrl-H&quot;: &quot;delCharBefore&quot;,
</span><span class="cx">     &quot;Alt-D&quot;: &quot;delWordAfter&quot;, &quot;Alt-Backspace&quot;: &quot;delWordBefore&quot;, &quot;Ctrl-K&quot;: &quot;killLine&quot;, &quot;Ctrl-T&quot;: &quot;transposeChars&quot;
</span><span class="cx">   };
</span><ins>+  keyMap[&quot;default&quot;] = mac ? keyMap.macDefault : keyMap.pcDefault;
</ins><span class="cx"> 
</span><span class="cx">   // KEYMAP DISPATCH
</span><span class="cx"> 
</span><span class="lines">@@ -3601,7 +4660,11 @@
</span><span class="cx">     else return val;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function lookupKey(name, maps, handle) {
</del><ins>+  // Given an array of keymaps and a key name, call handle on any
+  // bindings found, until that returns a truthy value, at which point
+  // we consider the key handled. Implements things like binding a key
+  // to false stopping further handling and keymap fallthrough.
+  var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) {
</ins><span class="cx">     function lookup(map) {
</span><span class="cx">       map = getKeyMap(map);
</span><span class="cx">       var found = map[name];
</span><span class="lines">@@ -3613,7 +4676,7 @@
</span><span class="cx">       if (fallthrough == null) return false;
</span><span class="cx">       if (Object.prototype.toString.call(fallthrough) != &quot;[object Array]&quot;)
</span><span class="cx">         return lookup(fallthrough);
</span><del>-      for (var i = 0, e = fallthrough.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; fallthrough.length; ++i) {
</ins><span class="cx">         var done = lookup(fallthrough[i]);
</span><span class="cx">         if (done) return done;
</span><span class="cx">       }
</span><span class="lines">@@ -3624,13 +4687,18 @@
</span><span class="cx">       var done = lookup(maps[i]);
</span><span class="cx">       if (done) return done != &quot;stop&quot;;
</span><span class="cx">     }
</span><del>-  }
-  function isModifierKey(event) {
</del><ins>+  };
+
+  // Modifier key presses don't count as 'real' key presses for the
+  // purpose of keymap fallthrough.
+  var isModifierKey = CodeMirror.isModifierKey = function(event) {
</ins><span class="cx">     var name = keyNames[event.keyCode];
</span><span class="cx">     return name == &quot;Ctrl&quot; || name == &quot;Alt&quot; || name == &quot;Shift&quot; || name == &quot;Mod&quot;;
</span><del>-  }
-  function keyName(event, noShift) {
-    if (opera &amp;&amp; event.keyCode == 34 &amp;&amp; event[&quot;char&quot;]) return false;
</del><ins>+  };
+
+  // Look up the name of a key as indicated by an event object.
+  var keyName = CodeMirror.keyName = function(event, noShift) {
+    if (presto &amp;&amp; event.keyCode == 34 &amp;&amp; event[&quot;char&quot;]) return false;
</ins><span class="cx">     var name = keyNames[event.keyCode];
</span><span class="cx">     if (name == null || event.altGraphKey) return false;
</span><span class="cx">     if (event.altKey) name = &quot;Alt-&quot; + name;
</span><span class="lines">@@ -3638,10 +4706,7 @@
</span><span class="cx">     if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = &quot;Cmd-&quot; + name;
</span><span class="cx">     if (!noShift &amp;&amp; event.shiftKey) name = &quot;Shift-&quot; + name;
</span><span class="cx">     return name;
</span><del>-  }
-  CodeMirror.lookupKey = lookupKey;
-  CodeMirror.isModifierKey = isModifierKey;
-  CodeMirror.keyName = keyName;
</del><ins>+  };
</ins><span class="cx"> 
</span><span class="cx">   // FROMTEXTAREA
</span><span class="cx"> 
</span><span class="lines">@@ -3655,9 +4720,7 @@
</span><span class="cx">     // Set autofocus to true if this textarea is focused, or if it has
</span><span class="cx">     // autofocus and no other element is focused.
</span><span class="cx">     if (options.autofocus == null) {
</span><del>-      var hasFocus = document.body;
-      // doc.activeElement occasionally throws on IE
-      try { hasFocus = document.activeElement; } catch(e) {}
</del><ins>+      var hasFocus = activeElt();
</ins><span class="cx">       options.autofocus = hasFocus == textarea ||
</span><span class="cx">         textarea.getAttribute(&quot;autofocus&quot;) != null &amp;&amp; hasFocus == document.body;
</span><span class="cx">     }
</span><span class="lines">@@ -3703,17 +4766,17 @@
</span><span class="cx">   // Fed to the mode parsers, provides helper functions to make
</span><span class="cx">   // parsers more succinct.
</span><span class="cx"> 
</span><del>-  // The character stream used by a mode's parser.
-  function StringStream(string, tabSize) {
</del><ins>+  var StringStream = CodeMirror.StringStream = function(string, tabSize) {
</ins><span class="cx">     this.pos = this.start = 0;
</span><span class="cx">     this.string = string;
</span><span class="cx">     this.tabSize = tabSize || 8;
</span><span class="cx">     this.lastColumnPos = this.lastColumnValue = 0;
</span><del>-  }
</del><ins>+    this.lineStart = 0;
+  };
</ins><span class="cx"> 
</span><span class="cx">   StringStream.prototype = {
</span><span class="cx">     eol: function() {return this.pos &gt;= this.string.length;},
</span><del>-    sol: function() {return this.pos == 0;},
</del><ins>+    sol: function() {return this.pos == this.lineStart;},
</ins><span class="cx">     peek: function() {return this.string.charAt(this.pos) || undefined;},
</span><span class="cx">     next: function() {
</span><span class="cx">       if (this.pos &lt; this.string.length)
</span><span class="lines">@@ -3746,9 +4809,12 @@
</span><span class="cx">         this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
</span><span class="cx">         this.lastColumnPos = this.start;
</span><span class="cx">       }
</span><del>-      return this.lastColumnValue;
</del><ins>+      return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
</ins><span class="cx">     },
</span><del>-    indentation: function() {return countColumn(this.string, null, this.tabSize);},
</del><ins>+    indentation: function() {
+      return countColumn(this.string, null, this.tabSize) -
+        (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+    },
</ins><span class="cx">     match: function(pattern, consume, caseInsensitive) {
</span><span class="cx">       if (typeof pattern == &quot;string&quot;) {
</span><span class="cx">         var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
</span><span class="lines">@@ -3764,20 +4830,34 @@
</span><span class="cx">         return match;
</span><span class="cx">       }
</span><span class="cx">     },
</span><del>-    current: function(){return this.string.slice(this.start, this.pos);}
</del><ins>+    current: function(){return this.string.slice(this.start, this.pos);},
+    hideFirstChars: function(n, inner) {
+      this.lineStart += n;
+      try { return inner(); }
+      finally { this.lineStart -= n; }
+    }
</ins><span class="cx">   };
</span><del>-  CodeMirror.StringStream = StringStream;
</del><span class="cx"> 
</span><span class="cx">   // TEXTMARKERS
</span><span class="cx"> 
</span><del>-  function TextMarker(doc, type) {
</del><ins>+  // Created with markText and setBookmark methods. A TextMarker is a
+  // handle that can be used to clear or find a marked position in the
+  // document. Line objects hold arrays (markedSpans) containing
+  // {from, to, marker} object pointing to such marker objects, and
+  // indicating that such a marker is present on that line. Multiple
+  // lines may point to the same marker when it spans across lines.
+  // The spans will have null for their from/to properties when the
+  // marker continues beyond the start/end of the line. Markers have
+  // links back to the lines they currently touch.
+
+  var TextMarker = CodeMirror.TextMarker = function(doc, type) {
</ins><span class="cx">     this.lines = [];
</span><span class="cx">     this.type = type;
</span><span class="cx">     this.doc = doc;
</span><del>-  }
-  CodeMirror.TextMarker = TextMarker;
</del><ins>+  };
</ins><span class="cx">   eventMixin(TextMarker);
</span><span class="cx"> 
</span><ins>+  // Clear the marker.
</ins><span class="cx">   TextMarker.prototype.clear = function() {
</span><span class="cx">     if (this.explicitlyCleared) return;
</span><span class="cx">     var cm = this.doc.cm, withOp = cm &amp;&amp; !cm.curOp;
</span><span class="lines">@@ -3790,15 +4870,17 @@
</span><span class="cx">     for (var i = 0; i &lt; this.lines.length; ++i) {
</span><span class="cx">       var line = this.lines[i];
</span><span class="cx">       var span = getMarkedSpanFor(line.markedSpans, this);
</span><del>-      if (span.to != null) max = lineNo(line);
</del><ins>+      if (cm &amp;&amp; !this.collapsed) regLineChange(cm, lineNo(line), &quot;text&quot;);
+      else if (cm) {
+        if (span.to != null) max = lineNo(line);
+        if (span.from != null) min = lineNo(line);
+      }
</ins><span class="cx">       line.markedSpans = removeMarkedSpan(line.markedSpans, span);
</span><del>-      if (span.from != null)
-        min = lineNo(line);
-      else if (this.collapsed &amp;&amp; !lineIsHidden(this.doc, line) &amp;&amp; cm)
</del><ins>+      if (span.from == null &amp;&amp; this.collapsed &amp;&amp; !lineIsHidden(this.doc, line) &amp;&amp; cm)
</ins><span class="cx">         updateLineHeight(line, textHeight(cm.display));
</span><span class="cx">     }
</span><span class="cx">     if (cm &amp;&amp; this.collapsed &amp;&amp; !cm.options.lineWrapping) for (var i = 0; i &lt; this.lines.length; ++i) {
</span><del>-      var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
</del><ins>+      var visual = visualLine(this.lines[i]), len = lineLength(visual);
</ins><span class="cx">       if (len &gt; cm.display.maxLineLength) {
</span><span class="cx">         cm.display.maxLine = visual;
</span><span class="cx">         cm.display.maxLineLength = len;
</span><span class="lines">@@ -3806,46 +4888,62 @@
</span><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (min != null &amp;&amp; cm) regChange(cm, min, max + 1);
</del><ins>+    if (min != null &amp;&amp; cm &amp;&amp; this.collapsed) regChange(cm, min, max + 1);
</ins><span class="cx">     this.lines.length = 0;
</span><span class="cx">     this.explicitlyCleared = true;
</span><span class="cx">     if (this.atomic &amp;&amp; this.doc.cantEdit) {
</span><span class="cx">       this.doc.cantEdit = false;
</span><del>-      if (cm) reCheckSelection(cm);
</del><ins>+      if (cm) reCheckSelection(cm.doc);
</ins><span class="cx">     }
</span><ins>+    if (cm) signalLater(cm, &quot;markerCleared&quot;, cm, this);
</ins><span class="cx">     if (withOp) endOperation(cm);
</span><ins>+    if (this.parent) this.parent.clear();
</ins><span class="cx">   };
</span><span class="cx"> 
</span><del>-  TextMarker.prototype.find = function() {
</del><ins>+  // Find the position of the marker in the document. Returns a {from,
+  // to} object by default. Side can be passed to get a specific side
+  // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
+  // Pos objects returned contain a line object, rather than a line
+  // number (used to prevent looking up the same line twice).
+  TextMarker.prototype.find = function(side, lineObj) {
+    if (side == null &amp;&amp; this.type == &quot;bookmark&quot;) side = 1;
</ins><span class="cx">     var from, to;
</span><span class="cx">     for (var i = 0; i &lt; this.lines.length; ++i) {
</span><span class="cx">       var line = this.lines[i];
</span><span class="cx">       var span = getMarkedSpanFor(line.markedSpans, this);
</span><del>-      if (span.from != null || span.to != null) {
-        var found = lineNo(line);
-        if (span.from != null) from = Pos(found, span.from);
-        if (span.to != null) to = Pos(found, span.to);
</del><ins>+      if (span.from != null) {
+        from = Pos(lineObj ? line : lineNo(line), span.from);
+        if (side == -1) return from;
</ins><span class="cx">       }
</span><ins>+      if (span.to != null) {
+        to = Pos(lineObj ? line : lineNo(line), span.to);
+        if (side == 1) return to;
+      }
</ins><span class="cx">     }
</span><del>-    if (this.type == &quot;bookmark&quot;) return from;
</del><span class="cx">     return from &amp;&amp; {from: from, to: to};
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Signals that the marker's widget changed, and surrounding layout
+  // should be recomputed.
</ins><span class="cx">   TextMarker.prototype.changed = function() {
</span><del>-    var pos = this.find(), cm = this.doc.cm;
</del><ins>+    var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
</ins><span class="cx">     if (!pos || !cm) return;
</span><del>-    if (this.type != &quot;bookmark&quot;) pos = pos.from;
-    var line = getLine(this.doc, pos.line);
-    clearCachedMeasurement(cm, line);
-    if (pos.line &gt;= cm.display.showingFrom &amp;&amp; pos.line &lt; cm.display.showingTo) {
-      for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
-        if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
-        break;
</del><ins>+    runInOp(cm, function() {
+      var line = pos.line, lineN = lineNo(pos.line);
+      var view = findViewForLine(cm, lineN);
+      if (view) {
+        clearLineMeasurementCacheFor(view);
+        cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
</ins><span class="cx">       }
</span><del>-      runInOp(cm, function() {
-        cm.curOp.selectionChanged = cm.curOp.forceUpdate = cm.curOp.updateMaxLine = true;
-      });
-    }
</del><ins>+      cm.curOp.updateMaxLine = true;
+      if (!lineIsHidden(widget.doc, line) &amp;&amp; widget.height != null) {
+        var oldHeight = widget.height;
+        widget.height = null;
+        var dHeight = widgetHeight(widget) - oldHeight;
+        if (dHeight)
+          updateLineHeight(line, line.height + dHeight);
+      }
+    });
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   TextMarker.prototype.attachLine = function(line) {
</span><span class="lines">@@ -3864,42 +4962,53 @@
</span><span class="cx">     }
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Collapsed markers have unique ids, in order to be able to order
+  // them, which is needed for uniquely determining an outer marker
+  // when they overlap (they may nest, but not partially overlap).
+  var nextMarkerId = 0;
+
+  // Create a marker, wire it up to the right lines, and
</ins><span class="cx">   function markText(doc, from, to, options, type) {
</span><ins>+    // Shared markers (across linked documents) are handled separately
+    // (markTextShared will call out to this again, once per
+    // document).
</ins><span class="cx">     if (options &amp;&amp; options.shared) return markTextShared(doc, from, to, options, type);
</span><ins>+    // Ensure we are in an operation.
</ins><span class="cx">     if (doc.cm &amp;&amp; !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
</span><span class="cx"> 
</span><del>-    var marker = new TextMarker(doc, type);
-    if (posLess(to, from) || posEq(from, to) &amp;&amp; type == &quot;range&quot; &amp;&amp;
-        !(options.inclusiveLeft &amp;&amp; options.inclusiveRight))
</del><ins>+    var marker = new TextMarker(doc, type), diff = cmp(from, to);
+    if (options) copyObj(options, marker, false);
+    // Don't connect empty markers unless clearWhenEmpty is false
+    if (diff &gt; 0 || diff == 0 &amp;&amp; marker.clearWhenEmpty !== false)
</ins><span class="cx">       return marker;
</span><del>-    if (options) copyObj(options, marker);
</del><span class="cx">     if (marker.replacedWith) {
</span><ins>+      // Showing up as a widget implies collapsed (widget replaces text)
</ins><span class="cx">       marker.collapsed = true;
</span><del>-      marker.replacedWith = elt(&quot;span&quot;, [marker.replacedWith], &quot;CodeMirror-widget&quot;);
-      if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true;
</del><ins>+      marker.widgetNode = elt(&quot;span&quot;, [marker.replacedWith], &quot;CodeMirror-widget&quot;);
+      if (!options.handleMouseEvents) marker.widgetNode.ignoreEvents = true;
+      if (options.insertLeft) marker.widgetNode.insertLeft = true;
</ins><span class="cx">     }
</span><del>-    if (marker.collapsed) sawCollapsedSpans = true;
</del><ins>+    if (marker.collapsed) {
+      if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
+          from.line != to.line &amp;&amp; conflictingCollapsedRange(doc, to.line, from, to, marker))
+        throw new Error(&quot;Inserting collapsed marker partially overlapping an existing one&quot;);
+      sawCollapsedSpans = true;
+    }
</ins><span class="cx"> 
</span><span class="cx">     if (marker.addToHistory)
</span><del>-      addToHistory(doc, {from: from, to: to, origin: &quot;markText&quot;},
-                   {head: doc.sel.head, anchor: doc.sel.anchor}, NaN);
</del><ins>+      addChangeToHistory(doc, {from: from, to: to, origin: &quot;markText&quot;}, doc.sel, NaN);
</ins><span class="cx"> 
</span><del>-    var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
</del><ins>+    var curLine = from.line, cm = doc.cm, updateMaxLine;
</ins><span class="cx">     doc.iter(curLine, to.line + 1, function(line) {
</span><del>-      if (cm &amp;&amp; marker.collapsed &amp;&amp; !cm.options.lineWrapping &amp;&amp; visualLine(doc, line) == cm.display.maxLine)
</del><ins>+      if (cm &amp;&amp; marker.collapsed &amp;&amp; !cm.options.lineWrapping &amp;&amp; visualLine(line) == cm.display.maxLine)
</ins><span class="cx">         updateMaxLine = true;
</span><del>-      var span = {from: null, to: null, marker: marker};
-      size += line.text.length;
-      if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
-      if (curLine == to.line) {span.to = to.ch; size -= line.text.length - to.ch;}
-      if (marker.collapsed) {
-        if (curLine == to.line) collapsedAtEnd = collapsedSpanAt(line, to.ch);
-        if (curLine == from.line) collapsedAtStart = collapsedSpanAt(line, from.ch);
-        else updateLineHeight(line, 0);
-      }
-      addMarkedSpan(line, span);
</del><ins>+      if (marker.collapsed &amp;&amp; curLine != from.line) updateLineHeight(line, 0);
+      addMarkedSpan(line, new MarkedSpan(marker,
+                                         curLine == from.line ? from.ch : null,
+                                         curLine == to.line ? to.ch : null));
</ins><span class="cx">       ++curLine;
</span><span class="cx">     });
</span><ins>+    // lineIsHidden depends on the presence of the spans, so needs a second pass
</ins><span class="cx">     if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
</span><span class="cx">       if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
</span><span class="cx">     });
</span><span class="lines">@@ -3912,31 +5021,33 @@
</span><span class="cx">         doc.clearHistory();
</span><span class="cx">     }
</span><span class="cx">     if (marker.collapsed) {
</span><del>-      if (collapsedAtStart != collapsedAtEnd)
-        throw new Error(&quot;Inserting collapsed marker overlapping an existing one&quot;);
-      marker.size = size;
</del><ins>+      marker.id = ++nextMarkerId;
</ins><span class="cx">       marker.atomic = true;
</span><span class="cx">     }
</span><span class="cx">     if (cm) {
</span><ins>+      // Sync editor state
</ins><span class="cx">       if (updateMaxLine) cm.curOp.updateMaxLine = true;
</span><del>-      if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed)
</del><ins>+      if (marker.collapsed)
</ins><span class="cx">         regChange(cm, from.line, to.line + 1);
</span><del>-      if (marker.atomic) reCheckSelection(cm);
</del><ins>+      else if (marker.className || marker.title || marker.startStyle || marker.endStyle)
+        for (var i = from.line; i &lt;= to.line; i++) regLineChange(cm, i, &quot;text&quot;);
+      if (marker.atomic) reCheckSelection(cm.doc);
+      signalLater(cm, &quot;markerAdded&quot;, cm, marker);
</ins><span class="cx">     }
</span><span class="cx">     return marker;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // SHARED TEXTMARKERS
</span><span class="cx"> 
</span><del>-  function SharedTextMarker(markers, primary) {
</del><ins>+  // A shared marker spans multiple linked documents. It is
+  // implemented as a meta-marker-object controlling multiple normal
+  // markers.
+  var SharedTextMarker = CodeMirror.SharedTextMarker = function(markers, primary) {
</ins><span class="cx">     this.markers = markers;
</span><span class="cx">     this.primary = primary;
</span><del>-    for (var i = 0, me = this; i &lt; markers.length; ++i) {
</del><ins>+    for (var i = 0; i &lt; markers.length; ++i)
</ins><span class="cx">       markers[i].parent = this;
</span><del>-      on(markers[i], &quot;clear&quot;, function(){me.clear();});
-    }
-  }
-  CodeMirror.SharedTextMarker = SharedTextMarker;
</del><ins>+  };
</ins><span class="cx">   eventMixin(SharedTextMarker);
</span><span class="cx"> 
</span><span class="cx">   SharedTextMarker.prototype.clear = function() {
</span><span class="lines">@@ -3946,17 +5057,17 @@
</span><span class="cx">       this.markers[i].clear();
</span><span class="cx">     signalLater(this, &quot;clear&quot;);
</span><span class="cx">   };
</span><del>-  SharedTextMarker.prototype.find = function() {
-    return this.primary.find();
</del><ins>+  SharedTextMarker.prototype.find = function(side, lineObj) {
+    return this.primary.find(side, lineObj);
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   function markTextShared(doc, from, to, options, type) {
</span><span class="cx">     options = copyObj(options);
</span><span class="cx">     options.shared = false;
</span><span class="cx">     var markers = [markText(doc, from, to, options, type)], primary = markers[0];
</span><del>-    var widget = options.replacedWith;
</del><ins>+    var widget = options.widgetNode;
</ins><span class="cx">     linkedDocs(doc, function(doc) {
</span><del>-      if (widget) options.replacedWith = widget.cloneNode(true);
</del><ins>+      if (widget) options.widgetNode = widget.cloneNode(true);
</ins><span class="cx">       markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
</span><span class="cx">       for (var i = 0; i &lt; doc.linked.length; ++i)
</span><span class="cx">         if (doc.linked[i].isParent) return;
</span><span class="lines">@@ -3965,60 +5076,104 @@
</span><span class="cx">     return new SharedTextMarker(markers, primary);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function findSharedMarkers(doc) {
+    return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())),
+                         function(m) { return m.parent; });
+  }
+
+  function copySharedMarkers(doc, markers) {
+    for (var i = 0; i &lt; markers.length; i++) {
+      var marker = markers[i], pos = marker.find();
+      var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
+      if (cmp(mFrom, mTo)) {
+        var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
+        marker.markers.push(subMark);
+        subMark.parent = marker;
+      }
+    }
+  }
+
+  function detachSharedMarkers(markers) {
+    for (var i = 0; i &lt; markers.length; i++) {
+      var marker = markers[i], linked = [marker.primary.doc];;
+      linkedDocs(marker.primary.doc, function(d) { linked.push(d); });
+      for (var j = 0; j &lt; marker.markers.length; j++) {
+        var subMarker = marker.markers[j];
+        if (indexOf(linked, subMarker.doc) == -1) {
+          subMarker.parent = null;
+          marker.markers.splice(j--, 1);
+        }
+      }
+    }
+  }
+
</ins><span class="cx">   // TEXTMARKER SPANS
</span><span class="cx"> 
</span><ins>+  function MarkedSpan(marker, from, to) {
+    this.marker = marker;
+    this.from = from; this.to = to;
+  }
+
+  // Search an array of spans for a span matching the given marker.
</ins><span class="cx">   function getMarkedSpanFor(spans, marker) {
</span><span class="cx">     if (spans) for (var i = 0; i &lt; spans.length; ++i) {
</span><span class="cx">       var span = spans[i];
</span><span class="cx">       if (span.marker == marker) return span;
</span><span class="cx">     }
</span><span class="cx">   }
</span><ins>+  // Remove a span from an array, returning undefined if no spans are
+  // left (we don't store arrays for lines without spans).
</ins><span class="cx">   function removeMarkedSpan(spans, span) {
</span><span class="cx">     for (var r, i = 0; i &lt; spans.length; ++i)
</span><span class="cx">       if (spans[i] != span) (r || (r = [])).push(spans[i]);
</span><span class="cx">     return r;
</span><span class="cx">   }
</span><ins>+  // Add a span to a line.
</ins><span class="cx">   function addMarkedSpan(line, span) {
</span><span class="cx">     line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
</span><span class="cx">     span.marker.attachLine(line);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used for the algorithm that adjusts markers for a change in the
+  // document. These functions cut an array of spans at a given
+  // character position, returning an array of remaining chunks (or
+  // undefined if nothing remains).
</ins><span class="cx">   function markedSpansBefore(old, startCh, isInsert) {
</span><span class="cx">     if (old) for (var i = 0, nw; i &lt; old.length; ++i) {
</span><span class="cx">       var span = old[i], marker = span.marker;
</span><span class="cx">       var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from &lt;= startCh : span.from &lt; startCh);
</span><del>-      if (startsBefore ||
-          (marker.inclusiveLeft &amp;&amp; marker.inclusiveRight || marker.type == &quot;bookmark&quot;) &amp;&amp;
-          span.from == startCh &amp;&amp; (!isInsert || !span.marker.insertLeft)) {
</del><ins>+      if (startsBefore || span.from == startCh &amp;&amp; marker.type == &quot;bookmark&quot; &amp;&amp; (!isInsert || !span.marker.insertLeft)) {
</ins><span class="cx">         var endsAfter = span.to == null || (marker.inclusiveRight ? span.to &gt;= startCh : span.to &gt; startCh);
</span><del>-        (nw || (nw = [])).push({from: span.from,
-                                to: endsAfter ? null : span.to,
-                                marker: marker});
</del><ins>+        (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     return nw;
</span><span class="cx">   }
</span><del>-
</del><span class="cx">   function markedSpansAfter(old, endCh, isInsert) {
</span><span class="cx">     if (old) for (var i = 0, nw; i &lt; old.length; ++i) {
</span><span class="cx">       var span = old[i], marker = span.marker;
</span><span class="cx">       var endsAfter = span.to == null || (marker.inclusiveRight ? span.to &gt;= endCh : span.to &gt; endCh);
</span><del>-      if (endsAfter || marker.type == &quot;bookmark&quot; &amp;&amp; span.from == endCh &amp;&amp; (!isInsert || span.marker.insertLeft)) {
</del><ins>+      if (endsAfter || span.from == endCh &amp;&amp; marker.type == &quot;bookmark&quot; &amp;&amp; (!isInsert || span.marker.insertLeft)) {
</ins><span class="cx">         var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from &lt;= endCh : span.from &lt; endCh);
</span><del>-        (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
-                                to: span.to == null ? null : span.to - endCh,
-                                marker: marker});
</del><ins>+        (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
+                                              span.to == null ? null : span.to - endCh));
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">     return nw;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Given a change object, compute the new set of marker spans that
+  // cover the line in which the change took place. Removes spans
+  // entirely within the change, reconnects spans belonging to the
+  // same marker that appear on both sides of the change, and cuts off
+  // spans partially within the change. Returns an array of span
+  // arrays with one element for each line in (after) the change.
</ins><span class="cx">   function stretchSpansOverChange(doc, change) {
</span><span class="cx">     var oldFirst = isLine(doc, change.from.line) &amp;&amp; getLine(doc, change.from.line).markedSpans;
</span><span class="cx">     var oldLast = isLine(doc, change.to.line) &amp;&amp; getLine(doc, change.to.line).markedSpans;
</span><span class="cx">     if (!oldFirst &amp;&amp; !oldLast) return null;
</span><span class="cx"> 
</span><del>-    var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
</del><ins>+    var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
</ins><span class="cx">     // Get the spans that 'stick out' on both sides
</span><span class="cx">     var first = markedSpansBefore(oldFirst, startCh, isInsert);
</span><span class="cx">     var last = markedSpansAfter(oldLast, endCh, isInsert);
</span><span class="lines">@@ -4053,13 +5208,9 @@
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><del>-    if (sameLine &amp;&amp; first) {
-      // Make sure we didn't create any zero-length spans
-      for (var i = 0; i &lt; first.length; ++i)
-        if (first[i].from != null &amp;&amp; first[i].from == first[i].to &amp;&amp; first[i].marker.type != &quot;bookmark&quot;)
-          first.splice(i--, 1);
-      if (!first.length) first = null;
-    }
</del><ins>+    // Make sure we didn't create any zero-length spans
+    if (first) first = clearEmptySpans(first);
+    if (last &amp;&amp; last != first) last = clearEmptySpans(last);
</ins><span class="cx"> 
</span><span class="cx">     var newMarkers = [first];
</span><span class="cx">     if (!sameLine) {
</span><span class="lines">@@ -4068,7 +5219,7 @@
</span><span class="cx">       if (gap &gt; 0 &amp;&amp; first)
</span><span class="cx">         for (var i = 0; i &lt; first.length; ++i)
</span><span class="cx">           if (first[i].to == null)
</span><del>-            (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
</del><ins>+            (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null));
</ins><span class="cx">       for (var i = 0; i &lt; gap; ++i)
</span><span class="cx">         newMarkers.push(gapMarkers);
</span><span class="cx">       newMarkers.push(last);
</span><span class="lines">@@ -4076,6 +5227,22 @@
</span><span class="cx">     return newMarkers;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Remove spans that are empty and don't have a clearWhenEmpty
+  // option of false.
+  function clearEmptySpans(spans) {
+    for (var i = 0; i &lt; spans.length; ++i) {
+      var span = spans[i];
+      if (span.from != null &amp;&amp; span.from == span.to &amp;&amp; span.marker.clearWhenEmpty !== false)
+        spans.splice(i--, 1);
+    }
+    if (!spans.length) return null;
+    return spans;
+  }
+
+  // Used for un/re-doing changes from the history. Combines the
+  // result of computing the existing spans with the set of spans that
+  // existed in the history (so that deleting around a span and then
+  // undoing brings back the span).
</ins><span class="cx">   function mergeOldSpans(doc, change) {
</span><span class="cx">     var old = getOldSpans(doc, change);
</span><span class="cx">     var stretched = stretchSpansOverChange(doc, change);
</span><span class="lines">@@ -4098,6 +5265,7 @@
</span><span class="cx">     return old;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Used to 'clip' out readOnly ranges when making a change.
</ins><span class="cx">   function removeReadOnlyRanges(doc, from, to) {
</span><span class="cx">     var markers = null;
</span><span class="cx">     doc.iter(from.line, to.line + 1, function(line) {
</span><span class="lines">@@ -4110,14 +5278,14 @@
</span><span class="cx">     if (!markers) return null;
</span><span class="cx">     var parts = [{from: from, to: to}];
</span><span class="cx">     for (var i = 0; i &lt; markers.length; ++i) {
</span><del>-      var mk = markers[i], m = mk.find();
</del><ins>+      var mk = markers[i], m = mk.find(0);
</ins><span class="cx">       for (var j = 0; j &lt; parts.length; ++j) {
</span><span class="cx">         var p = parts[j];
</span><del>-        if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
-        var newParts = [j, 1];
-        if (posLess(p.from, m.from) || !mk.inclusiveLeft &amp;&amp; posEq(p.from, m.from))
</del><ins>+        if (cmp(p.to, m.from) &lt; 0 || cmp(p.from, m.to) &gt; 0) continue;
+        var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
+        if (dfrom &lt; 0 || !mk.inclusiveLeft &amp;&amp; !dfrom)
</ins><span class="cx">           newParts.push({from: p.from, to: m.from});
</span><del>-        if (posLess(m.to, p.to) || !mk.inclusiveRight &amp;&amp; posEq(p.to, m.to))
</del><ins>+        if (dto &gt; 0 || !mk.inclusiveRight &amp;&amp; !dto)
</ins><span class="cx">           newParts.push({from: m.to, to: p.to});
</span><span class="cx">         parts.splice.apply(parts, newParts);
</span><span class="cx">         j += newParts.length - 1;
</span><span class="lines">@@ -4126,71 +5294,148 @@
</span><span class="cx">     return parts;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function collapsedSpanAt(line, ch) {
</del><ins>+  // Connect or disconnect spans from a line.
+  function detachMarkedSpans(line) {
+    var spans = line.markedSpans;
+    if (!spans) return;
+    for (var i = 0; i &lt; spans.length; ++i)
+      spans[i].marker.detachLine(line);
+    line.markedSpans = null;
+  }
+  function attachMarkedSpans(line, spans) {
+    if (!spans) return;
+    for (var i = 0; i &lt; spans.length; ++i)
+      spans[i].marker.attachLine(line);
+    line.markedSpans = spans;
+  }
+
+  // Helpers used when computing which overlapping collapsed span
+  // counts as the larger one.
+  function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; }
+  function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; }
+
+  // Returns a number indicating which of two overlapping collapsed
+  // spans is larger (and thus includes the other). Falls back to
+  // comparing ids when the spans cover exactly the same range.
+  function compareCollapsedMarkers(a, b) {
+    var lenDiff = a.lines.length - b.lines.length;
+    if (lenDiff != 0) return lenDiff;
+    var aPos = a.find(), bPos = b.find();
+    var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
+    if (fromCmp) return -fromCmp;
+    var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
+    if (toCmp) return toCmp;
+    return b.id - a.id;
+  }
+
+  // Find out whether a line ends or starts in a collapsed span. If
+  // so, return the marker for that span.
+  function collapsedSpanAtSide(line, start) {
</ins><span class="cx">     var sps = sawCollapsedSpans &amp;&amp; line.markedSpans, found;
</span><span class="cx">     if (sps) for (var sp, i = 0; i &lt; sps.length; ++i) {
</span><span class="cx">       sp = sps[i];
</span><del>-      if (!sp.marker.collapsed) continue;
-      if ((sp.from == null || sp.from &lt; ch) &amp;&amp;
-          (sp.to == null || sp.to &gt; ch) &amp;&amp;
-          (!found || found.width &lt; sp.marker.width))
</del><ins>+      if (sp.marker.collapsed &amp;&amp; (start ? sp.from : sp.to) == null &amp;&amp;
+          (!found || compareCollapsedMarkers(found, sp.marker) &lt; 0))
</ins><span class="cx">         found = sp.marker;
</span><span class="cx">     }
</span><span class="cx">     return found;
</span><span class="cx">   }
</span><del>-  function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); }
-  function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); }
</del><ins>+  function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); }
+  function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); }
</ins><span class="cx"> 
</span><del>-  function visualLine(doc, line) {
</del><ins>+  // Test whether there exists a collapsed span that partially
+  // overlaps (covers the start or end, but not both) of a new span.
+  // Such overlap is not allowed.
+  function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
+    var line = getLine(doc, lineNo);
+    var sps = sawCollapsedSpans &amp;&amp; line.markedSpans;
+    if (sps) for (var i = 0; i &lt; sps.length; ++i) {
+      var sp = sps[i];
+      if (!sp.marker.collapsed) continue;
+      var found = sp.marker.find(0);
+      var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
+      var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
+      if (fromCmp &gt;= 0 &amp;&amp; toCmp &lt;= 0 || fromCmp &lt;= 0 &amp;&amp; toCmp &gt;= 0) continue;
+      if (fromCmp &lt;= 0 &amp;&amp; (cmp(found.to, from) || extraRight(sp.marker) - extraLeft(marker)) &gt; 0 ||
+          fromCmp &gt;= 0 &amp;&amp; (cmp(found.from, to) || extraLeft(sp.marker) - extraRight(marker)) &lt; 0)
+        return true;
+    }
+  }
+
+  // A visual line is a line as drawn on the screen. Folding, for
+  // example, can cause multiple logical lines to appear on the same
+  // visual line. This finds the start of the visual line that the
+  // given line is part of (usually that is the line itself).
+  function visualLine(line) {
</ins><span class="cx">     var merged;
</span><span class="cx">     while (merged = collapsedSpanAtStart(line))
</span><del>-      line = getLine(doc, merged.find().from.line);
</del><ins>+      line = merged.find(-1, true).line;
</ins><span class="cx">     return line;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Returns an array of logical lines that continue the visual line
+  // started by the argument, or undefined if there are no such lines.
+  function visualLineContinued(line) {
+    var merged, lines;
+    while (merged = collapsedSpanAtEnd(line)) {
+      line = merged.find(1, true).line;
+      (lines || (lines = [])).push(line);
+    }
+    return lines;
+  }
+
+  // Get the line number of the start of the visual line that the
+  // given line number is part of.
+  function visualLineNo(doc, lineN) {
+    var line = getLine(doc, lineN), vis = visualLine(line);
+    if (line == vis) return lineN;
+    return lineNo(vis);
+  }
+  // Get the line number of the start of the next visual line after
+  // the given line.
+  function visualLineEndNo(doc, lineN) {
+    if (lineN &gt; doc.lastLine()) return lineN;
+    var line = getLine(doc, lineN), merged;
+    if (!lineIsHidden(doc, line)) return lineN;
+    while (merged = collapsedSpanAtEnd(line))
+      line = merged.find(1, true).line;
+    return lineNo(line) + 1;
+  }
+
+  // Compute whether a line is hidden. Lines count as hidden when they
+  // are part of a visual line that starts with another line, or when
+  // they are entirely covered by collapsed, non-widget span.
</ins><span class="cx">   function lineIsHidden(doc, line) {
</span><span class="cx">     var sps = sawCollapsedSpans &amp;&amp; line.markedSpans;
</span><span class="cx">     if (sps) for (var sp, i = 0; i &lt; sps.length; ++i) {
</span><span class="cx">       sp = sps[i];
</span><span class="cx">       if (!sp.marker.collapsed) continue;
</span><span class="cx">       if (sp.from == null) return true;
</span><del>-      if (sp.marker.replacedWith) continue;
</del><ins>+      if (sp.marker.widgetNode) continue;
</ins><span class="cx">       if (sp.from == 0 &amp;&amp; sp.marker.inclusiveLeft &amp;&amp; lineIsHiddenInner(doc, line, sp))
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx">   function lineIsHiddenInner(doc, line, span) {
</span><span class="cx">     if (span.to == null) {
</span><del>-      var end = span.marker.find().to, endLine = getLine(doc, end.line);
-      return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
</del><ins>+      var end = span.marker.find(1, true);
+      return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker));
</ins><span class="cx">     }
</span><span class="cx">     if (span.marker.inclusiveRight &amp;&amp; span.to == line.text.length)
</span><span class="cx">       return true;
</span><span class="cx">     for (var sp, i = 0; i &lt; line.markedSpans.length; ++i) {
</span><span class="cx">       sp = line.markedSpans[i];
</span><del>-      if (sp.marker.collapsed &amp;&amp; !sp.marker.replacedWith &amp;&amp; sp.from == span.to &amp;&amp;
</del><ins>+      if (sp.marker.collapsed &amp;&amp; !sp.marker.widgetNode &amp;&amp; sp.from == span.to &amp;&amp;
+          (sp.to == null || sp.to != span.from) &amp;&amp;
</ins><span class="cx">           (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &amp;&amp;
</span><span class="cx">           lineIsHiddenInner(doc, line, sp)) return true;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function detachMarkedSpans(line) {
-    var spans = line.markedSpans;
-    if (!spans) return;
-    for (var i = 0; i &lt; spans.length; ++i)
-      spans[i].marker.detachLine(line);
-    line.markedSpans = null;
-  }
</del><ins>+  // LINE WIDGETS
</ins><span class="cx"> 
</span><del>-  function attachMarkedSpans(line, spans) {
-    if (!spans) return;
-    for (var i = 0; i &lt; spans.length; ++i)
-      spans[i].marker.attachLine(line);
-    line.markedSpans = spans;
-  }
</del><ins>+  // Line widgets are block elements displayed above or below a line.
</ins><span class="cx"> 
</span><del>-  // LINE WIDGETS
-
</del><span class="cx">   var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
</span><span class="cx">     if (options) for (var opt in options) if (options.hasOwnProperty(opt))
</span><span class="cx">       this[opt] = options[opt];
</span><span class="lines">@@ -4198,38 +5443,39 @@
</span><span class="cx">     this.node = node;
</span><span class="cx">   };
</span><span class="cx">   eventMixin(LineWidget);
</span><del>-  function widgetOperation(f) {
-    return function() {
-      var withOp = !this.cm.curOp;
-      if (withOp) startOperation(this.cm);
-      try {var result = f.apply(this, arguments);}
-      finally {if (withOp) endOperation(this.cm);}
-      return result;
-    };
</del><ins>+
+  function adjustScrollWhenAboveVisible(cm, line, diff) {
+    if (heightAtLine(line) &lt; ((cm.curOp &amp;&amp; cm.curOp.scrollTop) || cm.doc.scrollTop))
+      addToScrollPos(cm, null, diff);
</ins><span class="cx">   }
</span><del>-  LineWidget.prototype.clear = widgetOperation(function() {
-    var ws = this.line.widgets, no = lineNo(this.line);
</del><ins>+
+  LineWidget.prototype.clear = function() {
+    var cm = this.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
</ins><span class="cx">     if (no == null || !ws) return;
</span><span class="cx">     for (var i = 0; i &lt; ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
</span><del>-    if (!ws.length) this.line.widgets = null;
-    var aboveVisible = heightAtLine(this.cm, this.line) &lt; this.cm.doc.scrollTop;
-    updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
-    if (aboveVisible) addToScrollPos(this.cm, 0, -this.height);
-    regChange(this.cm, no, no + 1);
-  });
-  LineWidget.prototype.changed = widgetOperation(function() {
-    var oldH = this.height;
</del><ins>+    if (!ws.length) line.widgets = null;
+    var height = widgetHeight(this);
+    runInOp(cm, function() {
+      adjustScrollWhenAboveVisible(cm, line, -height);
+      regLineChange(cm, no, &quot;widget&quot;);
+      updateLineHeight(line, Math.max(0, line.height - height));
+    });
+  };
+  LineWidget.prototype.changed = function() {
+    var oldH = this.height, cm = this.cm, line = this.line;
</ins><span class="cx">     this.height = null;
</span><span class="cx">     var diff = widgetHeight(this) - oldH;
</span><span class="cx">     if (!diff) return;
</span><del>-    updateLineHeight(this.line, this.line.height + diff);
-    var no = lineNo(this.line);
-    regChange(this.cm, no, no + 1);
-  });
</del><ins>+    runInOp(cm, function() {
+      cm.curOp.forceUpdate = true;
+      adjustScrollWhenAboveVisible(cm, line, diff);
+      updateLineHeight(line, line.height + diff);
+    });
+  };
</ins><span class="cx"> 
</span><span class="cx">   function widgetHeight(widget) {
</span><span class="cx">     if (widget.height != null) return widget.height;
</span><del>-    if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
</del><ins>+    if (!contains(document.body, widget.node))
</ins><span class="cx">       removeChildrenAndAdd(widget.cm.display.measure, elt(&quot;div&quot;, [widget.node], null, &quot;position: relative&quot;));
</span><span class="cx">     return widget.height = widget.node.offsetHeight;
</span><span class="cx">   }
</span><span class="lines">@@ -4237,15 +5483,16 @@
</span><span class="cx">   function addLineWidget(cm, handle, node, options) {
</span><span class="cx">     var widget = new LineWidget(cm, node, options);
</span><span class="cx">     if (widget.noHScroll) cm.display.alignWidgets = true;
</span><del>-    changeLine(cm, handle, function(line) {
</del><ins>+    changeLine(cm, handle, &quot;widget&quot;, function(line) {
</ins><span class="cx">       var widgets = line.widgets || (line.widgets = []);
</span><span class="cx">       if (widget.insertAt == null) widgets.push(widget);
</span><span class="cx">       else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget);
</span><span class="cx">       widget.line = line;
</span><del>-      if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
-        var aboveVisible = heightAtLine(cm, line) &lt; cm.doc.scrollTop;
</del><ins>+      if (!lineIsHidden(cm.doc, line)) {
+        var aboveVisible = heightAtLine(line) &lt; cm.doc.scrollTop;
</ins><span class="cx">         updateLineHeight(line, line.height + widgetHeight(widget));
</span><del>-        if (aboveVisible) addToScrollPos(cm, 0, widget.height);
</del><ins>+        if (aboveVisible) addToScrollPos(cm, null, widget.height);
+        cm.curOp.forceUpdate = true;
</ins><span class="cx">       }
</span><span class="cx">       return true;
</span><span class="cx">     });
</span><span class="lines">@@ -4264,6 +5511,9 @@
</span><span class="cx">   eventMixin(Line);
</span><span class="cx">   Line.prototype.lineNo = function() { return lineNo(this); };
</span><span class="cx"> 
</span><ins>+  // Change the content (text, markers) of a line. Automatically
+  // invalidates cached information and tries to re-estimate the
+  // line's height.
</ins><span class="cx">   function updateLine(line, text, markedSpans, estimateHeight) {
</span><span class="cx">     line.text = text;
</span><span class="cx">     if (line.stateAfter) line.stateAfter = null;
</span><span class="lines">@@ -4275,20 +5525,47 @@
</span><span class="cx">     if (estHeight != line.height) updateLineHeight(line, estHeight);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Detach a line from the document tree and its markers.
</ins><span class="cx">   function cleanUpLine(line) {
</span><span class="cx">     line.parent = null;
</span><span class="cx">     detachMarkedSpans(line);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // Run the given mode's parser over a line, update the styles
-  // array, which contains alternating fragments of text and CSS
-  // classes.
-  function runMode(cm, text, mode, state, f, forceToEnd) {
</del><ins>+  function extractLineClasses(type, output) {
+    if (type) for (;;) {
+      var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
+      if (!lineClass) break;
+      type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
+      var prop = lineClass[1] ? &quot;bgClass&quot; : &quot;textClass&quot;;
+      if (output[prop] == null)
+        output[prop] = lineClass[2];
+      else if (!(new RegExp(&quot;(?:^|\s)&quot; + lineClass[2] + &quot;(?:$|\s)&quot;)).test(output[prop]))
+        output[prop] += &quot; &quot; + lineClass[2];
+    }
+    return type;
+  }
+
+  function callBlankLine(mode, state) {
+    if (mode.blankLine) return mode.blankLine(state);
+    if (!mode.innerMode) return;
+    var inner = CodeMirror.innerMode(mode, state);
+    if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
+  }
+
+  function readToken(mode, stream, state) {
+    var style = mode.token(stream, state);
+    if (stream.pos &lt;= stream.start)
+      throw new Error(&quot;Mode &quot; + mode.name + &quot; failed to advance stream.&quot;);
+    return style;
+  }
+
+  // Run the given mode's parser over a line, calling f for each token.
+  function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
</ins><span class="cx">     var flattenSpans = mode.flattenSpans;
</span><span class="cx">     if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
</span><span class="cx">     var curStart = 0, curStyle = null;
</span><span class="cx">     var stream = new StringStream(text, cm.options.tabSize), style;
</span><del>-    if (text == &quot;&quot; &amp;&amp; mode.blankLine) mode.blankLine(state);
</del><ins>+    if (text == &quot;&quot;) extractLineClasses(callBlankLine(mode, state), lineClasses);
</ins><span class="cx">     while (!stream.eol()) {
</span><span class="cx">       if (stream.pos &gt; cm.options.maxHighlightLength) {
</span><span class="cx">         flattenSpans = false;
</span><span class="lines">@@ -4296,8 +5573,12 @@
</span><span class="cx">         stream.pos = text.length;
</span><span class="cx">         style = null;
</span><span class="cx">       } else {
</span><del>-        style = mode.token(stream, state);
</del><ins>+        style = extractLineClasses(readToken(mode, stream, state), lineClasses);
</ins><span class="cx">       }
</span><ins>+      if (cm.options.addModeClass) {
+        var mName = CodeMirror.innerMode(mode, state).mode.name;
+        if (mName) style = &quot;m-&quot; + (style ? mName + &quot; &quot; + style : mName);
+      }
</ins><span class="cx">       if (!flattenSpans || curStyle != style) {
</span><span class="cx">         if (curStart &lt; stream.start) f(stream.start, curStyle);
</span><span class="cx">         curStart = stream.start; curStyle = style;
</span><span class="lines">@@ -4312,14 +5593,18 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Compute a style array (an array starting with a mode generation
+  // -- for invalidation -- followed by pairs of end positions and
+  // style strings), which is used to highlight the tokens on the
+  // line.
</ins><span class="cx">   function highlightLine(cm, line, state, forceToEnd) {
</span><span class="cx">     // A styles array always starts with a number identifying the
</span><span class="cx">     // mode/overlays that it is based on (for easy invalidation).
</span><del>-    var st = [cm.state.modeGen];
</del><ins>+    var st = [cm.state.modeGen], lineClasses = {};
</ins><span class="cx">     // Compute the base array of styles
</span><span class="cx">     runMode(cm, line.text, cm.doc.mode, state, function(end, style) {
</span><span class="cx">       st.push(end, style);
</span><del>-    }, forceToEnd);
</del><ins>+    }, lineClasses, forceToEnd);
</ins><span class="cx"> 
</span><span class="cx">     // Run overlays, adjust style array.
</span><span class="cx">     for (var o = 0; o &lt; cm.state.overlays.length; ++o) {
</span><span class="lines">@@ -4344,96 +5629,95 @@
</span><span class="cx">             st[start+1] = cur ? cur + &quot; &quot; + style : style;
</span><span class="cx">           }
</span><span class="cx">         }
</span><del>-      });
</del><ins>+      }, lineClasses);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return st;
</del><ins>+    return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function getLineStyles(cm, line) {
</span><del>-    if (!line.styles || line.styles[0] != cm.state.modeGen)
-      line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
</del><ins>+    if (!line.styles || line.styles[0] != cm.state.modeGen) {
+      var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
+      line.styles = result.styles;
+      if (result.classes) line.styleClasses = result.classes;
+      else if (line.styleClasses) line.styleClasses = null;
+    }
</ins><span class="cx">     return line.styles;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Lightweight form of highlight -- proceed over this line and
</span><del>-  // update state, but don't save a style array.
</del><ins>+  // update state, but don't save a style array. Used for lines that
+  // aren't currently visible.
</ins><span class="cx">   function processLine(cm, text, state, startAt) {
</span><span class="cx">     var mode = cm.doc.mode;
</span><span class="cx">     var stream = new StringStream(text, cm.options.tabSize);
</span><span class="cx">     stream.start = stream.pos = startAt || 0;
</span><del>-    if (text == &quot;&quot; &amp;&amp; mode.blankLine) mode.blankLine(state);
</del><ins>+    if (text == &quot;&quot;) callBlankLine(mode, state);
</ins><span class="cx">     while (!stream.eol() &amp;&amp; stream.pos &lt;= cm.options.maxHighlightLength) {
</span><del>-      mode.token(stream, state);
</del><ins>+      readToken(mode, stream, state);
</ins><span class="cx">       stream.start = stream.pos;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var styleToClassCache = {};
-  function interpretTokenStyle(style, builder) {
-    if (!style) return null;
-    for (;;) {
-      var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/);
-      if (!lineClass) break;
-      style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length);
-      var prop = lineClass[1] ? &quot;bgClass&quot; : &quot;textClass&quot;;
-      if (builder[prop] == null)
-        builder[prop] = lineClass[2];
-      else if (!(new RegExp(&quot;(?:^|\s)&quot; + lineClass[2] + &quot;(?:$|\s)&quot;)).test(builder[prop]))
-        builder[prop] += &quot; &quot; + lineClass[2];
-    }
-    return styleToClassCache[style] ||
-      (styleToClassCache[style] = &quot;cm-&quot; + style.replace(/ +/g, &quot; cm-&quot;));
</del><ins>+  // Convert a style as returned by a mode (either null, or a string
+  // containing one or more styles) to a CSS style. This is cached,
+  // and also looks for line-wide styles.
+  var styleToClassCache = {}, styleToClassCacheWithMode = {};
+  function interpretTokenStyle(style, options) {
+    if (!style || /^\s*$/.test(style)) return null;
+    var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+    return cache[style] ||
+      (cache[style] = style.replace(/\S+/g, &quot;cm-$&amp;&quot;));
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildLineContent(cm, realLine, measure, copyWidgets) {
-    var merged, line = realLine, empty = true;
-    while (merged = collapsedSpanAtStart(line))
-      line = getLine(cm.doc, merged.find().from.line);
</del><ins>+  // Render the DOM representation of the text of a line. Also builds
+  // up a 'line map', which points at the DOM nodes that represent
+  // specific stretches of text, and is used by the measuring code.
+  // The returned object contains the DOM node, this map, and
+  // information about line-wide styles that were set by the mode.
+  function buildLineContent(cm, lineView) {
+    // The padding-right forces the element to have a 'border', which
+    // is needed on Webkit to be able to get line-level bounding
+    // rectangles for it (in measureChar).
+    var content = elt(&quot;span&quot;, null, null, webkit ? &quot;padding-right: .1px&quot; : null);
+    var builder = {pre: elt(&quot;pre&quot;, [content]), content: content, col: 0, pos: 0, cm: cm};
+    lineView.measure = {};
</ins><span class="cx"> 
</span><del>-    var builder = {pre: elt(&quot;pre&quot;), col: 0, pos: 0,
-                   measure: null, measuredSomething: false, cm: cm,
-                   copyWidgets: copyWidgets};
-
-    do {
-      if (line.text) empty = false;
-      builder.measure = line == realLine &amp;&amp; measure;
</del><ins>+    // Iterate over the logical lines that make up this visual line.
+    for (var i = 0; i &lt;= (lineView.rest ? lineView.rest.length : 0); i++) {
+      var line = i ? lineView.rest[i - 1] : lineView.line, order;
</ins><span class="cx">       builder.pos = 0;
</span><del>-      builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
</del><ins>+      builder.addToken = buildToken;
+      // Optionally wire in some hacks into the token-rendering
+      // algorithm, to deal with browser quirks.
</ins><span class="cx">       if ((ie || webkit) &amp;&amp; cm.getOption(&quot;lineWrapping&quot;))
</span><span class="cx">         builder.addToken = buildTokenSplitSpaces(builder.addToken);
</span><del>-      var next = insertLineContent(line, builder, getLineStyles(cm, line));
-      if (measure &amp;&amp; line == realLine &amp;&amp; !builder.measuredSomething) {
-        measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
-        builder.measuredSomething = true;
</del><ins>+      if (hasBadBidiRects(cm.display.measure) &amp;&amp; (order = getOrder(line)))
+        builder.addToken = buildTokenBadBidi(builder.addToken, order);
+      builder.map = [];
+      insertLineContent(line, builder, getLineStyles(cm, line));
+      if (line.styleClasses) {
+        if (line.styleClasses.bgClass)
+          builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || &quot;&quot;);
+        if (line.styleClasses.textClass)
+          builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || &quot;&quot;);
</ins><span class="cx">       }
</span><del>-      if (next) line = getLine(cm.doc, next.to.line);
-    } while (next);
</del><span class="cx"> 
</span><del>-    if (measure &amp;&amp; !builder.measuredSomething &amp;&amp; !measure[0])
-      measure[0] = builder.pre.appendChild(empty ? elt(&quot;span&quot;, &quot;\u00a0&quot;) : zeroWidthElement(cm.display.measure));
-    if (!builder.pre.firstChild &amp;&amp; !lineIsHidden(cm.doc, realLine))
-      builder.pre.appendChild(document.createTextNode(&quot;\u00a0&quot;));
</del><ins>+      // Ensure at least a single node is present, for measuring.
+      if (builder.map.length == 0)
+        builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure)));
</ins><span class="cx"> 
</span><del>-    var order;
-    // Work around problem with the reported dimensions of single-char
-    // direction spans on IE (issue #1129). See also the comment in
-    // cursorCoords.
-    if (measure &amp;&amp; (ie || ie_gt10) &amp;&amp; (order = getOrder(line))) {
-      var l = order.length - 1;
-      if (order[l].from == order[l].to) --l;
-      var last = order[l], prev = order[l - 1];
-      if (last.from + 1 == last.to &amp;&amp; prev &amp;&amp; last.level &lt; prev.level) {
-        var span = measure[builder.pos - 1];
-        if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
-                                               span.nextSibling);
</del><ins>+      // Store the map and a cache object for the current logical line
+      if (i == 0) {
+        lineView.measure.map = builder.map;
+        lineView.measure.cache = {};
+      } else {
+        (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map);
+        (lineView.measure.caches || (lineView.measure.caches = [])).push({});
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    var textClass = builder.textClass ? builder.textClass + &quot; &quot; + (realLine.textClass || &quot;&quot;) : realLine.textClass;
-    if (textClass) builder.pre.className = textClass;
-
-    signal(cm, &quot;renderLine&quot;, cm, realLine, builder.pre);
</del><ins>+    signal(cm, &quot;renderLine&quot;, cm, lineView.line, builder.pre);
</ins><span class="cx">     return builder;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -4443,12 +5727,17 @@
</span><span class="cx">     return token;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Build up the DOM representation for a single token, and add it to
+  // the line map. Takes care to render special characters separately.
</ins><span class="cx">   function buildToken(builder, text, style, startStyle, endStyle, title) {
</span><span class="cx">     if (!text) return;
</span><del>-    var special = builder.cm.options.specialChars;
</del><ins>+    var special = builder.cm.options.specialChars, mustWrap = false;
</ins><span class="cx">     if (!special.test(text)) {
</span><span class="cx">       builder.col += text.length;
</span><span class="cx">       var content = document.createTextNode(text);
</span><ins>+      builder.map.push(builder.pos, builder.pos + text.length, content);
+      if (ie_upto8) mustWrap = true;
+      builder.pos += text.length;
</ins><span class="cx">     } else {
</span><span class="cx">       var content = document.createDocumentFragment(), pos = 0;
</span><span class="cx">       while (true) {
</span><span class="lines">@@ -4456,59 +5745,40 @@
</span><span class="cx">         var m = special.exec(text);
</span><span class="cx">         var skipped = m ? m.index - pos : text.length - pos;
</span><span class="cx">         if (skipped) {
</span><del>-          content.appendChild(document.createTextNode(text.slice(pos, pos + skipped)));
</del><ins>+          var txt = document.createTextNode(text.slice(pos, pos + skipped));
+          if (ie_upto8) content.appendChild(elt(&quot;span&quot;, [txt]));
+          else content.appendChild(txt);
+          builder.map.push(builder.pos, builder.pos + skipped, txt);
</ins><span class="cx">           builder.col += skipped;
</span><ins>+          builder.pos += skipped;
</ins><span class="cx">         }
</span><span class="cx">         if (!m) break;
</span><span class="cx">         pos += skipped + 1;
</span><span class="cx">         if (m[0] == &quot;\t&quot;) {
</span><span class="cx">           var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
</span><del>-          content.appendChild(elt(&quot;span&quot;, spaceStr(tabWidth), &quot;cm-tab&quot;));
</del><ins>+          var txt = content.appendChild(elt(&quot;span&quot;, spaceStr(tabWidth), &quot;cm-tab&quot;));
</ins><span class="cx">           builder.col += tabWidth;
</span><span class="cx">         } else {
</span><del>-          var token = builder.cm.options.specialCharPlaceholder(m[0]);
-          content.appendChild(token);
</del><ins>+          var txt = builder.cm.options.specialCharPlaceholder(m[0]);
+          if (ie_upto8) content.appendChild(elt(&quot;span&quot;, [txt]));
+          else content.appendChild(txt);
</ins><span class="cx">           builder.col += 1;
</span><span class="cx">         }
</span><ins>+        builder.map.push(builder.pos, builder.pos + 1, txt);
+        builder.pos++;
</ins><span class="cx">       }
</span><span class="cx">     }
</span><del>-    if (style || startStyle || endStyle || builder.measure) {
</del><ins>+    if (style || startStyle || endStyle || mustWrap) {
</ins><span class="cx">       var fullStyle = style || &quot;&quot;;
</span><span class="cx">       if (startStyle) fullStyle += startStyle;
</span><span class="cx">       if (endStyle) fullStyle += endStyle;
</span><span class="cx">       var token = elt(&quot;span&quot;, [content], fullStyle);
</span><span class="cx">       if (title) token.title = title;
</span><del>-      return builder.pre.appendChild(token);
</del><ins>+      return builder.content.appendChild(token);
</ins><span class="cx">     }
</span><del>-    builder.pre.appendChild(content);
</del><ins>+    builder.content.appendChild(content);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
-    var wrapping = builder.cm.options.lineWrapping;
-    for (var i = 0; i &lt; text.length; ++i) {
-      var ch = text.charAt(i), start = i == 0;
-      if (ch &gt;= &quot;\ud800&quot; &amp;&amp; ch &lt; &quot;\udbff&quot; &amp;&amp; i &lt; text.length - 1) {
-        ch = text.slice(i, i + 2);
-        ++i;
-      } else if (i &amp;&amp; wrapping &amp;&amp; spanAffectsWrapping(text, i)) {
-        builder.pre.appendChild(elt(&quot;wbr&quot;));
-      }
-      var old = builder.measure[builder.pos];
-      var span = builder.measure[builder.pos] =
-        buildToken(builder, ch, style,
-                   start &amp;&amp; startStyle, i == text.length - 1 &amp;&amp; endStyle);
-      if (old) span.leftSide = old.leftSide || old;
-      // In IE single-space nodes wrap differently than spaces
-      // embedded in larger text nodes, except when set to
-      // white-space: normal (issue #1268).
-      if (ie &amp;&amp; wrapping &amp;&amp; ch == &quot; &quot; &amp;&amp; i &amp;&amp; !/\s/.test(text.charAt(i - 1)) &amp;&amp;
-          i &lt; text.length - 1 &amp;&amp; !/\s/.test(text.charAt(i + 1)))
-        span.style.whiteSpace = &quot;normal&quot;;
-      builder.pos += ch.length;
-    }
-    if (text.length) builder.measuredSomething = true;
-  }
-
</del><span class="cx">   function buildTokenSplitSpaces(inner) {
</span><span class="cx">     function split(old) {
</span><span class="cx">       var out = &quot; &quot;;
</span><span class="lines">@@ -4517,29 +5787,36 @@
</span><span class="cx">       return out;
</span><span class="cx">     }
</span><span class="cx">     return function(builder, text, style, startStyle, endStyle, title) {
</span><del>-      return inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
</del><ins>+      inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
</ins><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Work around nonsense dimensions being reported for stretches of
+  // right-to-left text.
+  function buildTokenBadBidi(inner, order) {
+    return function(builder, text, style, startStyle, endStyle, title) {
+      style = style ? style + &quot; cm-force-border&quot; : &quot;cm-force-border&quot;;
+      var start = builder.pos, end = start + text.length;
+      for (;;) {
+        // Find the part that overlaps with the start of this text
+        for (var i = 0; i &lt; order.length; i++) {
+          var part = order[i];
+          if (part.to &gt; start &amp;&amp; part.from &lt;= start) break;
+        }
+        if (part.to &gt;= end) return inner(builder, text, style, startStyle, endStyle, title);
+        inner(builder, text.slice(0, part.to - start), style, startStyle, null, title);
+        startStyle = null;
+        text = text.slice(part.to - start);
+        start = part.to;
+      }
+    };
+  }
+
</ins><span class="cx">   function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
</span><del>-    var widget = !ignoreWidget &amp;&amp; marker.replacedWith;
</del><ins>+    var widget = !ignoreWidget &amp;&amp; marker.widgetNode;
</ins><span class="cx">     if (widget) {
</span><del>-      if (builder.copyWidgets) widget = widget.cloneNode(true);
-      builder.pre.appendChild(widget);
-      if (builder.measure) {
-        if (size) {
-          builder.measure[builder.pos] = widget;
-        } else {
-          var elt = zeroWidthElement(builder.cm.display.measure);
-          if (marker.type == &quot;bookmark&quot; &amp;&amp; !marker.insertLeft)
-            builder.measure[builder.pos] = builder.pre.appendChild(elt);
-          else if (builder.measure[builder.pos])
-            return;
-          else
-            builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget);
-        }
-        builder.measuredSomething = true;
-      }
</del><ins>+      builder.map.push(builder.pos, builder.pos + size, widget);
+      builder.content.appendChild(widget);
</ins><span class="cx">     }
</span><span class="cx">     builder.pos += size;
</span><span class="cx">   }
</span><span class="lines">@@ -4550,7 +5827,7 @@
</span><span class="cx">     var spans = line.markedSpans, allText = line.text, at = 0;
</span><span class="cx">     if (!spans) {
</span><span class="cx">       for (var i = 1; i &lt; styles.length; i+=2)
</span><del>-        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder));
</del><ins>+        builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options));
</ins><span class="cx">       return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -4569,17 +5846,17 @@
</span><span class="cx">             if (m.startStyle &amp;&amp; sp.from == pos) spanStartStyle += &quot; &quot; + m.startStyle;
</span><span class="cx">             if (m.endStyle &amp;&amp; sp.to == nextChange) spanEndStyle += &quot; &quot; + m.endStyle;
</span><span class="cx">             if (m.title &amp;&amp; !title) title = m.title;
</span><del>-            if (m.collapsed &amp;&amp; (!collapsed || collapsed.marker.size &lt; m.size))
</del><ins>+            if (m.collapsed &amp;&amp; (!collapsed || compareCollapsedMarkers(collapsed.marker, m) &lt; 0))
</ins><span class="cx">               collapsed = sp;
</span><span class="cx">           } else if (sp.from &gt; pos &amp;&amp; nextChange &gt; sp.from) {
</span><span class="cx">             nextChange = sp.from;
</span><span class="cx">           }
</span><del>-          if (m.type == &quot;bookmark&quot; &amp;&amp; sp.from == pos &amp;&amp; m.replacedWith) foundBookmarks.push(m);
</del><ins>+          if (m.type == &quot;bookmark&quot; &amp;&amp; sp.from == pos &amp;&amp; m.widgetNode) foundBookmarks.push(m);
</ins><span class="cx">         }
</span><span class="cx">         if (collapsed &amp;&amp; (collapsed.from || 0) == pos) {
</span><del>-          buildCollapsedSpan(builder, (collapsed.to == null ? len : collapsed.to) - pos,
</del><ins>+          buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
</ins><span class="cx">                              collapsed.marker, collapsed.from == null);
</span><del>-          if (collapsed.to == null) return collapsed.marker.find();
</del><ins>+          if (collapsed.to == null) return;
</ins><span class="cx">         }
</span><span class="cx">         if (!collapsed &amp;&amp; foundBookmarks.length) for (var j = 0; j &lt; foundBookmarks.length; ++j)
</span><span class="cx">           buildCollapsedSpan(builder, 0, foundBookmarks[j]);
</span><span class="lines">@@ -4600,14 +5877,23 @@
</span><span class="cx">           spanStartStyle = &quot;&quot;;
</span><span class="cx">         }
</span><span class="cx">         text = allText.slice(at, at = styles[i++]);
</span><del>-        style = interpretTokenStyle(styles[i++], builder);
</del><ins>+        style = interpretTokenStyle(styles[i++], builder.cm.options);
</ins><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // DOCUMENT DATA STRUCTURE
</span><span class="cx"> 
</span><del>-  function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
</del><ins>+  // By default, updates that start and end at the beginning of a line
+  // are treated specially, in order to make the association of line
+  // widgets and marker elements with the text behave more intuitive.
+  function isWholeLineUpdate(doc, change) {
+    return change.from.ch == 0 &amp;&amp; change.to.ch == 0 &amp;&amp; lst(change.text) == &quot;&quot; &amp;&amp;
+      (!doc.cm || doc.cm.options.wholeLineUpdateBefore);
+  }
+
+  // Perform a change on the document data structure.
+  function updateDoc(doc, change, markedSpans, estimateHeight) {
</ins><span class="cx">     function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
</span><span class="cx">     function update(line, text, spans) {
</span><span class="cx">       updateLine(line, text, spans, estimateHeight);
</span><span class="lines">@@ -4618,12 +5904,11 @@
</span><span class="cx">     var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
</span><span class="cx">     var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
</span><span class="cx"> 
</span><del>-    // First adjust the line structure
-    if (from.ch == 0 &amp;&amp; to.ch == 0 &amp;&amp; lastText == &quot;&quot; &amp;&amp;
-        (!doc.cm || doc.cm.options.wholeLineUpdateBefore)) {
</del><ins>+    // Adjust the line structure
+    if (isWholeLineUpdate(doc, change)) {
</ins><span class="cx">       // This is a whole-line replace. Treated specially to make
</span><span class="cx">       // sure line objects move the way they are supposed to.
</span><del>-      for (var i = 0, e = text.length - 1, added = []; i &lt; e; ++i)
</del><ins>+      for (var i = 0, added = []; i &lt; text.length - 1; ++i)
</ins><span class="cx">         added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">       update(lastLine, lastLine.text, lastSpans);
</span><span class="cx">       if (nlines) doc.remove(from.line, nlines);
</span><span class="lines">@@ -4632,7 +5917,7 @@
</span><span class="cx">       if (text.length == 1) {
</span><span class="cx">         update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
</span><span class="cx">       } else {
</span><del>-        for (var added = [], i = 1, e = text.length - 1; i &lt; e; ++i)
</del><ins>+        for (var added = [], i = 1; i &lt; text.length - 1; ++i)
</ins><span class="cx">           added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">         added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
</span><span class="cx">         update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
</span><span class="lines">@@ -4644,20 +5929,32 @@
</span><span class="cx">     } else {
</span><span class="cx">       update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
</span><span class="cx">       update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
</span><del>-      for (var i = 1, e = text.length - 1, added = []; i &lt; e; ++i)
</del><ins>+      for (var i = 1, added = []; i &lt; text.length - 1; ++i)
</ins><span class="cx">         added.push(new Line(text[i], spansFor(i), estimateHeight));
</span><span class="cx">       if (nlines &gt; 1) doc.remove(from.line + 1, nlines - 1);
</span><span class="cx">       doc.insert(from.line + 1, added);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     signalLater(doc, &quot;change&quot;, doc, change);
</span><del>-    setSelection(doc, selAfter.anchor, selAfter.head, null, true);
</del><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // The document is represented as a BTree consisting of leaves, with
+  // chunk of lines in them, and branches, with up to ten leaves or
+  // other branch nodes below them. The top node is always a branch
+  // node, and is the document object itself (meaning it has
+  // additional methods and properties).
+  //
+  // All nodes have parent links. The tree is used both to go from
+  // line numbers to line objects, and to go from objects to numbers.
+  // It also indexes by height, and is used to convert between height
+  // and line object, and to find the total height of the document.
+  //
+  // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
</ins><span class="cx">   function LeafChunk(lines) {
</span><span class="cx">     this.lines = lines;
</span><span class="cx">     this.parent = null;
</span><del>-    for (var i = 0, e = lines.length, height = 0; i &lt; e; ++i) {
</del><ins>+    for (var i = 0, height = 0; i &lt; lines.length; ++i) {
</ins><span class="cx">       lines[i].parent = this;
</span><span class="cx">       height += lines[i].height;
</span><span class="cx">     }
</span><span class="lines">@@ -4666,6 +5963,7 @@
</span><span class="cx"> 
</span><span class="cx">   LeafChunk.prototype = {
</span><span class="cx">     chunkSize: function() { return this.lines.length; },
</span><ins>+    // Remove the n lines at offset 'at'.
</ins><span class="cx">     removeInner: function(at, n) {
</span><span class="cx">       for (var i = at, e = at + n; i &lt; e; ++i) {
</span><span class="cx">         var line = this.lines[i];
</span><span class="lines">@@ -4675,14 +5973,18 @@
</span><span class="cx">       }
</span><span class="cx">       this.lines.splice(at, n);
</span><span class="cx">     },
</span><ins>+    // Helper used to collapse a small branch into a single leaf.
</ins><span class="cx">     collapse: function(lines) {
</span><del>-      lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
</del><ins>+      lines.push.apply(lines, this.lines);
</ins><span class="cx">     },
</span><ins>+    // Insert the given array of lines at offset 'at', count them as
+    // having the given height.
</ins><span class="cx">     insertInner: function(at, lines, height) {
</span><span class="cx">       this.height += height;
</span><span class="cx">       this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
</span><del>-      for (var i = 0, e = lines.length; i &lt; e; ++i) lines[i].parent = this;
</del><ins>+      for (var i = 0; i &lt; lines.length; ++i) lines[i].parent = this;
</ins><span class="cx">     },
</span><ins>+    // Used to iterate over a part of the tree.
</ins><span class="cx">     iterN: function(at, n, op) {
</span><span class="cx">       for (var e = at + n; at &lt; e; ++at)
</span><span class="cx">         if (op(this.lines[at])) return true;
</span><span class="lines">@@ -4692,7 +5994,7 @@
</span><span class="cx">   function BranchChunk(children) {
</span><span class="cx">     this.children = children;
</span><span class="cx">     var size = 0, height = 0;
</span><del>-    for (var i = 0, e = children.length; i &lt; e; ++i) {
</del><ins>+    for (var i = 0; i &lt; children.length; ++i) {
</ins><span class="cx">       var ch = children[i];
</span><span class="cx">       size += ch.chunkSize(); height += ch.height;
</span><span class="cx">       ch.parent = this;
</span><span class="lines">@@ -4717,7 +6019,10 @@
</span><span class="cx">           at = 0;
</span><span class="cx">         } else at -= sz;
</span><span class="cx">       }
</span><del>-      if (this.size - n &lt; 25) {
</del><ins>+      // If the result is smaller than 25 lines, ensure that it is a
+      // single leaf node.
+      if (this.size - n &lt; 25 &amp;&amp;
+          (this.children.length &gt; 1 || !(this.children[0] instanceof LeafChunk))) {
</ins><span class="cx">         var lines = [];
</span><span class="cx">         this.collapse(lines);
</span><span class="cx">         this.children = [new LeafChunk(lines)];
</span><span class="lines">@@ -4725,12 +6030,12 @@
</span><span class="cx">       }
</span><span class="cx">     },
</span><span class="cx">     collapse: function(lines) {
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) this.children[i].collapse(lines);
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) this.children[i].collapse(lines);
</ins><span class="cx">     },
</span><span class="cx">     insertInner: function(at, lines, height) {
</span><span class="cx">       this.size += lines.length;
</span><span class="cx">       this.height += height;
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) {
</ins><span class="cx">         var child = this.children[i], sz = child.chunkSize();
</span><span class="cx">         if (at &lt;= sz) {
</span><span class="cx">           child.insertInner(at, lines, height);
</span><span class="lines">@@ -4749,6 +6054,7 @@
</span><span class="cx">         at -= sz;
</span><span class="cx">       }
</span><span class="cx">     },
</span><ins>+    // When a node has grown, check whether it should be split.
</ins><span class="cx">     maybeSpill: function() {
</span><span class="cx">       if (this.children.length &lt;= 10) return;
</span><span class="cx">       var me = this;
</span><span class="lines">@@ -4771,7 +6077,7 @@
</span><span class="cx">       me.parent.maybeSpill();
</span><span class="cx">     },
</span><span class="cx">     iterN: function(at, n, op) {
</span><del>-      for (var i = 0, e = this.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; this.children.length; ++i) {
</ins><span class="cx">         var child = this.children[i], sz = child.chunkSize();
</span><span class="cx">         if (at &lt; sz) {
</span><span class="cx">           var used = Math.min(n, sz - at);
</span><span class="lines">@@ -4792,43 +6098,52 @@
</span><span class="cx">     this.first = firstLine;
</span><span class="cx">     this.scrollTop = this.scrollLeft = 0;
</span><span class="cx">     this.cantEdit = false;
</span><del>-    this.history = makeHistory();
</del><span class="cx">     this.cleanGeneration = 1;
</span><span class="cx">     this.frontier = firstLine;
</span><span class="cx">     var start = Pos(firstLine, 0);
</span><del>-    this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
</del><ins>+    this.sel = simpleSelection(start);
+    this.history = new History(null);
</ins><span class="cx">     this.id = ++nextDocId;
</span><span class="cx">     this.modeOption = mode;
</span><span class="cx"> 
</span><span class="cx">     if (typeof text == &quot;string&quot;) text = splitLines(text);
</span><del>-    updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
</del><ins>+    updateDoc(this, {from: start, to: start, text: text});
+    setSelection(this, simpleSelection(start), sel_dontScroll);
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   Doc.prototype = createObj(BranchChunk.prototype, {
</span><span class="cx">     constructor: Doc,
</span><ins>+    // Iterate over the document. Supports two forms -- with only one
+    // argument, it calls that for each line in the document. With
+    // three, it iterates over the range given by the first two (with
+    // the second being non-inclusive).
</ins><span class="cx">     iter: function(from, to, op) {
</span><span class="cx">       if (op) this.iterN(from - this.first, to - from, op);
</span><span class="cx">       else this.iterN(this.first, this.first + this.size, from);
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    // Non-public interface for adding and removing lines.
</ins><span class="cx">     insert: function(at, lines) {
</span><span class="cx">       var height = 0;
</span><del>-      for (var i = 0, e = lines.length; i &lt; e; ++i) height += lines[i].height;
</del><ins>+      for (var i = 0; i &lt; lines.length; ++i) height += lines[i].height;
</ins><span class="cx">       this.insertInner(at - this.first, lines, height);
</span><span class="cx">     },
</span><span class="cx">     remove: function(at, n) { this.removeInner(at - this.first, n); },
</span><span class="cx"> 
</span><ins>+    // From here, the methods are part of the public interface. Most
+    // are also available from CodeMirror (editor) instances.
+
</ins><span class="cx">     getValue: function(lineSep) {
</span><span class="cx">       var lines = getLines(this, this.first, this.first + this.size);
</span><span class="cx">       if (lineSep === false) return lines;
</span><span class="cx">       return lines.join(lineSep || &quot;\n&quot;);
</span><span class="cx">     },
</span><del>-    setValue: function(code) {
</del><ins>+    setValue: docMethodOp(function(code) {
</ins><span class="cx">       var top = Pos(this.first, 0), last = this.first + this.size - 1;
</span><span class="cx">       makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
</span><del>-                        text: splitLines(code), origin: &quot;setValue&quot;},
-                 {head: top, anchor: top}, true);
-    },
</del><ins>+                        text: splitLines(code), origin: &quot;setValue&quot;}, true);
+      setSelection(this, simpleSelection(top));
+    }),
</ins><span class="cx">     replaceRange: function(code, from, to, origin) {
</span><span class="cx">       from = clipPos(this, from);
</span><span class="cx">       to = to ? clipPos(this, to) : from;
</span><span class="lines">@@ -4841,21 +6156,13 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     getLine: function(line) {var l = this.getLineHandle(line); return l &amp;&amp; l.text;},
</span><del>-    setLine: function(line, text) {
-      if (isLine(this, line))
-        replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
-    },
-    removeLine: function(line) {
-      if (line) replaceRange(this, &quot;&quot;, clipPos(this, Pos(line - 1)), clipPos(this, Pos(line)));
-      else replaceRange(this, &quot;&quot;, Pos(0, 0), clipPos(this, Pos(1, 0)));
-    },
</del><span class="cx"> 
</span><span class="cx">     getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
</span><span class="cx">     getLineNumber: function(line) {return lineNo(line);},
</span><span class="cx"> 
</span><span class="cx">     getLineHandleVisualStart: function(line) {
</span><span class="cx">       if (typeof line == &quot;number&quot;) line = getLine(this, line);
</span><del>-      return visualLine(this, line);
</del><ins>+      return visualLine(line);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     lineCount: function() {return this.size;},
</span><span class="lines">@@ -4865,47 +6172,103 @@
</span><span class="cx">     clipPos: function(pos) {return clipPos(this, pos);},
</span><span class="cx"> 
</span><span class="cx">     getCursor: function(start) {
</span><del>-      var sel = this.sel, pos;
-      if (start == null || start == &quot;head&quot;) pos = sel.head;
-      else if (start == &quot;anchor&quot;) pos = sel.anchor;
-      else if (start == &quot;end&quot; || start === false) pos = sel.to;
-      else pos = sel.from;
-      return copyPos(pos);
</del><ins>+      var range = this.sel.primary(), pos;
+      if (start == null || start == &quot;head&quot;) pos = range.head;
+      else if (start == &quot;anchor&quot;) pos = range.anchor;
+      else if (start == &quot;end&quot; || start == &quot;to&quot; || start === false) pos = range.to();
+      else pos = range.from();
+      return pos;
</ins><span class="cx">     },
</span><del>-    somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
</del><ins>+    listSelections: function() { return this.sel.ranges; },
+    somethingSelected: function() {return this.sel.somethingSelected();},
</ins><span class="cx"> 
</span><del>-    setCursor: docOperation(function(line, ch, extend) {
-      var pos = clipPos(this, typeof line == &quot;number&quot; ? Pos(line, ch || 0) : line);
-      if (extend) extendSelection(this, pos);
-      else setSelection(this, pos, pos);
</del><ins>+    setCursor: docMethodOp(function(line, ch, options) {
+      setSimpleSelection(this, clipPos(this, typeof line == &quot;number&quot; ? Pos(line, ch || 0) : line), null, options);
</ins><span class="cx">     }),
</span><del>-    setSelection: docOperation(function(anchor, head, bias) {
-      setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias);
</del><ins>+    setSelection: docMethodOp(function(anchor, head, options) {
+      setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
</ins><span class="cx">     }),
</span><del>-    extendSelection: docOperation(function(from, to, bias) {
-      extendSelection(this, clipPos(this, from), to &amp;&amp; clipPos(this, to), bias);
</del><ins>+    extendSelection: docMethodOp(function(head, other, options) {
+      extendSelection(this, clipPos(this, head), other &amp;&amp; clipPos(this, other), options);
</ins><span class="cx">     }),
</span><ins>+    extendSelections: docMethodOp(function(heads, options) {
+      extendSelections(this, clipPosArray(this, heads, options));
+    }),
+    extendSelectionsBy: docMethodOp(function(f, options) {
+      extendSelections(this, map(this.sel.ranges, f), options);
+    }),
+    setSelections: docMethodOp(function(ranges, primary, options) {
+      if (!ranges.length) return;
+      for (var i = 0, out = []; i &lt; ranges.length; i++)
+        out[i] = new Range(clipPos(this, ranges[i].anchor),
+                           clipPos(this, ranges[i].head));
+      if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex);
+      setSelection(this, normalizeSelection(out, primary), options);
+    }),
+    addSelection: docMethodOp(function(anchor, head, options) {
+      var ranges = this.sel.ranges.slice(0);
+      ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
+      setSelection(this, normalizeSelection(ranges, ranges.length - 1), options);
+    }),
</ins><span class="cx"> 
</span><del>-    getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
-    replaceSelection: function(code, collapse, origin) {
-      makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || &quot;around&quot;);
</del><ins>+    getSelection: function(lineSep) {
+      var ranges = this.sel.ranges, lines;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+        lines = lines ? lines.concat(sel) : sel;
+      }
+      if (lineSep === false) return lines;
+      else return lines.join(lineSep || &quot;\n&quot;);
</ins><span class="cx">     },
</span><del>-    undo: docOperation(function() {makeChangeFromHistory(this, &quot;undo&quot;);}),
-    redo: docOperation(function() {makeChangeFromHistory(this, &quot;redo&quot;);}),
</del><ins>+    getSelections: function(lineSep) {
+      var parts = [], ranges = this.sel.ranges;
+      for (var i = 0; i &lt; ranges.length; i++) {
+        var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+        if (lineSep !== false) sel = sel.join(lineSep || &quot;\n&quot;);
+        parts[i] = sel;
+      }
+      return parts;
+    },
+    replaceSelection: docMethodOp(function(code, collapse, origin) {
+      var dup = [];
+      for (var i = 0; i &lt; this.sel.ranges.length; i++)
+        dup[i] = code;
+      this.replaceSelections(dup, collapse, origin || &quot;+input&quot;);
+    }),
+    replaceSelections: function(code, collapse, origin) {
+      var changes = [], sel = this.sel;
+      for (var i = 0; i &lt; sel.ranges.length; i++) {
+        var range = sel.ranges[i];
+        changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin};
+      }
+      var newSel = collapse &amp;&amp; collapse != &quot;end&quot; &amp;&amp; computeReplacedSel(this, changes, collapse);
+      for (var i = changes.length - 1; i &gt;= 0; i--)
+        makeChange(this, changes[i]);
+      if (newSel) setSelectionReplaceHistory(this, newSel);
+      else if (this.cm) ensureCursorVisible(this.cm);
+    },
+    undo: docMethodOp(function() {makeChangeFromHistory(this, &quot;undo&quot;);}),
+    redo: docMethodOp(function() {makeChangeFromHistory(this, &quot;redo&quot;);}),
+    undoSelection: docMethodOp(function() {makeChangeFromHistory(this, &quot;undo&quot;, true);}),
+    redoSelection: docMethodOp(function() {makeChangeFromHistory(this, &quot;redo&quot;, true);}),
</ins><span class="cx"> 
</span><del>-    setExtending: function(val) {this.sel.extend = val;},
</del><ins>+    setExtending: function(val) {this.extend = val;},
+    getExtending: function() {return this.extend;},
</ins><span class="cx"> 
</span><span class="cx">     historySize: function() {
</span><del>-      var hist = this.history;
-      return {undo: hist.done.length, redo: hist.undone.length};
</del><ins>+      var hist = this.history, done = 0, undone = 0;
+      for (var i = 0; i &lt; hist.done.length; i++) if (!hist.done[i].ranges) ++done;
+      for (var i = 0; i &lt; hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone;
+      return {undo: done, redo: undone};
</ins><span class="cx">     },
</span><del>-    clearHistory: function() {this.history = makeHistory(this.history.maxGeneration);},
</del><ins>+    clearHistory: function() {this.history = new History(this.history.maxGeneration);},
</ins><span class="cx"> 
</span><span class="cx">     markClean: function() {
</span><del>-      this.cleanGeneration = this.changeGeneration();
</del><ins>+      this.cleanGeneration = this.changeGeneration(true);
</ins><span class="cx">     },
</span><del>-    changeGeneration: function() {
-      this.history.lastOp = this.history.lastOrigin = null;
</del><ins>+    changeGeneration: function(forceSplit) {
+      if (forceSplit)
+        this.history.lastOp = this.history.lastOrigin = null;
</ins><span class="cx">       return this.history.generation;
</span><span class="cx">     },
</span><span class="cx">     isClean: function (gen) {
</span><span class="lines">@@ -4917,9 +6280,9 @@
</span><span class="cx">               undone: copyHistoryArray(this.history.undone)};
</span><span class="cx">     },
</span><span class="cx">     setHistory: function(histData) {
</span><del>-      var hist = this.history = makeHistory(this.history.maxGeneration);
-      hist.done = histData.done.slice(0);
-      hist.undone = histData.undone.slice(0);
</del><ins>+      var hist = this.history = new History(this.history.maxGeneration);
+      hist.done = copyHistoryArray(histData.done.slice(0), null, true);
+      hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     markText: function(from, to, options) {
</span><span class="lines">@@ -4927,7 +6290,8 @@
</span><span class="cx">     },
</span><span class="cx">     setBookmark: function(pos, options) {
</span><span class="cx">       var realOpts = {replacedWith: options &amp;&amp; (options.nodeType == null ? options.widget : options),
</span><del>-                      insertLeft: options &amp;&amp; options.insertLeft};
</del><ins>+                      insertLeft: options &amp;&amp; options.insertLeft,
+                      clearWhenEmpty: false, shared: options &amp;&amp; options.shared};
</ins><span class="cx">       pos = clipPos(this, pos);
</span><span class="cx">       return markText(this, pos, pos, realOpts, &quot;bookmark&quot;);
</span><span class="cx">     },
</span><span class="lines">@@ -4942,6 +6306,23 @@
</span><span class="cx">       }
</span><span class="cx">       return markers;
</span><span class="cx">     },
</span><ins>+    findMarks: function(from, to, filter) {
+      from = clipPos(this, from); to = clipPos(this, to);
+      var found = [], lineNo = from.line;
+      this.iter(from.line, to.line + 1, function(line) {
+        var spans = line.markedSpans;
+        if (spans) for (var i = 0; i &lt; spans.length; i++) {
+          var span = spans[i];
+          if (!(lineNo == from.line &amp;&amp; from.ch &gt; span.to ||
+                span.from == null &amp;&amp; lineNo != from.line||
+                lineNo == to.line &amp;&amp; span.from &gt; to.ch) &amp;&amp;
+              (!filter || filter(span.marker)))
+            found.push(span.marker.parent || span.marker);
+        }
+        ++lineNo;
+      });
+      return found;
+    },
</ins><span class="cx">     getAllMarks: function() {
</span><span class="cx">       var markers = [];
</span><span class="cx">       this.iter(function(line) {
</span><span class="lines">@@ -4975,8 +6356,8 @@
</span><span class="cx">     copy: function(copyHistory) {
</span><span class="cx">       var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
</span><span class="cx">       doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
</span><del>-      doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
-                 shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
</del><ins>+      doc.sel = this.sel;
+      doc.extend = false;
</ins><span class="cx">       if (copyHistory) {
</span><span class="cx">         doc.history.undoDepth = this.history.undoDepth;
</span><span class="cx">         doc.setHistory(this.getHistory());
</span><span class="lines">@@ -4993,6 +6374,7 @@
</span><span class="cx">       if (options.sharedHist) copy.history = this.history;
</span><span class="cx">       (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
</span><span class="cx">       copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
</span><ins>+      copySharedMarkers(copy, findSharedMarkers(this));
</ins><span class="cx">       return copy;
</span><span class="cx">     },
</span><span class="cx">     unlinkDoc: function(other) {
</span><span class="lines">@@ -5002,13 +6384,14 @@
</span><span class="cx">         if (link.doc != other) continue;
</span><span class="cx">         this.linked.splice(i, 1);
</span><span class="cx">         other.unlinkDoc(this);
</span><ins>+        detachSharedMarkers(findSharedMarkers(this));
</ins><span class="cx">         break;
</span><span class="cx">       }
</span><span class="cx">       // If the histories were shared, split them again
</span><span class="cx">       if (other.history == this.history) {
</span><span class="cx">         var splitIds = [other.id];
</span><span class="cx">         linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
</span><del>-        other.history = makeHistory();
</del><ins>+        other.history = new History(null);
</ins><span class="cx">         other.history.done = copyHistoryArray(this.history.done, splitIds);
</span><span class="cx">         other.history.undone = copyHistoryArray(this.history.undone, splitIds);
</span><span class="cx">       }
</span><span class="lines">@@ -5019,9 +6402,10 @@
</span><span class="cx">     getEditor: function() {return this.cm;}
</span><span class="cx">   });
</span><span class="cx"> 
</span><ins>+  // Public alias.
</ins><span class="cx">   Doc.prototype.eachLine = Doc.prototype.iter;
</span><span class="cx"> 
</span><del>-  // The Doc methods that should be available on CodeMirror instances
</del><ins>+  // Set up methods on CodeMirror's prototype to redirect to the editor's document.
</ins><span class="cx">   var dontDelegate = &quot;iter insert remove copy getEditor&quot;.split(&quot; &quot;);
</span><span class="cx">   for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) &amp;&amp; indexOf(dontDelegate, prop) &lt; 0)
</span><span class="cx">     CodeMirror.prototype[prop] = (function(method) {
</span><span class="lines">@@ -5030,6 +6414,7 @@
</span><span class="cx"> 
</span><span class="cx">   eventMixin(Doc);
</span><span class="cx"> 
</span><ins>+  // Call f for all linked documents.
</ins><span class="cx">   function linkedDocs(doc, f, sharedHistOnly) {
</span><span class="cx">     function propagate(doc, skip, sharedHist) {
</span><span class="cx">       if (doc.linked) for (var i = 0; i &lt; doc.linked.length; ++i) {
</span><span class="lines">@@ -5044,22 +6429,25 @@
</span><span class="cx">     propagate(doc, null, true);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Attach a document to an editor.
</ins><span class="cx">   function attachDoc(cm, doc) {
</span><span class="cx">     if (doc.cm) throw new Error(&quot;This document is already in use.&quot;);
</span><span class="cx">     cm.doc = doc;
</span><span class="cx">     doc.cm = cm;
</span><span class="cx">     estimateLineHeights(cm);
</span><span class="cx">     loadMode(cm);
</span><del>-    if (!cm.options.lineWrapping) computeMaxLength(cm);
</del><ins>+    if (!cm.options.lineWrapping) findMaxLine(cm);
</ins><span class="cx">     cm.options.mode = doc.modeOption;
</span><span class="cx">     regChange(cm);
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // LINE UTILITIES
</span><span class="cx"> 
</span><del>-  function getLine(chunk, n) {
-    n -= chunk.first;
-    while (!chunk.lines) {
</del><ins>+  // Find the line object corresponding to the given line number.
+  function getLine(doc, n) {
+    n -= doc.first;
+    if (n &lt; 0 || n &gt;= doc.size) throw new Error(&quot;There is no line &quot; + (n + doc.first) + &quot; in the document.&quot;);
+    for (var chunk = doc; !chunk.lines;) {
</ins><span class="cx">       for (var i = 0;; ++i) {
</span><span class="cx">         var child = chunk.children[i], sz = child.chunkSize();
</span><span class="cx">         if (n &lt; sz) { chunk = child; break; }
</span><span class="lines">@@ -5069,6 +6457,8 @@
</span><span class="cx">     return chunk.lines[n];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Get the part of a document between two positions, as an array of
+  // strings.
</ins><span class="cx">   function getBetween(doc, start, end) {
</span><span class="cx">     var out = [], n = start.line;
</span><span class="cx">     doc.iter(start.line, end.line + 1, function(line) {
</span><span class="lines">@@ -5080,17 +6470,22 @@
</span><span class="cx">     });
</span><span class="cx">     return out;
</span><span class="cx">   }
</span><ins>+  // Get the lines between from and to, as array of strings.
</ins><span class="cx">   function getLines(doc, from, to) {
</span><span class="cx">     var out = [];
</span><span class="cx">     doc.iter(from, to, function(line) { out.push(line.text); });
</span><span class="cx">     return out;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Update the height of a line, propagating the height change
+  // upwards to parent nodes.
</ins><span class="cx">   function updateLineHeight(line, height) {
</span><span class="cx">     var diff = height - line.height;
</span><del>-    for (var n = line; n; n = n.parent) n.height += diff;
</del><ins>+    if (diff) for (var n = line; n; n = n.parent) n.height += diff;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Given a line object, find its line number by walking up through
+  // its parent links.
</ins><span class="cx">   function lineNo(line) {
</span><span class="cx">     if (line.parent == null) return null;
</span><span class="cx">     var cur = line.parent, no = indexOf(cur.lines, line);
</span><span class="lines">@@ -5103,10 +6498,12 @@
</span><span class="cx">     return no + cur.first;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Find the line at the given vertical position, using the height
+  // information in the document tree.
</ins><span class="cx">   function lineAtHeight(chunk, h) {
</span><span class="cx">     var n = chunk.first;
</span><span class="cx">     outer: do {
</span><del>-      for (var i = 0, e = chunk.children.length; i &lt; e; ++i) {
</del><ins>+      for (var i = 0; i &lt; chunk.children.length; ++i) {
</ins><span class="cx">         var child = chunk.children[i], ch = child.height;
</span><span class="cx">         if (h &lt; ch) { chunk = child; continue outer; }
</span><span class="cx">         h -= ch;
</span><span class="lines">@@ -5114,7 +6511,7 @@
</span><span class="cx">       }
</span><span class="cx">       return n;
</span><span class="cx">     } while (!chunk.lines);
</span><del>-    for (var i = 0, e = chunk.lines.length; i &lt; e; ++i) {
</del><ins>+    for (var i = 0; i &lt; chunk.lines.length; ++i) {
</ins><span class="cx">       var line = chunk.lines[i], lh = line.height;
</span><span class="cx">       if (h &lt; lh) break;
</span><span class="cx">       h -= lh;
</span><span class="lines">@@ -5122,9 +6519,11 @@
</span><span class="cx">     return n + i;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function heightAtLine(cm, lineObj) {
-    lineObj = visualLine(cm.doc, lineObj);
</del><span class="cx"> 
</span><ins>+  // Find the height above the given line.
+  function heightAtLine(lineObj) {
+    lineObj = visualLine(lineObj);
+
</ins><span class="cx">     var h = 0, chunk = lineObj.parent;
</span><span class="cx">     for (var i = 0; i &lt; chunk.lines.length; ++i) {
</span><span class="cx">       var line = chunk.lines[i];
</span><span class="lines">@@ -5141,6 +6540,9 @@
</span><span class="cx">     return h;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Get the bidi ordering for the given line (and cache it). Returns
+  // false for lines that are fully left-to-right, and an array of
+  // BidiSpan objects otherwise.
</ins><span class="cx">   function getOrder(line) {
</span><span class="cx">     var order = line.order;
</span><span class="cx">     if (order == null) order = line.order = bidiOrdering(line.text);
</span><span class="lines">@@ -5149,50 +6551,70 @@
</span><span class="cx"> 
</span><span class="cx">   // HISTORY
</span><span class="cx"> 
</span><del>-  function makeHistory(startGen) {
-    return {
-      // Arrays of history events. Doing something adds an event to
-      // done and clears undo. Undoing moves events from done to
-      // undone, redoing moves them in the other direction.
-      done: [], undone: [], undoDepth: Infinity,
-      // Used to track when changes can be merged into a single undo
-      // event
-      lastTime: 0, lastOp: null, lastOrigin: null,
-      // Used by the isClean() method
-      generation: startGen || 1, maxGeneration: startGen || 1
-    };
</del><ins>+  function History(startGen) {
+    // Arrays of change events and selections. Doing something adds an
+    // event to done and clears undo. Undoing moves events from done
+    // to undone, redoing moves them in the other direction.
+    this.done = []; this.undone = [];
+    this.undoDepth = Infinity;
+    // Used to track when changes can be merged into a single undo
+    // event
+    this.lastModTime = this.lastSelTime = 0;
+    this.lastOp = null;
+    this.lastOrigin = this.lastSelOrigin = null;
+    // Used by the isClean() method
+    this.generation = this.maxGeneration = startGen || 1;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function attachLocalSpans(doc, change, from, to) {
-    var existing = change[&quot;spans_&quot; + doc.id], n = 0;
-    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
-      if (line.markedSpans)
-        (existing || (existing = change[&quot;spans_&quot; + doc.id] = {}))[n] = line.markedSpans;
-      ++n;
-    });
-  }
-
</del><ins>+  // Create a history change event from an updateDoc-style change
+  // object.
</ins><span class="cx">   function historyChangeFromChange(doc, change) {
</span><del>-    var from = { line: change.from.line, ch: change.from.ch };
-    var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
</del><ins>+    var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
</ins><span class="cx">     attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
</span><span class="cx">     linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
</span><span class="cx">     return histChange;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function addToHistory(doc, change, selAfter, opId) {
</del><ins>+  // Pop all selection events off the end of a history array. Stop at
+  // a change event.
+  function clearSelectionEvents(array) {
+    while (array.length) {
+      var last = lst(array);
+      if (last.ranges) array.pop();
+      else break;
+    }
+  }
+
+  // Find the top change event in the history. Pop off selection
+  // events that are in the way.
+  function lastChangeEvent(hist, force) {
+    if (force) {
+      clearSelectionEvents(hist.done);
+      return lst(hist.done);
+    } else if (hist.done.length &amp;&amp; !lst(hist.done).ranges) {
+      return lst(hist.done);
+    } else if (hist.done.length &gt; 1 &amp;&amp; !hist.done[hist.done.length - 2].ranges) {
+      hist.done.pop();
+      return lst(hist.done);
+    }
+  }
+
+  // Register a change in the history. Merges changes that are within
+  // a single operation, ore are close together with an origin that
+  // allows merging (starting with &quot;+&quot;) into a single event.
+  function addChangeToHistory(doc, change, selAfter, opId) {
</ins><span class="cx">     var hist = doc.history;
</span><span class="cx">     hist.undone.length = 0;
</span><del>-    var time = +new Date, cur = lst(hist.done);
</del><ins>+    var time = +new Date, cur;
</ins><span class="cx"> 
</span><del>-    if (cur &amp;&amp;
-        (hist.lastOp == opId ||
</del><ins>+    if ((hist.lastOp == opId ||
</ins><span class="cx">          hist.lastOrigin == change.origin &amp;&amp; change.origin &amp;&amp;
</span><del>-         ((change.origin.charAt(0) == &quot;+&quot; &amp;&amp; doc.cm &amp;&amp; hist.lastTime &gt; time - doc.cm.options.historyEventDelay) ||
-          change.origin.charAt(0) == &quot;*&quot;))) {
</del><ins>+         ((change.origin.charAt(0) == &quot;+&quot; &amp;&amp; doc.cm &amp;&amp; hist.lastModTime &gt; time - doc.cm.options.historyEventDelay) ||
+          change.origin.charAt(0) == &quot;*&quot;)) &amp;&amp;
+        (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
</ins><span class="cx">       // Merge this change into the last event
</span><span class="cx">       var last = lst(cur.changes);
</span><del>-      if (posEq(change.from, change.to) &amp;&amp; posEq(change.from, last.to)) {
</del><ins>+      if (cmp(change.from, change.to) == 0 &amp;&amp; cmp(change.from, last.to) == 0) {
</ins><span class="cx">         // Optimized case for simple insertion -- don't want to add
</span><span class="cx">         // new changesets for every character typed
</span><span class="cx">         last.to = changeEnd(change);
</span><span class="lines">@@ -5200,23 +6622,81 @@
</span><span class="cx">         // Add new sub-event
</span><span class="cx">         cur.changes.push(historyChangeFromChange(doc, change));
</span><span class="cx">       }
</span><del>-      cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
</del><span class="cx">     } else {
</span><span class="cx">       // Can not be merged, start a new event.
</span><ins>+      var before = lst(hist.done);
+      if (!before || !before.ranges)
+        pushSelectionToHistory(doc.sel, hist.done);
</ins><span class="cx">       cur = {changes: [historyChangeFromChange(doc, change)],
</span><del>-             generation: hist.generation,
-             anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
-             anchorAfter: selAfter.anchor, headAfter: selAfter.head};
</del><ins>+             generation: hist.generation};
</ins><span class="cx">       hist.done.push(cur);
</span><del>-      hist.generation = ++hist.maxGeneration;
-      while (hist.done.length &gt; hist.undoDepth)
</del><ins>+      while (hist.done.length &gt; hist.undoDepth) {
</ins><span class="cx">         hist.done.shift();
</span><ins>+        if (!hist.done[0].ranges) hist.done.shift();
+      }
</ins><span class="cx">     }
</span><del>-    hist.lastTime = time;
</del><ins>+    hist.done.push(selAfter);
+    hist.generation = ++hist.maxGeneration;
+    hist.lastModTime = hist.lastSelTime = time;
</ins><span class="cx">     hist.lastOp = opId;
</span><del>-    hist.lastOrigin = change.origin;
</del><ins>+    hist.lastOrigin = hist.lastSelOrigin = change.origin;
+
+    if (!last) signal(doc, &quot;historyAdded&quot;);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function selectionEventCanBeMerged(doc, origin, prev, sel) {
+    var ch = origin.charAt(0);
+    return ch == &quot;*&quot; ||
+      ch == &quot;+&quot; &amp;&amp;
+      prev.ranges.length == sel.ranges.length &amp;&amp;
+      prev.somethingSelected() == sel.somethingSelected() &amp;&amp;
+      new Date - doc.history.lastSelTime &lt;= (doc.cm ? doc.cm.options.historyEventDelay : 500);
+  }
+
+  // Called whenever the selection changes, sets the new selection as
+  // the pending selection in the history, and pushes the old pending
+  // selection into the 'done' array when it was significantly
+  // different (in number of selected ranges, emptiness, or time).
+  function addSelectionToHistory(doc, sel, opId, options) {
+    var hist = doc.history, origin = options &amp;&amp; options.origin;
+
+    // A new event is started when the previous origin does not match
+    // the current, or the origins don't allow matching. Origins
+    // starting with * are always merged, those starting with + are
+    // merged when similar and close together in time.
+    if (opId == hist.lastOp ||
+        (origin &amp;&amp; hist.lastSelOrigin == origin &amp;&amp;
+         (hist.lastModTime == hist.lastSelTime &amp;&amp; hist.lastOrigin == origin ||
+          selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
+      hist.done[hist.done.length - 1] = sel;
+    else
+      pushSelectionToHistory(sel, hist.done);
+
+    hist.lastSelTime = +new Date;
+    hist.lastSelOrigin = origin;
+    hist.lastOp = opId;
+    if (options &amp;&amp; options.clearRedo !== false)
+      clearSelectionEvents(hist.undone);
+  }
+
+  function pushSelectionToHistory(sel, dest) {
+    var top = lst(dest);
+    if (!(top &amp;&amp; top.ranges &amp;&amp; top.equals(sel)))
+      dest.push(sel);
+  }
+
+  // Used to store marked span information in the history.
+  function attachLocalSpans(doc, change, from, to) {
+    var existing = change[&quot;spans_&quot; + doc.id], n = 0;
+    doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
+      if (line.markedSpans)
+        (existing || (existing = change[&quot;spans_&quot; + doc.id] = {}))[n] = line.markedSpans;
+      ++n;
+    });
+  }
+
+  // When un/re-doing restores text containing marked spans, those
+  // that have been explicitly cleared should not be restored.
</ins><span class="cx">   function removeClearedSpans(spans) {
</span><span class="cx">     if (!spans) return null;
</span><span class="cx">     for (var i = 0, out; i &lt; spans.length; ++i) {
</span><span class="lines">@@ -5226,6 +6706,7 @@
</span><span class="cx">     return !out ? spans : out.length ? out : null;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Retrieve and filter the old marked spans stored in a change event.
</ins><span class="cx">   function getOldSpans(doc, change) {
</span><span class="cx">     var found = change[&quot;spans_&quot; + doc.id];
</span><span class="cx">     if (!found) return null;
</span><span class="lines">@@ -5236,11 +6717,15 @@
</span><span class="cx"> 
</span><span class="cx">   // Used both to provide a JSON-safe object in .getHistory, and, when
</span><span class="cx">   // detaching a document, to split the history in two
</span><del>-  function copyHistoryArray(events, newGroup) {
</del><ins>+  function copyHistoryArray(events, newGroup, instantiateSel) {
</ins><span class="cx">     for (var i = 0, copy = []; i &lt; events.length; ++i) {
</span><del>-      var event = events[i], changes = event.changes, newChanges = [];
-      copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
-                 anchorAfter: event.anchorAfter, headAfter: event.headAfter});
</del><ins>+      var event = events[i];
+      if (event.ranges) {
+        copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
+        continue;
+      }
+      var changes = event.changes, newChanges = [];
+      copy.push({changes: newChanges});
</ins><span class="cx">       for (var j = 0; j &lt; changes.length; ++j) {
</span><span class="cx">         var change = changes[j], m;
</span><span class="cx">         newChanges.push({from: change.from, to: change.to, text: change.text});
</span><span class="lines">@@ -5257,7 +6742,7 @@
</span><span class="cx"> 
</span><span class="cx">   // Rebasing/resetting history to deal with externally-sourced changes
</span><span class="cx"> 
</span><del>-  function rebaseHistSel(pos, from, to, diff) {
</del><ins>+  function rebaseHistSelSingle(pos, from, to, diff) {
</ins><span class="cx">     if (to &lt; pos.line) {
</span><span class="cx">       pos.line += diff;
</span><span class="cx">     } else if (from &lt; pos.line) {
</span><span class="lines">@@ -5276,28 +6761,27 @@
</span><span class="cx">   function rebaseHistArray(array, from, to, diff) {
</span><span class="cx">     for (var i = 0; i &lt; array.length; ++i) {
</span><span class="cx">       var sub = array[i], ok = true;
</span><ins>+      if (sub.ranges) {
+        if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
+        for (var j = 0; j &lt; sub.ranges.length; j++) {
+          rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
+          rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
+        }
+        continue;
+      }
</ins><span class="cx">       for (var j = 0; j &lt; sub.changes.length; ++j) {
</span><span class="cx">         var cur = sub.changes[j];
</span><del>-        if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
</del><span class="cx">         if (to &lt; cur.from.line) {
</span><del>-          cur.from.line += diff;
-          cur.to.line += diff;
</del><ins>+          cur.from = Pos(cur.from.line + diff, cur.from.ch);
+          cur.to = Pos(cur.to.line + diff, cur.to.ch);
</ins><span class="cx">         } else if (from &lt;= cur.to.line) {
</span><span class="cx">           ok = false;
</span><span class="cx">           break;
</span><span class="cx">         }
</span><span class="cx">       }
</span><del>-      if (!sub.copied) {
-        sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
-        sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
-        sub.copied = true;
-      }
</del><span class="cx">       if (!ok) {
</span><span class="cx">         array.splice(0, i + 1);
</span><span class="cx">         i = 0;
</span><del>-      } else {
-        rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
-        rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
</del><span class="cx">       }
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="lines">@@ -5308,30 +6792,23 @@
</span><span class="cx">     rebaseHistArray(hist.undone, from, to, diff);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // EVENT OPERATORS
</del><ins>+  // EVENT UTILITIES
</ins><span class="cx"> 
</span><del>-  function stopMethod() {e_stop(this);}
-  // Ensure an event has a stop method.
-  function addStop(event) {
-    if (!event.stop) event.stop = stopMethod;
-    return event;
-  }
</del><ins>+  // Due to the fact that we still support jurassic IE versions, some
+  // compatibility wrappers are needed.
</ins><span class="cx"> 
</span><del>-  function e_preventDefault(e) {
</del><ins>+  var e_preventDefault = CodeMirror.e_preventDefault = function(e) {
</ins><span class="cx">     if (e.preventDefault) e.preventDefault();
</span><span class="cx">     else e.returnValue = false;
</span><del>-  }
-  function e_stopPropagation(e) {
</del><ins>+  };
+  var e_stopPropagation = CodeMirror.e_stopPropagation = function(e) {
</ins><span class="cx">     if (e.stopPropagation) e.stopPropagation();
</span><span class="cx">     else e.cancelBubble = true;
</span><del>-  }
</del><ins>+  };
</ins><span class="cx">   function e_defaultPrevented(e) {
</span><span class="cx">     return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false;
</span><span class="cx">   }
</span><del>-  function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
-  CodeMirror.e_stop = e_stop;
-  CodeMirror.e_preventDefault = e_preventDefault;
-  CodeMirror.e_stopPropagation = e_stopPropagation;
</del><ins>+  var e_stop = CodeMirror.e_stop = function(e) {e_preventDefault(e); e_stopPropagation(e);};
</ins><span class="cx"> 
</span><span class="cx">   function e_target(e) {return e.target || e.srcElement;}
</span><span class="cx">   function e_button(e) {
</span><span class="lines">@@ -5347,7 +6824,10 @@
</span><span class="cx"> 
</span><span class="cx">   // EVENT HANDLING
</span><span class="cx"> 
</span><del>-  function on(emitter, type, f) {
</del><ins>+  // Lightweight event framework. on/off also work on DOM nodes,
+  // registering native DOM handlers.
+
+  var on = CodeMirror.on = function(emitter, type, f) {
</ins><span class="cx">     if (emitter.addEventListener)
</span><span class="cx">       emitter.addEventListener(type, f, false);
</span><span class="cx">     else if (emitter.attachEvent)
</span><span class="lines">@@ -5357,9 +6837,9 @@
</span><span class="cx">       var arr = map[type] || (map[type] = []);
</span><span class="cx">       arr.push(f);
</span><span class="cx">     }
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function off(emitter, type, f) {
</del><ins>+  var off = CodeMirror.off = function(emitter, type, f) {
</ins><span class="cx">     if (emitter.removeEventListener)
</span><span class="cx">       emitter.removeEventListener(type, f, false);
</span><span class="cx">     else if (emitter.detachEvent)
</span><span class="lines">@@ -5370,15 +6850,22 @@
</span><span class="cx">       for (var i = 0; i &lt; arr.length; ++i)
</span><span class="cx">         if (arr[i] == f) { arr.splice(i, 1); break; }
</span><span class="cx">     }
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function signal(emitter, type /*, values...*/) {
</del><ins>+  var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
</ins><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="cx">     if (!arr) return;
</span><span class="cx">     var args = Array.prototype.slice.call(arguments, 2);
</span><span class="cx">     for (var i = 0; i &lt; arr.length; ++i) arr[i].apply(null, args);
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><ins>+  // Often, we want to signal events at a point where we are in the
+  // middle of some work, but don't want the handler to start calling
+  // other methods on the editor, which might be in an inconsistent
+  // state or simply not expect any other events to happen.
+  // signalLater looks whether there are any handlers, and schedules
+  // them to be executed when the last operation ends, or, if no
+  // operation is active, when a timeout fires.
</ins><span class="cx">   var delayedCallbacks, delayedCallbackDepth = 0;
</span><span class="cx">   function signalLater(emitter, type /*, values...*/) {
</span><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="lines">@@ -5394,11 +6881,6 @@
</span><span class="cx">       delayedCallbacks.push(bnd(arr[i]));
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function signalDOMEvent(cm, e, override) {
-    signal(cm, override || e.type, cm, e);
-    return e_defaultPrevented(e) || e.codemirrorIgnore;
-  }
-
</del><span class="cx">   function fireDelayed() {
</span><span class="cx">     --delayedCallbackDepth;
</span><span class="cx">     var delayed = delayedCallbacks;
</span><span class="lines">@@ -5406,13 +6888,21 @@
</span><span class="cx">     for (var i = 0; i &lt; delayed.length; ++i) delayed[i]();
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // The DOM events that CodeMirror handles can be overridden by
+  // registering a (non-DOM) handler on the editor for the event name,
+  // and preventDefault-ing the event in that handler.
+  function signalDOMEvent(cm, e, override) {
+    signal(cm, override || e.type, cm, e);
+    return e_defaultPrevented(e) || e.codemirrorIgnore;
+  }
+
</ins><span class="cx">   function hasHandler(emitter, type) {
</span><span class="cx">     var arr = emitter._handlers &amp;&amp; emitter._handlers[type];
</span><span class="cx">     return arr &amp;&amp; arr.length &gt; 0;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal;
-
</del><ins>+  // Add on and off methods to a constructor's prototype, to make
+  // registering events on such objects more convenient.
</ins><span class="cx">   function eventMixin(ctor) {
</span><span class="cx">     ctor.prototype.on = function(type, f) {on(this, type, f);};
</span><span class="cx">     ctor.prototype.off = function(type, f) {off(this, type, f);};
</span><span class="lines">@@ -5427,23 +6917,47 @@
</span><span class="cx">   // handling this'.
</span><span class="cx">   var Pass = CodeMirror.Pass = {toString: function(){return &quot;CodeMirror.Pass&quot;;}};
</span><span class="cx"> 
</span><ins>+  // Reused option objects for setSelection &amp; friends
+  var sel_dontScroll = {scroll: false}, sel_mouse = {origin: &quot;*mouse&quot;}, sel_move = {origin: &quot;+move&quot;};
+
</ins><span class="cx">   function Delayed() {this.id = null;}
</span><del>-  Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
</del><ins>+  Delayed.prototype.set = function(ms, f) {
+    clearTimeout(this.id);
+    this.id = setTimeout(f, ms);
+  };
</ins><span class="cx"> 
</span><span class="cx">   // Counts the column offset in a string, taking tabs into account.
</span><span class="cx">   // Used mostly to find indentation.
</span><del>-  function countColumn(string, end, tabSize, startIndex, startValue) {
</del><ins>+  var countColumn = CodeMirror.countColumn = function(string, end, tabSize, startIndex, startValue) {
</ins><span class="cx">     if (end == null) {
</span><span class="cx">       end = string.search(/[^\s\u00a0]/);
</span><span class="cx">       if (end == -1) end = string.length;
</span><span class="cx">     }
</span><del>-    for (var i = startIndex || 0, n = startValue || 0; i &lt; end; ++i) {
-      if (string.charAt(i) == &quot;\t&quot;) n += tabSize - (n % tabSize);
-      else ++n;
</del><ins>+    for (var i = startIndex || 0, n = startValue || 0;;) {
+      var nextTab = string.indexOf(&quot;\t&quot;, i);
+      if (nextTab &lt; 0 || nextTab &gt;= end)
+        return n + (end - i);
+      n += nextTab - i;
+      n += tabSize - (n % tabSize);
+      i = nextTab + 1;
</ins><span class="cx">     }
</span><del>-    return n;
</del><ins>+  };
+
+  // The inverse of countColumn -- find the offset that corresponds to
+  // a particular column.
+  function findColumn(string, goal, tabSize) {
+    for (var pos = 0, col = 0;;) {
+      var nextTab = string.indexOf(&quot;\t&quot;, pos);
+      if (nextTab == -1) nextTab = string.length;
+      var skipped = nextTab - pos;
+      if (nextTab == string.length || col + skipped &gt;= goal)
+        return pos + Math.min(skipped, goal - col);
+      col += nextTab - pos;
+      col += tabSize - (col % tabSize);
+      pos = nextTab + 1;
+      if (col &gt;= goal) return pos;
+    }
</ins><span class="cx">   }
</span><del>-  CodeMirror.countColumn = countColumn;
</del><span class="cx"> 
</span><span class="cx">   var spaceStrs = [&quot;&quot;];
</span><span class="cx">   function spaceStr(n) {
</span><span class="lines">@@ -5454,60 +6968,69 @@
</span><span class="cx"> 
</span><span class="cx">   function lst(arr) { return arr[arr.length-1]; }
</span><span class="cx"> 
</span><del>-  function selectInput(node) {
-    if (ios) { // Mobile Safari apparently has a bug where select() is broken.
-      node.selectionStart = 0;
-      node.selectionEnd = node.value.length;
-    } else {
-      // Suppress mysterious IE10 errors
-      try { node.select(); }
-      catch(_e) {}
-    }
-  }
</del><ins>+  var selectInput = function(node) { node.select(); };
+  if (ios) // Mobile Safari apparently has a bug where select() is broken.
+    selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; };
+  else if (ie) // Suppress mysterious IE10 errors
+    selectInput = function(node) { try { node.select(); } catch(_e) {} };
</ins><span class="cx"> 
</span><del>-  function indexOf(collection, elt) {
-    if (collection.indexOf) return collection.indexOf(elt);
-    for (var i = 0, e = collection.length; i &lt; e; ++i)
-      if (collection[i] == elt) return i;
</del><ins>+  function indexOf(array, elt) {
+    for (var i = 0; i &lt; array.length; ++i)
+      if (array[i] == elt) return i;
</ins><span class="cx">     return -1;
</span><span class="cx">   }
</span><ins>+  if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); };
+  function map(array, f) {
+    var out = [];
+    for (var i = 0; i &lt; array.length; i++) out[i] = f(array[i], i);
+    return out;
+  }
+  if ([].map) map = function(array, f) { return array.map(f); };
</ins><span class="cx"> 
</span><span class="cx">   function createObj(base, props) {
</span><del>-    function Obj() {}
-    Obj.prototype = base;
-    var inst = new Obj();
</del><ins>+    var inst;
+    if (Object.create) {
+      inst = Object.create(base);
+    } else {
+      var ctor = function() {};
+      ctor.prototype = base;
+      inst = new ctor();
+    }
</ins><span class="cx">     if (props) copyObj(props, inst);
</span><span class="cx">     return inst;
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><del>-  function copyObj(obj, target) {
</del><ins>+  function copyObj(obj, target, overwrite) {
</ins><span class="cx">     if (!target) target = {};
</span><del>-    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
</del><ins>+    for (var prop in obj)
+      if (obj.hasOwnProperty(prop) &amp;&amp; (overwrite !== false || !target.hasOwnProperty(prop)))
+        target[prop] = obj[prop];
</ins><span class="cx">     return target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function emptyArray(size) {
-    for (var a = [], i = 0; i &lt; size; ++i) a.push(undefined);
-    return a;
-  }
-
</del><span class="cx">   function bind(f) {
</span><span class="cx">     var args = Array.prototype.slice.call(arguments, 1);
</span><span class="cx">     return function(){return f.apply(null, args);};
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
-  function isWordChar(ch) {
</del><ins>+  var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+  var isWordChar = CodeMirror.isWordChar = function(ch) {
</ins><span class="cx">     return /\w/.test(ch) || ch &gt; &quot;\x80&quot; &amp;&amp;
</span><span class="cx">       (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
</span><del>-  }
</del><ins>+  };
</ins><span class="cx"> 
</span><span class="cx">   function isEmpty(obj) {
</span><span class="cx">     for (var n in obj) if (obj.hasOwnProperty(n) &amp;&amp; obj[n]) return false;
</span><span class="cx">     return true;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0–\u1DFF\u20D0–\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20–\uFE2F]/;
</del><ins>+  // Extending unicode characters. A series of a non-extending char +
+  // any number of extending chars is treated as a single unit as far
+  // as editing and measuring is concerned. This is not fully correct,
+  // since some scripts/fonts/browsers also treat other configurations
+  // of code points as a group.
+  var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
+  function isExtendingChar(ch) { return ch.charCodeAt(0) &gt;= 768 &amp;&amp; extendingChars.test(ch); }
</ins><span class="cx"> 
</span><span class="cx">   // DOM UTILITIES
</span><span class="cx"> 
</span><span class="lines">@@ -5515,11 +7038,27 @@
</span><span class="cx">     var e = document.createElement(tag);
</span><span class="cx">     if (className) e.className = className;
</span><span class="cx">     if (style) e.style.cssText = style;
</span><del>-    if (typeof content == &quot;string&quot;) setTextContent(e, content);
</del><ins>+    if (typeof content == &quot;string&quot;) e.appendChild(document.createTextNode(content));
</ins><span class="cx">     else if (content) for (var i = 0; i &lt; content.length; ++i) e.appendChild(content[i]);
</span><span class="cx">     return e;
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  var range;
+  if (document.createRange) range = function(node, start, end) {
+    var r = document.createRange();
+    r.setEnd(node, end);
+    r.setStart(node, start);
+    return r;
+  };
+  else range = function(node, start, end) {
+    var r = document.body.createTextRange();
+    r.moveToElementText(node.parentNode);
+    r.collapse(true);
+    r.moveEnd(&quot;character&quot;, end);
+    r.moveStart(&quot;character&quot;, start);
+    return r;
+  };
+
</ins><span class="cx">   function removeChildren(e) {
</span><span class="cx">     for (var count = e.childNodes.length; count &gt; 0; --count)
</span><span class="cx">       e.removeChild(e.firstChild);
</span><span class="lines">@@ -5530,17 +7069,35 @@
</span><span class="cx">     return removeChildren(parent).appendChild(e);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function setTextContent(e, str) {
-    if (ie_lt9) {
-      e.innerHTML = &quot;&quot;;
-      e.appendChild(document.createTextNode(str));
-    } else e.textContent = str;
</del><ins>+  function contains(parent, child) {
+    if (parent.contains)
+      return parent.contains(child);
+    while (child = child.parentNode)
+      if (child == parent) return true;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function getRect(node) {
-    return node.getBoundingClientRect();
</del><ins>+  function activeElt() { return document.activeElement; }
+  // Older versions of IE throws unspecified error when touching
+  // document.activeElement in some cases (during loading, in iframe)
+  if (ie_upto10) activeElt = function() {
+    try { return document.activeElement; }
+    catch(e) { return document.body; }
+  };
+
+  function classTest(cls) { return new RegExp(&quot;\\b&quot; + cls + &quot;\\b\\s*&quot;); }
+  function rmClass(node, cls) {
+    var test = classTest(cls);
+    if (test.test(node.className)) node.className = node.className.replace(test, &quot;&quot;);
</ins><span class="cx">   }
</span><del>-  CodeMirror.replaceGetRect = function(f) { getRect = f; };
</del><ins>+  function addClass(node, cls) {
+    if (!classTest(cls).test(node.className)) node.className += &quot; &quot; + cls;
+  }
+  function joinClasses(a, b) {
+    var as = a.split(&quot; &quot;);
+    for (var i = 0; i &lt; as.length; i++)
+      if (as[i] &amp;&amp; !classTest(as[i]).test(b)) b += &quot; &quot; + as[i];
+    return b;
+  }
</ins><span class="cx"> 
</span><span class="cx">   // FEATURE DETECTION
</span><span class="cx"> 
</span><span class="lines">@@ -5548,43 +7105,11 @@
</span><span class="cx">   var dragAndDrop = function() {
</span><span class="cx">     // There is *some* kind of drag-and-drop support in IE6-8, but I
</span><span class="cx">     // couldn't get it to work yet.
</span><del>-    if (ie_lt9) return false;
</del><ins>+    if (ie_upto8) return false;
</ins><span class="cx">     var div = elt('div');
</span><span class="cx">     return &quot;draggable&quot; in div || &quot;dragDrop&quot; in div;
</span><span class="cx">   }();
</span><span class="cx"> 
</span><del>-  // For a reason I have yet to figure out, some browsers disallow
-  // word wrapping between certain characters *only* if a new inline
-  // element is started between them. This makes it hard to reliably
-  // measure the position of things, since that requires inserting an
-  // extra span. This terribly fragile set of tests matches the
-  // character combinations that suffer from this phenomenon on the
-  // various browsers.
-  function spanAffectsWrapping() { return false; }
-  if (gecko) // Only for &quot;$'&quot;
-    spanAffectsWrapping = function(str, i) {
-      return str.charCodeAt(i - 1) == 36 &amp;&amp; str.charCodeAt(i) == 39;
-    };
-  else if (safari &amp;&amp; !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      var result = /\-[^ \-?]|\?[^ !\'\&quot;\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
-      return result;
-    };
-  else if (webkit &amp;&amp; /Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent))
-    spanAffectsWrapping = function(str, i) {
-      var code = str.charCodeAt(i - 1);
-      return code &gt;= 8208 &amp;&amp; code &lt;= 8212;
-    };
-  else if (webkit)
-    spanAffectsWrapping = function(str, i) {
-      if (i &gt; 1 &amp;&amp; str.charCodeAt(i - 1) == 45) {
-        if (/\w/.test(str.charAt(i - 2)) &amp;&amp; /[^\-?\.]/.test(str.charAt(i))) return true;
-        if (i &gt; 2 &amp;&amp; /[\d\.,]/.test(str.charAt(i - 2)) &amp;&amp; /[\d\.,]/.test(str.charAt(i))) return false;
-      }
-      var result = /[~!#%&amp;*)=+}\]\\|\&quot;\.&gt;,:;][({[&lt;]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&amp;*(_=+{[|&gt;&lt;]|…[\w~`@#$%\^&amp;*(_=+{[&gt;&lt;]/.test(str.slice(i - 1, i + 1));
-      return result;
-    };
-
</del><span class="cx">   var knownScrollbarWidth;
</span><span class="cx">   function scrollbarWidth(measure) {
</span><span class="cx">     if (knownScrollbarWidth != null) return knownScrollbarWidth;
</span><span class="lines">@@ -5601,15 +7126,26 @@
</span><span class="cx">       var test = elt(&quot;span&quot;, &quot;\u200b&quot;);
</span><span class="cx">       removeChildrenAndAdd(measure, elt(&quot;span&quot;, [test, document.createTextNode(&quot;x&quot;)]));
</span><span class="cx">       if (measure.firstChild.offsetHeight != 0)
</span><del>-        zwspSupported = test.offsetWidth &lt;= 1 &amp;&amp; test.offsetHeight &gt; 2 &amp;&amp; !ie_lt8;
</del><ins>+        zwspSupported = test.offsetWidth &lt;= 1 &amp;&amp; test.offsetHeight &gt; 2 &amp;&amp; !ie_upto7;
</ins><span class="cx">     }
</span><span class="cx">     if (zwspSupported) return elt(&quot;span&quot;, &quot;\u200b&quot;);
</span><span class="cx">     else return elt(&quot;span&quot;, &quot;\u00a0&quot;, null, &quot;display: inline-block; width: 1px; margin-right: -1px&quot;);
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  // Feature-detect IE's crummy client rect reporting for bidi text
+  var badBidiRects;
+  function hasBadBidiRects(measure) {
+    if (badBidiRects != null) return badBidiRects;
+    var txt = removeChildrenAndAdd(measure, document.createTextNode(&quot;A\u062eA&quot;));
+    var r0 = range(txt, 0, 1).getBoundingClientRect();
+    if (r0.left == r0.right) return false;
+    var r1 = range(txt, 1, 2).getBoundingClientRect();
+    return badBidiRects = (r1.right - r0.right &lt; 3);
+  }
+
</ins><span class="cx">   // See if &quot;&quot;.split is the broken IE version, if so, provide an
</span><span class="cx">   // alternative way to split lines.
</span><del>-  var splitLines = &quot;\n\nb&quot;.split(/\n/).length != 3 ? function(string) {
</del><ins>+  var splitLines = CodeMirror.splitLines = &quot;\n\nb&quot;.split(/\n/).length != 3 ? function(string) {
</ins><span class="cx">     var pos = 0, result = [], l = string.length;
</span><span class="cx">     while (pos &lt;= l) {
</span><span class="cx">       var nl = string.indexOf(&quot;\n&quot;, pos);
</span><span class="lines">@@ -5626,7 +7162,6 @@
</span><span class="cx">     }
</span><span class="cx">     return result;
</span><span class="cx">   } : function(string){return string.split(/\r\n?|\n/);};
</span><del>-  CodeMirror.splitLines = splitLines;
</del><span class="cx"> 
</span><span class="cx">   var hasSelection = window.getSelection ? function(te) {
</span><span class="cx">     try { return te.selectionStart != te.selectionEnd; }
</span><span class="lines">@@ -5642,22 +7177,22 @@
</span><span class="cx">     var e = elt(&quot;div&quot;);
</span><span class="cx">     if (&quot;oncopy&quot; in e) return true;
</span><span class="cx">     e.setAttribute(&quot;oncopy&quot;, &quot;return;&quot;);
</span><del>-    return typeof e.oncopy == 'function';
</del><ins>+    return typeof e.oncopy == &quot;function&quot;;
</ins><span class="cx">   })();
</span><span class="cx"> 
</span><del>-  // KEY NAMING
</del><ins>+  // KEY NAMES
</ins><span class="cx"> 
</span><span class="cx">   var keyNames = {3: &quot;Enter&quot;, 8: &quot;Backspace&quot;, 9: &quot;Tab&quot;, 13: &quot;Enter&quot;, 16: &quot;Shift&quot;, 17: &quot;Ctrl&quot;, 18: &quot;Alt&quot;,
</span><span class="cx">                   19: &quot;Pause&quot;, 20: &quot;CapsLock&quot;, 27: &quot;Esc&quot;, 32: &quot;Space&quot;, 33: &quot;PageUp&quot;, 34: &quot;PageDown&quot;, 35: &quot;End&quot;,
</span><span class="cx">                   36: &quot;Home&quot;, 37: &quot;Left&quot;, 38: &quot;Up&quot;, 39: &quot;Right&quot;, 40: &quot;Down&quot;, 44: &quot;PrintScrn&quot;, 45: &quot;Insert&quot;,
</span><del>-                  46: &quot;Delete&quot;, 59: &quot;;&quot;, 91: &quot;Mod&quot;, 92: &quot;Mod&quot;, 93: &quot;Mod&quot;, 109: &quot;-&quot;, 107: &quot;=&quot;, 127: &quot;Delete&quot;,
-                  186: &quot;;&quot;, 187: &quot;=&quot;, 188: &quot;,&quot;, 189: &quot;-&quot;, 190: &quot;.&quot;, 191: &quot;/&quot;, 192: &quot;`&quot;, 219: &quot;[&quot;, 220: &quot;\\&quot;,
-                  221: &quot;]&quot;, 222: &quot;'&quot;, 63276: &quot;PageUp&quot;, 63277: &quot;PageDown&quot;, 63275: &quot;End&quot;, 63273: &quot;Home&quot;,
-                  63234: &quot;Left&quot;, 63232: &quot;Up&quot;, 63235: &quot;Right&quot;, 63233: &quot;Down&quot;, 63302: &quot;Insert&quot;, 63272: &quot;Delete&quot;};
</del><ins>+                  46: &quot;Delete&quot;, 59: &quot;;&quot;, 61: &quot;=&quot;, 91: &quot;Mod&quot;, 92: &quot;Mod&quot;, 93: &quot;Mod&quot;, 107: &quot;=&quot;, 109: &quot;-&quot;, 127: &quot;Delete&quot;,
+                  173: &quot;-&quot;, 186: &quot;;&quot;, 187: &quot;=&quot;, 188: &quot;,&quot;, 189: &quot;-&quot;, 190: &quot;.&quot;, 191: &quot;/&quot;, 192: &quot;`&quot;, 219: &quot;[&quot;, 220: &quot;\\&quot;,
+                  221: &quot;]&quot;, 222: &quot;'&quot;, 63232: &quot;Up&quot;, 63233: &quot;Down&quot;, 63234: &quot;Left&quot;, 63235: &quot;Right&quot;, 63272: &quot;Delete&quot;,
+                  63273: &quot;Home&quot;, 63275: &quot;End&quot;, 63276: &quot;PageUp&quot;, 63277: &quot;PageDown&quot;, 63302: &quot;Insert&quot;};
</ins><span class="cx">   CodeMirror.keyNames = keyNames;
</span><span class="cx">   (function() {
</span><span class="cx">     // Number keys
</span><del>-    for (var i = 0; i &lt; 10; i++) keyNames[i + 48] = String(i);
</del><ins>+    for (var i = 0; i &lt; 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i);
</ins><span class="cx">     // Alphabetic keys
</span><span class="cx">     for (var i = 65; i &lt;= 90; i++) keyNames[i] = String.fromCharCode(i);
</span><span class="cx">     // Function keys
</span><span class="lines">@@ -5691,19 +7226,21 @@
</span><span class="cx"> 
</span><span class="cx">   function lineStart(cm, lineN) {
</span><span class="cx">     var line = getLine(cm.doc, lineN);
</span><del>-    var visual = visualLine(cm.doc, line);
</del><ins>+    var visual = visualLine(line);
</ins><span class="cx">     if (visual != line) lineN = lineNo(visual);
</span><span class="cx">     var order = getOrder(visual);
</span><span class="cx">     var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
</span><span class="cx">     return Pos(lineN, ch);
</span><span class="cx">   }
</span><span class="cx">   function lineEnd(cm, lineN) {
</span><del>-    var merged, line;
-    while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
-      lineN = merged.find().to.line;
</del><ins>+    var merged, line = getLine(cm.doc, lineN);
+    while (merged = collapsedSpanAtEnd(line)) {
+      line = merged.find(1, true).line;
+      lineN = null;
+    }
</ins><span class="cx">     var order = getOrder(line);
</span><span class="cx">     var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
</span><del>-    return Pos(lineN, ch);
</del><ins>+    return Pos(lineN == null ? lineNo(line) : lineN, ch);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function compareBidiLevel(order, a, b) {
</span><span class="lines">@@ -5714,38 +7251,37 @@
</span><span class="cx">   }
</span><span class="cx">   var bidiOther;
</span><span class="cx">   function getBidiPartAt(order, pos) {
</span><ins>+    bidiOther = null;
</ins><span class="cx">     for (var i = 0, found; i &lt; order.length; ++i) {
</span><span class="cx">       var cur = order[i];
</span><del>-      if (cur.from &lt; pos &amp;&amp; cur.to &gt; pos) { bidiOther = null; return i; }
-      if (cur.from == pos || cur.to == pos) {
</del><ins>+      if (cur.from &lt; pos &amp;&amp; cur.to &gt; pos) return i;
+      if ((cur.from == pos || cur.to == pos)) {
</ins><span class="cx">         if (found == null) {
</span><span class="cx">           found = i;
</span><span class="cx">         } else if (compareBidiLevel(order, cur.level, order[found].level)) {
</span><del>-          bidiOther = found;
</del><ins>+          if (cur.from != cur.to) bidiOther = found;
</ins><span class="cx">           return i;
</span><span class="cx">         } else {
</span><del>-          bidiOther = i;
</del><ins>+          if (cur.from != cur.to) bidiOther = i;
</ins><span class="cx">           return found;
</span><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><del>-    bidiOther = null;
</del><span class="cx">     return found;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function moveInLine(line, pos, dir, byUnit) {
</span><span class="cx">     if (!byUnit) return pos + dir;
</span><span class="cx">     do pos += dir;
</span><del>-    while (pos &gt; 0 &amp;&amp; isExtendingChar.test(line.text.charAt(pos)));
</del><ins>+    while (pos &gt; 0 &amp;&amp; isExtendingChar(line.text.charAt(pos)));
</ins><span class="cx">     return pos;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  // This is somewhat involved. It is needed in order to move
-  // 'visually' through bi-directional text -- i.e., pressing left
-  // should make the cursor go left, even when in RTL text. The
-  // tricky part is the 'jumps', where RTL and LTR text touch each
-  // other. This often requires the cursor offset to move more than
-  // one unit, in order to visually move one unit.
</del><ins>+  // This is needed in order to move 'visually' through bi-directional
+  // text -- i.e., pressing left should make the cursor go left, even
+  // when in RTL text. The tricky part is the 'jumps', where RTL and
+  // LTR text touch each other. This often requires the cursor offset
+  // to move more than one unit, in order to visually move one unit.
</ins><span class="cx">   function moveVisually(line, start, dir, byUnit) {
</span><span class="cx">     var bidi = getOrder(line);
</span><span class="cx">     if (!bidi) return moveLogically(line, start, dir, byUnit);
</span><span class="lines">@@ -5771,7 +7307,7 @@
</span><span class="cx"> 
</span><span class="cx">   function moveLogically(line, start, dir, byUnit) {
</span><span class="cx">     var target = start + dir;
</span><del>-    if (byUnit) while (target &gt; 0 &amp;&amp; isExtendingChar.test(line.text.charAt(target))) target += dir;
</del><ins>+    if (byUnit) while (target &gt; 0 &amp;&amp; isExtendingChar(line.text.charAt(target))) target += dir;
</ins><span class="cx">     return target &lt; 0 || target &gt; line.text.length ? null : target;
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -5800,14 +7336,16 @@
</span><span class="cx">   // objects) in the order in which they occur visually.
</span><span class="cx">   var bidiOrdering = (function() {
</span><span class="cx">     // Character types for codepoints 0 to 0xff
</span><del>-    var lowTypes = &quot;bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL&quot;;
</del><ins>+    var lowTypes = &quot;bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN&quot;;
</ins><span class="cx">     // Character types for codepoints 0x600 to 0x6ff
</span><del>-    var arabicTypes = &quot;rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr&quot;;
</del><ins>+    var arabicTypes = &quot;rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm&quot;;
</ins><span class="cx">     function charType(code) {
</span><del>-      if (code &lt;= 0xff) return lowTypes.charAt(code);
</del><ins>+      if (code &lt;= 0xf7) return lowTypes.charAt(code);
</ins><span class="cx">       else if (0x590 &lt;= code &amp;&amp; code &lt;= 0x5f4) return &quot;R&quot;;
</span><del>-      else if (0x600 &lt;= code &amp;&amp; code &lt;= 0x6ff) return arabicTypes.charAt(code - 0x600);
-      else if (0x700 &lt;= code &amp;&amp; code &lt;= 0x8ac) return &quot;r&quot;;
</del><ins>+      else if (0x600 &lt;= code &amp;&amp; code &lt;= 0x6ed) return arabicTypes.charAt(code - 0x600);
+      else if (0x6ee &lt;= code &amp;&amp; code &lt;= 0x8ac) return &quot;r&quot;;
+      else if (0x2000 &lt;= code &amp;&amp; code &lt;= 0x200b) return &quot;w&quot;;
+      else if (code == 0x200c) return &quot;b&quot;;
</ins><span class="cx">       else return &quot;L&quot;;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -5816,6 +7354,11 @@
</span><span class="cx">     // Browsers seem to always treat the boundaries of block elements as being L.
</span><span class="cx">     var outerType = &quot;L&quot;;
</span><span class="cx"> 
</span><ins>+    function BidiSpan(level, from, to) {
+      this.level = level;
+      this.from = from; this.to = to;
+    }
+
</ins><span class="cx">     return function(str) {
</span><span class="cx">       if (!bidiRE.test(str)) return false;
</span><span class="cx">       var len = str.length, types = [];
</span><span class="lines">@@ -5863,7 +7406,7 @@
</span><span class="cx">         if (type == &quot;,&quot;) types[i] = &quot;N&quot;;
</span><span class="cx">         else if (type == &quot;%&quot;) {
</span><span class="cx">           for (var end = i + 1; end &lt; len &amp;&amp; types[end] == &quot;%&quot;; ++end) {}
</span><del>-          var replace = (i &amp;&amp; types[i-1] == &quot;!&quot;) || (end &lt; len - 1 &amp;&amp; types[end] == &quot;1&quot;) ? &quot;1&quot; : &quot;N&quot;;
</del><ins>+          var replace = (i &amp;&amp; types[i-1] == &quot;!&quot;) || (end &lt; len &amp;&amp; types[end] == &quot;1&quot;) ? &quot;1&quot; : &quot;N&quot;;
</ins><span class="cx">           for (var j = i; j &lt; end; ++j) types[j] = replace;
</span><span class="cx">           i = end - 1;
</span><span class="cx">         }
</span><span class="lines">@@ -5888,7 +7431,7 @@
</span><span class="cx">         if (isNeutral.test(types[i])) {
</span><span class="cx">           for (var end = i + 1; end &lt; len &amp;&amp; isNeutral.test(types[end]); ++end) {}
</span><span class="cx">           var before = (i ? types[i-1] : outerType) == &quot;L&quot;;
</span><del>-          var after = (end &lt; len - 1 ? types[end] : outerType) == &quot;L&quot;;
</del><ins>+          var after = (end &lt; len ? types[end] : outerType) == &quot;L&quot;;
</ins><span class="cx">           var replace = before || after ? &quot;L&quot; : &quot;R&quot;;
</span><span class="cx">           for (var j = i; j &lt; end; ++j) types[j] = replace;
</span><span class="cx">           i = end - 1;
</span><span class="lines">@@ -5905,32 +7448,32 @@
</span><span class="cx">         if (countsAsLeft.test(types[i])) {
</span><span class="cx">           var start = i;
</span><span class="cx">           for (++i; i &lt; len &amp;&amp; countsAsLeft.test(types[i]); ++i) {}
</span><del>-          order.push({from: start, to: i, level: 0});
</del><ins>+          order.push(new BidiSpan(0, start, i));
</ins><span class="cx">         } else {
</span><span class="cx">           var pos = i, at = order.length;
</span><span class="cx">           for (++i; i &lt; len &amp;&amp; types[i] != &quot;L&quot;; ++i) {}
</span><span class="cx">           for (var j = pos; j &lt; i;) {
</span><span class="cx">             if (countsAsNum.test(types[j])) {
</span><del>-              if (pos &lt; j) order.splice(at, 0, {from: pos, to: j, level: 1});
</del><ins>+              if (pos &lt; j) order.splice(at, 0, new BidiSpan(1, pos, j));
</ins><span class="cx">               var nstart = j;
</span><span class="cx">               for (++j; j &lt; i &amp;&amp; countsAsNum.test(types[j]); ++j) {}
</span><del>-              order.splice(at, 0, {from: nstart, to: j, level: 2});
</del><ins>+              order.splice(at, 0, new BidiSpan(2, nstart, j));
</ins><span class="cx">               pos = j;
</span><span class="cx">             } else ++j;
</span><span class="cx">           }
</span><del>-          if (pos &lt; i) order.splice(at, 0, {from: pos, to: i, level: 1});
</del><ins>+          if (pos &lt; i) order.splice(at, 0, new BidiSpan(1, pos, i));
</ins><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">       if (order[0].level == 1 &amp;&amp; (m = str.match(/^\s+/))) {
</span><span class="cx">         order[0].from = m[0].length;
</span><del>-        order.unshift({from: 0, to: m[0].length, level: 0});
</del><ins>+        order.unshift(new BidiSpan(0, 0, m[0].length));
</ins><span class="cx">       }
</span><span class="cx">       if (lst(order).level == 1 &amp;&amp; (m = str.match(/\s+$/))) {
</span><span class="cx">         lst(order).to -= m[0].length;
</span><del>-        order.push({from: len - m[0].length, to: len, level: 0});
</del><ins>+        order.push(new BidiSpan(0, len - m[0].length, len));
</ins><span class="cx">       }
</span><span class="cx">       if (order[0].level != lst(order).level)
</span><del>-        order.push({from: len, to: len, level: order[0].level});
</del><ins>+        order.push(new BidiSpan(order[0].level, len, len));
</ins><span class="cx"> 
</span><span class="cx">       return order;
</span><span class="cx">     };
</span><span class="lines">@@ -5938,7 +7481,7 @@
</span><span class="cx"> 
</span><span class="cx">   // THE END
</span><span class="cx"> 
</span><del>-  CodeMirror.version = &quot;3.20.0&quot;;
</del><ins>+  CodeMirror.version = &quot;4.0.4&quot;;
</ins><span class="cx"> 
</span><span class="cx">   return CodeMirror;
</span><del>-})();
</del><ins>+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcoffeescriptjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/coffeescript.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/coffeescript.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/coffeescript.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -2,6 +2,16 @@
</span><span class="cx">  * Link to the project's GitHub page:
</span><span class="cx">  * https://github.com/pickhardt/coffeescript-codemirror-mode
</span><span class="cx">  */
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;coffeescript&quot;, function(conf) {
</span><span class="cx">   var ERRORCLASS = &quot;error&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -119,13 +129,13 @@
</span><span class="cx"> 
</span><span class="cx">     // Handle strings
</span><span class="cx">     if (stream.match(stringPrefixes)) {
</span><del>-      state.tokenize = tokenFactory(stream.current(), &quot;string&quot;);
</del><ins>+      state.tokenize = tokenFactory(stream.current(), false, &quot;string&quot;);
</ins><span class="cx">       return state.tokenize(stream, state);
</span><span class="cx">     }
</span><span class="cx">     // Handle regex literals
</span><span class="cx">     if (stream.match(regexPrefixes)) {
</span><span class="cx">       if (stream.current() != &quot;/&quot; || stream.match(/^.*\//, false)) { // prevent highlight of division
</span><del>-        state.tokenize = tokenFactory(stream.current(), &quot;string-2&quot;);
</del><ins>+        state.tokenize = tokenFactory(stream.current(), true, &quot;string-2&quot;);
</ins><span class="cx">         return state.tokenize(stream, state);
</span><span class="cx">       } else {
</span><span class="cx">         stream.backUp(1);
</span><span class="lines">@@ -161,8 +171,7 @@
</span><span class="cx">     return ERRORCLASS;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function tokenFactory(delimiter, outclass) {
-    var singleline = delimiter.length == 1;
</del><ins>+  function tokenFactory(delimiter, singleline, outclass) {
</ins><span class="cx">     return function(stream, state) {
</span><span class="cx">       while (!stream.eol()) {
</span><span class="cx">         stream.eatWhile(/[^'&quot;\/\\]/);
</span><span class="lines">@@ -352,3 +361,5 @@
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> CodeMirror.defineMIME(&quot;text/x-coffeescript&quot;, &quot;coffeescript&quot;);
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcommentjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/comment.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/comment.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/comment.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,4 +1,11 @@
</span><del>-(function() {
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
</ins><span class="cx">   &quot;use strict&quot;;
</span><span class="cx"> 
</span><span class="cx">   var noOptions = {};
</span><span class="lines">@@ -11,8 +18,21 @@
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   CodeMirror.commands.toggleComment = function(cm) {
</span><del>-    var from = cm.getCursor(&quot;start&quot;), to = cm.getCursor(&quot;end&quot;);
-    cm.uncomment(from, to) || cm.lineComment(from, to);
</del><ins>+    var minLine = Infinity, ranges = cm.listSelections(), mode = null;
+    for (var i = ranges.length - 1; i &gt;= 0; i--) {
+      var from = ranges[i].from(), to = ranges[i].to();
+      if (from.line &gt;= minLine) continue;
+      if (to.line &gt;= minLine) to = Pos(minLine, 0);
+      minLine = from.line;
+      if (mode == null) {
+        if (cm.uncomment(from, to)) mode = &quot;un&quot;;
+        else { cm.lineComment(from, to); mode = &quot;line&quot;; }
+      } else if (mode == &quot;un&quot;) {
+        cm.uncomment(from, to);
+      } else {
+        cm.lineComment(from, to);
+      }
+    }
</ins><span class="cx">   };
</span><span class="cx"> 
</span><span class="cx">   CodeMirror.defineExtension(&quot;lineComment&quot;, function(from, to, options) {
</span><span class="lines">@@ -96,8 +116,9 @@
</span><span class="cx">       for (var i = start; i &lt;= end; ++i) {
</span><span class="cx">         var line = self.getLine(i);
</span><span class="cx">         var found = line.indexOf(lineString);
</span><ins>+        if (found &gt; -1 &amp;&amp; !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
</ins><span class="cx">         if (found == -1 &amp;&amp; (i != end || i == start) &amp;&amp; nonWS.test(line)) break lineComment;
</span><del>-        if (i != start &amp;&amp; found &gt; -1 &amp;&amp; nonWS.test(line.slice(0, found))) break lineComment;
</del><ins>+        if (found &gt; -1 &amp;&amp; nonWS.test(line.slice(0, found))) break lineComment;
</ins><span class="cx">         lines.push(line);
</span><span class="cx">       }
</span><span class="cx">       self.operation(function() {
</span><span class="lines">@@ -124,7 +145,10 @@
</span><span class="cx">       endLine = self.getLine(--end);
</span><span class="cx">       close = endLine.lastIndexOf(endString);
</span><span class="cx">     }
</span><del>-    if (open == -1 || close == -1) return false;
</del><ins>+    if (open == -1 || close == -1 ||
+        !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) ||
+        !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1))))
+      return false;
</ins><span class="cx"> 
</span><span class="cx">     self.operation(function() {
</span><span class="cx">       self.replaceRange(&quot;&quot;, Pos(end, close - (pad &amp;&amp; endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
</span><span class="lines">@@ -142,4 +166,4 @@
</span><span class="cx">     });
</span><span class="cx">     return true;
</span><span class="cx">   });
</span><del>-})();
</del><ins>+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorcssjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/css.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,89 +1,91 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;css&quot;, function(config, parserConfig) {
</span><del>-  &quot;use strict&quot;;
-
</del><span class="cx">   if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode(&quot;text/css&quot;);
</span><span class="cx"> 
</span><del>-  var indentUnit = config.indentUnit || config.tabSize || 2,
-      hooks = parserConfig.hooks || {},
-      atMediaTypes = parserConfig.atMediaTypes || {},
-      atMediaFeatures = parserConfig.atMediaFeatures || {},
</del><ins>+  var indentUnit = config.indentUnit,
+      tokenHooks = parserConfig.tokenHooks,
+      mediaTypes = parserConfig.mediaTypes || {},
+      mediaFeatures = parserConfig.mediaFeatures || {},
</ins><span class="cx">       propertyKeywords = parserConfig.propertyKeywords || {},
</span><ins>+      nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
</ins><span class="cx">       colorKeywords = parserConfig.colorKeywords || {},
</span><span class="cx">       valueKeywords = parserConfig.valueKeywords || {},
</span><del>-      allowNested = !!parserConfig.allowNested,
-      type = null;
</del><ins>+      fontProperties = parserConfig.fontProperties || {},
+      allowNested = parserConfig.allowNested;
</ins><span class="cx"> 
</span><ins>+  var type, override;
</ins><span class="cx">   function ret(style, tp) { type = tp; return style; }
</span><span class="cx"> 
</span><ins>+  // Tokenizers
+
</ins><span class="cx">   function tokenBase(stream, state) {
</span><span class="cx">     var ch = stream.next();
</span><del>-    if (hooks[ch]) {
-      // result[0] is style and result[1] is type
-      var result = hooks[ch](stream, state);
</del><ins>+    if (tokenHooks[ch]) {
+      var result = tokenHooks[ch](stream, state);
</ins><span class="cx">       if (result !== false) return result;
</span><span class="cx">     }
</span><del>-    if (ch == &quot;@&quot;) {stream.eatWhile(/[\w\\\-]/); return ret(&quot;def&quot;, stream.current());}
-    else if (ch == &quot;=&quot;) ret(null, &quot;compare&quot;);
-    else if ((ch == &quot;~&quot; || ch == &quot;|&quot;) &amp;&amp; stream.eat(&quot;=&quot;)) return ret(null, &quot;compare&quot;);
-    else if (ch == &quot;\&quot;&quot; || ch == &quot;'&quot;) {
</del><ins>+    if (ch == &quot;@&quot;) {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret(&quot;def&quot;, stream.current());
+    } else if (ch == &quot;=&quot; || (ch == &quot;~&quot; || ch == &quot;|&quot;) &amp;&amp; stream.eat(&quot;=&quot;)) {
+      return ret(null, &quot;compare&quot;);
+    } else if (ch == &quot;\&quot;&quot; || ch == &quot;'&quot;) {
</ins><span class="cx">       state.tokenize = tokenString(ch);
</span><span class="cx">       return state.tokenize(stream, state);
</span><del>-    }
-    else if (ch == &quot;#&quot;) {
</del><ins>+    } else if (ch == &quot;#&quot;) {
</ins><span class="cx">       stream.eatWhile(/[\w\\\-]/);
</span><span class="cx">       return ret(&quot;atom&quot;, &quot;hash&quot;);
</span><del>-    }
-    else if (ch == &quot;!&quot;) {
</del><ins>+    } else if (ch == &quot;!&quot;) {
</ins><span class="cx">       stream.match(/^\s*\w*/);
</span><span class="cx">       return ret(&quot;keyword&quot;, &quot;important&quot;);
</span><del>-    }
-    else if (/\d/.test(ch) || ch == &quot;.&quot; &amp;&amp; stream.eat(/\d/)) {
</del><ins>+    } else if (/\d/.test(ch) || ch == &quot;.&quot; &amp;&amp; stream.eat(/\d/)) {
</ins><span class="cx">       stream.eatWhile(/[\w.%]/);
</span><span class="cx">       return ret(&quot;number&quot;, &quot;unit&quot;);
</span><del>-    }
-    else if (ch === &quot;-&quot;) {
-      if (/\d/.test(stream.peek())) {
</del><ins>+    } else if (ch === &quot;-&quot;) {
+      if (/[\d.]/.test(stream.peek())) {
</ins><span class="cx">         stream.eatWhile(/[\w.%]/);
</span><span class="cx">         return ret(&quot;number&quot;, &quot;unit&quot;);
</span><span class="cx">       } else if (stream.match(/^[^-]+-/)) {
</span><span class="cx">         return ret(&quot;meta&quot;, &quot;meta&quot;);
</span><span class="cx">       }
</span><del>-    }
-    else if (/[,+&gt;*\/]/.test(ch)) {
</del><ins>+    } else if (/[,+&gt;*\/]/.test(ch)) {
</ins><span class="cx">       return ret(null, &quot;select-op&quot;);
</span><del>-    }
-    else if (ch == &quot;.&quot; &amp;&amp; stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
</del><ins>+    } else if (ch == &quot;.&quot; &amp;&amp; stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
</ins><span class="cx">       return ret(&quot;qualifier&quot;, &quot;qualifier&quot;);
</span><del>-    }
-    else if (ch == &quot;:&quot;) {
-      return ret(&quot;operator&quot;, ch);
-    }
-    else if (/[;{}\[\]\(\)]/.test(ch)) {
</del><ins>+    } else if (/[:;{}\[\]\(\)]/.test(ch)) {
</ins><span class="cx">       return ret(null, ch);
</span><del>-    }
-    else if (ch == &quot;u&quot; &amp;&amp; stream.match(&quot;rl(&quot;)) {
</del><ins>+    } else if (ch == &quot;u&quot; &amp;&amp; stream.match(&quot;rl(&quot;)) {
</ins><span class="cx">       stream.backUp(1);
</span><span class="cx">       state.tokenize = tokenParenthesized;
</span><del>-      return ret(&quot;property&quot;, &quot;variable&quot;);
-    }
-    else {
</del><ins>+      return ret(&quot;property&quot;, &quot;word&quot;);
+    } else if (/[\w\\\-]/.test(ch)) {
</ins><span class="cx">       stream.eatWhile(/[\w\\\-]/);
</span><del>-      return ret(&quot;property&quot;, &quot;variable&quot;);
</del><ins>+      return ret(&quot;property&quot;, &quot;word&quot;);
+    } else {
+      return ret(null, null);
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function tokenString(quote, nonInclusive) {
</del><ins>+  function tokenString(quote) {
</ins><span class="cx">     return function(stream, state) {
</span><span class="cx">       var escaped = false, ch;
</span><span class="cx">       while ((ch = stream.next()) != null) {
</span><del>-        if (ch == quote &amp;&amp; !escaped)
</del><ins>+        if (ch == quote &amp;&amp; !escaped) {
+          if (quote == &quot;)&quot;) stream.backUp(1);
</ins><span class="cx">           break;
</span><ins>+        }
</ins><span class="cx">         escaped = !escaped &amp;&amp; ch == &quot;\\&quot;;
</span><span class="cx">       }
</span><del>-      if (!escaped) {
-        if (nonInclusive) stream.backUp(1);
-        state.tokenize = tokenBase;
-      }
</del><ins>+      if (ch == quote || !escaped &amp;&amp; quote != &quot;)&quot;) state.tokenize = null;
</ins><span class="cx">       return ret(&quot;string&quot;, &quot;string&quot;);
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="lines">@@ -91,218 +93,251 @@
</span><span class="cx">   function tokenParenthesized(stream, state) {
</span><span class="cx">     stream.next(); // Must be '('
</span><span class="cx">     if (!stream.match(/\s*[\&quot;\']/, false))
</span><del>-      state.tokenize = tokenString(&quot;)&quot;, true);
</del><ins>+      state.tokenize = tokenString(&quot;)&quot;);
</ins><span class="cx">     else
</span><del>-      state.tokenize = tokenBase;
</del><ins>+      state.tokenize = null;
</ins><span class="cx">     return ret(null, &quot;(&quot;);
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  return {
-    startState: function(base) {
-      return {tokenize: tokenBase,
-              baseIndent: base || 0,
-              stack: [],
-              lastToken: null};
-    },
</del><ins>+  // Context management
</ins><span class="cx"> 
</span><del>-    token: function(stream, state) {
</del><ins>+  function Context(type, indent, prev) {
+    this.type = type;
+    this.indent = indent;
+    this.prev = prev;
+  }
</ins><span class="cx"> 
</span><del>-      // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
-      //
-      // rule** or **ruleset:
-      // A selector + braces combo, or an at-rule.
-      //
-      // declaration block:
-      // A sequence of declarations.
-      //
-      // declaration:
-      // A property + colon + value combo.
-      //
-      // property value:
-      // The entire value of a property.
-      //
-      // component value:
-      // A single piece of a property value. Like the 5px in
-      // text-shadow: 0 0 5px blue;. Can also refer to things that are
-      // multiple terms, like the 1-4 terms that make up the background-size
-      // portion of the background shorthand.
-      //
-      // term:
-      // The basic unit of author-facing CSS, like a single number (5),
-      // dimension (5px), string (&quot;foo&quot;), or function. Officially defined
-      //  by the CSS 2.1 grammar (look for the 'term' production)
-      //
-      //
-      // simple selector:
-      // A single atomic selector, like a type selector, an attr selector, a
-      // class selector, etc.
-      //
-      // compound selector:
-      // One or more simple selectors without a combinator. div.example is
-      // compound, div &gt; .example is not.
-      //
-      // complex selector:
-      // One or more compound selectors chained with combinators.
-      //
-      // combinator:
-      // The parts of selectors that express relationships. There are four
-      // currently - the space (descendant combinator), the greater-than
-      // bracket (child combinator), the plus sign (next sibling combinator),
-      // and the tilda (following sibling combinator).
-      //
-      // sequence of selectors:
-      // One or more of the named type of selector chained with commas.
</del><ins>+  function pushContext(state, stream, type) {
+    state.context = new Context(type, stream.indentation() + indentUnit, state.context);
+    return type;
+  }
</ins><span class="cx"> 
</span><del>-      state.tokenize = state.tokenize || tokenBase;
-      if (state.tokenize == tokenBase &amp;&amp; stream.eatSpace()) return null;
-      var style = state.tokenize(stream, state);
-      if (style &amp;&amp; typeof style != &quot;string&quot;) style = ret(style[0], style[1]);
</del><ins>+  function popContext(state) {
+    state.context = state.context.prev;
+    return state.context.type;
+  }
</ins><span class="cx"> 
</span><del>-      // Changing style returned based on context
-      var context = state.stack[state.stack.length-1];
-      if (style == &quot;variable&quot;) {
-        if (type == &quot;variable-definition&quot;) state.stack.push(&quot;propertyValue&quot;);
-        return state.lastToken = &quot;variable-2&quot;;
-      } else if (style == &quot;property&quot;) {
-        var word = stream.current().toLowerCase();
-        if (context == &quot;propertyValue&quot;) {
-          if (valueKeywords.hasOwnProperty(word)) {
-            style = &quot;string-2&quot;;
-          } else if (colorKeywords.hasOwnProperty(word)) {
-            style = &quot;keyword&quot;;
-          } else {
-            style = &quot;variable-2&quot;;
-          }
-        } else if (context == &quot;rule&quot;) {
-          if (!propertyKeywords.hasOwnProperty(word)) {
-            style += &quot; error&quot;;
-          }
-        } else if (context == &quot;block&quot;) {
-          // if a value is present in both property, value, or color, the order
-          // of preference is property -&gt; color -&gt; value
-          if (propertyKeywords.hasOwnProperty(word)) {
-            style = &quot;property&quot;;
-          } else if (colorKeywords.hasOwnProperty(word)) {
-            style = &quot;keyword&quot;;
-          } else if (valueKeywords.hasOwnProperty(word)) {
-            style = &quot;string-2&quot;;
-          } else {
-            style = &quot;tag&quot;;
-          }
-        } else if (!context || context == &quot;@media{&quot;) {
-          style = &quot;tag&quot;;
-        } else if (context == &quot;@media&quot;) {
-          if (atMediaTypes[stream.current()]) {
-            style = &quot;attribute&quot;; // Known attribute
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;keyword&quot;;
-          } else if (word == &quot;and&quot;) {
-            style = &quot;error&quot;; // &quot;and&quot; is only allowed in @mediaType
-          } else if (atMediaFeatures.hasOwnProperty(word)) {
-            style = &quot;error&quot;; // Known property, should be in @mediaType(
-          } else {
-            // Unknown, expecting keyword or attribute, assuming attribute
-            style = &quot;attribute error&quot;;
-          }
-        } else if (context == &quot;@mediaType&quot;) {
-          if (atMediaTypes.hasOwnProperty(word)) {
-            style = &quot;attribute&quot;;
-          } else if (word == &quot;and&quot;) {
-            style = &quot;operator&quot;;
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;error&quot;; // Only allowed in @media
-          } else {
-            // Unknown attribute or property, but expecting property (preceded
-            // by &quot;and&quot;). Should be in parentheses
-            style = &quot;error&quot;;
-          }
-        } else if (context == &quot;@mediaType(&quot;) {
-          if (propertyKeywords.hasOwnProperty(word)) {
-            // do nothing, remains &quot;property&quot;
-          } else if (atMediaTypes.hasOwnProperty(word)) {
-            style = &quot;error&quot;; // Known property, should be in parentheses
-          } else if (word == &quot;and&quot;) {
-            style = &quot;operator&quot;;
-          } else if (/^(only|not)$/.test(word)) {
-            style = &quot;error&quot;; // Only allowed in @media
-          } else {
-            style += &quot; error&quot;;
-          }
-        } else if (context == &quot;@import&quot;) {
-          style = &quot;tag&quot;;
-        } else {
-          style = &quot;error&quot;;
-        }
-      } else if (style == &quot;atom&quot;) {
-        if(!context || context == &quot;@media{&quot; || context == &quot;block&quot;) {
-          style = &quot;builtin&quot;;
-        } else if (context == &quot;propertyValue&quot;) {
-          if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
-            style += &quot; error&quot;;
-          }
-        } else {
-          style = &quot;error&quot;;
-        }
-      } else if (context == &quot;@media&quot; &amp;&amp; type == &quot;{&quot;) {
-        style = &quot;error&quot;;
-      }
</del><ins>+  function pass(type, stream, state) {
+    return states[state.context.type](type, stream, state);
+  }
+  function popAndPass(type, stream, state, n) {
+    for (var i = n || 1; i &gt; 0; i--)
+      state.context = state.context.prev;
+    return pass(type, stream, state);
+  }
</ins><span class="cx"> 
</span><del>-      // Push/pop context stack
-      if (type == &quot;{&quot;) {
-        if (context == &quot;@media&quot; || context == &quot;@mediaType&quot;) {
-          state.stack[state.stack.length-1] = &quot;@media{&quot;;
-        }
-        else {
-          var newContext = allowNested ? &quot;block&quot; : &quot;rule&quot;;
-          state.stack.push(newContext);
-        }
</del><ins>+  // Parser
+
+  function wordAsValue(stream) {
+    var word = stream.current().toLowerCase();
+    if (valueKeywords.hasOwnProperty(word))
+      override = &quot;atom&quot;;
+    else if (colorKeywords.hasOwnProperty(word))
+      override = &quot;keyword&quot;;
+    else
+      override = &quot;variable&quot;;
+  }
+
+  var states = {};
+
+  states.top = function(type, stream, state) {
+    if (type == &quot;{&quot;) {
+      return pushContext(state, stream, &quot;block&quot;);
+    } else if (type == &quot;}&quot; &amp;&amp; state.context.prev) {
+      return popContext(state);
+    } else if (type == &quot;@media&quot;) {
+      return pushContext(state, stream, &quot;media&quot;);
+    } else if (type == &quot;@font-face&quot;) {
+      return &quot;font_face_before&quot;;
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+      return &quot;keyframes&quot;;
+    } else if (type &amp;&amp; type.charAt(0) == &quot;@&quot;) {
+      return pushContext(state, stream, &quot;at&quot;);
+    } else if (type == &quot;hash&quot;) {
+      override = &quot;builtin&quot;;
+    } else if (type == &quot;word&quot;) {
+      override = &quot;tag&quot;;
+    } else if (type == &quot;variable-definition&quot;) {
+      return &quot;maybeprop&quot;;
+    } else if (type == &quot;interpolation&quot;) {
+      return pushContext(state, stream, &quot;interpolation&quot;);
+    } else if (type == &quot;:&quot;) {
+      return &quot;pseudo&quot;;
+    } else if (allowNested &amp;&amp; type == &quot;(&quot;) {
+      return pushContext(state, stream, &quot;params&quot;);
+    }
+    return state.context.type;
+  };
+
+  states.block = function(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      var word = stream.current().toLowerCase();
+      if (propertyKeywords.hasOwnProperty(word)) {
+        override = &quot;property&quot;;
+        return &quot;maybeprop&quot;;
+      } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+        override = &quot;string-2&quot;;
+        return &quot;maybeprop&quot;;
+      } else if (allowNested) {
+        override = stream.match(/^\s*:/, false) ? &quot;property&quot; : &quot;tag&quot;;
+        return &quot;block&quot;;
+      } else {
+        override += &quot; error&quot;;
+        return &quot;maybeprop&quot;;
</ins><span class="cx">       }
</span><del>-      else if (type == &quot;}&quot;) {
-        if (context == &quot;interpolation&quot;) style = &quot;operator&quot;;
-        // Pop off end of array until { is reached
-        while(state.stack.length){
-          var removed = state.stack.pop();
-          if(removed.indexOf(&quot;{&quot;) &gt; -1 || removed == &quot;block&quot; || removed == &quot;rule&quot;){
-            break;
-          }
-        }
-      }
-      else if (type == &quot;interpolation&quot;) state.stack.push(&quot;interpolation&quot;);
-      else if (type == &quot;@media&quot;) state.stack.push(&quot;@media&quot;);
-      else if (type == &quot;@import&quot;) state.stack.push(&quot;@import&quot;);
-      else if (context == &quot;@media&quot; &amp;&amp; /\b(keyword|attribute)\b/.test(style))
-        state.stack[state.stack.length-1] = &quot;@mediaType&quot;;
-      else if (context == &quot;@mediaType&quot; &amp;&amp; stream.current() == &quot;,&quot;)
-        state.stack[state.stack.length-1] = &quot;@media&quot;;
-      else if (type == &quot;(&quot;) {
-        if (context == &quot;@media&quot; || context == &quot;@mediaType&quot;) {
-          // Make sure @mediaType is used to avoid error on {
-          state.stack[state.stack.length-1] = &quot;@mediaType&quot;;
-          state.stack.push(&quot;@mediaType(&quot;);
-        }
-        else state.stack.push(&quot;(&quot;);
-      }
-      else if (type == &quot;)&quot;) {
-        // Pop off end of array until ( is reached
-        while(state.stack.length){
-          var removed = state.stack.pop();
-          if(removed.indexOf(&quot;(&quot;) &gt; -1){
-            break;
-          }
-        }
-      }
-      else if (type == &quot;:&quot; &amp;&amp; state.lastToken == &quot;property&quot;) state.stack.push(&quot;propertyValue&quot;);
-      else if (context == &quot;propertyValue&quot; &amp;&amp; type == &quot;;&quot;) state.stack.pop();
-      else if (context == &quot;@import&quot; &amp;&amp; type == &quot;;&quot;) state.stack.pop();
</del><ins>+    } else if (type == &quot;meta&quot;) {
+      return &quot;block&quot;;
+    } else if (!allowNested &amp;&amp; (type == &quot;hash&quot; || type == &quot;qualifier&quot;)) {
+      override = &quot;error&quot;;
+      return &quot;block&quot;;
+    } else {
+      return states.top(type, stream, state);
+    }
+  };
</ins><span class="cx"> 
</span><del>-      return state.lastToken = style;
</del><ins>+  states.maybeprop = function(type, stream, state) {
+    if (type == &quot;:&quot;) return pushContext(state, stream, &quot;prop&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.prop = function(type, stream, state) {
+    if (type == &quot;;&quot;) return popContext(state);
+    if (type == &quot;{&quot; &amp;&amp; allowNested) return pushContext(state, stream, &quot;propBlock&quot;);
+    if (type == &quot;}&quot; || type == &quot;{&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;(&quot;) return pushContext(state, stream, &quot;parens&quot;);
+
+    if (type == &quot;hash&quot; &amp;&amp; !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
+      override += &quot; error&quot;;
+    } else if (type == &quot;word&quot;) {
+      wordAsValue(stream);
+    } else if (type == &quot;interpolation&quot;) {
+      return pushContext(state, stream, &quot;interpolation&quot;);
+    }
+    return &quot;prop&quot;;
+  };
+
+  states.propBlock = function(type, _stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;word&quot;) { override = &quot;property&quot;; return &quot;maybeprop&quot;; }
+    return state.context.type;
+  };
+
+  states.parens = function(type, stream, state) {
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;)&quot;) return popContext(state);
+    return &quot;parens&quot;;
+  };
+
+  states.pseudo = function(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      override = &quot;variable-3&quot;;
+      return state.context.type;
+    }
+    return pass(type, stream, state);
+  };
+
+  states.media = function(type, stream, state) {
+    if (type == &quot;(&quot;) return pushContext(state, stream, &quot;media_parens&quot;);
+    if (type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;{&quot;) return popContext(state) &amp;&amp; pushContext(state, stream, allowNested ? &quot;block&quot; : &quot;top&quot;);
+
+    if (type == &quot;word&quot;) {
+      var word = stream.current().toLowerCase();
+      if (word == &quot;only&quot; || word == &quot;not&quot; || word == &quot;and&quot;)
+        override = &quot;keyword&quot;;
+      else if (mediaTypes.hasOwnProperty(word))
+        override = &quot;attribute&quot;;
+      else if (mediaFeatures.hasOwnProperty(word))
+        override = &quot;property&quot;;
+      else
+        override = &quot;error&quot;;
+    }
+    return state.context.type;
+  };
+
+  states.media_parens = function(type, stream, state) {
+    if (type == &quot;)&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state, 2);
+    return states.media(type, stream, state);
+  };
+
+  states.font_face_before = function(type, stream, state) {
+    if (type == &quot;{&quot;)
+      return pushContext(state, stream, &quot;font_face&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.font_face = function(type, stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;word&quot;) {
+      if (!fontProperties.hasOwnProperty(stream.current().toLowerCase()))
+        override = &quot;error&quot;;
+      else
+        override = &quot;property&quot;;
+      return &quot;maybeprop&quot;;
+    }
+    return &quot;font_face&quot;;
+  };
+
+  states.keyframes = function(type, stream, state) {
+    if (type == &quot;word&quot;) { override = &quot;variable&quot;; return &quot;keyframes&quot;; }
+    if (type == &quot;{&quot;) return pushContext(state, stream, &quot;top&quot;);
+    return pass(type, stream, state);
+  };
+
+  states.at = function(type, stream, state) {
+    if (type == &quot;;&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;word&quot;) override = &quot;tag&quot;;
+    else if (type == &quot;hash&quot;) override = &quot;builtin&quot;;
+    return &quot;at&quot;;
+  };
+
+  states.interpolation = function(type, stream, state) {
+    if (type == &quot;}&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;;&quot;) return popAndPass(type, stream, state);
+    if (type != &quot;variable&quot;) override = &quot;error&quot;;
+    return &quot;interpolation&quot;;
+  };
+
+  states.params = function(type, stream, state) {
+    if (type == &quot;)&quot;) return popContext(state);
+    if (type == &quot;{&quot; || type == &quot;}&quot;) return popAndPass(type, stream, state);
+    if (type == &quot;word&quot;) wordAsValue(stream);
+    return &quot;params&quot;;
+  };
+
+  return {
+    startState: function(base) {
+      return {tokenize: null,
+              state: &quot;top&quot;,
+              context: new Context(&quot;top&quot;, base || 0, null)};
</ins><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    token: function(stream, state) {
+      if (!state.tokenize &amp;&amp; stream.eatSpace()) return null;
+      var style = (state.tokenize || tokenBase)(stream, state);
+      if (style &amp;&amp; typeof style == &quot;object&quot;) {
+        type = style[1];
+        style = style[0];
+      }
+      override = style;
+      state.state = states[state.state](type, stream, state);
+      return override;
+    },
+
</ins><span class="cx">     indent: function(state, textAfter) {
</span><del>-      var n = state.stack.length;
-      if (/^\}/.test(textAfter))
-        n -= state.stack[n-1] == &quot;propertyValue&quot; ? 2 : 1;
-      return state.baseIndent + n * indentUnit;
</del><ins>+      var cx = state.context, ch = textAfter &amp;&amp; textAfter.charAt(0);
+      var indent = cx.indent;
+      if (cx.type == &quot;prop&quot; &amp;&amp; ch == &quot;}&quot;) cx = cx.prev;
+      if (cx.prev &amp;&amp;
+          (ch == &quot;}&quot; &amp;&amp; (cx.type == &quot;block&quot; || cx.type == &quot;top&quot; || cx.type == &quot;interpolation&quot; || cx.type == &quot;font_face&quot;) ||
+           ch == &quot;)&quot; &amp;&amp; (cx.type == &quot;parens&quot; || cx.type == &quot;params&quot; || cx.type == &quot;media_parens&quot;) ||
+           ch == &quot;{&quot; &amp;&amp; (cx.type == &quot;at&quot; || cx.type == &quot;media&quot;))) {
+        indent = cx.indent - indentUnit;
+        cx = cx.prev;
+      }
+      return indent;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     electricChars: &quot;}&quot;,
</span><span class="lines">@@ -312,7 +347,6 @@
</span><span class="cx">   };
</span><span class="cx"> });
</span><span class="cx"> 
</span><del>-(function() {
</del><span class="cx">   function keySet(array) {
</span><span class="cx">     var keys = {};
</span><span class="cx">     for (var i = 0; i &lt; array.length; ++i) {
</span><span class="lines">@@ -321,12 +355,12 @@
</span><span class="cx">     return keys;
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var atMediaTypes = keySet([
</del><ins>+  var mediaTypes_ = [
</ins><span class="cx">     &quot;all&quot;, &quot;aural&quot;, &quot;braille&quot;, &quot;handheld&quot;, &quot;print&quot;, &quot;projection&quot;, &quot;screen&quot;,
</span><span class="cx">     &quot;tty&quot;, &quot;tv&quot;, &quot;embossed&quot;
</span><del>-  ]);
</del><ins>+  ], mediaTypes = keySet(mediaTypes_);
</ins><span class="cx"> 
</span><del>-  var atMediaFeatures = keySet([
</del><ins>+  var mediaFeatures_ = [
</ins><span class="cx">     &quot;width&quot;, &quot;min-width&quot;, &quot;max-width&quot;, &quot;height&quot;, &quot;min-height&quot;, &quot;max-height&quot;,
</span><span class="cx">     &quot;device-width&quot;, &quot;min-device-width&quot;, &quot;max-device-width&quot;, &quot;device-height&quot;,
</span><span class="cx">     &quot;min-device-height&quot;, &quot;max-device-height&quot;, &quot;aspect-ratio&quot;,
</span><span class="lines">@@ -335,15 +369,15 @@
</span><span class="cx">     &quot;max-color&quot;, &quot;color-index&quot;, &quot;min-color-index&quot;, &quot;max-color-index&quot;,
</span><span class="cx">     &quot;monochrome&quot;, &quot;min-monochrome&quot;, &quot;max-monochrome&quot;, &quot;resolution&quot;,
</span><span class="cx">     &quot;min-resolution&quot;, &quot;max-resolution&quot;, &quot;scan&quot;, &quot;grid&quot;
</span><del>-  ]);
</del><ins>+  ], mediaFeatures = keySet(mediaFeatures_);
</ins><span class="cx"> 
</span><del>-  var propertyKeywords = keySet([
</del><ins>+  var propertyKeywords_ = [
</ins><span class="cx">     &quot;align-content&quot;, &quot;align-items&quot;, &quot;align-self&quot;, &quot;alignment-adjust&quot;,
</span><span class="cx">     &quot;alignment-baseline&quot;, &quot;anchor-point&quot;, &quot;animation&quot;, &quot;animation-delay&quot;,
</span><del>-    &quot;animation-direction&quot;, &quot;animation-duration&quot;, &quot;animation-iteration-count&quot;,
-    &quot;animation-name&quot;, &quot;animation-play-state&quot;, &quot;animation-timing-function&quot;,
-    &quot;appearance&quot;, &quot;azimuth&quot;, &quot;backface-visibility&quot;, &quot;background&quot;,
-    &quot;background-attachment&quot;, &quot;background-clip&quot;, &quot;background-color&quot;,
</del><ins>+    &quot;animation-direction&quot;, &quot;animation-duration&quot;, &quot;animation-fill-mode&quot;,
+    &quot;animation-iteration-count&quot;, &quot;animation-name&quot;, &quot;animation-play-state&quot;,
+    &quot;animation-timing-function&quot;, &quot;appearance&quot;, &quot;azimuth&quot;, &quot;backface-visibility&quot;,
+    &quot;background&quot;, &quot;background-attachment&quot;, &quot;background-clip&quot;, &quot;background-color&quot;,
</ins><span class="cx">     &quot;background-image&quot;, &quot;background-origin&quot;, &quot;background-position&quot;,
</span><span class="cx">     &quot;background-repeat&quot;, &quot;background-size&quot;, &quot;baseline-shift&quot;, &quot;binding&quot;,
</span><span class="cx">     &quot;bleed&quot;, &quot;bookmark-label&quot;, &quot;bookmark-level&quot;, &quot;bookmark-state&quot;,
</span><span class="lines">@@ -374,10 +408,11 @@
</span><span class="cx">     &quot;font-stretch&quot;, &quot;font-style&quot;, &quot;font-synthesis&quot;, &quot;font-variant&quot;,
</span><span class="cx">     &quot;font-variant-alternates&quot;, &quot;font-variant-caps&quot;, &quot;font-variant-east-asian&quot;,
</span><span class="cx">     &quot;font-variant-ligatures&quot;, &quot;font-variant-numeric&quot;, &quot;font-variant-position&quot;,
</span><del>-    &quot;font-weight&quot;, &quot;grid-cell&quot;, &quot;grid-column&quot;, &quot;grid-column-align&quot;,
-    &quot;grid-column-sizing&quot;, &quot;grid-column-span&quot;, &quot;grid-columns&quot;, &quot;grid-flow&quot;,
-    &quot;grid-row&quot;, &quot;grid-row-align&quot;, &quot;grid-row-sizing&quot;, &quot;grid-row-span&quot;,
-    &quot;grid-rows&quot;, &quot;grid-template-areas&quot;, &quot;hanging-punctuation&quot;, &quot;height&quot;, &quot;hyphens&quot;,
</del><ins>+    &quot;font-weight&quot;, &quot;grid&quot;, &quot;grid-area&quot;, &quot;grid-auto-columns&quot;, &quot;grid-auto-flow&quot;,
+    &quot;grid-auto-position&quot;, &quot;grid-auto-rows&quot;, &quot;grid-column&quot;, &quot;grid-column-end&quot;,
+    &quot;grid-column-start&quot;, &quot;grid-row&quot;, &quot;grid-row-end&quot;, &quot;grid-row-start&quot;,
+    &quot;grid-template&quot;, &quot;grid-template-areas&quot;, &quot;grid-template-columns&quot;,
+    &quot;grid-template-rows&quot;, &quot;hanging-punctuation&quot;, &quot;height&quot;, &quot;hyphens&quot;,
</ins><span class="cx">     &quot;icon&quot;, &quot;image-orientation&quot;, &quot;image-rendering&quot;, &quot;image-resolution&quot;,
</span><span class="cx">     &quot;inline-box-align&quot;, &quot;justify-content&quot;, &quot;left&quot;, &quot;letter-spacing&quot;,
</span><span class="cx">     &quot;line-break&quot;, &quot;line-height&quot;, &quot;line-stacking&quot;, &quot;line-stacking-ruby&quot;,
</span><span class="lines">@@ -414,7 +449,7 @@
</span><span class="cx">     &quot;vertical-align&quot;, &quot;visibility&quot;, &quot;voice-balance&quot;, &quot;voice-duration&quot;,
</span><span class="cx">     &quot;voice-family&quot;, &quot;voice-pitch&quot;, &quot;voice-range&quot;, &quot;voice-rate&quot;, &quot;voice-stress&quot;,
</span><span class="cx">     &quot;voice-volume&quot;, &quot;volume&quot;, &quot;white-space&quot;, &quot;widows&quot;, &quot;width&quot;, &quot;word-break&quot;,
</span><del>-    &quot;word-spacing&quot;, &quot;word-wrap&quot;, &quot;z-index&quot;, &quot;zoom&quot;,
</del><ins>+    &quot;word-spacing&quot;, &quot;word-wrap&quot;, &quot;z-index&quot;,
</ins><span class="cx">     // SVG-specific
</span><span class="cx">     &quot;clip-path&quot;, &quot;clip-rule&quot;, &quot;mask&quot;, &quot;enable-background&quot;, &quot;filter&quot;, &quot;flood-color&quot;,
</span><span class="cx">     &quot;flood-opacity&quot;, &quot;lighting-color&quot;, &quot;stop-color&quot;, &quot;stop-opacity&quot;, &quot;pointer-events&quot;,
</span><span class="lines">@@ -425,9 +460,17 @@
</span><span class="cx">     &quot;stroke-miterlimit&quot;, &quot;stroke-opacity&quot;, &quot;stroke-width&quot;, &quot;text-rendering&quot;,
</span><span class="cx">     &quot;baseline-shift&quot;, &quot;dominant-baseline&quot;, &quot;glyph-orientation-horizontal&quot;,
</span><span class="cx">     &quot;glyph-orientation-vertical&quot;, &quot;kerning&quot;, &quot;text-anchor&quot;, &quot;writing-mode&quot;
</span><del>-  ]);
</del><ins>+  ], propertyKeywords = keySet(propertyKeywords_);
</ins><span class="cx"> 
</span><del>-  var colorKeywords = keySet([
</del><ins>+  var nonStandardPropertyKeywords = [
+    &quot;scrollbar-arrow-color&quot;, &quot;scrollbar-base-color&quot;, &quot;scrollbar-dark-shadow-color&quot;,
+    &quot;scrollbar-face-color&quot;, &quot;scrollbar-highlight-color&quot;, &quot;scrollbar-shadow-color&quot;,
+    &quot;scrollbar-3d-light-color&quot;, &quot;scrollbar-track-color&quot;, &quot;shape-inside&quot;,
+    &quot;searchfield-cancel-button&quot;, &quot;searchfield-decoration&quot;, &quot;searchfield-results-button&quot;,
+    &quot;searchfield-results-decoration&quot;, &quot;zoom&quot;
+  ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords);
+
+  var colorKeywords_ = [
</ins><span class="cx">     &quot;aliceblue&quot;, &quot;antiquewhite&quot;, &quot;aqua&quot;, &quot;aquamarine&quot;, &quot;azure&quot;, &quot;beige&quot;,
</span><span class="cx">     &quot;bisque&quot;, &quot;black&quot;, &quot;blanchedalmond&quot;, &quot;blue&quot;, &quot;blueviolet&quot;, &quot;brown&quot;,
</span><span class="cx">     &quot;burlywood&quot;, &quot;cadetblue&quot;, &quot;chartreuse&quot;, &quot;chocolate&quot;, &quot;coral&quot;, &quot;cornflowerblue&quot;,
</span><span class="lines">@@ -454,9 +497,9 @@
</span><span class="cx">     &quot;slateblue&quot;, &quot;slategray&quot;, &quot;snow&quot;, &quot;springgreen&quot;, &quot;steelblue&quot;, &quot;tan&quot;,
</span><span class="cx">     &quot;teal&quot;, &quot;thistle&quot;, &quot;tomato&quot;, &quot;turquoise&quot;, &quot;violet&quot;, &quot;wheat&quot;, &quot;white&quot;,
</span><span class="cx">     &quot;whitesmoke&quot;, &quot;yellow&quot;, &quot;yellowgreen&quot;
</span><del>-  ]);
</del><ins>+  ], colorKeywords = keySet(colorKeywords_);
</ins><span class="cx"> 
</span><del>-  var valueKeywords = keySet([
</del><ins>+  var valueKeywords_ = [
</ins><span class="cx">     &quot;above&quot;, &quot;absolute&quot;, &quot;activeborder&quot;, &quot;activecaption&quot;, &quot;afar&quot;,
</span><span class="cx">     &quot;after-white-space&quot;, &quot;ahead&quot;, &quot;alias&quot;, &quot;all&quot;, &quot;all-scroll&quot;, &quot;alternate&quot;,
</span><span class="cx">     &quot;always&quot;, &quot;amharic&quot;, &quot;amharic-abegede&quot;, &quot;antialiased&quot;, &quot;appworkspace&quot;,
</span><span class="lines">@@ -515,7 +558,7 @@
</span><span class="cx">     &quot;polygon&quot;, &quot;portrait&quot;, &quot;pre&quot;, &quot;pre-line&quot;, &quot;pre-wrap&quot;, &quot;preserve-3d&quot;, &quot;progress&quot;, &quot;push-button&quot;,
</span><span class="cx">     &quot;radio&quot;, &quot;read-only&quot;, &quot;read-write&quot;, &quot;read-write-plaintext-only&quot;, &quot;rectangle&quot;, &quot;region&quot;,
</span><span class="cx">     &quot;relative&quot;, &quot;repeat&quot;, &quot;repeat-x&quot;, &quot;repeat-y&quot;, &quot;reset&quot;, &quot;reverse&quot;, &quot;rgb&quot;, &quot;rgba&quot;,
</span><del>-    &quot;ridge&quot;, &quot;right&quot;, &quot;round&quot;, &quot;row-resize&quot;, &quot;rtl&quot;, &quot;running&quot;,
</del><ins>+    &quot;ridge&quot;, &quot;right&quot;, &quot;round&quot;, &quot;row-resize&quot;, &quot;rtl&quot;, &quot;run-in&quot;, &quot;running&quot;,
</ins><span class="cx">     &quot;s-resize&quot;, &quot;sans-serif&quot;, &quot;scroll&quot;, &quot;scrollbar&quot;, &quot;se-resize&quot;, &quot;searchfield&quot;,
</span><span class="cx">     &quot;searchfield-cancel-button&quot;, &quot;searchfield-decoration&quot;,
</span><span class="cx">     &quot;searchfield-results-button&quot;, &quot;searchfield-results-decoration&quot;,
</span><span class="lines">@@ -539,8 +582,17 @@
</span><span class="cx">     &quot;visibleStroke&quot;, &quot;visual&quot;, &quot;w-resize&quot;, &quot;wait&quot;, &quot;wave&quot;, &quot;wider&quot;,
</span><span class="cx">     &quot;window&quot;, &quot;windowframe&quot;, &quot;windowtext&quot;, &quot;x-large&quot;, &quot;x-small&quot;, &quot;xor&quot;,
</span><span class="cx">     &quot;xx-large&quot;, &quot;xx-small&quot;
</span><del>-  ]);
</del><ins>+  ], valueKeywords = keySet(valueKeywords_);
</ins><span class="cx"> 
</span><ins>+  var fontProperties_ = [
+    &quot;font-family&quot;, &quot;src&quot;, &quot;unicode-range&quot;, &quot;font-variant&quot;, &quot;font-feature-settings&quot;,
+    &quot;font-stretch&quot;, &quot;font-weight&quot;, &quot;font-style&quot;
+  ], fontProperties = keySet(fontProperties_);
+
+  var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_)
+    .concat(nonStandardPropertyKeywords).concat(colorKeywords_).concat(valueKeywords_);
+  CodeMirror.registerHelper(&quot;hintWords&quot;, &quot;css&quot;, allWords);
+
</ins><span class="cx">   function tokenCComment(stream, state) {
</span><span class="cx">     var maybeEnd = false, ch;
</span><span class="cx">     while ((ch = stream.next()) != null) {
</span><span class="lines">@@ -553,67 +605,90 @@
</span><span class="cx">     return [&quot;comment&quot;, &quot;comment&quot;];
</span><span class="cx">   }
</span><span class="cx"> 
</span><ins>+  function tokenSGMLComment(stream, state) {
+    if (stream.skipTo(&quot;--&gt;&quot;)) {
+      stream.match(&quot;--&gt;&quot;);
+      state.tokenize = null;
+    } else {
+      stream.skipToEnd();
+    }
+    return [&quot;comment&quot;, &quot;comment&quot;];
+  }
+
</ins><span class="cx">   CodeMirror.defineMIME(&quot;text/css&quot;, {
</span><del>-    atMediaTypes: atMediaTypes,
-    atMediaFeatures: atMediaFeatures,
</del><ins>+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
</ins><span class="cx">     propertyKeywords: propertyKeywords,
</span><ins>+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
</ins><span class="cx">     colorKeywords: colorKeywords,
</span><span class="cx">     valueKeywords: valueKeywords,
</span><del>-    hooks: {
</del><ins>+    fontProperties: fontProperties,
+    tokenHooks: {
</ins><span class="cx">       &quot;&lt;&quot;: function(stream, state) {
</span><del>-        function tokenSGMLComment(stream, state) {
-          var dashes = 0, ch;
-          while ((ch = stream.next()) != null) {
-            if (dashes &gt;= 2 &amp;&amp; ch == &quot;&gt;&quot;) {
-              state.tokenize = null;
-              break;
-            }
-            dashes = (ch == &quot;-&quot;) ? dashes + 1 : 0;
-          }
-          return [&quot;comment&quot;, &quot;comment&quot;];
-        }
-        if (stream.eat(&quot;!&quot;)) {
-          state.tokenize = tokenSGMLComment;
-          return tokenSGMLComment(stream, state);
-        }
</del><ins>+        if (!stream.match(&quot;!--&quot;)) return false;
+        state.tokenize = tokenSGMLComment;
+        return tokenSGMLComment(stream, state);
</ins><span class="cx">       },
</span><span class="cx">       &quot;/&quot;: function(stream, state) {
</span><del>-        if (stream.eat(&quot;*&quot;)) {
-          state.tokenize = tokenCComment;
-          return tokenCComment(stream, state);
-        }
-        return false;
</del><ins>+        if (!stream.eat(&quot;*&quot;)) return false;
+        state.tokenize = tokenCComment;
+        return tokenCComment(stream, state);
</ins><span class="cx">       }
</span><span class="cx">     },
</span><span class="cx">     name: &quot;css&quot;
</span><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/x-scss&quot;, {
</span><del>-    atMediaTypes: atMediaTypes,
-    atMediaFeatures: atMediaFeatures,
</del><ins>+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
</ins><span class="cx">     propertyKeywords: propertyKeywords,
</span><ins>+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
</ins><span class="cx">     colorKeywords: colorKeywords,
</span><span class="cx">     valueKeywords: valueKeywords,
</span><ins>+    fontProperties: fontProperties,
</ins><span class="cx">     allowNested: true,
</span><del>-    hooks: {
</del><ins>+    tokenHooks: {
+      &quot;/&quot;: function(stream, state) {
+        if (stream.eat(&quot;/&quot;)) {
+          stream.skipToEnd();
+          return [&quot;comment&quot;, &quot;comment&quot;];
+        } else if (stream.eat(&quot;*&quot;)) {
+          state.tokenize = tokenCComment;
+          return tokenCComment(stream, state);
+        } else {
+          return [&quot;operator&quot;, &quot;operator&quot;];
+        }
+      },
</ins><span class="cx">       &quot;:&quot;: function(stream) {
</span><del>-        if (stream.match(/\s*{/)) {
</del><ins>+        if (stream.match(/\s*{/))
</ins><span class="cx">           return [null, &quot;{&quot;];
</span><del>-        }
</del><span class="cx">         return false;
</span><span class="cx">       },
</span><span class="cx">       &quot;$&quot;: function(stream) {
</span><span class="cx">         stream.match(/^[\w-]+/);
</span><del>-        if (stream.peek() == &quot;:&quot;) {
-          return [&quot;variable&quot;, &quot;variable-definition&quot;];
-        }
-        return [&quot;variable&quot;, &quot;variable&quot;];
</del><ins>+        if (stream.match(/^\s*:/, false))
+          return [&quot;variable-2&quot;, &quot;variable-definition&quot;];
+        return [&quot;variable-2&quot;, &quot;variable&quot;];
</ins><span class="cx">       },
</span><del>-      &quot;,&quot;: function(stream, state) {
-        if (state.stack[state.stack.length - 1] == &quot;propertyValue&quot; &amp;&amp; stream.match(/^ *\$/, false)) {
-          return [&quot;operator&quot;, &quot;;&quot;];
-        }
-      },
</del><ins>+      &quot;#&quot;: function(stream) {
+        if (!stream.eat(&quot;{&quot;)) return false;
+        return [null, &quot;interpolation&quot;];
+      }
+    },
+    name: &quot;css&quot;,
+    helperType: &quot;scss&quot;
+  });
+
+  CodeMirror.defineMIME(&quot;text/x-less&quot;, {
+    mediaTypes: mediaTypes,
+    mediaFeatures: mediaFeatures,
+    propertyKeywords: propertyKeywords,
+    nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+    colorKeywords: colorKeywords,
+    valueKeywords: valueKeywords,
+    fontProperties: fontProperties,
+    allowNested: true,
+    tokenHooks: {
</ins><span class="cx">       &quot;/&quot;: function(stream, state) {
</span><span class="cx">         if (stream.eat(&quot;/&quot;)) {
</span><span class="cx">           stream.skipToEnd();
</span><span class="lines">@@ -625,15 +700,19 @@
</span><span class="cx">           return [&quot;operator&quot;, &quot;operator&quot;];
</span><span class="cx">         }
</span><span class="cx">       },
</span><del>-      &quot;#&quot;: function(stream) {
-        if (stream.eat(&quot;{&quot;)) {
-          return [&quot;operator&quot;, &quot;interpolation&quot;];
-        } else {
-          stream.eatWhile(/[\w\\\-]/);
-          return [&quot;atom&quot;, &quot;hash&quot;];
-        }
</del><ins>+      &quot;@&quot;: function(stream) {
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        stream.eatWhile(/[\w\\\-]/);
+        if (stream.match(/^\s*:/, false))
+          return [&quot;variable-2&quot;, &quot;variable-definition&quot;];
+        return [&quot;variable-2&quot;, &quot;variable&quot;];
+      },
+      &quot;&amp;&quot;: function() {
+        return [&quot;atom&quot;, &quot;atom&quot;];
</ins><span class="cx">       }
</span><span class="cx">     },
</span><del>-    name: &quot;css&quot;
</del><ins>+    name: &quot;css&quot;,
+    helperType: &quot;less&quot;
</ins><span class="cx">   });
</span><del>-})();
</del><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorhtmlmixedjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/htmlmixed.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/htmlmixed.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/htmlmixed.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,5 +1,18 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;), require(&quot;../xml/xml&quot;), require(&quot;../javascript/javascript&quot;), require(&quot;../css/css&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;, &quot;../xml/xml&quot;, &quot;../javascript/javascript&quot;, &quot;../css/css&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;htmlmixed&quot;, function(config, parserConfig) {
</span><del>-  var htmlMode = CodeMirror.getMode(config, {name: &quot;xml&quot;, htmlMode: true});
</del><ins>+  var htmlMode = CodeMirror.getMode(config, {name: &quot;xml&quot;,
+                                             htmlMode: true,
+                                             multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
+                                             multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag});
</ins><span class="cx">   var cssMode = CodeMirror.getMode(config, &quot;css&quot;);
</span><span class="cx"> 
</span><span class="cx">   var scriptTypes = [], scriptTypesConf = parserConfig &amp;&amp; parserConfig.scriptTypes;
</span><span class="lines">@@ -93,8 +106,6 @@
</span><span class="cx">         return CodeMirror.Pass;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    electricChars: &quot;/{}:&quot;,
-
</del><span class="cx">     innerMode: function(state) {
</span><span class="cx">       return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
</span><span class="cx">     }
</span><span class="lines">@@ -102,3 +113,5 @@
</span><span class="cx"> }, &quot;xml&quot;, &quot;javascript&quot;, &quot;css&quot;);
</span><span class="cx"> 
</span><span class="cx"> CodeMirror.defineMIME(&quot;text/html&quot;, &quot;htmlmixed&quot;);
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorjavascriptjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/javascript.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/javascript.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/javascript.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,9 +1,20 @@
</span><span class="cx"> // TODO actually recognize syntax of TypeScript constructs
</span><span class="cx"> 
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;javascript&quot;, function(config, parserConfig) {
</span><span class="cx">   var indentUnit = config.indentUnit;
</span><span class="cx">   var statementIndent = parserConfig.statementIndent;
</span><del>-  var jsonMode = parserConfig.json;
</del><ins>+  var jsonldMode = parserConfig.jsonld;
+  var jsonMode = parserConfig.json || jsonldMode;
</ins><span class="cx">   var isTS = parserConfig.typescript;
</span><span class="cx"> 
</span><span class="cx">   // Tokenizer
</span><span class="lines">@@ -15,7 +26,7 @@
</span><span class="cx"> 
</span><span class="cx">     var jsKeywords = {
</span><span class="cx">       &quot;if&quot;: kw(&quot;if&quot;), &quot;while&quot;: A, &quot;with&quot;: A, &quot;else&quot;: B, &quot;do&quot;: B, &quot;try&quot;: B, &quot;finally&quot;: B,
</span><del>-      &quot;return&quot;: C, &quot;break&quot;: C, &quot;continue&quot;: C, &quot;new&quot;: C, &quot;delete&quot;: C, &quot;throw&quot;: C,
</del><ins>+      &quot;return&quot;: C, &quot;break&quot;: C, &quot;continue&quot;: C, &quot;new&quot;: C, &quot;delete&quot;: C, &quot;throw&quot;: C, &quot;debugger&quot;: C,
</ins><span class="cx">       &quot;var&quot;: kw(&quot;var&quot;), &quot;const&quot;: kw(&quot;var&quot;), &quot;let&quot;: kw(&quot;var&quot;),
</span><span class="cx">       &quot;function&quot;: kw(&quot;function&quot;), &quot;catch&quot;: kw(&quot;catch&quot;),
</span><span class="cx">       &quot;for&quot;: kw(&quot;for&quot;), &quot;switch&quot;: kw(&quot;switch&quot;), &quot;case&quot;: kw(&quot;case&quot;), &quot;default&quot;: kw(&quot;default&quot;),
</span><span class="lines">@@ -53,15 +64,18 @@
</span><span class="cx">   }();
</span><span class="cx"> 
</span><span class="cx">   var isOperatorChar = /[+\-*&amp;%=&lt;&gt;!?|~^]/;
</span><ins>+  var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)&quot;/;
</ins><span class="cx"> 
</span><del>-  function nextUntilUnescaped(stream, end) {
-    var escaped = false, next;
</del><ins>+  function readRegexp(stream) {
+    var escaped = false, next, inSet = false;
</ins><span class="cx">     while ((next = stream.next()) != null) {
</span><del>-      if (next == end &amp;&amp; !escaped)
-        return false;
</del><ins>+      if (!escaped) {
+        if (next == &quot;/&quot; &amp;&amp; !inSet) return;
+        if (next == &quot;[&quot;) inSet = true;
+        else if (inSet &amp;&amp; next == &quot;]&quot;) inSet = false;
+      }
</ins><span class="cx">       escaped = !escaped &amp;&amp; next == &quot;\\&quot;;
</span><span class="cx">     }
</span><del>-    return escaped;
</del><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   // Used as scratch variables to communicate multiple values without
</span><span class="lines">@@ -83,7 +97,7 @@
</span><span class="cx">     } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
</span><span class="cx">       return ret(ch);
</span><span class="cx">     } else if (ch == &quot;=&quot; &amp;&amp; stream.eat(&quot;&gt;&quot;)) {
</span><del>-      return ret(&quot;=&gt;&quot;);
</del><ins>+      return ret(&quot;=&gt;&quot;, &quot;operator&quot;);
</ins><span class="cx">     } else if (ch == &quot;0&quot; &amp;&amp; stream.eat(/x/i)) {
</span><span class="cx">       stream.eatWhile(/[\da-f]/i);
</span><span class="cx">       return ret(&quot;number&quot;, &quot;number&quot;);
</span><span class="lines">@@ -99,12 +113,12 @@
</span><span class="cx">         return ret(&quot;comment&quot;, &quot;comment&quot;);
</span><span class="cx">       } else if (state.lastType == &quot;operator&quot; || state.lastType == &quot;keyword c&quot; ||
</span><span class="cx">                state.lastType == &quot;sof&quot; || /^[\[{}\(,;:]$/.test(state.lastType)) {
</span><del>-        nextUntilUnescaped(stream, &quot;/&quot;);
</del><ins>+        readRegexp(stream);
</ins><span class="cx">         stream.eatWhile(/[gimy]/); // 'y' is &quot;sticky&quot; option in Mozilla
</span><span class="cx">         return ret(&quot;regexp&quot;, &quot;string-2&quot;);
</span><span class="cx">       } else {
</span><span class="cx">         stream.eatWhile(isOperatorChar);
</span><del>-        return ret(&quot;operator&quot;, null, stream.current());
</del><ins>+        return ret(&quot;operator&quot;, &quot;operator&quot;, stream.current());
</ins><span class="cx">       }
</span><span class="cx">     } else if (ch == &quot;`&quot;) {
</span><span class="cx">       state.tokenize = tokenQuasi;
</span><span class="lines">@@ -114,7 +128,7 @@
</span><span class="cx">       return ret(&quot;error&quot;, &quot;error&quot;);
</span><span class="cx">     } else if (isOperatorChar.test(ch)) {
</span><span class="cx">       stream.eatWhile(isOperatorChar);
</span><del>-      return ret(&quot;operator&quot;, null, stream.current());
</del><ins>+      return ret(&quot;operator&quot;, &quot;operator&quot;, stream.current());
</ins><span class="cx">     } else {
</span><span class="cx">       stream.eatWhile(/[\w\$_]/);
</span><span class="cx">       var word = stream.current(), known = keywords.propertyIsEnumerable(word) &amp;&amp; keywords[word];
</span><span class="lines">@@ -125,8 +139,16 @@
</span><span class="cx"> 
</span><span class="cx">   function tokenString(quote) {
</span><span class="cx">     return function(stream, state) {
</span><del>-      if (!nextUntilUnescaped(stream, quote))
</del><ins>+      var escaped = false, next;
+      if (jsonldMode &amp;&amp; stream.peek() == &quot;@&quot; &amp;&amp; stream.match(isJsonldKeyword)){
</ins><span class="cx">         state.tokenize = tokenBase;
</span><ins>+        return ret(&quot;jsonld-keyword&quot;, &quot;meta&quot;);
+      }
+      while ((next = stream.next()) != null) {
+        if (next == quote &amp;&amp; !escaped) break;
+        escaped = !escaped &amp;&amp; next == &quot;\\&quot;;
+      }
+      if (!escaped) state.tokenize = tokenBase;
</ins><span class="cx">       return ret(&quot;string&quot;, &quot;string&quot;);
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="lines">@@ -189,7 +211,7 @@
</span><span class="cx"> 
</span><span class="cx">   // Parser
</span><span class="cx"> 
</span><del>-  var atomicTypes = {&quot;atom&quot;: true, &quot;number&quot;: true, &quot;variable&quot;: true, &quot;string&quot;: true, &quot;regexp&quot;: true, &quot;this&quot;: true};
</del><ins>+  var atomicTypes = {&quot;atom&quot;: true, &quot;number&quot;: true, &quot;variable&quot;: true, &quot;string&quot;: true, &quot;regexp&quot;: true, &quot;this&quot;: true, &quot;jsonld-keyword&quot;: true};
</ins><span class="cx"> 
</span><span class="cx">   function JSLexical(indented, column, type, align, prev, info) {
</span><span class="cx">     this.indented = indented;
</span><span class="lines">@@ -289,11 +311,12 @@
</span><span class="cx">   poplex.lex = true;
</span><span class="cx"> 
</span><span class="cx">   function expect(wanted) {
</span><del>-    return function(type) {
</del><ins>+    function exp(type) {
</ins><span class="cx">       if (type == wanted) return cont();
</span><span class="cx">       else if (wanted == &quot;;&quot;) return pass();
</span><del>-      else return cont(arguments.callee);
</del><ins>+      else return cont(exp);
</ins><span class="cx">     };
</span><ins>+    return exp;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   function statement(type, value) {
</span><span class="lines">@@ -302,9 +325,13 @@
</span><span class="cx">     if (type == &quot;keyword b&quot;) return cont(pushlex(&quot;form&quot;), statement, poplex);
</span><span class="cx">     if (type == &quot;{&quot;) return cont(pushlex(&quot;}&quot;), block, poplex);
</span><span class="cx">     if (type == &quot;;&quot;) return cont();
</span><del>-    if (type == &quot;if&quot;) return cont(pushlex(&quot;form&quot;), expression, statement, poplex, maybeelse);
</del><ins>+    if (type == &quot;if&quot;) {
+      if (cx.state.lexical.info == &quot;else&quot; &amp;&amp; cx.state.cc[cx.state.cc.length - 1] == poplex)
+        cx.state.cc.pop()();
+      return cont(pushlex(&quot;form&quot;), expression, statement, poplex, maybeelse);
+    }
</ins><span class="cx">     if (type == &quot;function&quot;) return cont(functiondef);
</span><del>-    if (type == &quot;for&quot;) return cont(pushlex(&quot;form&quot;), forspec, poplex, statement, poplex);
</del><ins>+    if (type == &quot;for&quot;) return cont(pushlex(&quot;form&quot;), forspec, statement, poplex);
</ins><span class="cx">     if (type == &quot;variable&quot;) return cont(pushlex(&quot;stat&quot;), maybelabel);
</span><span class="cx">     if (type == &quot;switch&quot;) return cont(pushlex(&quot;form&quot;), expression, pushlex(&quot;}&quot;, &quot;switch&quot;), expect(&quot;{&quot;),
</span><span class="cx">                                       block, poplex, poplex);
</span><span class="lines">@@ -327,18 +354,18 @@
</span><span class="cx">   function expressionInner(type, noComma) {
</span><span class="cx">     if (cx.state.fatArrowAt == cx.stream.start) {
</span><span class="cx">       var body = noComma ? arrowBodyNoComma : arrowBody;
</span><del>-      if (type == &quot;(&quot;) return cont(pushcontext, commasep(pattern, &quot;)&quot;), expect(&quot;=&gt;&quot;), body, popcontext);
</del><ins>+      if (type == &quot;(&quot;) return cont(pushcontext, pushlex(&quot;)&quot;), commasep(pattern, &quot;)&quot;), poplex, expect(&quot;=&gt;&quot;), body, popcontext);
</ins><span class="cx">       else if (type == &quot;variable&quot;) return pass(pushcontext, pattern, expect(&quot;=&gt;&quot;), body, popcontext);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
</span><span class="cx">     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
</span><del>-    if (type == &quot;function&quot;) return cont(functiondef);
</del><ins>+    if (type == &quot;function&quot;) return cont(functiondef, maybeop);
</ins><span class="cx">     if (type == &quot;keyword c&quot;) return cont(noComma ? maybeexpressionNoComma : maybeexpression);
</span><span class="cx">     if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), maybeexpression, comprehension, expect(&quot;)&quot;), poplex, maybeop);
</span><span class="cx">     if (type == &quot;operator&quot; || type == &quot;spread&quot;) return cont(noComma ? expressionNoComma : expression);
</span><del>-    if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), expressionNoComma, maybeArrayComprehension, poplex, maybeop);
-    if (type == &quot;{&quot;) return cont(commasep(objprop, &quot;}&quot;), maybeop);
</del><ins>+    if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), arrayLiteral, poplex, maybeop);
+    if (type == &quot;{&quot;) return contCommasep(objprop, &quot;}&quot;, null, maybeop);
</ins><span class="cx">     return cont();
</span><span class="cx">   }
</span><span class="cx">   function maybeexpression(type) {
</span><span class="lines">@@ -365,12 +392,11 @@
</span><span class="cx">     }
</span><span class="cx">     if (type == &quot;quasi&quot;) { cx.cc.push(me); return quasi(value); }
</span><span class="cx">     if (type == &quot;;&quot;) return;
</span><del>-    if (type == &quot;(&quot;) return cont(commasep(expressionNoComma, &quot;)&quot;, &quot;call&quot;), me);
</del><ins>+    if (type == &quot;(&quot;) return contCommasep(expressionNoComma, &quot;)&quot;, &quot;call&quot;, me);
</ins><span class="cx">     if (type == &quot;.&quot;) return cont(property, me);
</span><span class="cx">     if (type == &quot;[&quot;) return cont(pushlex(&quot;]&quot;), maybeexpression, expect(&quot;]&quot;), poplex, me);
</span><span class="cx">   }
</span><span class="cx">   function quasi(value) {
</span><del>-    if (!value) debugger;
</del><span class="cx">     if (value.slice(value.length - 2) != &quot;${&quot;) return cont();
</span><span class="cx">     return cont(expression, continueQuasi);
</span><span class="cx">   }
</span><span class="lines">@@ -403,7 +429,7 @@
</span><span class="cx">       cx.marked = &quot;property&quot;;
</span><span class="cx">       if (value == &quot;get&quot; || value == &quot;set&quot;) return cont(getterSetter);
</span><span class="cx">     } else if (type == &quot;number&quot; || type == &quot;string&quot;) {
</span><del>-      cx.marked = type + &quot; property&quot;;
</del><ins>+      cx.marked = jsonldMode ? &quot;property&quot; : (type + &quot; property&quot;);
</ins><span class="cx">     } else if (type == &quot;[&quot;) {
</span><span class="cx">       return cont(expression, expect(&quot;]&quot;), afterprop);
</span><span class="cx">     }
</span><span class="lines">@@ -418,7 +444,7 @@
</span><span class="cx">     if (type == &quot;:&quot;) return cont(expressionNoComma);
</span><span class="cx">     if (type == &quot;(&quot;) return pass(functiondef);
</span><span class="cx">   }
</span><del>-  function commasep(what, end, info) {
</del><ins>+  function commasep(what, end) {
</ins><span class="cx">     function proceed(type) {
</span><span class="cx">       if (type == &quot;,&quot;) {
</span><span class="cx">         var lex = cx.state.lexical;
</span><span class="lines">@@ -430,10 +456,14 @@
</span><span class="cx">     }
</span><span class="cx">     return function(type) {
</span><span class="cx">       if (type == end) return cont();
</span><del>-      if (info === false) return pass(what, proceed);
-      return pass(pushlex(end, info), what, proceed, poplex);
</del><ins>+      return pass(what, proceed);
</ins><span class="cx">     };
</span><span class="cx">   }
</span><ins>+  function contCommasep(what, end, info) {
+    for (var i = 3; i &lt; arguments.length; i++)
+      cx.cc.push(arguments[i]);
+    return cont(pushlex(end, info), commasep(what, end), poplex);
+  }
</ins><span class="cx">   function block(type) {
</span><span class="cx">     if (type == &quot;}&quot;) return cont();
</span><span class="cx">     return pass(statement, block);
</span><span class="lines">@@ -449,8 +479,8 @@
</span><span class="cx">   }
</span><span class="cx">   function pattern(type, value) {
</span><span class="cx">     if (type == &quot;variable&quot;) { register(value); return cont(); }
</span><del>-    if (type == &quot;[&quot;) return cont(commasep(pattern, &quot;]&quot;));
-    if (type == &quot;{&quot;) return cont(commasep(proppattern, &quot;}&quot;));
</del><ins>+    if (type == &quot;[&quot;) return contCommasep(pattern, &quot;]&quot;);
+    if (type == &quot;{&quot;) return contCommasep(proppattern, &quot;}&quot;);
</ins><span class="cx">   }
</span><span class="cx">   function proppattern(type, value) {
</span><span class="cx">     if (type == &quot;variable&quot; &amp;&amp; !cx.stream.match(/^\s*:/, false)) {
</span><span class="lines">@@ -467,10 +497,10 @@
</span><span class="cx">     if (type == &quot;,&quot;) return cont(vardef);
</span><span class="cx">   }
</span><span class="cx">   function maybeelse(type, value) {
</span><del>-    if (type == &quot;keyword b&quot; &amp;&amp; value == &quot;else&quot;) return cont(pushlex(&quot;form&quot;), statement, poplex);
</del><ins>+    if (type == &quot;keyword b&quot; &amp;&amp; value == &quot;else&quot;) return cont(pushlex(&quot;form&quot;, &quot;else&quot;), statement, poplex);
</ins><span class="cx">   }
</span><span class="cx">   function forspec(type) {
</span><del>-    if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), forspec1, expect(&quot;)&quot;));
</del><ins>+    if (type == &quot;(&quot;) return cont(pushlex(&quot;)&quot;), forspec1, expect(&quot;)&quot;), poplex);
</ins><span class="cx">   }
</span><span class="cx">   function forspec1(type) {
</span><span class="cx">     if (type == &quot;var&quot;) return cont(vardef, expect(&quot;;&quot;), forspec2);
</span><span class="lines">@@ -493,7 +523,7 @@
</span><span class="cx">   function functiondef(type, value) {
</span><span class="cx">     if (value == &quot;*&quot;) {cx.marked = &quot;keyword&quot;; return cont(functiondef);}
</span><span class="cx">     if (type == &quot;variable&quot;) {register(value); return cont(functiondef);}
</span><del>-    if (type == &quot;(&quot;) return cont(pushcontext, commasep(funarg, &quot;)&quot;), statement, popcontext);
</del><ins>+    if (type == &quot;(&quot;) return cont(pushcontext, pushlex(&quot;)&quot;), commasep(funarg, &quot;)&quot;), poplex, statement, popcontext);
</ins><span class="cx">   }
</span><span class="cx">   function funarg(type) {
</span><span class="cx">     if (type == &quot;spread&quot;) return cont(funarg);
</span><span class="lines">@@ -506,7 +536,7 @@
</span><span class="cx">     if (value == &quot;extends&quot;) return cont(expression);
</span><span class="cx">   }
</span><span class="cx">   function objlit(type) {
</span><del>-    if (type == &quot;{&quot;) return cont(commasep(objprop, &quot;}&quot;));
</del><ins>+    if (type == &quot;{&quot;) return contCommasep(objprop, &quot;}&quot;);
</ins><span class="cx">   }
</span><span class="cx">   function afterModule(type, value) {
</span><span class="cx">     if (type == &quot;string&quot;) return cont(statement);
</span><span class="lines">@@ -522,17 +552,21 @@
</span><span class="cx">     return pass(importSpec, maybeFrom);
</span><span class="cx">   }
</span><span class="cx">   function importSpec(type, value) {
</span><del>-    if (type == &quot;{&quot;) return cont(commasep(importSpec, &quot;}&quot;));
</del><ins>+    if (type == &quot;{&quot;) return contCommasep(importSpec, &quot;}&quot;);
</ins><span class="cx">     if (type == &quot;variable&quot;) register(value);
</span><span class="cx">     return cont();
</span><span class="cx">   }
</span><span class="cx">   function maybeFrom(_type, value) {
</span><span class="cx">     if (value == &quot;from&quot;) { cx.marked = &quot;keyword&quot;; return cont(expression); }
</span><span class="cx">   }
</span><ins>+  function arrayLiteral(type) {
+    if (type == &quot;]&quot;) return cont();
+    return pass(expressionNoComma, maybeArrayComprehension);
+  }
</ins><span class="cx">   function maybeArrayComprehension(type) {
</span><del>-    if (type == &quot;for&quot;) return pass(comprehension);
-    if (type == &quot;,&quot;) return cont(commasep(expressionNoComma, &quot;]&quot;, false));
-    return pass(commasep(expressionNoComma, &quot;]&quot;, false));
</del><ins>+    if (type == &quot;for&quot;) return pass(comprehension, expect(&quot;]&quot;));
+    if (type == &quot;,&quot;) return cont(commasep(expressionNoComma, &quot;]&quot;));
+    return pass(commasep(expressionNoComma, &quot;]&quot;));
</ins><span class="cx">   }
</span><span class="cx">   function comprehension(type) {
</span><span class="cx">     if (type == &quot;for&quot;) return cont(forspec, comprehension);
</span><span class="lines">@@ -552,7 +586,8 @@
</span><span class="cx">         context: parserConfig.localVars &amp;&amp; {vars: parserConfig.localVars},
</span><span class="cx">         indented: 0
</span><span class="cx">       };
</span><del>-      if (parserConfig.globalVars) state.globalVars = parserConfig.globalVars;
</del><ins>+      if (parserConfig.globalVars &amp;&amp; typeof parserConfig.globalVars == &quot;object&quot;)
+        state.globalVars = parserConfig.globalVars;
</ins><span class="cx">       return state;
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -575,7 +610,7 @@
</span><span class="cx">       if (state.tokenize != tokenBase) return 0;
</span><span class="cx">       var firstChar = textAfter &amp;&amp; textAfter.charAt(0), lexical = state.lexical;
</span><span class="cx">       // Kludge to prevent 'maybelse' from blocking lexical scope pops
</span><del>-      for (var i = state.cc.length - 1; i &gt;= 0; --i) {
</del><ins>+      if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i &gt;= 0; --i) {
</ins><span class="cx">         var c = state.cc[i];
</span><span class="cx">         if (c == poplex) lexical = lexical.prev;
</span><span class="cx">         else if (c != maybeelse) break;
</span><span class="lines">@@ -603,6 +638,7 @@
</span><span class="cx">     fold: &quot;brace&quot;,
</span><span class="cx"> 
</span><span class="cx">     helperType: jsonMode ? &quot;json&quot; : &quot;javascript&quot;,
</span><ins>+    jsonldMode: jsonldMode,
</ins><span class="cx">     jsonMode: jsonMode
</span><span class="cx">   };
</span><span class="cx"> });
</span><span class="lines">@@ -613,5 +649,8 @@
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/ecmascript&quot;, &quot;javascript&quot;);
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/json&quot;, {name: &quot;javascript&quot;, json: true});
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/x-json&quot;, {name: &quot;javascript&quot;, json: true});
</span><ins>+CodeMirror.defineMIME(&quot;application/ld+json&quot;, {name: &quot;javascript&quot;, jsonld: true});
</ins><span class="cx"> CodeMirror.defineMIME(&quot;text/typescript&quot;, { name: &quot;javascript&quot;, typescript: true });
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/typescript&quot;, { name: &quot;javascript&quot;, typescript: true });
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorlivescriptjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/livescript.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/livescript.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/livescript.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -2,6 +2,17 @@
</span><span class="cx">  * Link to the project's GitHub page:
</span><span class="cx">  * https://github.com/duralog/CodeMirror
</span><span class="cx">  */
</span><ins>+
+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> (function() {
</span><span class="cx">   CodeMirror.defineMode('livescript', function(){
</span><span class="cx">     var tokenBase, external;
</span><span class="lines">@@ -265,3 +276,5 @@
</span><span class="cx"> })();
</span><span class="cx"> 
</span><span class="cx"> CodeMirror.defineMIME('text/x-livescript', 'livescript');
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrormatchbracketsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/matchbrackets.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/matchbrackets.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/matchbrackets.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,73 +1,100 @@
</span><del>-(function() {
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
</ins><span class="cx">   var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &amp;&amp;
</span><span class="cx">     (document.documentMode == null || document.documentMode &lt; 8);
</span><span class="cx"> 
</span><span class="cx">   var Pos = CodeMirror.Pos;
</span><span class="cx"> 
</span><span class="cx">   var matching = {&quot;(&quot;: &quot;)&gt;&quot;, &quot;)&quot;: &quot;(&lt;&quot;, &quot;[&quot;: &quot;]&gt;&quot;, &quot;]&quot;: &quot;[&lt;&quot;, &quot;{&quot;: &quot;}&gt;&quot;, &quot;}&quot;: &quot;{&lt;&quot;};
</span><del>-  function findMatchingBracket(cm, where, strict) {
-    var state = cm.state.matchBrackets;
-    var maxScanLen = (state &amp;&amp; state.maxScanLineLength) || 10000;
-    var maxScanLines = (state &amp;&amp; state.maxScanLines) || 100;
</del><span class="cx"> 
</span><del>-    var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
</del><ins>+  function findMatchingBracket(cm, where, strict, config) {
+    var line = cm.getLineHandle(where.line), pos = where.ch - 1;
</ins><span class="cx">     var match = (pos &gt;= 0 &amp;&amp; matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
</span><span class="cx">     if (!match) return null;
</span><del>-    var forward = match.charAt(1) == &quot;&gt;&quot;, d = forward ? 1 : -1;
-    if (strict &amp;&amp; forward != (pos == cur.ch)) return null;
-    var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1));
</del><ins>+    var dir = match.charAt(1) == &quot;&gt;&quot; ? 1 : -1;
+    if (strict &amp;&amp; (dir &gt; 0) != (pos == where.ch)) return null;
+    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
</ins><span class="cx"> 
</span><del>-    var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
-    function scan(line, lineNo, start) {
-      if (!line.text) return;
-      var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
-      if (line.text.length &gt; maxScanLen) return null;
-      if (start != null) pos = start + d;
-      for (; pos != end; pos += d) {
-        var ch = line.text.charAt(pos);
-        if (re.test(ch) &amp;&amp; cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) {
</del><ins>+    var found = scanForBracket(cm, Pos(where.line, pos + (dir &gt; 0 ? 1 : 0)), dir, style || null, config);
+    if (found == null) return null;
+    return {from: Pos(where.line, pos), to: found &amp;&amp; found.pos,
+            match: found &amp;&amp; found.ch == match.charAt(0), forward: dir &gt; 0};
+  }
+
+  // bracketRegex is used to specify which type of bracket to scan
+  // should be a regexp, e.g. /[[\]]/
+  //
+  // Note: If &quot;where&quot; is on an open bracket, then this bracket is ignored.
+  //
+  // Returns false when no bracket was found, null when it reached
+  // maxScanLines and gave up
+  function scanForBracket(cm, where, dir, style, config) {
+    var maxScanLen = (config &amp;&amp; config.maxScanLineLength) || 10000;
+    var maxScanLines = (config &amp;&amp; config.maxScanLines) || 1000;
+
+    var stack = [];
+    var re = config &amp;&amp; config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
+    var lineEnd = dir &gt; 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
+                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
+    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+      var line = cm.getLine(lineNo);
+      if (!line) continue;
+      var pos = dir &gt; 0 ? 0 : line.length - 1, end = dir &gt; 0 ? line.length : -1;
+      if (line.length &gt; maxScanLen) continue;
+      if (lineNo == where.line) pos = where.ch - (dir &lt; 0 ? 1 : 0);
+      for (; pos != end; pos += dir) {
+        var ch = line.charAt(pos);
+        if (re.test(ch) &amp;&amp; (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
</ins><span class="cx">           var match = matching[ch];
</span><del>-          if (match.charAt(1) == &quot;&gt;&quot; == forward) stack.push(ch);
-          else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
-          else if (!stack.length) return {pos: pos, match: true};
</del><ins>+          if ((match.charAt(1) == &quot;&gt;&quot;) == (dir &gt; 0)) stack.push(ch);
+          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
+          else stack.pop();
</ins><span class="cx">         }
</span><span class="cx">       }
</span><span class="cx">     }
</span><del>-    for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) {
-      if (i == cur.line) found = scan(line, i, pos);
-      else found = scan(cm.getLineHandle(i), i);
-      if (found) break;
-    }
-    return {from: Pos(cur.line, pos), to: found &amp;&amp; Pos(i, found.pos),
-            match: found &amp;&amp; found.match, forward: forward};
</del><ins>+    return lineNo - dir == (dir &gt; 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
</ins><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function matchBrackets(cm, autoclear) {
</del><ins>+  function matchBrackets(cm, autoclear, config) {
</ins><span class="cx">     // Disable brace matching in long lines, since it'll cause hugely slow updates
</span><span class="cx">     var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
</span><del>-    var found = findMatchingBracket(cm);
-    if (!found || cm.getLine(found.from.line).length &gt; maxHighlightLen ||
-       found.to &amp;&amp; cm.getLine(found.to.line).length &gt; maxHighlightLen)
-      return;
</del><ins>+    var marks = [], ranges = cm.listSelections();
+    for (var i = 0; i &lt; ranges.length; i++) {
+      var match = ranges[i].empty() &amp;&amp; findMatchingBracket(cm, ranges[i].head, false, config);
+      if (match &amp;&amp; cm.getLine(match.from.line).length &lt;= maxHighlightLen) {
+        var style = match.match ? &quot;CodeMirror-matchingbracket&quot; : &quot;CodeMirror-nonmatchingbracket&quot;;
+        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
+        if (match.to &amp;&amp; cm.getLine(match.to.line).length &lt;= maxHighlightLen)
+          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
+      }
+    }
</ins><span class="cx"> 
</span><del>-    var style = found.match ? &quot;CodeMirror-matchingbracket&quot; : &quot;CodeMirror-nonmatchingbracket&quot;;
-    var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
-    var two = found.to &amp;&amp; cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
-    // Kludge to work around the IE bug from issue #1193, where text
-    // input stops going to the textare whever this fires.
-    if (ie_lt8 &amp;&amp; cm.state.focused) cm.display.input.focus();
-    var clear = function() {
-      cm.operation(function() { one.clear(); two &amp;&amp; two.clear(); });
-    };
-    if (autoclear) setTimeout(clear, 800);
-    else return clear;
</del><ins>+    if (marks.length) {
+      // Kludge to work around the IE bug from issue #1193, where text
+      // input stops going to the textare whever this fires.
+      if (ie_lt8 &amp;&amp; cm.state.focused) cm.display.input.focus();
+
+      var clear = function() {
+        cm.operation(function() {
+          for (var i = 0; i &lt; marks.length; i++) marks[i].clear();
+        });
+      };
+      if (autoclear) setTimeout(clear, 800);
+      else return clear;
+    }
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   var currentlyHighlighted = null;
</span><span class="cx">   function doMatchBrackets(cm) {
</span><span class="cx">     cm.operation(function() {
</span><span class="cx">       if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
</span><del>-      if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
</del><ins>+      currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
</ins><span class="cx">     });
</span><span class="cx">   }
</span><span class="cx"> 
</span><span class="lines">@@ -81,7 +108,10 @@
</span><span class="cx">   });
</span><span class="cx"> 
</span><span class="cx">   CodeMirror.defineExtension(&quot;matchBrackets&quot;, function() {matchBrackets(this, true);});
</span><del>-  CodeMirror.defineExtension(&quot;findMatchingBracket&quot;, function(pos, strict){
-    return findMatchingBracket(this, pos, strict);
</del><ins>+  CodeMirror.defineExtension(&quot;findMatchingBracket&quot;, function(pos, strict, config){
+    return findMatchingBracket(this, pos, strict, config);
</ins><span class="cx">   });
</span><del>-})();
</del><ins>+  CodeMirror.defineExtension(&quot;scanForBracket&quot;, function(pos, dir, style, config){
+    return scanForBracket(this, pos, dir, style, config);
+  });
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirroroverlayjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/overlay.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/overlay.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/overlay.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -6,15 +6,25 @@
</span><span class="cx"> // overlay wins, unless the combine argument was true, in which case
</span><span class="cx"> // the styles are combined.
</span><span class="cx"> 
</span><del>-// overlayParser is the old, deprecated name
-CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) {
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
+CodeMirror.overlayMode = function(base, overlay, combine) {
</ins><span class="cx">   return {
</span><span class="cx">     startState: function() {
</span><span class="cx">       return {
</span><span class="cx">         base: CodeMirror.startState(base),
</span><span class="cx">         overlay: CodeMirror.startState(overlay),
</span><span class="cx">         basePos: 0, baseCur: null,
</span><del>-        overlayPos: 0, overlayCur: null
</del><ins>+        overlayPos: 0, overlayCur: null,
+        lineSeen: null
</ins><span class="cx">       };
</span><span class="cx">     },
</span><span class="cx">     copyState: function(state) {
</span><span class="lines">@@ -27,6 +37,12 @@
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     token: function(stream, state) {
</span><ins>+      if (stream.sol() || stream.string != state.lineSeen ||
+          Math.min(state.basePos, state.overlayPos) &lt; stream.start) {
+        state.lineSeen = stream.string;
+        state.basePos = state.overlayPos = stream.start;
+      }
+
</ins><span class="cx">       if (stream.start == state.basePos) {
</span><span class="cx">         state.baseCur = base.token(stream, state.base);
</span><span class="cx">         state.basePos = stream.pos;
</span><span class="lines">@@ -37,7 +53,6 @@
</span><span class="cx">         state.overlayPos = stream.pos;
</span><span class="cx">       }
</span><span class="cx">       stream.pos = Math.min(state.basePos, state.overlayPos);
</span><del>-      if (stream.eol()) state.basePos = state.overlayPos = 0;
</del><span class="cx"> 
</span><span class="cx">       if (state.overlayCur == null) return state.baseCur;
</span><span class="cx">       if (state.baseCur != null &amp;&amp; combine) return state.baseCur + &quot; &quot; + state.overlayCur;
</span><span class="lines">@@ -57,3 +72,5 @@
</span><span class="cx">     }
</span><span class="cx">   };
</span><span class="cx"> };
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorplaceholderjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/placeholder.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/placeholder.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/placeholder.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,4 +1,11 @@
</span><del>-(function() {
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
</ins><span class="cx">   CodeMirror.defineOption(&quot;placeholder&quot;, &quot;&quot;, function(cm, val, old) {
</span><span class="cx">     var prev = old &amp;&amp; old != CodeMirror.Init;
</span><span class="cx">     if (val &amp;&amp; !prev) {
</span><span class="lines">@@ -45,4 +52,4 @@
</span><span class="cx">   function isEmpty(cm) {
</span><span class="cx">     return (cm.lineCount() === 1) &amp;&amp; (cm.getLine(0) === &quot;&quot;);
</span><span class="cx">   }
</span><del>-})();
</del><ins>+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorrunmodejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/runmode.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/runmode.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/runmode.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.runMode = function(string, modespec, callback, options) {
</span><span class="cx">   var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
</span><span class="cx">   var ie = /MSIE \d/.test(navigator.userAgent);
</span><span class="lines">@@ -43,7 +53,7 @@
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
</del><ins>+  var lines = CodeMirror.splitLines(string), state = (options &amp;&amp; options.state) || CodeMirror.startState(mode);
</ins><span class="cx">   for (var i = 0, e = lines.length; i &lt; e; ++i) {
</span><span class="cx">     if (i) callback(&quot;\n&quot;);
</span><span class="cx">     var stream = new CodeMirror.StringStream(lines[i]);
</span><span class="lines">@@ -54,3 +64,5 @@
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> };
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsassjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sass.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sass.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sass.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;sass&quot;, function(config) {
</span><span class="cx">   var tokenRegexp = function(words){
</span><span class="cx">     return new RegExp(&quot;^&quot; + words.join(&quot;|&quot;));
</span><span class="lines">@@ -328,3 +338,5 @@
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="cx"> CodeMirror.defineMIME(&quot;text/x-sass&quot;, &quot;sass&quot;);
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsearchcursorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/searchcursor.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/searchcursor.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/searchcursor.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,4 +1,12 @@
</span><del>-(function(){
</del><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  &quot;use strict&quot;;
</ins><span class="cx">   var Pos = CodeMirror.Pos;
</span><span class="cx"> 
</span><span class="cx">   function SearchCursor(doc, query, pos, caseFold) {
</span><span class="lines">@@ -47,6 +55,7 @@
</span><span class="cx">                   match: match};
</span><span class="cx">       };
</span><span class="cx">     } else { // String query
</span><ins>+      var origQuery = query;
</ins><span class="cx">       if (caseFold) query = query.toLowerCase();
</span><span class="cx">       var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
</span><span class="cx">       var target = query.split(&quot;\n&quot;);
</span><span class="lines">@@ -58,33 +67,45 @@
</span><span class="cx">           this.matches = function() {};
</span><span class="cx">         } else {
</span><span class="cx">           this.matches = function(reverse, pos) {
</span><del>-            var line = fold(doc.getLine(pos.line)), len = query.length, match;
-            if (reverse ? (pos.ch &gt;= len &amp;&amp; (match = line.lastIndexOf(query, pos.ch - len)) != -1)
-                        : (match = line.indexOf(query, pos.ch)) != -1)
-              return {from: Pos(pos.line, match),
-                      to: Pos(pos.line, match + len)};
</del><ins>+            if (reverse) {
+              var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig);
+              var match = line.lastIndexOf(query);
+              if (match &gt; -1) {
+                match = adjustPos(orig, line, match);
+                return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
+              }
+             } else {
+               var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig);
+               var match = line.indexOf(query);
+               if (match &gt; -1) {
+                 match = adjustPos(orig, line, match) + pos.ch;
+                 return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)};
+               }
+            }
</ins><span class="cx">           };
</span><span class="cx">         }
</span><span class="cx">       } else {
</span><ins>+        var origTarget = origQuery.split(&quot;\n&quot;);
</ins><span class="cx">         this.matches = function(reverse, pos) {
</span><del>-          var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln));
-          var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
-          if (reverse ? offsetA &gt; pos.ch || offsetA != match.length
-              : offsetA &lt; pos.ch || offsetA != line.length - match.length)
-            return;
-          for (;;) {
-            if (reverse ? !ln : ln == doc.lineCount() - 1) return;
-            line = fold(doc.getLine(ln += reverse ? -1 : 1));
-            match = target[reverse ? --idx : ++idx];
-            if (idx &gt; 0 &amp;&amp; idx &lt; target.length - 1) {
-              if (line != match) return;
-              else continue;
-            }
-            var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
-            if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
-              return;
-            var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
-            return {from: reverse ? end : start, to: reverse ? start : end};
</del><ins>+          var last = target.length - 1;
+          if (reverse) {
+            if (pos.line - (target.length - 1) &lt; doc.firstLine()) return;
+            if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return;
+            var to = Pos(pos.line, origTarget[last].length);
+            for (var ln = pos.line - 1, i = last - 1; i &gt;= 1; --i, --ln)
+              if (target[i] != fold(doc.getLine(ln))) return;
+            var line = doc.getLine(ln), cut = line.length - origTarget[0].length;
+            if (fold(line.slice(cut)) != target[0]) return;
+            return {from: Pos(ln, cut), to: to};
+          } else {
+            if (pos.line + (target.length - 1) &gt; doc.lastLine()) return;
+            var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length;
+            if (fold(line.slice(cut)) != target[0]) return;
+            var from = Pos(pos.line, cut);
+            for (var ln = pos.line + 1, i = 1; i &lt; last; ++i, ++ln)
+              if (target[i] != fold(doc.getLine(ln))) return;
+            if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return;
+            return {from: from, to: Pos(ln, origTarget[last].length)};
</ins><span class="cx">           }
</span><span class="cx">         };
</span><span class="cx">       }
</span><span class="lines">@@ -106,7 +127,6 @@
</span><span class="cx"> 
</span><span class="cx">       for (;;) {
</span><span class="cx">         if (this.pos = this.matches(reverse, pos)) {
</span><del>-          if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
</del><span class="cx">           this.atOccurrence = true;
</span><span class="cx">           return this.pos.match || true;
</span><span class="cx">         }
</span><span class="lines">@@ -134,10 +154,33 @@
</span><span class="cx">     }
</span><span class="cx">   };
</span><span class="cx"> 
</span><ins>+  // Maps a position in a case-folded line back to a position in the original line
+  // (compensating for codepoints increasing in number during folding)
+  function adjustPos(orig, folded, pos) {
+    if (orig.length == folded.length) return pos;
+    for (var pos1 = Math.min(pos, orig.length);;) {
+      var len1 = orig.slice(0, pos1).toLowerCase().length;
+      if (len1 &lt; pos) ++pos1;
+      else if (len1 &gt; pos) --pos1;
+      else return pos1;
+    }
+  }
+
</ins><span class="cx">   CodeMirror.defineExtension(&quot;getSearchCursor&quot;, function(query, pos, caseFold) {
</span><span class="cx">     return new SearchCursor(this.doc, query, pos, caseFold);
</span><span class="cx">   });
</span><span class="cx">   CodeMirror.defineDocExtension(&quot;getSearchCursor&quot;, function(query, pos, caseFold) {
</span><span class="cx">     return new SearchCursor(this, query, pos, caseFold);
</span><span class="cx">   });
</span><del>-})();
</del><ins>+
+  CodeMirror.defineExtension(&quot;selectMatches&quot;, function(query, caseFold) {
+    var ranges = [], next;
+    var cur = this.getSearchCursor(query, this.getCursor(&quot;from&quot;), caseFold);
+    while (next = cur.findNext()) {
+      if (CodeMirror.cmpPos(cur.to(), this.getCursor(&quot;to&quot;)) &gt; 0) break;
+      ranges.push({anchor: cur.from(), head: cur.to()});
+    }
+    if (ranges.length)
+      this.setSelections(ranges, 0);
+  });
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorsqljs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sql.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sql.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/sql.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;sql&quot;, function(config, parserConfig) {
</span><span class="cx">   &quot;use strict&quot;;
</span><span class="cx"> 
</span><span class="lines">@@ -177,9 +187,10 @@
</span><span class="cx"> 
</span><span class="cx">     indent: function(state, textAfter) {
</span><span class="cx">       var cx = state.context;
</span><del>-      if (!cx) return CodeMirror.Pass;
-      if (cx.align) return cx.col + (textAfter.charAt(0) == cx.type ? 0 : 1);
-      else return cx.indent + config.indentUnit;
</del><ins>+      if (!cx) return 0;
+      var closing = textAfter.charAt(0) == cx.type;
+      if (cx.align) return cx.col + (closing ? 0 : 1);
+      else return cx.indent + (closing ? 0 : config.indentUnit);
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     blockCommentStart: &quot;/*&quot;,
</span><span class="lines">@@ -325,14 +336,27 @@
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/x-plsql&quot;, {
</span><span class="cx">     name:       &quot;sql&quot;,
</span><span class="cx">     client:     set(&quot;appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap&quot;),
</span><del>-    keywords:   set(&quot;abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work&quot;),
-    builtin:    set(&quot;bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2 abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize&quot;),
</del><ins>+    keywords:   set(&quot;abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work&quot;),
+    builtin:    set(&quot;abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml&quot;),
</ins><span class="cx">     operatorChars: /^[*+\-%&lt;&gt;!=~]/,
</span><span class="cx">     dateSQL:    set(&quot;date time timestamp&quot;),
</span><span class="cx">     support:    set(&quot;doubleQuote nCharCast zerolessFloat binaryNumber hexNumber&quot;)
</span><span class="cx">   });
</span><ins>+
+  // Created to support specific hive keywords
+  CodeMirror.defineMIME(&quot;text/x-hive&quot;, {
+    name: &quot;sql&quot;,
+    keywords: set(&quot;select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with&quot;),
+    builtin: set(&quot;bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype&quot;),
+    atoms: set(&quot;false true null unknown&quot;),
+    operatorChars: /^[*+\-%&lt;&gt;!=]/,
+    dateSQL: set(&quot;date timestamp&quot;),
+    support: set(&quot;ODBCdotTable doubleQuote binaryNumber hexNumber&quot;)
+  });
</ins><span class="cx"> }());
</span><span class="cx"> 
</span><ins>+});
+
</ins><span class="cx"> /*
</span><span class="cx">   How Properties of Mime Types are used by SQL Mode
</span><span class="cx">   =================================================
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceExternalCodeMirrorxmljs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/xml.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/xml.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/External/CodeMirror/xml.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -1,7 +1,18 @@
</span><ins>+(function(mod) {
+  if (typeof exports == &quot;object&quot; &amp;&amp; typeof module == &quot;object&quot;) // CommonJS
+    mod(require(&quot;../../lib/codemirror&quot;));
+  else if (typeof define == &quot;function&quot; &amp;&amp; define.amd) // AMD
+    define([&quot;../../lib/codemirror&quot;], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+&quot;use strict&quot;;
+
</ins><span class="cx"> CodeMirror.defineMode(&quot;xml&quot;, function(config, parserConfig) {
</span><span class="cx">   var indentUnit = config.indentUnit;
</span><span class="cx">   var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
</span><del>-  var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true;
</del><ins>+  var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
+  if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
</ins><span class="cx"> 
</span><span class="cx">   var Kludges = parserConfig.htmlMode ? {
</span><span class="cx">     autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
</span><span class="lines">@@ -33,19 +44,21 @@
</span><span class="cx">     },
</span><span class="cx">     doNotIndent: {&quot;pre&quot;: true},
</span><span class="cx">     allowUnquoted: true,
</span><del>-    allowMissing: true
</del><ins>+    allowMissing: true,
+    caseFold: true
</ins><span class="cx">   } : {
</span><span class="cx">     autoSelfClosers: {},
</span><span class="cx">     implicitlyClosed: {},
</span><span class="cx">     contextGrabbers: {},
</span><span class="cx">     doNotIndent: {},
</span><span class="cx">     allowUnquoted: false,
</span><del>-    allowMissing: false
</del><ins>+    allowMissing: false,
+    caseFold: false
</ins><span class="cx">   };
</span><span class="cx">   var alignCDATA = parserConfig.alignCDATA;
</span><span class="cx"> 
</span><span class="cx">   // Return variables for tokenizers
</span><del>-  var tagName, type;
</del><ins>+  var type, setStyle;
</ins><span class="cx"> 
</span><span class="cx">   function inText(stream, state) {
</span><span class="cx">     function chain(parser) {
</span><span class="lines">@@ -72,14 +85,9 @@
</span><span class="cx">         state.tokenize = inBlock(&quot;meta&quot;, &quot;?&gt;&quot;);
</span><span class="cx">         return &quot;meta&quot;;
</span><span class="cx">       } else {
</span><del>-        var isClose = stream.eat(&quot;/&quot;);
-        tagName = &quot;&quot;;
-        var c;
-        while ((c = stream.eat(/[^\s\u00a0=&lt;&gt;\&quot;\'\/?]/))) tagName += c;
-        if (!tagName) return &quot;tag error&quot;;
-        type = isClose ? &quot;closeTag&quot; : &quot;openTag&quot;;
</del><ins>+        type = stream.eat(&quot;/&quot;) ? &quot;closeTag&quot; : &quot;openTag&quot;;
</ins><span class="cx">         state.tokenize = inTag;
</span><del>-        return &quot;tag&quot;;
</del><ins>+        return &quot;tag bracket&quot;;
</ins><span class="cx">       }
</span><span class="cx">     } else if (ch == &quot;&amp;&quot;) {
</span><span class="cx">       var ok;
</span><span class="lines">@@ -104,12 +112,14 @@
</span><span class="cx">     if (ch == &quot;&gt;&quot; || (ch == &quot;/&quot; &amp;&amp; stream.eat(&quot;&gt;&quot;))) {
</span><span class="cx">       state.tokenize = inText;
</span><span class="cx">       type = ch == &quot;&gt;&quot; ? &quot;endTag&quot; : &quot;selfcloseTag&quot;;
</span><del>-      return &quot;tag&quot;;
</del><ins>+      return &quot;tag bracket&quot;;
</ins><span class="cx">     } else if (ch == &quot;=&quot;) {
</span><span class="cx">       type = &quot;equals&quot;;
</span><span class="cx">       return null;
</span><span class="cx">     } else if (ch == &quot;&lt;&quot;) {
</span><span class="cx">       state.tokenize = inText;
</span><ins>+      state.state = baseState;
+      state.tagName = state.tagStart = null;
</ins><span class="cx">       var next = state.tokenize(stream, state);
</span><span class="cx">       return next ? next + &quot; error&quot; : &quot;error&quot;;
</span><span class="cx">     } else if (/[\'\&quot;]/.test(ch)) {
</span><span class="lines">@@ -117,7 +127,7 @@
</span><span class="cx">       state.stringStartCol = stream.column();
</span><span class="cx">       return state.tokenize(stream, state);
</span><span class="cx">     } else {
</span><del>-      stream.eatWhile(/[^\s\u00a0=&lt;&gt;\&quot;\']/);
</del><ins>+      stream.match(/^[^\s\u00a0=&lt;&gt;\&quot;\']*[^\s\u00a0=&lt;&gt;\&quot;\'\/]/);
</ins><span class="cx">       return &quot;word&quot;;
</span><span class="cx">     }
</span><span class="cx">   }
</span><span class="lines">@@ -169,139 +179,141 @@
</span><span class="cx">     };
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  var curState, curStream, setStyle;
-  function pass() {
-    for (var i = arguments.length - 1; i &gt;= 0; i--) curState.cc.push(arguments[i]);
</del><ins>+  function Context(state, tagName, startOfLine) {
+    this.prev = state.context;
+    this.tagName = tagName;
+    this.indent = state.indented;
+    this.startOfLine = startOfLine;
+    if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context &amp;&amp; state.context.noIndent))
+      this.noIndent = true;
</ins><span class="cx">   }
</span><del>-  function cont() {
-    pass.apply(null, arguments);
-    return true;
</del><ins>+  function popContext(state) {
+    if (state.context) state.context = state.context.prev;
</ins><span class="cx">   }
</span><del>-
-  function pushContext(tagName, startOfLine) {
-    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context &amp;&amp; curState.context.noIndent);
-    curState.context = {
-      prev: curState.context,
-      tagName: tagName,
-      indent: curState.indented,
-      startOfLine: startOfLine,
-      noIndent: noIndent
-    };
</del><ins>+  function maybePopContext(state, nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!state.context) {
+        return;
+      }
+      parentTagName = state.context.tagName;
+      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext(state);
+    }
</ins><span class="cx">   }
</span><del>-  function popContext() {
-    if (curState.context) curState.context = curState.context.prev;
-  }
</del><span class="cx"> 
</span><del>-  function element(type) {
</del><ins>+  function baseState(type, stream, state) {
</ins><span class="cx">     if (type == &quot;openTag&quot;) {
</span><del>-      curState.tagName = tagName;
-      curState.tagStart = curStream.column();
-      return cont(attributes, endtag(curState.startOfLine));
</del><ins>+      state.tagStart = stream.column();
+      return tagNameState;
</ins><span class="cx">     } else if (type == &quot;closeTag&quot;) {
</span><del>-      var err = false;
-      if (curState.context) {
-        if (curState.context.tagName != tagName) {
-          if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
-            popContext();
-          }
-          err = !curState.context || curState.context.tagName != tagName;
-        }
-      } else {
-        err = true;
-      }
-      if (err) setStyle = &quot;error&quot;;
-      return cont(endclosetag(err));
</del><ins>+      return closeTagNameState;
+    } else {
+      return baseState;
</ins><span class="cx">     }
</span><del>-    return cont();
</del><span class="cx">   }
</span><del>-  function endtag(startOfLine) {
-    return function(type) {
-      var tagName = curState.tagName;
-      curState.tagName = curState.tagStart = null;
-      if (type == &quot;selfcloseTag&quot; ||
-          (type == &quot;endTag&quot; &amp;&amp; Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
-        maybePopContext(tagName.toLowerCase());
-        return cont();
-      }
-      if (type == &quot;endTag&quot;) {
-        maybePopContext(tagName.toLowerCase());
-        pushContext(tagName, startOfLine);
-        return cont();
-      }
-      return cont();
-    };
-  }
-  function endclosetag(err) {
-    return function(type) {
-      if (err) setStyle = &quot;error&quot;;
-      if (type == &quot;endTag&quot;) { popContext(); return cont(); }
</del><ins>+  function tagNameState(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      state.tagName = stream.current();
+      setStyle = &quot;tag&quot;;
+      return attrState;
+    } else {
</ins><span class="cx">       setStyle = &quot;error&quot;;
</span><del>-      return cont(arguments.callee);
-    };
</del><ins>+      return tagNameState;
+    }
</ins><span class="cx">   }
</span><del>-  function maybePopContext(nextTagName) {
-    var parentTagName;
-    while (true) {
-      if (!curState.context) {
-        return;
</del><ins>+  function closeTagNameState(type, stream, state) {
+    if (type == &quot;word&quot;) {
+      var tagName = stream.current();
+      if (state.context &amp;&amp; state.context.tagName != tagName &amp;&amp;
+          Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
+        popContext(state);
+      if (state.context &amp;&amp; state.context.tagName == tagName) {
+        setStyle = &quot;tag&quot;;
+        return closeState;
+      } else {
+        setStyle = &quot;tag error&quot;;
+        return closeStateErr;
</ins><span class="cx">       }
</span><del>-      parentTagName = curState.context.tagName.toLowerCase();
-      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
-          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
-        return;
-      }
-      popContext();
</del><ins>+    } else {
+      setStyle = &quot;error&quot;;
+      return closeStateErr;
</ins><span class="cx">     }
</span><span class="cx">   }
</span><span class="cx"> 
</span><del>-  function attributes(type) {
-    if (type == &quot;word&quot;) {setStyle = &quot;attribute&quot;; return cont(attribute, attributes);}
-    if (type == &quot;endTag&quot; || type == &quot;selfcloseTag&quot;) return pass();
</del><ins>+  function closeState(type, _stream, state) {
+    if (type != &quot;endTag&quot;) {
+      setStyle = &quot;error&quot;;
+      return closeState;
+    }
+    popContext(state);
+    return baseState;
+  }
+  function closeStateErr(type, stream, state) {
</ins><span class="cx">     setStyle = &quot;error&quot;;
</span><del>-    return cont(attributes);
</del><ins>+    return closeState(type, stream, state);
</ins><span class="cx">   }
</span><del>-  function attribute(type) {
-    if (type == &quot;equals&quot;) return cont(attvalue, attributes);
</del><ins>+
+  function attrState(type, _stream, state) {
+    if (type == &quot;word&quot;) {
+      setStyle = &quot;attribute&quot;;
+      return attrEqState;
+    } else if (type == &quot;endTag&quot; || type == &quot;selfcloseTag&quot;) {
+      var tagName = state.tagName, tagStart = state.tagStart;
+      state.tagName = state.tagStart = null;
+      if (type == &quot;selfcloseTag&quot; ||
+          Kludges.autoSelfClosers.hasOwnProperty(tagName)) {
+        maybePopContext(state, tagName);
+      } else {
+        maybePopContext(state, tagName);
+        state.context = new Context(state, tagName, tagStart == state.indented);
+      }
+      return baseState;
+    }
+    setStyle = &quot;error&quot;;
+    return attrState;
+  }
+  function attrEqState(type, stream, state) {
+    if (type == &quot;equals&quot;) return attrValueState;
</ins><span class="cx">     if (!Kludges.allowMissing) setStyle = &quot;error&quot;;
</span><del>-    else if (type == &quot;word&quot;) {setStyle = &quot;attribute&quot;; return cont(attribute, attributes);}
-    return (type == &quot;endTag&quot; || type == &quot;selfcloseTag&quot;) ? pass() : cont();
</del><ins>+    return attrState(type, stream, state);
</ins><span class="cx">   }
</span><del>-  function attvalue(type) {
-    if (type == &quot;string&quot;) return cont(attvaluemaybe);
-    if (type == &quot;word&quot; &amp;&amp; Kludges.allowUnquoted) {setStyle = &quot;string&quot;; return cont();}
</del><ins>+  function attrValueState(type, stream, state) {
+    if (type == &quot;string&quot;) return attrContinuedState;
+    if (type == &quot;word&quot; &amp;&amp; Kludges.allowUnquoted) {setStyle = &quot;string&quot;; return attrState;}
</ins><span class="cx">     setStyle = &quot;error&quot;;
</span><del>-    return (type == &quot;endTag&quot; || type == &quot;selfCloseTag&quot;) ? pass() : cont();
</del><ins>+    return attrState(type, stream, state);
</ins><span class="cx">   }
</span><del>-  function attvaluemaybe(type) {
-    if (type == &quot;string&quot;) return cont(attvaluemaybe);
-    else return pass();
</del><ins>+  function attrContinuedState(type, stream, state) {
+    if (type == &quot;string&quot;) return attrContinuedState;
+    return attrState(type, stream, state);
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">   return {
</span><span class="cx">     startState: function() {
</span><del>-      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null};
</del><ins>+      return {tokenize: inText,
+              state: baseState,
+              indented: 0,
+              tagName: null, tagStart: null,
+              context: null};
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     token: function(stream, state) {
</span><del>-      if (!state.tagName &amp;&amp; stream.sol()) {
-        state.startOfLine = true;
</del><ins>+      if (!state.tagName &amp;&amp; stream.sol())
</ins><span class="cx">         state.indented = stream.indentation();
</span><del>-      }
</del><ins>+
</ins><span class="cx">       if (stream.eatSpace()) return null;
</span><del>-
-      setStyle = type = tagName = null;
</del><ins>+      type = null;
</ins><span class="cx">       var style = state.tokenize(stream, state);
</span><del>-      state.type = type;
</del><span class="cx">       if ((style || type) &amp;&amp; style != &quot;comment&quot;) {
</span><del>-        curState = state; curStream = stream;
-        while (true) {
-          var comb = state.cc.pop() || element;
-          if (comb(type || style)) break;
-        }
</del><ins>+        setStyle = null;
+        state.state = state.state(type || style, stream, state);
+        if (setStyle)
+          style = setStyle == &quot;error&quot; ? style + &quot; error&quot; : setStyle;
</ins><span class="cx">       }
</span><del>-      state.startOfLine = false;
-      if (setStyle)
-        style = setStyle == &quot;error&quot; ? style + &quot; error&quot; : setStyle;
</del><span class="cx">       return style;
</span><span class="cx">     },
</span><span class="cx"> 
</span><span class="lines">@@ -311,8 +323,8 @@
</span><span class="cx">       if (state.tokenize.isInAttribute) {
</span><span class="cx">         return state.stringStartCol + 1;
</span><span class="cx">       }
</span><del>-      if ((state.tokenize != inTag &amp;&amp; state.tokenize != inText) ||
-          context &amp;&amp; context.noIndent)
</del><ins>+      if (context &amp;&amp; context.noIndent) return CodeMirror.Pass;
+      if (state.tokenize != inTag &amp;&amp; state.tokenize != inText)
</ins><span class="cx">         return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
</span><span class="cx">       // Indent the starts of attribute names.
</span><span class="cx">       if (state.tagName) {
</span><span class="lines">@@ -322,15 +334,34 @@
</span><span class="cx">           return state.tagStart + indentUnit * multilineTagIndentFactor;
</span><span class="cx">       }
</span><span class="cx">       if (alignCDATA &amp;&amp; /&lt;!\[CDATA\[/.test(textAfter)) return 0;
</span><del>-      if (context &amp;&amp; /^&lt;\//.test(textAfter))
-        context = context.prev;
</del><ins>+      var tagAfter = textAfter &amp;&amp; /^&lt;(\/)?([\w_:\.-]*)/.exec(textAfter);
+      if (tagAfter &amp;&amp; tagAfter[1]) { // Closing tag spotted
+        while (context) {
+          if (context.tagName == tagAfter[2]) {
+            context = context.prev;
+            break;
+          } else if (Kludges.implicitlyClosed.hasOwnProperty(context.tagName)) {
+            context = context.prev;
+          } else {
+            break;
+          }
+        }
+      } else if (tagAfter) { // Opening tag spotted
+        while (context) {
+          var grabbers = Kludges.contextGrabbers[context.tagName];
+          if (grabbers &amp;&amp; grabbers.hasOwnProperty(tagAfter[2]))
+            context = context.prev;
+          else
+            break;
+        }
+      }
</ins><span class="cx">       while (context &amp;&amp; !context.startOfLine)
</span><span class="cx">         context = context.prev;
</span><span class="cx">       if (context) return context.indent + indentUnit;
</span><span class="cx">       else return 0;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    electricChars: &quot;/&quot;,
</del><ins>+    electricInput: /&lt;\/[\s\w:]+&gt;$/,
</ins><span class="cx">     blockCommentStart: &quot;&lt;!--&quot;,
</span><span class="cx">     blockCommentEnd: &quot;--&gt;&quot;,
</span><span class="cx"> 
</span><span class="lines">@@ -343,3 +374,5 @@
</span><span class="cx"> CodeMirror.defineMIME(&quot;application/xml&quot;, &quot;xml&quot;);
</span><span class="cx"> if (!CodeMirror.mimeModes.hasOwnProperty(&quot;text/html&quot;))
</span><span class="cx">   CodeMirror.defineMIME(&quot;text/html&quot;, {name: &quot;xml&quot;, htmlMode: true});
</span><ins>+
+});
</ins></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceMainhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Main.html (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/Main.html        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -150,7 +150,6 @@
</span><span class="cx">     &lt;script src=&quot;External/CodeMirror/css.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;External/CodeMirror/htmlmixed.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;External/CodeMirror/javascript.js&quot;&gt;&lt;/script&gt;
</span><del>-    &lt;script src=&quot;External/CodeMirror/less.js&quot;&gt;&lt;/script&gt;
</del><span class="cx">     &lt;script src=&quot;External/CodeMirror/livescript.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;External/CodeMirror/matchbrackets.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;External/CodeMirror/overlay.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -61,7 +61,7 @@
</span><span class="cx">     display: none;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines pre span:not(.css-style-declaration-property):not(.CodeMirror-widget):not(.cm-comment):not(.cm-tab),
</del><ins>+.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines pre &gt; span span:not(.css-style-declaration-property):not(.CodeMirror-widget):not(.cm-comment):not(.cm-tab),
</ins><span class="cx"> .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.disabled,
</span><span class="cx"> .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.invalid,
</span><span class="cx"> .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.other-vendor,
</span><span class="lines">@@ -72,7 +72,7 @@
</span><span class="cx">     -webkit-text-stroke-color: rgba(0, 0, 0, 0.6);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines pre span:not(.css-style-declaration-property):not(.CodeMirror-widget):not(.cm-comment):not(.cm-tab),
</del><ins>+.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines pre &gt; span span:not(.css-style-declaration-property):not(.CodeMirror-widget):not(.cm-comment):not(.cm-tab),
</ins><span class="cx"> .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.invalid {
</span><span class="cx">     -webkit-text-stroke-color: rgba(255, 0, 0, 0.6);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -398,7 +398,7 @@
</span><span class="cx">                 swatchElement.title = WebInspector.UIString(&quot;Click to open a colorpicker. Shift-click to change color format.&quot;);
</span><span class="cx">                 swatchElement.className = WebInspector.CSSStyleDeclarationTextEditor.ColorSwatchElementStyleClassName;
</span><span class="cx">                 swatchElement.addEventListener(&quot;click&quot;, this._colorSwatchClicked.bind(this));
</span><del>-                            
</del><ins>+
</ins><span class="cx">                 var swatchInnerElement = document.createElement(&quot;span&quot;);
</span><span class="cx">                 swatchInnerElement.style.backgroundColor = colorString;
</span><span class="cx">                 swatchElement.appendChild(swatchInnerElement);
</span><span class="lines">@@ -852,7 +852,7 @@
</span><span class="cx">                 this._codeMirror.setValue(styleText);
</span><span class="cx"> 
</span><span class="cx">                 if (this._prefixWhitespace)
</span><del>-                    this._codeMirror.removeLine(0);
</del><ins>+                    this._codeMirror.replaceRange(&quot;&quot;, {line: 0, ch: 0}, {line: 1, ch: 0});
</ins><span class="cx"> 
</span><span class="cx">                 if (this._suffixWhitespace) {
</span><span class="cx">                     var lineCount = this._codeMirror.lineCount();
</span><span class="lines">@@ -953,7 +953,7 @@
</span><span class="cx">         if (!this._style || !this._style.ownerRule || !this._style.ownerRule.sourceCodeLocation)
</span><span class="cx">             this._jumpToSymbolTrackingModeEnabled = false;
</span><span class="cx">         else
</span><del>-            this._jumpToSymbolTrackingModeEnabled = WebInspector.modifierKeys.metaKey &amp;&amp; !WebInspector.modifierKeys.altKey &amp;&amp; !WebInspector.modifierKeys.shiftKey;
</del><ins>+            this._jumpToSymbolTrackingModeEnabled = WebInspector.modifierKeys.altKey &amp;&amp; !WebInspector.modifierKeys.metaKey &amp;&amp; !WebInspector.modifierKeys.shiftKey;
</ins><span class="cx"> 
</span><span class="cx">         if (oldJumpToSymbolTrackingModeEnabled !== this._jumpToSymbolTrackingModeEnabled) {
</span><span class="cx">             if (this._jumpToSymbolTrackingModeEnabled) {
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSyntaxHighlightingDefaultThemecss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SyntaxHighlightingDefaultTheme.css (167293 => 167294)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SyntaxHighlightingDefaultTheme.css        2014-04-15 03:53:47 UTC (rev 167293)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SyntaxHighlightingDefaultTheme.css        2014-04-15 04:31:41 UTC (rev 167294)
</span><span class="lines">@@ -41,14 +41,16 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .cm-s-default .cm-tag,
</span><ins>+.cm-s-default .cm-bracket,
+.cm-s-default .cm-tag.cm-bracket,
</ins><span class="cx"> .cm-s-default .cm-atom,
</span><span class="cx"> .cm-s-default .cm-keyword,
</span><ins>+.cm-s-default .cm-m-css.cm-tag,
</ins><span class="cx"> .cm-s-default .cm-m-css.cm-meta,
</span><del>-.cm-s-default .cm-m-css.cm-property,
-.cm-s-default .cm-m-css.cm-string-2,
</del><ins>+.cm-s-default .cm-m-css.cm-variable-3,
</ins><span class="cx"> .cm-s-default .cm-m-javascript.cm-builtin,
</span><span class="cx"> .syntax-highlighted .css-keyword,
</span><del>-.syntax-highlighted .css-property,
</del><ins>+.syntax-highlighted .css-tag,
</ins><span class="cx"> .syntax-highlighted .css-at-rule,
</span><span class="cx"> .syntax-highlighted .css-important,
</span><span class="cx"> .syntax-highlighted .javascript-keyword,
</span><span class="lines">@@ -69,9 +71,10 @@
</span><span class="cx"> .cm-s-default .cm-qualifier,
</span><span class="cx"> .cm-s-default .cm-variable,
</span><span class="cx"> .cm-s-default .cm-variable-2,
</span><del>-.cm-s-default .cm-variable-3,
-.cm-s-default .cm-m-css.cm-tag,
</del><ins>+.cm-s-default .cm-m-css.cm-string-2,
</ins><span class="cx"> .cm-s-default .cm-m-css.cm-builtin,
</span><ins>+.cm-s-default .cm-m-css.cm-property,
+.syntax-highlighted .css-property,
</ins><span class="cx"> .syntax-highlighted .css-selector,
</span><span class="cx"> .syntax-highlighted .javascript-ident {
</span><span class="cx">     color: inherit;
</span></span></pre>
</div>
</div>

</body>
</html>