98 changes: 73 additions & 25 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -218,24 +218,6 @@ <h2 id="config">Configuration</h2>
simply <code>true</code>), focusing of the editor is also
disallowed.</dd>

<dt id="option_cursorBlinkRate"><code>cursorBlinkRate (number)</code></dt>
<dd>Half-period in milliseconds used for cursor blinking. The default blink
rate is 530ms.</dd>

<dt id="option_workTime"><code>workTime, workDelay (number)</code></dt>
<dd>Highlighting is done by a pseudo background-thread that will
work for <code>workTime</code> milliseconds, and then use
timeout to sleep for <code>workDelay</code> milliseconds. The
defaults are 200 and 300, you can change these options to make
the highlighting more or less aggressive.</dd>

<dt id="option_pollInterval"><code>pollInterval (number)</code></dt>
<dd>Indicates how quickly CodeMirror should poll its input
textarea for changes (when focused). Most input is captured by
events, but some things, like IME input on some browsers, don't
generate events that allow CodeMirror to properly detect it.
Thus, it polls. Default is 100 milliseconds.</dd>

<dt id="option_undoDepth"><code>undoDepth (integer)</code></dt>
<dd>The maximum number of undo levels that the editor stores.
Defaults to 40.</dd>
Expand All @@ -253,7 +235,13 @@ <h2 id="config">Configuration</h2>
set to true when either the source textarea is focused, or it
has an <code>autofocus</code> attribute and no other element is
focused.</dd>
</dl>

<p>Below this a few more specialized, low-level options are
listed. These are only useful in very specific situations, you
might want to skip them the first time you read this manual.</p>

<dl>
<dt id="option_dragDrop"><code>dragDrop (boolean)</code></dt>
<dd>Controls whether drag-and-drop is enabled. On by default.</dd>

Expand Down Expand Up @@ -284,6 +272,50 @@ <h2 id="config">Configuration</h2>
its <code>type</code> property and only do something when it
is <code>keydown</code> (or <code>keypress</code> for actions
that need character data).</dd>

<dt id="option_cursorBlinkRate"><code>cursorBlinkRate (number)</code></dt>
<dd>Half-period in milliseconds used for cursor blinking. The default blink
rate is 530ms.</dd>

<dt id="option_cursorHeight"><code>cursorHeight (number)</code></dt>
<dd>Determines the height of the cursor. Default is 1, meaning
it spans the whole height of the line. For some fonts (and by
some tastes) a smaller height (for example <code>0.85</code>),
which causes the cursor to not reach all the way to the bottom
of the line, looks better</dd>

<dt id="option_workTime"><code>workTime, workDelay (number)</code></dt>
<dd>Highlighting is done by a pseudo background-thread that will
work for <code>workTime</code> milliseconds, and then use
timeout to sleep for <code>workDelay</code> milliseconds. The
defaults are 200 and 300, you can change these options to make
the highlighting more or less aggressive.</dd>

<dt id="option_pollInterval"><code>pollInterval (number)</code></dt>
<dd>Indicates how quickly CodeMirror should poll its input
textarea for changes (when focused). Most input is captured by
events, but some things, like IME input on some browsers, don't
generate events that allow CodeMirror to properly detect it.
Thus, it polls. Default is 100 milliseconds.</dd>

<dt id="option_flattenSpans"><code>flattenSpans (boolean)</code></dt>
<dd>By default, CodeMirror will combine adjacent tokens into a
single span if they have the same class. This will result in a
simpler DOM tree, and thus perform better. With some kinds of
styling (such as rounded corners), this will change the way the
document looks. You can set this option to false to disable this
behavior.</dd>

<dt id="option_viewportMargin"><code>viewportMargin (integer)</code></dt>
<dd>Specifies the amount of lines that are rendered above and
below the part of the document that's currently scrolled into
view. This affects the amount of updates needed when scrolling,
and the amount of work that such an update does. You should
usually leave it at its default, 100. Can be set
to <code>Infinity</code> to make sure the whole document is
always rendered, and thus the browser's text search works on it.
This <em>will</em> have bad effects on performance of big
documents.</dd>
</dl>

<h2 id="events">Events</h2>
Expand Down Expand Up @@ -738,18 +770,24 @@ <h2 id="api">Programming API</h2>
background (which lies behind the selection).
Pass <code>null</code> to clear the classes for a line.</dd>

<dt id="foldLines"><code>foldLines(from, to, unfoldOnEnter) → foldHandle</code></dt>
<dt id="foldLines"><code>foldLines(from, to, options) → foldHandle</code></dt>
<dd>Hide the given range of lines (<code>to</code> is
non-inclusive). Hidden lines don't show up in the editor.
Deleting a region around them does delete them, and copying a
region around will include them in the copied text. Vertical
cursor movement will skip hidden lines.
cursor movement will skip hidden lines. The <code>options</code>
parameter is optional. When given, it should be an object
containing <code>unfoldOnEnter</code>
and/or <code>showWidgets</code> properties.
When <code>unfoldOnEnter</code> is true, horizontal movement
into the range will un-hide the range. When it is false,
horizonal cursor movement will skip it as if it isn't there. The
handle this function returns can be used to unfold the range,
and emits an <a href="#event_unfold"><code>unfold</code></a>
event when unfolded. Folded ranges may overlap and nest.</dd>
horizonal cursor movement will skip it as if it isn't there.
When <code>showWidgets</code> is
true, <a href="#addLineWidget">line widgets</a> in the folded
range will remain visible. The handle this function returns can
be used to unfold the range, and emits
an <a href="#event_unfold"><code>unfold</code></a> event when
unfolded. Folded ranges may overlap and nest.</dd>
<dt id="unfoldLines"><code>unfoldLines(foldHandle)</code></dt>
<dd>When given a handle as returned
by <a href="#foldLines"><code>foldLines</code></a>, this will
Expand Down Expand Up @@ -806,6 +844,9 @@ <h2 id="api">Programming API</h2>
<dt><code>noHScroll (boolean)</code></dt>
<dd>Whether the widget should stay fixed in the face of
horizontal scrolling.</dd>
<dt><code>above (boolean)</code></dt>
<dd>Causes the widget to be placed above instead of below
the text of the line.</dd>
</dl>
Note that the widget node will become a descendant of nodes with
CodeMirror-specific CSS classes, and those classes might in some
Expand Down Expand Up @@ -916,6 +957,13 @@ <h2 id="api">Programming API</h2>
and whenever the option is modified
through <a href="#setOption"><code>setOption</code></a>.</p>

<p id="defineInitHook">If your extention just needs to run some
code whenever a CodeMirror instance is initialized,
use <code>CodeMirror.defineInitHook</code>. Give it a function as
its only argument, and from then on, that function will be called
(with the instance as argument) whenever a new CodeMirror instance
is initialized.</p>

<h2 id="addons">Add-ons</h2>

<p>The <code>lib/util</code> directory in the distribution
Expand Down Expand Up @@ -1207,7 +1255,7 @@ <h2 id="modeapi">Writing CodeMirror Modes</h2>
state.</p>

<p id="indent">If you want your mode to provide smart indentation
(though the <a href="#indentLine"><code>indentLine</code></a>
(through the <a href="#indentLine"><code>indentLine</code></a>
method and the <code>indentAuto</code>
and <code>newlineAndIndent</code> commands, which keys can be
<a href="#option_extraKeys">bound</a> to), you must define
Expand Down
51 changes: 51 additions & 0 deletions doc/oldrelease.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,57 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
</pre>
</div>

<p class="rel">27-02-2012: <a href="http://codemirror.net/codemirror-2.22.zip">Version 2.22</a>:</p>

<ul class="rel-note">
<li>Allow <a href="manual.html#keymaps">key handlers</a> to pass up events, allow binding characters.</li>
<li>Add <a href="manual.html#option_autoClearEmptyLines"><code>autoClearEmptyLines</code></a> option.</li>
<li>Properly use tab stops when rendering tabs.</li>
<li>Make PHP mode more robust.</li>
<li>Support indentation blocks in <a href="manual.html#util_foldcode">code folder</a>.</li>
<li>Add a script for <a href="manual.html#util_match-highlighter">highlighting instances of the selection</a>.</li>
<li>New <a href="../mode/properties/index.html">.properties</a> mode.</li>
<li>Fix many bugs.</li>
</ul>

<p class="rel">27-01-2012: <a href="http://codemirror.net/codemirror-2.21.zip">Version 2.21</a>:</p>

<ul class="rel-note">
<li>Added <a href="../mode/less/index.html">LESS</a>, <a href="../mode/mysql/index.html">MySQL</a>,
<a href="../mode/go/index.html">Go</a>, and <a href="../mode/verilog/index.html">Verilog</a> modes.</li>
<li>Add <a href="manual.html#option_smartIndent"><code>smartIndent</code></a>
option.</li>
<li>Support a cursor in <a href="manual.html#option_readOnly"><code>readOnly</code></a>-mode.</li>
<li>Support assigning multiple styles to a token.</li>
<li>Use a new approach to drawing the selection.</li>
<li>Add <a href="manual.html#scrollTo"><code>scrollTo</code></a> method.</li>
<li>Allow undo/redo events to span non-adjacent lines.</li>
<li>Lots and lots of bugfixes.</li>
</ul>

<p class="rel">20-12-2011: <a href="http://codemirror.net/codemirror-2.2.zip">Version 2.2</a>:</p>

<ul class="rel-note">
<li>Slightly incompatible API changes. Read <a href="upgrade_v2.2.html">this</a>.</li>
<li>New approach
to <a href="manual.html#option_extraKeys">binding</a> keys,
support for <a href="manual.html#option_keyMap">custom
bindings</a>.</li>
<li>Support for overwrite (insert).</li>
<li><a href="manual.html#option_tabSize">Custom-width</a>
and <a href="../demo/visibletabs.html">stylable</a> tabs.</li>
<li>Moved more code into <a href="manual.html#addons">add-on scripts</a>.</li>
<li>Support for sane vertical cursor movement in wrapped lines.</li>
<li>More reliable handling of
editing <a href="manual.html#markText">marked text</a>.</li>
<li>Add minimal <a href="../demo/emacs.html">emacs</a>
and <a href="../demo/vim.html">vim</a> bindings.</li>
<li>Rename <code>coordsFromIndex</code>
to <a href="manual.html#posFromIndex"><code>posFromIndex</code></a>,
add <a href="manual.html#indexFromPos"><code>indexFromPos</code></a>
method.</li>
</ul>

<p class="rel">21-11-2011: <a href="http://codemirror.net/codemirror-2.18.zip">Version 2.18</a>:</p>
<p class="rel-note">Fixes <code>TextMarker.clear</code>, which is broken in 2.17.</p>

Expand Down
72 changes: 72 additions & 0 deletions doc/realworld.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>CodeMirror: Real-world uses</title>
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/>
<link rel="stylesheet" type="text/css" href="docs.css"/>
</head>
<body>

<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1>

<div class="grey">
<img src="baboon.png" class="logo" alt="logo"/>
<pre>
/* Real world uses,
full list */
</pre>
</div>

<p><a href="mailto:marijnh@gmail.com">Contact me</a> if you'd like
your project to be added to this list.</p>

<ul>
<li><a href="http://brackets.io">Adobe Brackets</a> (code editor)</li>
<li><a href="http://bluegriffon.org/">BlueGriffon</a> (HTML editor)</li>
<li><a href="http://cargocollective.com/">Cargo Collective</a> (creative publishing platform)</li>
<li><a href="http://www.codebugapp.com/">Codebug</a> (PHP Xdebug front-end)</li>
<li><a href="http://code.google.com/p/codemirror2-gwt/">CodeMirror2-GWT</a> (Google Web Toolkit wrapper)</li>
<li><a href="http://codev.it/">Codev</a> (collaborative IDE)</li>
<li><a href="http://ot.substance.io/demo/">Collaborative CodeMirror demo</a> (CodeMirror + operational transforms)</li>
<li><a href="http://www.ckwnc.com/">CKWNC</a> (UML editor)</li>
<li><a href="http://cssdeck.com/">CSSDeck</a> (CSS showcase)</li>
<li><a href="http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code">Deck.js integration</a> (slides with editors)</li>
<li><a href="http://www.dbninja.com">DbNinja</a> (MySQL access interface)</li>
<li><a href="http://elm-lang.org/Examples.elm">Elm language examples</a></li>
<li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript</a> (book)</li>
<li><a href="http://www.fastfig.com/">Fastfig</a> (online computation/math tool)</li>
<li><a href="http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html">FathomJS integration</a> (slides with editors, again)</li>
<li><a href="http://tour.golang.org">Go language tour</a></li>
<li><a href="https://github.com/github/android">GitHub's Android app</a></li>
<li><a href="https://script.google.com/">Google Apps Script</a></li>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground) </li>
<li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
<li><a href="http://icecoder.net">ICEcoder</a> (web IDE)</li>
<li><a href="http://extensions.joomla.org/extensions/edition/editors/8723">Joomla plugin</a></li>
<li><a href="http://jsbin.com">jsbin.com</a> (JS playground)</li>
<li><a href="http://www.jshint.com/">JSHint</a> (JS linter)</li>
<li><a href="http://kl1p.com/cmtest/1">kl1p</a> (paste service)</li>
<li><a href="http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/">Light Table</a> (experimental IDE)</li>
<li><a href="http://www.mergely.com/">Mergely</a> (interactive diffing)</li>
<li><a href="https://notex.ch">NoTex</a> (rST authoring)</li>
<li><a href="https://github.com/mamacdon/orion-codemirror">Orion-CodeMirror integration</a> (running CodeMirror modes in Orion)</li>
<li><a href="http://paperjs.org/">Paper.js</a> (graphics scripting)</li>
<li><a href="http://prose.io/">Prose.io</a> (github content editor)</li>
<li><a href="http://ql.io/">ql.io</a> (http API query helper)</li>
<li><a href="http://qyapp.com">QiYun web app platform</a></li>
<li><a href="http://ariya.ofilabs.com/2011/09/hybrid-webnative-desktop-codemirror.html">Qt+Webkit integration</a> (building a desktop CodeMirror app)</li>
<li><a href="http://www.sketchpatch.net/labs/livecodelabIntro.html">sketchPatch Livecodelab</a></li>
<li><a href="http://www.skulpt.org/">Skulpt</a> (in-browser Python environment)</li.
<li><a href="http://www.solidshops.com/">SolidShops</a> (hosted e-commerce platform)</li>
<li><a href="http://sqlfiddle.com">SQLFiddle</a> (SQL playground)</li>
<li><a href="https://thefiletree.com">The File Tree</a> (collab editor)</li>
<li><a href="http://enjalot.com/tributary/2636296/sinwaves.js">Tributary</a> (augmented editing)</li>
<li><a href="http://webglplayground.net/">WebGL playground</a></li>
<li><a href="http://www.wescheme.org/">WeScheme</a> (learning tool)</li>
<li><a href="http://wordpress.org/extend/plugins/codemirror-for-codeeditor/">WordPress plugin</a></li>
<li><a href="http://media.chikuyonok.ru/codemirror2/">Zen Coding</a> (fast XML editing)</li>
</ul>

</body>
</html>
2 changes: 1 addition & 1 deletion doc/upgrade_v3.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ <h2 id=new>New features</h2>
<li>Arbitrary line heights. Using fonts with different heights
inside the editor (whether off by one pixel or fifty) is now
supported and handled gracefully.</li>
<li>In-line widgets. See <a href="../demo/widgets.html">the demo</a>
<li>In-line widgets. See <a href="../demo/widget.html">the demo</a>
and <a href="manual.html#addLineWidget">the docs</a>.</li>
<li>Defining custom options
with <a href="manual.html#defineOption"><code>CodeMirror.defineOption</code></a>.</li>
Expand Down
103 changes: 44 additions & 59 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ <h2 style="margin-top: 0">Usage demos:</h2>
<li><a href="demo/complete.html">Autocompletion</a> (<a href="demo/xmlcomplete.html">XML</a>)</li>
<li><a href="demo/search.html">Search/replace</a></li>
<li><a href="demo/folding.html">Code folding</a></li>
<li><a href="demo/widget.html">Line widgets</a></li>
<li><a href="demo/widget.html">Line widgets</a> (via JSHint)</li>
<li><a href="demo/mustache.html">Mode overlays</a></li>
<li><a href="demo/multiplex.html">Mode multiplexer</a></li>
<li><a href="demo/preview.html">HTML editor with preview</a></li>
Expand All @@ -116,6 +116,7 @@ <h2 style="margin-top: 0">Usage demos:</h2>
<li><a href="demo/vim.html">Vim keybindings</a></li>
<li><a href="demo/closetag.html">Automatic xml tag closing</a></li>
<li><a href="demo/loadmode.html">Lazy mode loading</a></li>
<li><a href="demo/btree.html">Document tree visualization</a></li>
</ul>

<h2>Real-world uses:</h2>
Expand All @@ -139,15 +140,14 @@ <h2>Real-world uses:</h2>
<li><a href="http://ql.io/">ql.io</a> (http API query helper)</li>
<li><a href="http://elm-lang.org/Examples.elm">Elm language examples</a></li>
<li><a href="https://thefiletree.com">The File Tree</a> (collab editor)</li>
<li><a href="http://bluegriffon.org/">BlueGriffon</a> (HTML editor)</li>
<li><a href="http://www.jshint.com/">JSHint</a> (JS linter)</li>
<li><a href="http://kl1p.com/cmtest/1">kl1p</a> (paste service)</li>
<li><a href="http://sqlfiddle.com">SQLFiddle</a> (SQL playground)</li>
<li><a href="http://try.haxe.org">Try Haxe</a> (Haxe Playground) </li>
<li><a href="http://cssdeck.com/">CSSDeck</a> (CSS showcase)</li>
<li><a href="http://www.ckwnc.com/">CKWNC</a> (UML editor)</li>
<li><a href="http://www.sketchpatch.net/labs/livecodelabIntro.html">sketchPatch Livecodelab</a></li>
<li><a href="https://notex.ch">NoTex</a> (rST authoring)</li>
<li><a href="doc/realworld.html">More...</a></li>
</ul>

</div></div>
Expand Down Expand Up @@ -179,9 +179,12 @@ <h2 id="documention">Documentation</h2>
of how to use the editor, and then describes the API in detail.</p>

<p>For those who want to learn more about the code, there is
an <a href="doc/internals.html">overview of the internals</a> available.
a <a href="http://marijnhaverbeke.nl/blog/#cm-internals">series of
posts</a> on CodeMirror on my blog, and the
old <a href="doc/internals.html">overview of the editor
internals</a>.
The <a href="http://github.com/marijnh/CodeMirror">source code</a>
itself is, for the most part, also well commented.</p>
itself is, for the most part, also very readable.</p>

<h2 id="support">Support and bug reports</h2>

Expand Down Expand Up @@ -262,9 +265,10 @@ <h2>Support CodeMirror</h2>
<ul>
<li>Donate
(<span onclick="document.getElementById('paypal').submit();"
class="quasilink">Paypal</span>
or <span onclick="document.getElementById('bankinfo').style.display = 'block';"
class="quasilink">bank</span>)</li>
class="quasilink">Paypal</span>,
<span onclick="document.getElementById('bankinfo').style.display = 'block';"
class="quasilink">bank</span>, or
<a href="https://www.gittip.com/marijnh">Gittip</a>)</li>
<li>Purchase <a href="#commercial">commercial support</a></li>
</ul>

Expand All @@ -286,6 +290,38 @@ <h2>Reading material</h2>

<h2 id=releases>Releases</h2>

<p class="rel">22-10-2012: <a href="http://codemirror.net/codemirror-2.35.zip">Version 2.35</a>:</p>

<ul class="rel-note">
<li>New (sub) mode: <a href="mode/javascript/typescript.html">TypeScript</a>.</li>
<li>Don't overwrite (insert key) when pasing.</li>
<li>Fix several bugs in <a href="doc/manual.html#markText"><code>markText</code></a>/undo interaction.</li>
<li>Better indentation of JavaScript code without semicolons.</li>
<li>Add <a href="doc/manual.html#defineInitHook"><code>defineInitHook</code></a> function.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v2.34...v2.35">list of patches</a>.</li>
</ul>

<p class="rel">22-10-2012: <a href="http://codemirror.net/codemirror-3.0beta2.zip">Version 3.0, beta 2</a>:</p>

<p class="rel-note"><strong>BETA release, new major version</strong>. Only partially
backwards-compatible. See
the <a href="doc/upgrade_v3.html">upgrading
guide</a> for more information. Changes since beta1:</p>

<ul class="rel-note">
<li>Fix page-based coordinate computation.</li>
<li>Fix firing of <a href="doc/manual.html#event_gutterClick"><code>gutterClick</code></a> event.</li>
<li>Add <a href="doc/manual.html#option_cursorHeight"><code>cursorHeight</code></a> option.</li>
<li>Fix bi-directional text regression.</li>
<li>Add <a href="doc/manual.html#option_viewportMargin"><code>viewportMargin</code></a> option.</li>
<li>Directly handle mousewheel events (again, hopefully better).</li>
<li>Make vertical cursor movement more robust (through widgets, big line gaps).</li>
<li>Add <a href="doc/manual.html#option_flattenSpans"><code>flattenSpans</code></a> option.</li>
<li>Many optimizations. Poor responsiveness should be fixed.</li>
<li>Initialization in hidden state works again.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v3.0beta1...v3.0beta2">list of patches</a>.</li>
</ul>

<p class="rel">19-09-2012: <a href="http://codemirror.net/codemirror-2.34.zip">Version 2.34</a>:</p>

<ul class="rel-note">
Expand Down Expand Up @@ -411,57 +447,6 @@ <h2 id=releases>Releases</h2>
<li>Add <a href="doc/manual.html#findMarksAt"><code>findMarksAt</code></a> method.</li>
</ul>

<p class="rel">27-02-2012: <a href="http://codemirror.net/codemirror-2.22.zip">Version 2.22</a>:</p>

<ul class="rel-note">
<li>Allow <a href="doc/manual.html#keymaps">key handlers</a> to pass up events, allow binding characters.</li>
<li>Add <a href="doc/manual.html#option_autoClearEmptyLines"><code>autoClearEmptyLines</code></a> option.</li>
<li>Properly use tab stops when rendering tabs.</li>
<li>Make PHP mode more robust.</li>
<li>Support indentation blocks in <a href="doc/manual.html#util_foldcode">code folder</a>.</li>
<li>Add a script for <a href="doc/manual.html#util_match-highlighter">highlighting instances of the selection</a>.</li>
<li>New <a href="mode/properties/index.html">.properties</a> mode.</li>
<li>Fix many bugs.</li>
</ul>

<p class="rel">27-01-2012: <a href="http://codemirror.net/codemirror-2.21.zip">Version 2.21</a>:</p>

<ul class="rel-note">
<li>Added <a href="mode/less/index.html">LESS</a>, <a href="mode/mysql/index.html">MySQL</a>,
<a href="mode/go/index.html">Go</a>, and <a href="mode/verilog/index.html">Verilog</a> modes.</li>
<li>Add <a href="doc/manual.html#option_smartIndent"><code>smartIndent</code></a>
option.</li>
<li>Support a cursor in <a href="doc/manual.html#option_readOnly"><code>readOnly</code></a>-mode.</li>
<li>Support assigning multiple styles to a token.</li>
<li>Use a new approach to drawing the selection.</li>
<li>Add <a href="doc/manual.html#scrollTo"><code>scrollTo</code></a> method.</li>
<li>Allow undo/redo events to span non-adjacent lines.</li>
<li>Lots and lots of bugfixes.</li>
</ul>

<p class="rel">20-12-2011: <a href="http://codemirror.net/codemirror-2.2.zip">Version 2.2</a>:</p>

<ul class="rel-note">
<li>Slightly incompatible API changes. Read <a href="doc/upgrade_v2.2.html">this</a>.</li>
<li>New approach
to <a href="doc/manual.html#option_extraKeys">binding</a> keys,
support for <a href="doc/manual.html#option_keyMap">custom
bindings</a>.</li>
<li>Support for overwrite (insert).</li>
<li><a href="doc/manual.html#option_tabSize">Custom-width</a>
and <a href="demo/visibletabs.html">stylable</a> tabs.</li>
<li>Moved more code into <a href="doc/manual.html#addons">add-on scripts</a>.</li>
<li>Support for sane vertical cursor movement in wrapped lines.</li>
<li>More reliable handling of
editing <a href="doc/manual.html#markText">marked text</a>.</li>
<li>Add minimal <a href="demo/emacs.html">emacs</a>
and <a href="demo/vim.html">vim</a> bindings.</li>
<li>Rename <code>coordsFromIndex</code>
to <a href="doc/manual.html#posFromIndex"><code>posFromIndex</code></a>,
add <a href="doc/manual.html#indexFromPos"><code>indexFromPos</code></a>
method.</li>
</ul>

<p><a href="doc/oldrelease.html">Older releases...</a></p>

</div></div>
Expand Down
19 changes: 9 additions & 10 deletions keymap/vim.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,12 @@
};

// standard mode switching
iterList(["d", "t", "T", "f", "F", "c", "r"],
function (ch) {
CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) {
cm.setOption("keyMap", "vim-prefix-" + ch);
emptyBuffer();
};
});
iterList(["d", "t", "T", "f", "F", "c", "r"], function (ch) {
CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) {
cm.setOption("keyMap", "vim-prefix-" + ch);
emptyBuffer();
};
});

function addCountBindings(keyMap) {
// Add bindings for number keys
Expand Down Expand Up @@ -645,7 +644,7 @@
};

// Map our movement actions each operator and non-operational movement
motionList.forEach(function(key, index, array) {
iterList(motionList, function(key, index, array) {
CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) {
// Get our selected range
var start = cm.getCursor();
Expand Down Expand Up @@ -695,7 +694,7 @@
});

var nums = [1,2,3,4,5,6,7,8,9];
nums.forEach(function(key, index, array) {
iterList(nums, function(key, index, array) {
CodeMirror.keyMap['vim'][key] = function (cm) {
reptTimes = (reptTimes * 10) + key;
};
Expand All @@ -713,7 +712,7 @@
// Create our keymaps for each operator and make xa and xi where x is an operator
// change to the corrosponding keymap
var operators = ['d', 'y', 'c'];
operators.forEach(function(key, index, array) {
iterList(operators, function(key, index, array) {
CodeMirror.keyMap['vim-prefix-'+key+'a'] = {
auto: 'vim', nofallthrough: true, style: "fat-cursor"
};
Expand Down
14 changes: 9 additions & 5 deletions lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
border-right: 1px solid #ddd;
background-color: #f7f7f7;
}
.CodeMirror-linenumbers {
padding: 0 4px;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
Expand Down Expand Up @@ -177,6 +175,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
Expand All @@ -189,6 +188,11 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
z-index: 0;
}

.CodeMirror-linewidget {
position: relative;
z-index: 2;
}

.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
Expand All @@ -211,8 +215,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
visibility: visible;
}

div.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }

.CodeMirror-searching {
background: #ffa;
Expand Down
1,118 changes: 635 additions & 483 deletions lib/codemirror.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/util/foldcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) {
} else {
var end = rangeFinder(cm, line, hideEnd);
if (end == null) return;
var handle = cm.foldLines(line + 1, end, true);
var handle = cm.foldLines(line + 1, end, {unfoldOnEnter: true});
CodeMirror.on(handle, "unfold", function() {
cm.setGutterMarker(l, "CodeMirror-folded", null);
});
Expand Down
7 changes: 6 additions & 1 deletion lib/util/overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb
},
electricChars: base.electricChars,

innerMode: function(state) { return {state: state.base, mode: base}; }
innerMode: function(state) { return {state: state.base, mode: base}; },

blankLine: function(state) {
if (base.blankLine) base.blankLine(state.base);
if (overlay.blankLine) overlay.blankLine(state.overlay);
}
};
};
2 changes: 1 addition & 1 deletion lib/util/runmode.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CodeMirror.runMode = function(string, modespec, callback, options) {
function esc(str) {
return str.replace(/[<&]/, function(ch) { return ch == "<" ? "&lt;" : "&amp;"; });
return str.replace(/[<&]/g, function(ch) { return ch == "<" ? "&lt;" : "&amp;"; });
}

var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
Expand Down
6 changes: 3 additions & 3 deletions lib/util/searchcursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
query.lastIndex = 0;
var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
while (match) {
start += match.index;
line = line.slice(match.index);
start += match.index + 1;
line = line.slice(start);
query.lastIndex = 0;
var newmatch = query.exec(line);
if (newmatch) match = newmatch;
else break;
start++;
}
start--;
} else {
query.lastIndex = pos.ch;
var line = cm.getLine(pos.line), match = query.exec(line),
Expand Down
13 changes: 9 additions & 4 deletions lib/util/simple-hint.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
editor.replaceRange(str, result.from, result.to);
}
// When there is only one completion, use it directly.
if (completions.length == 1) {insert(completions[0]); return true;}
if (options.completeSingle && completions.length == 1) {
insert(completions[0]);
return true;
}

// Build the select widget
var complete = document.createElement("div");
Expand All @@ -41,7 +44,7 @@
}
sel.firstChild.selected = true;
sel.size = Math.min(10, completions.length);
var pos = editor.cursorCoords();
var pos = editor.cursorCoords(options.alignWithWord ? result.from : null);
complete.style.left = pos.left + "px";
complete.style.top = pos.bottom + "px";
document.body.appendChild(complete);
Expand Down Expand Up @@ -71,7 +74,7 @@
if (code == 13) {CodeMirror.e_stop(event); pick();}
// Escape
else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
else if (code != 38 && code != 40 && code != 33 && code != 34) {
else if (code != 38 && code != 40 && code != 33 && code != 34 && !CodeMirror.isModifierKey(event)) {
close(); editor.focus();
// Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
editor.triggerOnKeyDown(event);
Expand All @@ -92,6 +95,8 @@
};
CodeMirror.simpleHint.defaults = {
closeOnBackspace: true,
closeOnTokenChange: false
closeOnTokenChange: false,
completeSingle: true,
alignWithWord: true
};
})();
1 change: 1 addition & 0 deletions mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
},

indent: function(state, textAfter) {
if (state.tokenize == tokenComment) return CodeMirror.Pass;
if (state.tokenize != tokenBase && state.tokenize != null) return 0;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
Expand Down
2 changes: 1 addition & 1 deletion mode/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ CodeMirror.defineMode("css", function(config) {
else if (/[,+>*\/]/.test(ch)) {
return ret(null, "select-op");
}
else if (ch == "." && stream.match(/^\w+/)) {
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
return ret("qualifier", type);
}
else if (ch == ":") {
Expand Down
4 changes: 2 additions & 2 deletions mode/css/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@ MT.testMode(

MT.testMode(
'classSelector',
'.foo { }',
'.foo-bar_hello { }',
[
'qualifier', '.foo',
'qualifier', '.foo-bar_hello',
null, ' { }'
]
);
Expand Down
222 changes: 83 additions & 139 deletions mode/gfm/gfm.js
Original file line number Diff line number Diff line change
@@ -1,150 +1,94 @@
CodeMirror.defineMode("gfm", function(config, parserConfig) {
var mdMode = CodeMirror.getMode(config, "markdown");
var aliases = {
html: "htmlmixed",
js: "javascript",
json: "application/json",
c: "text/x-csrc",
"c++": "text/x-c++src",
java: "text/x-java",
csharp: "text/x-csharp",
"c#": "text/x-csharp"
};

// make this lazy so that we don't need to load GFM last
var getMode = (function () {
var i, modes = {}, mimes = {}, mime;

var list = CodeMirror.listModes();
for (i = 0; i < list.length; i++) {
modes[list[i]] = list[i];
}
var mimesList = CodeMirror.listMIMEs();
for (i = 0; i < mimesList.length; i++) {
mime = mimesList[i].mime;
mimes[mime] = mimesList[i].mime;
}

for (var a in aliases) {
if (aliases[a] in modes || aliases[a] in mimes)
modes[a] = aliases[a];
}

return function (lang) {
return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null;
};
}());

function markdown(stream, state) {
// intercept fenced code blocks
if (stream.sol() && stream.match(/^```([\w+#]*)/)) {
// try switching mode
state.localMode = getMode(RegExp.$1);
if (state.localMode)
state.localState = state.localMode.startState();

state.token = local;
return 'code';
}

return mdMode.token(stream, state.mdState);
}

function local(stream, state) {
if (stream.sol() && stream.match(/^```/)) {
state.localMode = state.localState = null;
state.token = markdown;
return 'code';
}
else if (state.localMode) {
return state.localMode.token(stream, state.localState);
} else {
stream.skipToEnd();
return 'code';
}
}

// custom handleText to prevent emphasis in the middle of a word
// and add autolinking
function handleText(stream, mdState) {
var match;
if (stream.match(/^\w+:\/\/\S+/)) {
return 'link';
}
if (stream.match(/^[^\[*\\<>` _][^\[*\\<>` ]*[^\[*\\<>` _]/)) {
return mdMode.getType(mdState);
}
if (match = stream.match(/^[^\[*\\<>` ]+/)) {
var word = match[0];
if (word[0] === '_' && word[word.length-1] === '_') {
stream.backUp(word.length);
return undefined;
}
return mdMode.getType(mdState);
}
if (stream.eatSpace()) {
return null;
}
var codeDepth = 0;
function blankLine(state) {
state.code = false;
return null;
}

return {
var gfmOverlay = {
startState: function() {
var mdState = mdMode.startState();
mdState.text = handleText;
return {token: markdown, mode: "markdown", mdState: mdState,
localMode: null, localState: null};
return {
code: false,
codeBlock: false,
ateSpace: false
};
},

copyState: function(state) {
return {token: state.token, mdState: CodeMirror.copyState(mdMode, state.mdState),
localMode: state.localMode,
localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null};
copyState: function(s) {
return {
code: s.code,
codeBlock: s.codeBlock,
ateSpace: s.ateSpace
};
},

token: function(stream, state) {
/* Parse GFM double bracket links */
var ch;
if ((ch = stream.peek()) != undefined && ch == '[') {
stream.next(); // Advance the stream

/* Only handle double bracket links */
if ((ch = stream.peek()) == undefined || ch != '[') {
stream.backUp(1);
return state.token(stream, state);
}

while ((ch = stream.next()) != undefined && ch != ']') {}

if (ch == ']' && (ch = stream.next()) != undefined && ch == ']')
return 'link';

/* If we did not find the second ']' */
stream.backUp(1);
}

/* Match GFM latex formulas, as well as latex formulas within '$' */
if (stream.match(/^\$[^\$]+\$/)) {
return "string";
// Hack to prevent formatting override inside code blocks (block and inline)
if (state.codeBlock) {
if (stream.match(/^```/)) {
state.codeBlock = false;
return null;
}

if (stream.match(/^\\\((.*?)\\\)/)) {
return "string";
}

if (stream.match(/^\$\$[^\$]+\$\$/)) {
return "string";
stream.skipToEnd();
return null;
}
if (stream.sol()) {
state.code = false;
}
if (stream.sol() && stream.match(/^```/)) {
stream.skipToEnd();
state.codeBlock = true;
return null;
}
// If this block is changed, it may need to be updated in Markdown mode
if (stream.peek() === '`') {
stream.next();
var before = stream.pos;
stream.eatWhile('`');
var difference = 1 + stream.pos - before;
if (!state.code) {
codeDepth = difference;
state.code = true;
} else {
if (difference === codeDepth) { // Must be exact
state.code = false;
}
}

if (stream.match(/^\\\[(.*?)\\\]/)) {
return "string";
return null;
} else if (state.code) {
stream.next();
return null;
}
// Check if space. If so, links can be formatted later on
if (stream.eatSpace()) {
state.ateSpace = true;
return null;
}
if (stream.sol() || state.ateSpace) {
state.ateSpace = false;
if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
// User/Project@SHA
// User@SHA
// SHA
return "link";
} else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) {
// User/Project#Num
// User#Num
// #Num
return "link";
}

return state.token(stream, state);
}
if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»]))/i)) {
// URLs
// Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
return "link";
}
stream.next();
return null;
},

innerMode: function(state) {
if (state.token == markdown) return {state: state.mdState, mode: mdMode};
else return {state: state.localState, mode: state.localMode};
}
blankLine: blankLine
};
}, "markdown");
CodeMirror.defineMIME("gfmBase", {
name: "markdown",
underscoresBreakWords: false,
fencedCodeBlocks: true
});
return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay);
});
31 changes: 27 additions & 4 deletions mode/gfm/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,52 @@
<title>CodeMirror: GFM mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/overlay.js"></script>
<script src="../xml/xml.js"></script>
<script src="../markdown/markdown.js"></script>
<script src="gfm.js"></script>

<!-- Code block highlighting modes -->
<script src="../javascript/javascript.js"></script>
<script src="../css/css.js"></script>
<script src="../htmlmixed/htmlmixed.js"></script>
<script src="../clike/clike.js"></script>

<link rel="stylesheet" href="../markdown/markdown.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<link rel="stylesheet" href="../../doc/docs.css">
</head>
<body>
<h1>CodeMirror: GFM mode</h1>

<!-- source: http://daringfireball.net/projects/markdown/basics.text -->
<form><textarea id="code" name="code">
Github Flavored Markdown
GitHub Flavored Markdown
========================

Everything from markdown plus GFM features:

## Fenced code blocks
## URL autolinking

Underscores_are_allowed_between_words.

## Fenced code blocks (and syntax highlighting... someday)

```javascript
for (var i = 0; i &lt; items.length; i++) {
console.log(items[i], i); // log them
}
```

See http://github.github.com/github-flavored-markdown/
## A bit of GitHub spice

* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* \#Num: #1
* User/#Num: mojombo#1
* User/Project#Num: mojombo/god#1

See http://github.github.com/github-flavored-markdown/.

</textarea></form>

Expand All @@ -43,5 +62,9 @@ <h1>CodeMirror: GFM mode</h1>
});
</script>

<p>Optionally depends on other modes for properly highlighted code blocks.</p>

<p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gfm_*">normal</a>, <a href="../../test/index.html#verbose,gfm_*">verbose</a>.</p>

</body>
</html>
225 changes: 225 additions & 0 deletions mode/gfm/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// Initiate ModeTest and set defaults
var MT = ModeTest;
MT.modeName = 'gfm';
MT.modeOptions = {};

// Emphasis characters within a word
MT.testMode(
'emInWordAsterisk',
'foo*bar*hello',
[
null, 'foo',
'em', '*bar*',
null, 'hello'
]
);
MT.testMode(
'emInWordUnderscore',
'foo_bar_hello',
[
null, 'foo_bar_hello'
]
);
MT.testMode(
'emStrongUnderscore',
'___foo___ bar',
[
'strong', '__',
'emstrong', '_foo__',
'em', '_',
null, ' bar'
]
);

// Fenced code blocks
MT.testMode(
'fencedCodeBlocks',
'```\nfoo\n\n```\nbar',
[
'comment', '```',
'comment', 'foo',
'comment', '```',
null, 'bar'
]
);
// Fenced code block mode switching
MT.testMode(
'fencedCodeBlockModeSwitching',
'```javascript\nfoo\n\n```\nbar',
[
'comment', '```javascript',
'variable', 'foo',
'comment', '```',
null, 'bar'
]
);

// SHA
MT.testMode(
'SHA',
'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 bar',
[
null, 'foo ',
'link', 'be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2',
null, ' bar'
]
);
// GitHub highlights hashes 7-40 chars in length
MT.testMode(
'shortSHA',
'foo be6a8cc bar',
[
null, 'foo ',
'link', 'be6a8cc',
null, ' bar'
]
);
// Invalid SHAs
//
// GitHub does not highlight hashes shorter than 7 chars
MT.testMode(
'tooShortSHA',
'foo be6a8c bar',
[
null, 'foo be6a8c bar'
]
);
// GitHub does not highlight hashes longer than 40 chars
MT.testMode(
'longSHA',
'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar',
[
null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar'
]
);
MT.testMode(
'badSHA',
'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar',
[
null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar'
]
);
// User@SHA
MT.testMode(
'userSHA',
'foo bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 hello',
[
null, 'foo ',
'link', 'bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2',
null, ' hello'
]
);
// User/Project@SHA
MT.testMode(
'userProjectSHA',
'foo bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 world',
[
null, 'foo ',
'link', 'bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2',
null, ' world'
]
);

// #Num
MT.testMode(
'num',
'foo #1 bar',
[
null, 'foo ',
'link', '#1',
null, ' bar'
]
);
// bad #Num
MT.testMode(
'badNum',
'foo #1bar hello',
[
null, 'foo #1bar hello'
]
);
// User#Num
MT.testMode(
'userNum',
'foo bar#1 hello',
[
null, 'foo ',
'link', 'bar#1',
null, ' hello'
]
);
// User/Project#Num
MT.testMode(
'userProjectNum',
'foo bar/hello#1 world',
[
null, 'foo ',
'link', 'bar/hello#1',
null, ' world'
]
);

// Vanilla links
MT.testMode(
'vanillaLink',
'foo http://www.example.com/ bar',
[
null, 'foo ',
'link', 'http://www.example.com/',
null, ' bar'
]
);
MT.testMode(
'vanillaLinkPunctuation',
'foo http://www.example.com/. bar',
[
null, 'foo ',
'link', 'http://www.example.com/',
null, '. bar'
]
);
MT.testMode(
'vanillaLinkExtension',
'foo http://www.example.com/index.html bar',
[
null, 'foo ',
'link', 'http://www.example.com/index.html',
null, ' bar'
]
);
// Not a link
MT.testMode(
'notALink',
'```css\nfoo {color:black;}\n```http://www.example.com/',
[
'comment', '```css',
'tag', 'foo',
null, ' {',
'property', 'color',
'operator', ':',
'keyword', 'black',
null, ';}',
'comment', '```',
'link', 'http://www.example.com/'
]
);
// Not a link
MT.testMode(
'notALink',
'``foo `bar` http://www.example.com/`` hello',
[
'comment', '``foo `bar` http://www.example.com/``',
null, ' hello'
]
);
// Not a link
MT.testMode(
'notALink',
'`foo\nhttp://www.example.com/\n`foo\n\nhttp://www.example.com/',
[
'comment', '`foo',
'link', 'http://www.example.com/',
'comment', '`foo',
'link', 'http://www.example.com/'
]
);
1 change: 1 addition & 0 deletions mode/htmlembedded/htmlembedded.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"});
CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"});
CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"});
CodeMirror.defineMIME("application/x-erb", { name: "htmlembedded", scriptingModeSpec:"ruby"});
15 changes: 11 additions & 4 deletions mode/javascript/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,17 @@ <h1>CodeMirror: JavaScript mode</h1>
});
</script>

<p>JavaScript mode supports a single configuration
option, <code>json</code>, which will set the mode to expect JSON
data rather than a JavaScript program.</p>
<p>
JavaScript mode supports a two configuration
options:
<ul>
<li><code>json</code> which will set the mode to expect JSON data rather than a JavaScript program.</li>
<li>
<code>typescript</code> which will activate additional syntax highlighting and some other things for TypeScript code (<a href="typescript.html">demo</a>).
</li>
</ul>
</p>

<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p>
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>, <code>text/typescript</code>, <code>application/typescript</code>.</p>
</body>
</html>
78 changes: 63 additions & 15 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// TODO actually recognize syntax of TypeScript constructs

CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;

// Tokenizer

var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
return {

var jsKeywords = {
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
Expand All @@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
};

// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
"interface": kw("interface"),
"class": kw("class"),
"extends": kw("extends"),
"constructor": kw("constructor"),

// scope modifiers
"public": kw("public"),
"private": kw("private"),
"protected": kw("protected"),
"static": kw("static"),

"super": kw("super"),

// types
"string": type, "number": type, "bool": type, "any": type
};

for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}

return jsKeywords;
}();

var isOperatorChar = /[+\-*&%=<>!?|]/;
Expand Down Expand Up @@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
stream.skipToEnd();
return ret("comment", "comment");
}
else if (state.reAllowed) {
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
/^[\[{}\(,;:]$/.test(state.lastType)) {
nextUntilUnescaped(stream, "/");
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
return ret("regexp", "string-2");
Expand All @@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
else {
stream.eatWhile(/[\w\$_]/);
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
ret("variable", "variable", word);
}
}
Expand Down Expand Up @@ -275,19 +309,30 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "}") return cont();
return pass(statement, block);
}
function maybetype(type) {
if (type == ":") return cont(typedef);
return pass();
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
return pass();
}
function vardef1(type, value) {
if (type == "variable"){register(value); return cont(vardef2);}
return cont();
if (type == "variable") {
register(value);
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
}
return pass();
}
function vardef2(type, value) {
if (value == "=") return cont(expression, vardef2);
if (type == ",") return cont(vardef1);
}
function forspec1(type) {
if (type == "var") return cont(vardef1, forspec2);
if (type == ";") return pass(forspec2);
if (type == "var") return cont(vardef1, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybein);
return pass(forspec2);
return cont(forspec2);
}
function formaybein(type, value) {
if (value == "in") return cont(expression);
Expand All @@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
}
function funarg(type, value) {
if (type == "variable") {register(value); return cont();}
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
}

// Interface
Expand All @@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
startState: function(basecolumn) {
return {
tokenize: jsTokenBase,
reAllowed: true,
kwAllowed: true,
lastType: null,
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
Expand All @@ -334,19 +378,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
state.kwAllowed = type != '.';
state.lastType = type;
return parseJS(state, style, type, content, stream);
},

indent: function(state, textAfter) {
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
if (state.tokenize != jsTokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + 4;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
else if (lexical.info == "switch" && !closing)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
Expand All @@ -359,3 +405,5 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {

CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
48 changes: 48 additions & 0 deletions mode/javascript/typescript.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: TypeScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="javascript.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
</head>
<body>
<h1>CodeMirror: TypeScript mode</h1>

<div><textarea id="code" name="code">
class Greeter {
greeting: string;
constructor (message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}

var greeter = new Greeter("world");

var button = document.createElement('button')
button.innerText = "Say Hello"
button.onclick = function() {
alert(greeter.greet())
}

document.body.appendChild(button)

</textarea></div>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/typescript"
});
</script>

<p>This is a specialization of the <a href="index.html">JavaScript mode</a>.</p>
</body>
</html>
2 changes: 1 addition & 1 deletion mode/lua/lua.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) {
function normal(stream, state) {
var ch = stream.next();
if (ch == "-" && stream.eat("-")) {
if (stream.eat("["))
if (stream.eat("[") && stream.eat("["))
return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state);
stream.skipToEnd();
return "comment";
Expand Down
114 changes: 103 additions & 11 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,51 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html");
var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain");
var aliases = {
html: "htmlmixed",
js: "javascript",
json: "application/json",
c: "text/x-csrc",
"c++": "text/x-c++src",
java: "text/x-java",
csharp: "text/x-csharp",
"c#": "text/x-csharp"
};

var getMode = (function () {
var i, modes = {}, mimes = {}, mime;

var list = [];
for (var m in CodeMirror.modes)
if (CodeMirror.modes.propertyIsEnumerable(m)) list.push(m);
for (i = 0; i < list.length; i++) {
modes[list[i]] = list[i];
}
var mimesList = [];
for (var m in CodeMirror.mimeModes)
if (CodeMirror.mimeModes.propertyIsEnumerable(m))
mimesList.push({mime: m, mode: CodeMirror.mimeModes[m]});
for (i = 0; i < mimesList.length; i++) {
mime = mimesList[i].mime;
mimes[mime] = mimesList[i].mime;
}

for (var a in aliases) {
if (aliases[a] in modes || aliases[a] in mimes)
modes[a] = aliases[a];
}

return function (lang) {
return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null;
};
}());

// Should underscores in words open/close em/strong?
if (modeCfg.underscoresBreakWords === undefined)
modeCfg.underscoresBreakWords = true;

// Turn on fenced code blocks? ("```" to start/end)
if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;

var codeDepth = 0;
var prevLineHasContent = false
Expand Down Expand Up @@ -43,8 +88,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
function blankLine(state) {
// Reset linkTitle state
state.linkTitle = false;
// Reset CODE state
state.code = false;
// Reset EM state
state.em = false;
// Reset STRONG state
Expand All @@ -59,7 +102,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}

function blockNormal(stream, state) {
var match;

if (state.list !== false && state.indentationDiff >= 0) { // Continued list
if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
Expand All @@ -85,9 +127,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return switchInline(stream, state, footnoteLink);
} else if (stream.match(hrRE, true)) {
return hr;
} else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
} else if (stream.match(ulRE, true) || stream.match(olRE, true)) {
state.indentation += 4;
state.list = true;
} else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) {
// try switching mode
state.localMode = getMode(RegExp.$1);
if (state.localMode) state.localState = state.localMode.startState();
switchBlock(stream, state, local);
return code;
}

return switchInline(stream, state, state.inline);
Expand All @@ -107,6 +155,30 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return style;
}

function local(stream, state) {
if (stream.sol() && stream.match(/^```/, true)) {
state.localMode = state.localState = null;
state.f = inlineNormal;
state.block = blockNormal;
return code;
} else if (state.localMode) {
return state.localMode.token(stream, state.localState);
} else {
stream.skipToEnd();
return code;
}
}

function codeBlock(stream, state) {
if(stream.match(codeBlockRE, true)){
state.f = inlineNormal;
state.block = blockNormal;
switchInline(stream, state, state.inline);
return code;
}
stream.skipToEnd();
return code;
}

// Inline
function getType(state) {
Expand Down Expand Up @@ -164,6 +236,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
}

// If this block is changed, it may need to be updated in GFM mode
if (ch === '`') {
var t = getType(state);
var before = stream.pos;
Expand All @@ -189,12 +262,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.inline = state.f = linkHref;
return image;
}
if (ch === '[' && stream.match(/.*\] ?(?:\(|\[)/, false)) {

if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
state.linkText = true;
return getType(state);
}

if (ch === ']' && state.linkText) {
var type = getType(state);
state.linkText = false;
Expand Down Expand Up @@ -227,8 +300,20 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return "tag";
}

var ignoreUnderscore = false;
if (!modeCfg.underscoresBreakWords) {
if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
var prevPos = stream.pos - 2;
if (prevPos >= 0) {
var prevCh = stream.string.charAt(prevPos);
if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
ignoreUnderscore = true;
}
}
}
}
var t = getType(state);
if (ch === '*' || ch === '_') {
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
state.strong = false;
return t;
Expand Down Expand Up @@ -292,15 +377,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return linkhref;
}

var savedInlineRE = [];
function inlineRE(endChar) {
if (!inlineRE[endChar]) {
if (!savedInlineRE[endChar]) {
// Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
// Match any non-endChar, escaped character, as well as the closing
// endChar.
inlineRE[endChar] = new RegExp('^(?:[^\\\\]+?|\\\\.)*?(' + endChar + ')');
savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
}
return inlineRE[endChar];
return savedInlineRE[endChar];
}

function inlineElement(type, endChar, next) {
Expand Down Expand Up @@ -343,6 +429,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
block: s.block,
htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
indentation: s.indentation,

localMode: s.localMode,
localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,

inline: s.inline,
text: s.text,
Expand Down Expand Up @@ -371,6 +460,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

// Reset state.header
state.header = false;

// Reset state.code
state.code = false;

state.f = state.block;
var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
Expand Down
24 changes: 23 additions & 1 deletion mode/markdown/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ MT.testMode(
]
);

// Block code using single backtick (shouldn't work)
MT.testMode(
'blockCodeSingleBacktick',
'`\nfoo\n`',
[
'comment', '`',
null, 'foo',
'comment', '`'
]
);

// Unclosed backticks
// Instead of simply marking as CODE, it would be nice to have an
// incomplete flag for CODE, that is styled slightly different.
Expand Down Expand Up @@ -782,6 +793,17 @@ MT.testMode(
]
);

// Not a link. Should be normal text due to square brackets being used
// regularly in text, especially in quoted material, and no space is allowed
// between square brackets and parentheses (per Dingus).
MT.testMode(
'notALink',
'[foo] (bar)',
[
null, '[foo] (bar)'
]
);

// Reference-style links
MT.testMode(
'linkReference',
Expand Down Expand Up @@ -1241,4 +1263,4 @@ MT.testMode(
[
null, '\\\\# foo'
]
);
);
12 changes: 1 addition & 11 deletions mode/rst/rst.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,7 @@ CodeMirror.defineMode('rst', function(config, options) {
}

function hasMode(mode) {
if (mode) {
var modes = CodeMirror.listModes();

for (var i in modes) {
if (modes[i] == mode) {
return true;
}
}
}

return false;
return mode && CodeMirror.modes.hasOwnProperty(mode);
}

function getMode(mode) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"3.0.1",
"version":"3.0.2",
"main": "codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
3 changes: 3 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<link rel="stylesheet" href="../doc/docs.css">
<link rel="stylesheet" href="mode_test.css">
<script src="../lib/codemirror.js"></script>
<script src="../lib/util/overlay.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/xml/xml.js"></script>

Expand Down Expand Up @@ -45,6 +46,8 @@ <h1>CodeMirror: Test Suite</h1>
<script src="../mode/css/test.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/markdown/test.js"></script>
<script src="../mode/gfm/gfm.js"></script>
<script src="../mode/gfm/test.js"></script>
<script src="../mode/stex/stex.js"></script>
<script src="../mode/stex/test.js"></script>
<script src="../mode/xquery/xquery.js"></script>
Expand Down
66 changes: 58 additions & 8 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,22 +293,35 @@ testCM("markTextMultiLine", function(cm) {
});

testCM("markTextUndo", function(cm) {
var marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, "CodeMirror-matchingbracket");
var marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, "CodeMirror-matchingbracket");
var bookmark = cm.setBookmark({line: 1, ch: 5});
cm.replaceRange("foo", {line: 0, ch: 2});
cm.replaceRange("bar\baz\bug\n", {line: 2, ch: 0}, {line: 3, ch: 0});
var marker1, marker2, bookmark;
cm.compoundChange(function(){
marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, "CodeMirror-matchingbracket");
marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, "CodeMirror-matchingbracket");
bookmark = cm.setBookmark({line: 1, ch: 5});
});
cm.compoundChange(function(){
cm.replaceRange("foo", {line: 0, ch: 2});
cm.replaceRange("bar\baz\bug\n", {line: 2, ch: 0}, {line: 3, ch: 0});
});
cm.setValue("");
eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null);
cm.undo();
eqPos(bookmark.find(), {line: 1, ch: 5});
cm.undo(); cm.undo();
cm.undo();
var m1Pos = marker1.find(), m2Pos = marker2.find();
eqPos(m1Pos.from, {line: 0, ch: 1}); eqPos(m1Pos.to, {line: 0, ch: 3});
eqPos(m2Pos.from, {line: 0, ch: 0}); eqPos(m2Pos.to, {line: 2, ch: 1});
eqPos(bookmark.find(), {line: 1, ch: 5});
}, {value: "1234\n56789\n00\n"});

testCM("markTextStayGone", function(cm) {
var m1 = cm.markText({line: 0, ch: 0}, {line: 0, ch: 1}, "CodeMirror-matchingbracket");
cm.replaceRange("hi", {line: 0, ch: 2});
m1.clear();
cm.undo();
eq(m1.find(), null);
}, {value: "hello"});

testCM("markClearBetween", function(cm) {
cm.setValue("aaa\nbbb\nccc\nddd\n");
cm.markText({line: 0, ch: 0}, {line: 2}, "foo");
Expand Down Expand Up @@ -460,13 +473,13 @@ testCM("hiddenLines", function(cm) {
});

testCM("hiddenLinesAutoUnfold", function(cm) {
var folded = cm.foldLines(1, 3, true), unfolded = 0;
var folded = cm.foldLines(1, 3, {unfoldOnEnter: true}), unfolded = 0;
CodeMirror.on(folded, "unfold", function() {unfolded++;});
cm.setCursor({line: 3, ch: 0});
eq(unfolded, 0);
cm.execCommand("goCharLeft");
eq(unfolded, 1);
var folded = cm.foldLines(1, 3, true), unfolded = 0;
var folded = cm.foldLines(1, 3, {unfoldOnEnter: true}), unfolded = 0;
CodeMirror.on(folded, "unfold", function() {unfolded++;});
eqPos(cm.getCursor(), {line: 3, ch: 0});
cm.setCursor({line: 0, ch: 3});
Expand Down Expand Up @@ -718,6 +731,14 @@ testCM("rtlMovement", function(cm) {
});
});

// Verify that updating a line clears its bidi ordering
testCM("bidiUpdate", function(cm) {
cm.setCursor({line: 0, ch: 2});
cm.replaceSelection("خحج", "start");
cm.execCommand("goCharRight");
eqPos(cm.getCursor(), {line: 0, ch: 4});
}, {value: "abcd\n"});

testCM("movebyTextUnit", function(cm) {
cm.setValue("בְּרֵאשִ\ńéée\n");
cm.execCommand("goLineEnd");
Expand Down Expand Up @@ -783,3 +804,32 @@ testCM("getLineNumber", function(cm) {
cm.setValue("");
eq(cm.getLineNumber(h1), null);
});

testCM("jumpTheGap", function(cm) {
var longLine = "abcdef ghiklmnop qrstuvw xyz ";
longLine += longLine; longLine += longLine; longLine += longLine;
cm.setLine(2, longLine);
cm.setSize("200px", null);
cm.getWrapperElement().style.lineHeight = 2;
cm.setCursor({line: 0, ch: 1});
cm.execCommand("goLineDown");
eqPos(cm.getCursor(), {line: 1, ch: 1});
cm.execCommand("goLineDown");
eqPos(cm.getCursor(), {line: 2, ch: 1});
cm.execCommand("goLineDown");
eq(cm.getCursor().line, 2);
is(cm.getCursor().ch > 1);
cm.execCommand("goLineUp");
eqPos(cm.getCursor(), {line: 2, ch: 1});
cm.execCommand("goLineUp");
eqPos(cm.getCursor(), {line: 1, ch: 1});
var node = document.createElement("div");
node.innerHTML = "hi"; node.style.height = "30px";
cm.addLineWidget(0, node);
cm.addLineWidget(1, node.cloneNode(true), {above: true});
cm.setCursor({line: 0, ch: 2});
cm.execCommand("goLineDown");
eqPos(cm.getCursor(), {line: 1, ch: 2});
cm.execCommand("goLineUp");
eqPos(cm.getCursor(), {line: 0, ch: 2});
}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"});