40 changes: 21 additions & 19 deletions doc/manual.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>

<title>CodeMirror: User Manual</title>
<meta charset="utf-8"/>
Expand Down Expand Up @@ -69,7 +69,7 @@
<section class=first id=overview>
<h2 style="position: relative">
User manual and reference guide
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 5.10.0</span>
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 5.11.0</span>
</h2>

<p>CodeMirror is a code-editor component that can be embedded in
Expand Down Expand Up @@ -577,7 +577,7 @@ <h2>Events</h2>
<dd>Fired whenever new input is read from the hidden textarea
(typed or pasted by the user).</dd>

<dt id="event_electricInput"><code><strong>"electrictInput"</strong> (instance: CodeMirror, line: integer)</code></dt>
<dt id="event_electricInput"><code><strong>"electricInput"</strong> (instance: CodeMirror, line: integer)</code></dt>
<dd>Fired if text input matched the
mode's <a href="#option_electricChars">electric</a> patterns,
and this caused the line's indentation to change.</dd>
Expand Down Expand Up @@ -653,8 +653,10 @@ <h2>Events</h2>
should <em>not</em> try to change the state of the editor.</dd>

<dt id="event_dom"><code><strong>"mousedown"</strong>,
<strong>"dblclick"</strong>, <strong>"contextmenu"</strong>, <strong>"keydown"</strong>, <strong>"keypress"</strong>,
<strong>"keyup"</strong>, <strong>"paste"</strong>, <strong>"dragstart"</strong>, <strong>"dragenter"</strong>,
<strong>"dblclick"</strong>, <strong>"touchstart"</strong>, <strong>"contextmenu"</strong>,
<strong>"keydown"</strong>, <strong>"keypress"</strong>,
<strong>"keyup"</strong>, <strong>"cut"</strong>, <strong>"copy"</strong>, <strong>"paste"</strong>,
<strong>"dragstart"</strong>, <strong>"dragenter"</strong>,
<strong>"dragover"</strong>, <strong>"drop"</strong>
(instance: CodeMirror, event: Event)</code></dt>
<dd>Fired when CodeMirror is handling a DOM event of this type.
Expand Down Expand Up @@ -1271,7 +1273,7 @@ <h3 id="api_selection">Cursor and selection methods</h3>

<dt id="getCursor"><code><strong>doc.getCursor</strong>(?start: string) → {line, ch}</code></dt>
<dd>Retrieve one end of the <em>primary</em>
selection. <code>start</code> is a an optional string indicating
selection. <code>start</code> is an optional string indicating
which end of the selection to return. It may
be <code>"from"</code>, <code>"to"</code>, <code>"head"</code>
(the side of the selection that moves when you press
Expand Down Expand Up @@ -1305,7 +1307,7 @@ <h3 id="api_selection">Cursor and selection methods</h3>
<dd>Determines whether the selection head should be scrolled
into view. Defaults to true.</dd>
<dt id="selection_origin"><code><strong>origin</strong>: string</code></dt>
<dd>Detemines whether the selection history event may be
<dd>Determines whether the selection history event may be
merged with the previous one. When an origin starts with the
character <code>+</code>, and the last recorded selection had
the same origin and was similar (close
Expand Down Expand Up @@ -1440,7 +1442,7 @@ <h3 id="api_configuration">Configuration methods</h3>
<dt id="removeOverlay"><code><strong>cm.removeOverlay</strong>(mode: string|object)</code></dt>
<dd>Pass this the exact value passed for the <code>mode</code>
parameter to <a href="#addOverlay"><code>addOverlay</code></a>,
or a string that corresponds to the <code>name</code> propery of
or a string that corresponds to the <code>name</code> property of
that value, to remove an overlay again.</dd>

<dt id="on"><code><strong>cm.on</strong>(type: string, func: (...args))</code></dt>
Expand Down Expand Up @@ -1785,7 +1787,7 @@ <h3 id="api_sizing">Sizing, scrolling and positioning methods</h3>

<dl>
<dt id="setSize"><code><strong>cm.setSize</strong>(width: number|string, height: number|string)</code></dt>
<dd>Programatically set the size of the editor (overriding the
<dd>Programmatically set the size of the editor (overriding the
applicable <a href="#css-resize">CSS
rules</a>). <code>width</code> and <code>height</code>
can be either numbers (interpreted as pixels) or CSS units
Expand Down Expand Up @@ -2110,7 +2112,7 @@ <h3 id="api_static">Static properties</h3>
through <a href="#setOption"><code>setOption</code></a>.</dd>

<dt id="defineInitHook"><code><strong>CodeMirror.defineInitHook</strong>(func: function)</code></dt>
<dd>If your extention just needs to run some
<dd>If your extension 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
Expand Down Expand Up @@ -2296,7 +2298,7 @@ <h2 id="addons">Addons</h2>
the <code>CodeMirror-matchingtag</code> class). Also
defines
a <a href="#commands">command</a> <code>toMatchingTag</code>,
which you can bind a key to in order to jump to the tag mathing
which you can bind a key to in order to jump to the tag matching
the one under the cursor. Depends on
the <code>addon/fold/xml-fold.js</code>
addon. <a href="../demo/matchtags.html">Demo here.</a></dd>
Expand Down Expand Up @@ -2458,7 +2460,7 @@ <h2 id="addons">Addons</h2>
<dd>Can be used to run a CodeMirror mode over text without
actually opening an editor instance.
See <a href="../demo/runmode.html">the demo</a> for an example.
There are alternate versions of the file avaible for
There are alternate versions of the file available for
running <a href="../addon/runmode/runmode-standalone.js">stand-alone</a>
(without including all of CodeMirror) and
for <a href="../addon/runmode/runmode.node.js">running under
Expand Down Expand Up @@ -2595,9 +2597,9 @@ <h2 id="addons">Addons</h2>
has <code>moveFocus(n)</code>, <code>setFocus(n)</code>, <code>pick()</code>,
and <code>close()</code> methods (see the source for details),
that can be used to change the focused element, pick the
current element or close the menu. Additionnaly <code>menuSize()</code>
current element or close the menu. Additionally <code>menuSize()</code>
can give you access to the size of the current dropdown menu,
<code>length</code> give you the number of availlable completions, and
<code>length</code> give you the number of available completions, and
<code>data</code> give you full access to the completion returned by the
hinting function.</dd>
<dt><code><strong>extraKeys</strong>: keymap</code></dt>
Expand Down Expand Up @@ -2805,7 +2807,7 @@ <h2 id="addons">Addons</h2>
here</a>.</dd>

<dt id="addon_autorefresh"><a href="../addon/display/autorefresh.js"><code>display/autorefresh.js</code></a></dt>
<dd>This addon can be useful when initalizing an editor in a
<dd>This addon can be useful when initializing an editor in a
hidden DOM node, in cases where it is difficult to
call <a href="#refresh"><code>refresh</code></a> when the editor
becomes visible. It defines an option <code>autoRefresh</code>
Expand Down Expand Up @@ -2837,14 +2839,14 @@ <h2 id="addons">Addons</h2>
set of annotations. The method returns an object
whose <code>update</code> method can be called with an array
of <code>{from: Pos, to: Pos}</code> objects marking the ranges
to be higlighed. To detach the annotations, call the
to be highlighted. To detach the annotations, call the
object's <code>clear</code> method.</dd>

<dt id="addon_rulers"><a href="../addon/display/rulers.js"><code>display/rulers.js</code></a></dt>
<dd>Adds a <code>rulers</code> option, which can be used to show
one or more vertical rulers in the editor. The option, if
defined, should be given an array of <code>{column [, className,
color, lineStyle, width]}</code> objects or numbers (wich
color, lineStyle, width]}</code> objects or numbers (which
indicate a column). The ruler will be displayed at the column
indicated by the number or the <code>column</code> property.
The <code>className</code> property can be used to assign a
Expand Down Expand Up @@ -2902,7 +2904,7 @@ <h2 id="addons">Addons</h2>
<dt><code><strong>wrapRange</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>
<dd>Wraps the given range as one big paragraph.</dd>
<dt><code><strong>wrapParagraphsInRange</strong>(from: {line, ch}, to: {line, ch}, ?options: object)</code></dt>
<dd>Wrapps the paragraphs in (and overlapping with) the
<dd>Wraps the paragraphs in (and overlapping with) the
given range individually.</dd>
</dl>
The following options are recognized:
Expand Down Expand Up @@ -3349,7 +3351,7 @@ <h3 id="vimapi_extending">Extending VIM</h3>
be set to a certain position after the operation finishes, it
can return a cursor object.</dd>

<dt id="vimapi_defineActon"><strong><code>defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))</strong></code></dt>
<dt id="vimapi_defineActon"><strong><code>defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))</code></strong></dt>
<dd>Defines an action command, similar to
<strong><code>defineMotion</code></strong>. Action commands
can have arbitrary behavior, making them more flexible than
Expand Down
4 changes: 3 additions & 1 deletion doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ <h2>CodeMirror real-world uses</h2>
<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://web.uvic.ca/~siefkenj/graphit/graphit.html">Graphit</a> (function graphing)</li>
<li><a href="https://hackmd.io">HackMD</a> (Realtime collaborative markdown notes on all platforms)</li>
<li><a href="http://www.handcraft.com/">Handcraft</a> (HTML prototyping)</li>
<li><a href="http://hawkee.com/">Hawkee</a></li>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground) </li>
Expand Down Expand Up @@ -148,6 +149,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://xuanji.appspot.com/isicp/">Structure and Interpretation of Computer Programs</a>, Interactive Version</li>
<li><a href="http://syframework.alwaysdata.net">SyBox</a> (PHP playground)</li>
<li><a href="http://www.tagspaces.org/">TagSpaces</a> (personal data manager)</li>
<li><a href="https://textbox.io/">Textbox.io</a> (WYSIWYG rich text editor)</a></li>
<li><a href="https://thefiletree.com">The File Tree</a> (collab editor)</li>
<li><a href="http://www.mapbox.com/tilemill/">TileMill</a> (map design tool)</li>
<li><a href="http://doc.tiki.org/Syntax+Highlighter">Tiki</a> (wiki CMS groupware)</li>
Expand All @@ -156,7 +158,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://blog.englard.net/post/39608000629/codeintumblr">Tumblr code highlighting shim</a></li>
<li><a href="http://turbopy.com/">TurboPY</a> (web publishing framework)</li>
<li><a href="http://cruise.eecs.uottawa.ca/umpleonline/">UmpleOnline</a> (model-oriented programming tool)</li>
<li><a href="https://upsource.jetbrains.com/#idea/view/923f30395f2603cd9f42a32bcafd13b6c28de0ff/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java">Upsource</a> (code viewer)</li>
<li><a href="https://upsource.jetbrains.com/idea-ce/file/idea-ce-7706e7832aa9e2fd0c2decdb5cbef2225692c696/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorFactoryImpl.java">Upsource</a> (code browser and review tool)</li>
<li><a href="https://github.com/mgaitan/waliki">Waliki</a> (wiki engine)</li>
<li><a href="http://wamer.net/">Wamer</a> (web application builder)</li>
<li><a href="https://github.com/brettz9/webappfind">webappfind</a> (windows file bindings for webapps)</li>
Expand Down
13 changes: 12 additions & 1 deletion doc/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ <h2>Release notes and version history</h2>

<h2>Version 5.x</h2>

<p class="rel">20-01-2016: <a href="http://codemirror.net/codemirror-5.11.zip">Version 5.11</a>:</p>

<ul>
<li>New modes: <a href="../mode/jsx/index.html">JSX</a>, <a href="../mode/haskell-literate/index.html">literate Haskell</a></li>
<li>The editor now forwards more <a href="manual.html#event_dom">DOM events</a>: <code>cut</code>, <code>copy</code>, <code>paste</code>, and <code>touchstart</code>. It will also forward <code>mousedown</code> for drag events</li>
<li>Fixes a bug where bookmarks next to collapsed spans were not rendered</li>
<li>The <a href="../mode/swift/index.html">Swift</a> mode now supports auto-indentation</li>
<li>Frontmatters in the <a href="../mode/yaml-frontmatter/index.html">YAML frontmatter</a> mode are now optional as intended</li>
<li>Full <a href="https://github.com/codemirror/CodeMirror/compare/5.10.0...5.11.0">list of patches</a></li>
</ul>

<p class="rel">21-12-2015: <a href="http://codemirror.net/codemirror-5.10.zip">Version 5.10</a>:</p>

<ul class="rel-note">
Expand Down Expand Up @@ -408,7 +419,7 @@ <h2>Version 3.x</h2>
<p class="rel">16-01-2014: <a href="http://codemirror.net/codemirror-3.21.zip">Version 3.21</a>:</p>

<ul class="rel-note">
<li>Auto-indenting a block will no longer add trailing whitespace to blank lines.</a>
<li>Auto-indenting a block will no longer add trailing whitespace to blank lines.</li>
<li>Marking text has a new option <a href="manual.html#markText"><code>clearWhenEmpty</code></a> to control auto-removal.</li>
<li>Several bugfixes in the handling of bidirectional text.</li>
<li>The <a href="../mode/xml/index.html">XML</a> and <a href="../mode/css/index.html">CSS</a> modes were largely rewritten. <a href="../mode/css/less.html">LESS</a> support was added to the CSS mode.</li>
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ <h2>This is CodeMirror</h2>
</div>
</div>
<div class=actionsleft>
Get the current version: <a href="http://codemirror.net/codemirror.zip">5.10</a>.<br>
Get the current version: <a href="http://codemirror.net/codemirror.zip">5.11</a>.<br>
You can see the <a href="https://github.com/codemirror/codemirror" title="Github repository">code</a> or<br>
read the <a href="doc/releases.html">release notes</a>.<br>
There is a <a href="doc/compress.html">minification helper</a>.
Expand Down
27 changes: 15 additions & 12 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,7 @@
});

function prepareCopyCut(e) {
if (signalDOMEvent(cm, e)) return
if (cm.somethingSelected()) {
lastCopied = cm.getSelections();
if (input.inaccurateSelection) {
Expand Down Expand Up @@ -1615,6 +1616,7 @@
});

function onCopyCut(e) {
if (signalDOMEvent(cm, e)) return
if (cm.somethingSelected()) {
lastCopied = cm.getSelections();
if (e.type == "cut") cm.replaceSelection("", null, "cut");
Expand Down Expand Up @@ -3433,7 +3435,7 @@
return dx * dx + dy * dy > 20 * 20;
}
on(d.scroller, "touchstart", function(e) {
if (!isMouseLikeTouchEvent(e)) {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
clearTimeout(touchFinished);
var now = +new Date;
d.activeTouch = {start: now, moved: false,
Expand Down Expand Up @@ -3562,7 +3564,7 @@
// not interfere with, such as a scrollbar or widget.
function onMouseDown(e) {
var cm = this, display = cm.display;
if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return;
if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return;
display.shift = e.shiftKey;

if (eventInWidget(display, e)) {
Expand Down Expand Up @@ -4814,10 +4816,9 @@
function findPosH(doc, pos, dir, unit, visually) {
var line = pos.line, ch = pos.ch, origDir = dir;
var lineObj = getLine(doc, line);
var possible = true;
function findNextLine() {
var l = line + dir;
if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
if (l < doc.first || l >= doc.first + doc.size) return false
line = l;
return lineObj = getLine(doc, l);
}
Expand All @@ -4827,14 +4828,16 @@
if (!boundToLine && findNextLine()) {
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
else ch = dir < 0 ? lineObj.text.length : 0;
} else return (possible = false);
} else return false
} else ch = next;
return true;
}

if (unit == "char") moveOnce();
else if (unit == "column") moveOnce(true);
else if (unit == "word" || unit == "group") {
if (unit == "char") {
moveOnce()
} else if (unit == "column") {
moveOnce(true)
} else if (unit == "word" || unit == "group") {
var sawType = null, group = unit == "group";
var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
for (var first = true;; first = false) {
Expand All @@ -4855,7 +4858,7 @@
}
}
var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true);
if (!possible) result.hitSide = true;
if (!cmp(pos, result)) result.hitSide = true;
return result;
}

Expand Down Expand Up @@ -7113,14 +7116,14 @@
if (endStyles) for (var j = 0; j < endStyles.length; j += 2)
if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j]

if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j)
buildCollapsedSpan(builder, 0, foundBookmarks[j]);
if (collapsed && (collapsed.from || 0) == pos) {
buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
collapsed.marker, collapsed.from == null);
if (collapsed.to == null) return;
if (collapsed.to == pos) collapsed = false;
}
if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j)
buildCollapsedSpan(builder, 0, foundBookmarks[j]);
}
if (pos >= len) break;

Expand Down Expand Up @@ -8881,7 +8884,7 @@

// THE END

CodeMirror.version = "5.10.0";
CodeMirror.version = "5.11.0";

return CodeMirror;
});
3 changes: 1 addition & 2 deletions mode/asn.1/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>

<title>CodeMirror: ASN.1 mode</title>
<meta charset="utf-8"/>
Expand Down Expand Up @@ -73,6 +73,5 @@ <h2>ASN.1 example</h2>
<p>The development of this mode has been sponsored by <a href="http://www.ericsson.com/">Ericsson
</a>.</p>
<p>Coded by Asmelash Tsegay Gebretsadkan </p>
</article>
</article>

4 changes: 1 addition & 3 deletions mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
function cppHook(stream, state) {
if (!state.startOfLine) return false
for (var ch, next = null; ch = stream.peek();) {
if (!ch) {
break
} else if (ch == "\\" && stream.match(/^.$/)) {
if (ch == "\\" && stream.match(/^.$/)) {
next = cppHook
break
} else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
Expand Down
11 changes: 8 additions & 3 deletions mode/clojure/clojure.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ CodeMirror.defineMode("clojure", function (options) {
sign: /[+-]/,
exponent: /e/i,
keyword_char: /[^\s\(\[\;\)\]]/,
symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/
symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/,
block_indent: /^(?:def|with)[^\/]+$|\/(?:def|with)/
};

function stateStack(indent, type, prev) { // represents a state stack object
Expand Down Expand Up @@ -96,6 +97,9 @@ CodeMirror.defineMode("clojure", function (options) {
if ( '.' == stream.peek() ) {
stream.eat('.');
stream.eatWhile(tests.digit);
} else if ('/' == stream.peek() ) {
stream.eat('/');
stream.eatWhile(tests.digit);
}

if ( stream.eat(tests.exponent) ) {
Expand Down Expand Up @@ -139,7 +143,7 @@ CodeMirror.defineMode("clojure", function (options) {
}

// skip spaces
if (stream.eatSpace()) {
if (state.mode != "string" && stream.eatSpace()) {
return null;
}
var returnType = null;
Expand Down Expand Up @@ -187,7 +191,7 @@ CodeMirror.defineMode("clojure", function (options) {
}

if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) ||
/^(?:def|with)/.test(keyWord))) { // indent-word
tests.block_indent.test(keyWord))) { // indent-word
pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
} else { // non-indent word
// we continue eating the spaces
Expand Down Expand Up @@ -240,5 +244,6 @@ CodeMirror.defineMode("clojure", function (options) {
});

CodeMirror.defineMIME("text/x-clojure", "clojure");
CodeMirror.defineMIME("text/x-clojurescript", "clojure");

});
3 changes: 3 additions & 0 deletions mode/clojure/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ <h2>Clojure mode</h2>
\tab \return \backspace
\u1000 \uAaAa \u9F9F)

;; Let's play with numbers
(+ 1 -1 1/2 -1/2 -0.5 0.5)

</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
Expand Down
27 changes: 14 additions & 13 deletions mode/css/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"animation-direction", "animation-duration", "animation-fill-mode",
"animation-iteration-count", "animation-name", "animation-play-state",
"animation-timing-function", "appearance", "azimuth", "backface-visibility",
"background", "background-attachment", "background-clip", "background-color",
"background-image", "background-origin", "background-position",
"background", "background-attachment", "background-blend-mode", "background-clip",
"background-color", "background-image", "background-origin", "background-position",
"background-repeat", "background-size", "baseline-shift", "binding",
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
"bookmark-target", "border", "border-bottom", "border-bottom-color",
Expand Down Expand Up @@ -597,11 +597,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
"cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
"col-resize", "collapse", "column", "column-reverse", "compact", "condensed", "contain", "content",
"col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
"compact", "condensed", "contain", "content",
"content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
"cross", "crosshair", "currentcolor", "cursive", "cyclic", "dashed", "decimal",
"cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
"decimal-leading-zero", "default", "default-button", "destination-atop",
"destination-in", "destination-out", "destination-over", "devanagari",
"destination-in", "destination-out", "destination-over", "devanagari", "difference",
"disc", "discard", "disclosure-closed", "disclosure-open", "document",
"dot-dash", "dot-dot-dash",
"dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
Expand All @@ -612,23 +613,23 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
"ethiopic-numeric", "ew-resize", "expanded", "extends", "extra-condensed",
"ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
"inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
"italic", "japanese-formal", "japanese-informal", "justify", "kannada",
"katakana", "katakana-iroha", "keep-all", "khmer",
"korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
"landscape", "lao", "large", "larger", "left", "level", "lighter",
"landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
"line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
"lower-roman", "lowercase", "ltr", "malayalam", "match", "matrix", "matrix3d",
"lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
"media-controls-background", "media-current-time-display",
"media-fullscreen-button", "media-mute-button", "media-play-button",
"media-return-to-realtime-button", "media-rewind-button",
Expand All @@ -637,7 +638,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
"menu", "menulist", "menulist-button", "menulist-text",
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
"mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
"mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
"narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
"ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
Expand All @@ -651,15 +652,15 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
"rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
"rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
"s-resize", "sans-serif", "scale", "scale3d", "scaleX", "scaleY", "scaleZ",
"s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
"scroll", "scrollbar", "se-resize", "searchfield",
"searchfield-cancel-button", "searchfield-decoration",
"searchfield-results-button", "searchfield-results-decoration",
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
"simp-chinese-formal", "simp-chinese-informal", "single",
"skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
"small", "small-caps", "small-caption", "smaller", "solid", "somali",
"small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
"source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
"square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
"subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
Expand Down
4 changes: 2 additions & 2 deletions mode/css/less_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@

MT("mixin",
"[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
" [property color]: [variable darken]([variable-2 @color], [number 10%]);",
" [property color]: [atom darken]([variable-2 @color], [number 10%]);",
"}",
"[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
" [property color]: [variable lighten]([variable-2 @color], [number 10%]);",
" [property color]: [atom lighten]([variable-2 @color], [number 10%]);",
"}",
"[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
" [property display]: [atom block];",
Expand Down
2 changes: 1 addition & 1 deletion mode/css/scss_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@

MT('indent_parentheses',
"[tag foo] {",
" [property color]: [variable darken]([variable-2 $blue],",
" [property color]: [atom darken]([variable-2 $blue],",
" [number 9%]);",
"}");

Expand Down
7 changes: 3 additions & 4 deletions mode/django/django.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@

// Ignore completely any stream series that do not match the
// Django template opening tags.
while (stream.next() != null && !stream.match("{{", false) && !stream.match("{%", false)) {}
while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {}
return null;
}

Expand Down Expand Up @@ -317,9 +317,8 @@

// Mark everything as comment inside the tag and the tag itself.
function inComment (stream, state) {
if (stream.match("#}")) {
state.tokenize = tokenBase;
}
if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase
else stream.skipToEnd()
return "comment";
}

Expand Down
5 changes: 2 additions & 3 deletions mode/handlebars/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>

<title>CodeMirror: Handlebars mode</title>
<meta charset="utf-8"/>
Expand Down Expand Up @@ -67,8 +67,7 @@ <h1>{{t 'article_list'}}</h1>
mode: {name: "handlebars", base: "text/html"}
});
</script>
</script>


<p>Handlebars syntax highlighting for CodeMirror.</p>

<p><strong>MIME types defined:</strong> <code>text/x-handlebars-template</code></p>
Expand Down
43 changes: 43 additions & 0 deletions mode/haskell-literate/haskell-literate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../haskell/haskell"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../haskell/haskell"], mod)
else // Plain browser env
mod(CodeMirror)
})(function (CodeMirror) {
"use strict"

CodeMirror.defineMode("haskell-literate", function (config, parserConfig) {
var baseMode = CodeMirror.getMode(config, (parserConfig && parserConfig.base) || "haskell")

return {
startState: function () {
return {
inCode: false,
baseState: CodeMirror.startState(baseMode)
}
},
token: function (stream, state) {
if (stream.sol()) {
if (state.inCode = stream.eat(">"))
return "meta"
}
if (state.inCode) {
return baseMode.token(stream, state.baseState)
} else {
stream.skipToEnd()
return "comment"
}
},
innerMode: function (state) {
return state.inCode ? {state: state.baseState, mode: baseMode} : null
}
}
}, "haskell")

CodeMirror.defineMIME("text/x-literate-haskell", "haskell-literate")
})
282 changes: 282 additions & 0 deletions mode/haskell-literate/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
<!doctype html>

<title>CodeMirror: Haskell-literate mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="haskell-literate.js"></script>
<script src="../haskell/haskell.js"></script>
<style>.CodeMirror {
border-top : 1px solid #DDDDDD;
border-bottom : 1px solid #DDDDDD;
}</style>
<div id=nav>
<a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo
src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/codemirror/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Haskell-literate</a>
</ul>
</div>

<article>
<h2>Haskell literate mode</h2>
<form>
<textarea id="code" name="code">
> {-# LANGUAGE OverloadedStrings #-}
> {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
> import Control.Applicative ((<$>), (<*>))
> import Data.Maybe (isJust)

> import Data.Text (Text)
> import Text.Blaze ((!))
> import qualified Data.Text as T
> import qualified Happstack.Server as Happstack
> import qualified Text.Blaze.Html5 as H
> import qualified Text.Blaze.Html5.Attributes as A

> import Text.Digestive
> import Text.Digestive.Blaze.Html5
> import Text.Digestive.Happstack
> import Text.Digestive.Util

Simple forms and validation
---------------------------

Let's start by creating a very simple datatype to represent a user:

> data User = User
> { userName :: Text
> , userMail :: Text
> } deriving (Show)

And dive in immediately to create a `Form` for a user. The `Form v m a` type
has three parameters:

- `v`: the type for messages and errors (usually a `String`-like type, `Text` in
this case);
- `m`: the monad we are operating in, not specified here;
- `a`: the return type of the `Form`, in this case, this is obviously `User`.

> userForm :: Monad m => Form Text m User

We create forms by using the `Applicative` interface. A few form types are
provided in the `Text.Digestive.Form` module, such as `text`, `string`,
`bool`...

In the `digestive-functors` library, the developer is required to label each
field using the `.:` operator. This might look like a bit of a burden, but it
allows you to do some really useful stuff, like separating the `Form` from the
actual HTML layout.

> userForm = User
> <$> "name" .: text Nothing
> <*> "mail" .: check "Not a valid email address" checkEmail (text Nothing)

The `check` function enables you to validate the result of a form. For example,
we can validate the email address with a really naive `checkEmail` function.

> checkEmail :: Text -> Bool
> checkEmail = isJust . T.find (== '@')

More validation
---------------

For our example, we also want descriptions of Haskell libraries, and in order to
do that, we need package versions...

> type Version = [Int]

We want to let the user input a version number such as `0.1.0.0`. This means we
need to validate if the input `Text` is of this form, and then we need to parse
it to a `Version` type. Fortunately, we can do this in a single function:
`validate` allows conversion between values, which can optionally fail.

`readMaybe :: Read a => String -> Maybe a` is a utility function imported from
`Text.Digestive.Util`.

> validateVersion :: Text -> Result Text Version
> validateVersion = maybe (Error "Cannot parse version") Success .
> mapM (readMaybe . T.unpack) . T.split (== '.')

A quick test in GHCi:

ghci> validateVersion (T.pack "0.3.2.1")
Success [0,3,2,1]
ghci> validateVersion (T.pack "0.oops")
Error "Cannot parse version"

It works! This means we can now easily add a `Package` type and a `Form` for it:

> data Category = Web | Text | Math
> deriving (Bounded, Enum, Eq, Show)

> data Package = Package Text Version Category
> deriving (Show)

> packageForm :: Monad m => Form Text m Package
> packageForm = Package
> <$> "name" .: text Nothing
> <*> "version" .: validate validateVersion (text (Just "0.0.0.1"))
> <*> "category" .: choice categories Nothing
> where
> categories = [(x, T.pack (show x)) | x <- [minBound .. maxBound]]

Composing forms
---------------

A release has an author and a package. Let's use this to illustrate the
composability of the digestive-functors library: we can reuse the forms we have
written earlier on.

> data Release = Release User Package
> deriving (Show)

> releaseForm :: Monad m => Form Text m Release
> releaseForm = Release
> <$> "author" .: userForm
> <*> "package" .: packageForm

Views
-----

As mentioned before, one of the advantages of using digestive-functors is
separation of forms and their actual HTML layout. In order to do this, we have
another type, `View`.

We can get a `View` from a `Form` by supplying input. A `View` contains more
information than a `Form`, it has:

- the original form;
- the input given by the user;
- any errors that have occurred.

It is this view that we convert to HTML. For this tutorial, we use the
[blaze-html] library, and some helpers from the `digestive-functors-blaze`
library.

[blaze-html]: http://jaspervdj.be/blaze/

Let's write a view for the `User` form. As you can see, we here refer to the
different fields in the `userForm`. The `errorList` will generate a list of
errors for the `"mail"` field.

> userView :: View H.Html -> H.Html
> userView view = do
> label "name" view "Name: "
> inputText "name" view
> H.br
>
> errorList "mail" view
> label "mail" view "Email address: "
> inputText "mail" view
> H.br

Like forms, views are also composable: let's illustrate that by adding a view
for the `releaseForm`, in which we reuse `userView`. In order to do this, we
take only the parts relevant to the author from the view by using `subView`. We
can then pass the resulting view to our own `userView`.
We have no special view code for `Package`, so we can just add that to
`releaseView` as well. `childErrorList` will generate a list of errors for each
child of the specified form. In this case, this means a list of errors from
`"package.name"` and `"package.version"`. Note how we use `foo.bar` to refer to
nested forms.

> releaseView :: View H.Html -> H.Html
> releaseView view = do
> H.h2 "Author"
> userView $ subView "author" view
>
> H.h2 "Package"
> childErrorList "package" view
>
> label "package.name" view "Name: "
> inputText "package.name" view
> H.br
>
> label "package.version" view "Version: "
> inputText "package.version" view
> H.br
>
> label "package.category" view "Category: "
> inputSelect "package.category" view
> H.br

The attentive reader might have wondered what the type parameter for `View` is:
it is the `String`-like type used for e.g. error messages.
But wait! We have
releaseForm :: Monad m => Form Text m Release
releaseView :: View H.Html -> H.Html
... doesn't this mean that we need a `View Text` rather than a `View Html`? The
answer is yes -- but having `View Html` allows us to write these views more
easily with the `digestive-functors-blaze` library. Fortunately, we will be able
to fix this using the `Functor` instance of `View`.
fmap :: Monad m => (v -> w) -> View v -> View w
A backend
---------
To finish this tutorial, we need to be able to actually run this code. We need
an HTTP server for that, and we use [Happstack] for this tutorial. The
`digestive-functors-happstack` library gives about everything we need for this.
[Happstack]: http://happstack.com/

> site :: Happstack.ServerPart Happstack.Response
> site = do
> Happstack.decodeBody $ Happstack.defaultBodyPolicy "/tmp" 4096 4096 4096
> r <- runForm "test" releaseForm
> case r of
> (view, Nothing) -> do
> let view' = fmap H.toHtml view
> Happstack.ok $ Happstack.toResponse $
> template $
> form view' "/" $ do
> releaseView view'
> H.br
> inputSubmit "Submit"
> (_, Just release) -> Happstack.ok $ Happstack.toResponse $
> template $ do
> css
> H.h1 "Release received"
> H.p $ H.toHtml $ show release
>
> main :: IO ()
> main = Happstack.simpleHTTP Happstack.nullConf site

Utilities
---------

> template :: H.Html -> H.Html
> template body = H.docTypeHtml $ do
> H.head $ do
> H.title "digestive-functors tutorial"
> css
> H.body body
> css :: H.Html
> css = H.style ! A.type_ "text/css" $ do
> "label {width: 130px; float: left; clear: both}"
> "ul.digestive-functors-error-list {"
> " color: red;"
> " list-style-type: none;"
> " padding-left: 0px;"
> "}"
</textarea>
</form>

<p><strong>MIME types
defined:</strong> <code>text/x-literate-haskell</code>.</p>

<p>Parser configuration parameters recognized: <code>base</code> to
set the base mode (defaults to <code>"haskell"</code>).</p>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "haskell-literate"});
</script>

</article>
4 changes: 2 additions & 2 deletions mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ <h2>Language modes</h2>
<li><a href="groovy/index.html">Groovy</a></li>
<li><a href="haml/index.html">HAML</a></li>
<li><a href="handlebars/index.html">Handlebars</a></li>
<li><a href="haskell/index.html">Haskell</a></li>
<li><a href="haskell/index.html">Haskell</a> (<a href="haskell-literate/index.html">Literate</a>)</li>
<li><a href="haxe/index.html">Haxe</a></li>
<li><a href="htmlembedded/index.html">HTML embedded</a> (JSP, ASP.NET)</li>
<li><a href="htmlmixed/index.html">HTML mixed-mode</a></li>
<li><a href="http/index.html">HTTP</a></li>
<li><a href="idl/index.html">IDL</a></li>
<li><a href="clike/index.html">Java</a></li>
<li><a href="jade/index.html">Jade</a></li>
<li><a href="javascript/index.html">JavaScript</a></li>
<li><a href="javascript/index.html">JavaScript</a> (<a href="jsx/index.html">JSX</a>)</li>
<li><a href="jinja2/index.html">Jinja2</a></li>
<li><a href="julia/index.html">Julia</a></li>
<li><a href="kotlin/index.html">Kotlin</a></li>
Expand Down
6 changes: 3 additions & 3 deletions mode/jade/jade.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ CodeMirror.defineMode('jade', function (config) {
res.javaScriptArguments = this.javaScriptArguments;
res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
res.isInterpolating = this.isInterpolating;
res.interpolationNesting = this.intpolationNesting;
res.interpolationNesting = this.interpolationNesting;

res.jsState = CodeMirror.copyState(jsMode, this.jsState);

Expand Down Expand Up @@ -167,7 +167,7 @@ CodeMirror.defineMode('jade', function (config) {
if (state.interpolationNesting < 0) {
stream.next();
state.isInterpolating = false;
return 'puncutation';
return 'punctuation';
}
} else if (stream.peek() === '{') {
state.interpolationNesting++;
Expand Down Expand Up @@ -583,7 +583,7 @@ CodeMirror.defineMode('jade', function (config) {
copyState: copyState,
token: nextToken
};
});
}, 'javascript', 'css', 'htmlmixed');

CodeMirror.defineMIME('text/x-jade', 'jade');

Expand Down
19 changes: 15 additions & 4 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
})(function(CodeMirror) {
"use strict";

function expressionAllowed(stream, state, backUp) {
return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}

CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
Expand Down Expand Up @@ -126,8 +131,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
} else if (/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - 1)))) {
} else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
return ret("regexp", "string-2");
Expand Down Expand Up @@ -533,6 +537,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern);
if (type == "}") return pass();
return cont(expect(":"), pattern, maybeAssign);
}
function maybeAssign(_type, value) {
Expand Down Expand Up @@ -655,7 +660,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: 0
indented: basecolumn || 0
};
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
state.globalVars = parserConfig.globalVars;
Expand Down Expand Up @@ -711,7 +716,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {

helperType: jsonMode ? "json" : "javascript",
jsonldMode: jsonldMode,
jsonMode: jsonMode
jsonMode: jsonMode,

expressionAllowed: expressionAllowed,
skipExpression: function(state) {
var top = state.cc[state.cc.length - 1]
if (top == expression || top == expressionNoComma) state.cc.pop()
}
};
});

Expand Down
4 changes: 4 additions & 0 deletions mode/javascript/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
" [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
"})();");

MT("destructure_trailing_comma",
"[keyword let] {[def a], [def b],} [operator =] [variable foo];",
"[keyword let] [def c];"); // Parser still in good state?

MT("class_body",
"[keyword class] [def Foo] {",
" [property constructor]() {}",
Expand Down
89 changes: 89 additions & 0 deletions mode/jsx/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!doctype html>

<title>CodeMirror: JSX mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../javascript/javascript.js"></script>
<script src="../xml/xml.js"></script>
<script src="jsx.js"></script>
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<div id=nav>
<a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/codemirror/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">JSX</a>
</ul>
</div>

<article>
<h2>JSX mode</h2>

<div><textarea id="code" name="code">// Code snippets from http://facebook.github.io/react/docs/jsx-in-depth.html

// Rendering HTML tags
var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

// Rendering React components
var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));

// Namespaced components
var Form = MyFormComponent;

var App = (
<Form>
<Form.Row>
<Form.Label />
<Form.Input />
</Form.Row>
</Form>
);

// Attribute JavaScript expressions
var person = <Person name={window.isLoggedIn ? window.name : ''} />;

// Boolean attributes
<input type="button" disabled />;
<input type="button" disabled={true} />;

// Child JavaScript expressions
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;

// Comments
var content = (
<Nav>
{/* child comment, put {} around */}
<Person
/* multi
line
comment */
name={window.isLoggedIn ? window.name : ''} // end of line comment
/>
</Nav>
);
</textarea></div>

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

<p>JSX Mode for <a href="http://facebook.github.io/react">React</a>'s
JavaScript syntax extension.</p>

<p><strong>MIME types defined:</strong> <code>text/jsx</code>.</p>

</article>
147 changes: 147 additions & 0 deletions mode/jsx/jsx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"))
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
"use strict"

// Depth means the amount of open braces in JS context, in XML
// context 0 means not in tag, 1 means in tag, and 2 means in tag
// and js block comment.
function Context(state, mode, depth, prev) {
this.state = state; this.mode = mode; this.depth = depth; this.prev = prev
}

function copyContext(context) {
return new Context(CodeMirror.copyState(context.mode, context.state),
context.mode,
context.depth,
context.prev && copyContext(context.prev))
}

CodeMirror.defineMode("jsx", function(config) {
var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
var jsMode = CodeMirror.getMode(config, "javascript")

function flatXMLIndent(state) {
var tagName = state.tagName
state.tagName = null
var result = xmlMode.indent(state, "")
state.tagName = tagName
return result
}

function token(stream, state) {
if (state.context.mode == xmlMode)
return xmlToken(stream, state, state.context)
else
return jsToken(stream, state, state.context)
}

function xmlToken(stream, state, cx) {
if (cx.depth == 2) { // Inside a JS /* */ comment
if (stream.match(/^.*?\*\//)) cx.depth = 1
else stream.skipToEnd()
return "comment"
}

if (stream.peek() == "{") {
xmlMode.skipAttribute(cx.state)

var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context
// If JS starts on same line as tag
if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) {
while (xmlContext.prev && !xmlContext.startOfLine)
xmlContext = xmlContext.prev
// If tag starts the line, use XML indentation level
if (xmlContext.startOfLine) indent -= config.indentUnit
// Else use JS indentation level
else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented
// Else if inside of tag
} else if (cx.depth == 1) {
indent += config.indentUnit
}

state.context = new Context(CodeMirror.startState(jsMode, indent),
jsMode, 0, state.context)
return null
}

if (cx.depth == 1) { // Inside of tag
if (stream.peek() == "<") { // Tag inside of tag
xmlMode.skipAttribute(cx.state)
state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),
xmlMode, 0, state.context)
return null
} else if (stream.match("//")) {
stream.skipToEnd()
return "comment"
} else if (stream.match("/*")) {
cx.depth = 2
return token(stream, state)
}
}

var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop
if (/\btag\b/.test(style)) {
if (/>$/.test(cur)) {
if (cx.state.context) cx.depth = 0
else state.context = state.context.prev
} else if (/^</.test(cur)) {
cx.depth = 1
}
} else if (!style && (stop = cur.indexOf("{")) > -1) {
stream.backUp(cur.length - stop)
}
return style
}

function jsToken(stream, state, cx) {
if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
jsMode.skipExpression(cx.state)
state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
xmlMode, 0, state.context)
return null
}

var style = jsMode.token(stream, cx.state)
if (!style && cx.depth != null) {
var cur = stream.current()
if (cur == "{") {
cx.depth++
} else if (cur == "}") {
if (--cx.depth == 0) state.context = state.context.prev
}
}
return style
}

return {
startState: function() {
return {context: new Context(CodeMirror.startState(jsMode), jsMode)}
},

copyState: function(state) {
return {context: copyContext(state.context)}
},

token: token,

indent: function(state, textAfter, fullLine) {
return state.context.mode.indent(state.context.state, textAfter, fullLine)
},

innerMode: function(state) {
return state.context
}
}
}, "xml", "javascript")

CodeMirror.defineMIME("text/jsx", "jsx")
})
69 changes: 69 additions & 0 deletions mode/jsx/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "jsx")
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) }

MT("selfclose",
"[keyword var] [def x] [operator =] [bracket&tag <] [tag foo] [bracket&tag />] [operator +] [number 1];")

MT("openclose",
"([bracket&tag <][tag foo][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("attr",
"([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("braced_attr",
"([bracket&tag <][tag foo] [attribute abc]={[number 10]}[bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("braced_text",
"([bracket&tag <][tag foo][bracket&tag >]hello {[number 10]} [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("nested_tag",
"([bracket&tag <][tag foo][bracket&tag ><][tag bar][bracket&tag ></][tag bar][bracket&tag ></][tag foo][bracket&tag >][operator ++])")

MT("nested_jsx",
"[keyword return] (",
" [bracket&tag <][tag foo][bracket&tag >]",
" say {[number 1] [operator +] [bracket&tag <][tag bar] [attribute attr]={[number 10]}[bracket&tag />]}!",
" [bracket&tag </][tag foo][bracket&tag >][operator ++]",
")")

MT("preserve_js_context",
"[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]")

MT("line_comment",
"([bracket&tag <][tag foo] [comment // hello]",
" [bracket&tag ></][tag foo][bracket&tag >][operator ++])")

MT("line_comment_not_in_tag",
"([bracket&tag <][tag foo][bracket&tag >] // hello",
" [bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("block_comment",
"([bracket&tag <][tag foo] [comment /* hello]",
"[comment line 2]",
"[comment line 3 */] [bracket&tag ></][tag foo][bracket&tag >][operator ++])")

MT("block_comment_not_in_tag",
"([bracket&tag <][tag foo][bracket&tag >]/* hello",
" line 2",
" line 3 */ [bracket&tag </][tag foo][bracket&tag >][operator ++])")

MT("missing_attr",
"([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])")

MT("indent_js",
"([bracket&tag <][tag foo][bracket&tag >]",
" [bracket&tag <][tag bar] [attribute baz]={[keyword function]() {",
" [keyword return] [number 10]",
" }}[bracket&tag />]",
" [bracket&tag </][tag foo][bracket&tag >])")

MT("spread",
"([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])")

MT("tag_attribute",
"([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])")
})()
221 changes: 143 additions & 78 deletions mode/julia/julia.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,56 +18,69 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return new RegExp("^((" + words.join(")|(") + "))\\b");
}

var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/;
var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/;
var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
var identifiers = parserConf.identifiers || /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
var blockClosers = ["end", "else", "elseif", "catch", "finally"];
var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall'];
var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];
var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype'];
var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf'];

//var stringPrefixes = new RegExp("^[br]?('|\")")
var stringPrefixes = /^(`|'|"{3}|([br]?"))/;
var stringPrefixes = /^(`|'|"{3}|([brv]?"))/;
var keywords = wordRegexp(keywordList);
var builtins = wordRegexp(builtinList);
var openers = wordRegexp(blockOpeners);
var closers = wordRegexp(blockClosers);
var macro = /^@[_A-Za-z][_A-Za-z0-9]*/;
var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/;
var symbol = /^:[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
var typeAnnotation = /^::[^.,;"{()=$\s]+({[^}]*}+)*/;

function in_array(state) {
var ch = cur_scope(state);
if(ch=="[" || ch=="{") {
function inArray(state) {
var ch = currentScope(state);
if (ch == '[') {
return true;
}
else {
return false;
}
return false;
}

function cur_scope(state) {
if(state.scopes.length==0) {
function currentScope(state) {
if (state.scopes.length == 0) {
return null;
}
return state.scopes[state.scopes.length - 1];
}

// tokenizers
function tokenBase(stream, state) {
//Handle multiline comments
if (stream.match(/^#=\s*/)) {
state.scopes.push('#=');
}
if (currentScope(state) == '#=' && stream.match(/^=#/)) {
state.scopes.pop();
return 'comment';
}
if (state.scopes.indexOf('#=') >= 0) {
if (!stream.match(/.*?(?=(#=|=#))/)) {
stream.skipToEnd();
}
return 'comment';
}

// Handle scope changes
var leaving_expr = state.leaving_expr;
if(stream.sol()) {
leaving_expr = false;
var leavingExpr = state.leavingExpr;
if (stream.sol()) {
leavingExpr = false;
}
state.leaving_expr = false;
if(leaving_expr) {
if(stream.match(/^'+/)) {
state.leavingExpr = false;
if (leavingExpr) {
if (stream.match(/^'+/)) {
return 'operator';
}

}

if(stream.match(/^\.{2,3}/)) {
if (stream.match(/^\.{2,3}/)) {
return 'operator';
}

Expand All @@ -76,56 +89,51 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
}

var ch = stream.peek();
// Handle Comments

// Handle single line comments
if (ch === '#') {
stream.skipToEnd();
return 'comment';
}
if(ch==='[') {
state.scopes.push("[");
stream.skipToEnd();
return 'comment';
}

if(ch==='{') {
state.scopes.push("{");
if (ch === '[') {
state.scopes.push('[');
}

var scope=cur_scope(state);
var scope = currentScope(state);

if(scope==='[' && ch===']') {
if (scope == '[' && ch === ']') {
state.scopes.pop();
state.leaving_expr=true;
state.leavingExpr = true;
}

if(scope==='{' && ch==='}') {
if (scope == '(' && ch === ')') {
state.scopes.pop();
state.leaving_expr=true;
}

if(ch===')') {
state.leaving_expr = true;
state.leavingExpr = true;
}

var match;
if(!in_array(state) && (match=stream.match(openers, false))) {
if (!inArray(state) && (match=stream.match(openers, false))) {
state.scopes.push(match);
}

if(!in_array(state) && stream.match(closers, false)) {
if (!inArray(state) && stream.match(closers, false)) {
state.scopes.pop();
}

if(in_array(state)) {
if(stream.match(/^end/)) {
if (inArray(state)) {
if (state.lastToken == 'end' && stream.match(/^:/)) {
return 'operator';
}
if (stream.match(/^end/)) {
return 'number';
}

}

if(stream.match(/^=>/)) {
if (stream.match(/^=>/)) {
return 'operator';
}


// Handle Number Literals
if (stream.match(/^[0-9\.]/, false)) {
var imMatcher = RegExp(/^im\b/);
Expand All @@ -134,10 +142,11 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
if (stream.match(/^\.\d+/)) { floatLiteral = true; }
if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { floatLiteral = true; }
if (floatLiteral) {
// Float literals may be "imaginary"
stream.match(imMatcher);
state.leaving_expr = true;
state.leavingExpr = true;
return 'number';
}
// Integers
Expand All @@ -157,26 +166,34 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
if (intLiteral) {
// Integer literals may be "long"
stream.match(imMatcher);
state.leaving_expr = true;
state.leavingExpr = true;
return 'number';
}
}

if(stream.match(/^(::)|(<:)/)) {
if (stream.match(/^<:/)) {
return 'operator';
}

if (stream.match(typeAnnotation)) {
return 'builtin';
}

// Handle symbols
if(!leaving_expr && stream.match(symbol)) {
return 'string';
if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) {
return 'builtin';
}

// Handle parametric types
if (stream.match(/^{[^}]*}(?=\()/)) {
return 'builtin';
}

// Handle operators and Delimiters
if (stream.match(operators)) {
return 'operator';
}


// Handle Strings
if (stream.match(stringPrefixes)) {
state.tokenize = tokenStringFactory(stream.current());
Expand All @@ -187,7 +204,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return 'meta';
}


if (stream.match(delimiters)) {
return null;
}
Expand All @@ -200,21 +216,74 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return 'builtin';
}

var isDefinition = state.isDefinition ||
state.lastToken == 'function' ||
state.lastToken == 'macro' ||
state.lastToken == 'type' ||
state.lastToken == 'immutable';

if (stream.match(identifiers)) {
state.leaving_expr=true;
if (isDefinition) {
if (stream.peek() === '.') {
state.isDefinition = true;
return 'variable';
}
state.isDefinition = false;
return 'def';
}
if (stream.match(/^({[^}]*})*\(/, false)) {
return callOrDef(stream, state);
}
state.leavingExpr = true;
return 'variable';
}

// Handle non-detected items
stream.next();
return ERRORCLASS;
}

function callOrDef(stream, state) {
var match = stream.match(/^(\(\s*)/);
if (match) {
if (state.firstParenPos < 0)
state.firstParenPos = state.scopes.length;
state.scopes.push('(');
state.charsAdvanced += match[1].length;
}
if (currentScope(state) == '(' && stream.match(/^\)/)) {
state.scopes.pop();
state.charsAdvanced += 1;
if (state.scopes.length <= state.firstParenPos) {
var isDefinition = stream.match(/^\s*?=(?!=)/, false);
stream.backUp(state.charsAdvanced);
state.firstParenPos = -1;
state.charsAdvanced = 0;
if (isDefinition)
return 'def';
return 'builtin';
}
}
// Unfortunately javascript does not support multiline strings, so we have
// to undo anything done upto here if a function call or definition splits
// over two or more lines.
if (stream.match(/^$/g, false)) {
stream.backUp(state.charsAdvanced);
while (state.scopes.length > state.firstParenPos + 1)
state.scopes.pop();
state.firstParenPos = -1;
state.charsAdvanced = 0;
return 'builtin';
}
state.charsAdvanced += stream.match(/^([^()]*)/)[1].length;
return callOrDef(stream, state);
}

function tokenStringFactory(delimiter) {
while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
delimiter = delimiter.substr(1);
}
var singleline = delimiter.length == 1;
var singleline = delimiter == "'";
var OUTCLASS = 'string';

function tokenString(stream, state) {
Expand Down Expand Up @@ -245,42 +314,38 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return tokenString;
}

function tokenLexer(stream, state) {
var style = state.tokenize(stream, state);
var current = stream.current();

// Handle '.' connected identifiers
if (current === '.') {
style = stream.match(identifiers, false) ? null : ERRORCLASS;
if (style === null && state.lastStyle === 'meta') {
// Apply 'meta' style to '.' connected identifiers when
// appropriate.
style = 'meta';
}
return style;
}

return style;
}

var external = {
startState: function() {
return {
tokenize: tokenBase,
scopes: [],
leaving_expr: false
lastToken: null,
leavingExpr: false,
isDefinition: false,
charsAdvanced: 0,
firstParenPos: -1
};
},

token: function(stream, state) {
var style = tokenLexer(stream, state);
state.lastStyle = style;
var style = state.tokenize(stream, state);
var current = stream.current();

if (current && style) {
state.lastToken = current;
}

// Handle '.' connected identifiers
if (current === '.') {
style = stream.match(identifiers, false) || stream.match(macro, false) ||
stream.match(/\(/, false) ? 'operator' : ERRORCLASS;
}
return style;
},

indent: function(state, textAfter) {
var delta = 0;
if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") {
if (textAfter == "end" || textAfter == "]" || textAfter == "}" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") {
delta = -1;
}
return (state.scopes.length + delta) * _conf.indentUnit;
Expand Down
2 changes: 1 addition & 1 deletion mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}

function local(stream, state) {
if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
if (state.fencedChars && stream.match(state.fencedChars, false)) {
state.localMode = state.localState = null;
state.f = state.block = leavingLocal;
return null;
Expand Down
3 changes: 3 additions & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
{name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
{name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
{name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
{name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
{name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
{name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
{name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
Expand Down Expand Up @@ -56,6 +57,7 @@
{name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
{name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
{name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
{name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
{name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
{name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
{name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
Expand All @@ -69,6 +71,7 @@
mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
{name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
{name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
{name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
{name: "Jinja2", mime: "null", mode: "jinja2"},
{name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
{name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
Expand Down
4 changes: 2 additions & 2 deletions mode/mumps/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!doctype html>

<title>CodeMirror: MUMPS mode</title>
<meta charset="utf-8"/>
Expand Down Expand Up @@ -73,7 +73,7 @@ <h2>MUMPS mode</h2>
IF '$LENGTH($PIECE(XUSER(1),U,2)) QUIT 21 ;p419, p434
Q 0
;
</textarea>
</textarea></div>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "mumps",
Expand Down
4 changes: 2 additions & 2 deletions mode/nginx/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!doctype html>

<!doctype html>
<head>
<title>CodeMirror: NGINX mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
Expand Down
2 changes: 1 addition & 1 deletion mode/nginx/nginx.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,6 @@ CodeMirror.defineMode("nginx", function(config) {
};
});

CodeMirror.defineMIME("text/nginx", "text/x-nginx-conf");
CodeMirror.defineMIME("text/x-nginx-conf", "nginx");

});
4 changes: 1 addition & 3 deletions mode/pig/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<!doctype html>

<!doctype html>
<title>CodeMirror: Pig Latin mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
Expand Down Expand Up @@ -51,5 +50,4 @@ <h2>Pig Latin mode</h2>

<p><strong>MIME type defined:</strong> <code>text/x-pig</code>
(PIG code)
</html>
</article>
2 changes: 1 addition & 1 deletion mode/ruby/ruby.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ CodeMirror.defineMode("ruby", function(config) {
(state.continuedLine ? config.indentUnit : 0);
},

electricChars: "}de", // enD and rescuE
electricInput: /^\s*(?:end|rescue|\})$/,
lineComment: "#"
};
});
Expand Down
45 changes: 42 additions & 3 deletions mode/swift/swift.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
"private","extension"])
var operators = "+-/*%=|&<>#"
var punc = ";,.(){}[]"
var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/
var number = /^-?(?:(?:[\d_]+\.[_\d]*|\.[_\d]+|0o[0-7_\.]+|0b[01_\.]+)(?:e-?[\d_]+)?|0x[\d_a-f\.]+(?:p-?[\d_]+)?)/i
var identifier = /^[_A-Za-z$][_A-Za-z$0-9]*/
var property = /^[@\.][_A-Za-z$][_A-Za-z$0-9]*/
var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\//

function tokenBase(stream, state, prev) {
if (stream.sol()) state.indented = stream.indentation()
if (stream.eatSpace()) return null

var ch = stream.peek()
Expand All @@ -60,7 +60,8 @@
return "operator"
}
if (punc.indexOf(ch) > -1) {
stream.match(delimiters)
stream.next()
stream.match("..")
return "punctuation"
}
if (ch == '"' || ch == "'") {
Expand Down Expand Up @@ -136,23 +137,61 @@
return "comment"
}

CodeMirror.defineMode("swift", function() {
function Context(prev, align, indented) {
this.prev = prev
this.align = align
this.indented = indented
}

function pushContext(state, stream) {
var align = stream.match(/^\s*($|\/[\/\*])/, false) ? null : stream.column() + 1
state.context = new Context(state.context, align, state.indented)
}

function popContext(state) {
if (state.context) {
state.indented = state.context.indented
state.context = state.context.prev
}
}

CodeMirror.defineMode("swift", function(config) {
return {
startState: function() {
return {
prev: null,
context: null,
indented: 0,
tokenize: []
}
},

token: function(stream, state) {
var prev = state.prev
state.prev = null
var tokenize = state.tokenize[state.tokenize.length - 1] || tokenBase
var style = tokenize(stream, state, prev)
if (!style || style == "comment") state.prev = prev
else if (!state.prev) state.prev = style

if (style == "punctuation") {
var bracket = /[\(\[\{]|([\]\)\}])/.exec(stream.current())
if (bracket) (bracket[1] ? popContext : pushContext)(state, stream)
}

return style
},

indent: function(state, textAfter) {
var cx = state.context
if (!cx) return 0
var closing = /^[\]\}\)]/.test(textAfter)
if (cx.align != null) return cx.align - (closing ? 1 : 0)
return cx.indented + (closing ? 0 : config.indentUnit)
},

electricInput: /^\s*[\)\}\]]$/,

lineComment: "//",
blockCommentStart: "/*",
blockCommentEnd: "*/"
Expand Down
2 changes: 1 addition & 1 deletion mode/tiki/tiki.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}

.cm-tw-box {
border-top-width: 0px ! important;
border-top-width: 0px !important;
border-style: solid;
border-width: 1px;
border-color: inherit;
Expand Down
147 changes: 78 additions & 69 deletions mode/xml/xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,56 @@
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
var htmlConfig = {
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
'track': true, 'wbr': true, 'menuitem': true},
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
'th': true, 'tr': true},
contextGrabbers: {
'dd': {'dd': true, 'dt': true},
'dt': {'dd': true, 'dt': true},
'li': {'li': true},
'option': {'option': true, 'optgroup': true},
'optgroup': {'optgroup': true},
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
'rp': {'rp': true, 'rt': true},
'rt': {'rp': true, 'rt': true},
'tbody': {'tbody': true, 'tfoot': true},
'td': {'td': true, 'th': true},
'tfoot': {'tbody': true},
'th': {'td': true, 'th': true},
'thead': {'tbody': true, 'tfoot': true},
'tr': {'tr': true}
},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: true,
caseFold: true
}

var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
'track': true, 'wbr': true, 'menuitem': true},
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
'th': true, 'tr': true},
contextGrabbers: {
'dd': {'dd': true, 'dt': true},
'dt': {'dd': true, 'dt': true},
'li': {'li': true},
'option': {'option': true, 'optgroup': true},
'optgroup': {'optgroup': true},
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
'rp': {'rp': true, 'rt': true},
'rt': {'rp': true, 'rt': true},
'tbody': {'tbody': true, 'tfoot': true},
'td': {'td': true, 'th': true},
'tfoot': {'tbody': true},
'th': {'td': true, 'th': true},
'thead': {'tbody': true, 'tfoot': true},
'tr': {'tr': true}
},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: true,
caseFold: true
} : {
autoSelfClosers: {},
implicitlyClosed: {},
contextGrabbers: {},
doNotIndent: {},
allowUnquoted: false,
allowMissing: false,
caseFold: false
};
var alignCDATA = parserConfig.alignCDATA;
var xmlConfig = {
autoSelfClosers: {},
implicitlyClosed: {},
contextGrabbers: {},
doNotIndent: {},
allowUnquoted: false,
allowMissing: false,
caseFold: false
}

CodeMirror.defineMode("xml", function(editorConf, config_) {
var indentUnit = editorConf.indentUnit
var config = {}
var defaults = config_.htmlMode ? htmlConfig : xmlConfig
for (var prop in defaults) config[prop] = defaults[prop]
for (var prop in config_) config[prop] = config_[prop]

// Return variables for tokenizers
var type, setStyle;
Expand Down Expand Up @@ -188,7 +190,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
this.tagName = tagName;
this.indent = state.indented;
this.startOfLine = startOfLine;
if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
this.noIndent = true;
}
function popContext(state) {
Expand All @@ -201,8 +203,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
return;
}
parentTagName = state.context.tagName;
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
!config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
return;
}
popContext(state);
Expand Down Expand Up @@ -233,7 +235,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
if (type == "word") {
var tagName = stream.current();
if (state.context && state.context.tagName != tagName &&
Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
config.implicitlyClosed.hasOwnProperty(state.context.tagName))
popContext(state);
if (state.context && state.context.tagName == tagName) {
setStyle = "tag";
Expand Down Expand Up @@ -269,7 +271,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
var tagName = state.tagName, tagStart = state.tagStart;
state.tagName = state.tagStart = null;
if (type == "selfcloseTag" ||
Kludges.autoSelfClosers.hasOwnProperty(tagName)) {
config.autoSelfClosers.hasOwnProperty(tagName)) {
maybePopContext(state, tagName);
} else {
maybePopContext(state, tagName);
Expand All @@ -282,12 +284,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
}
function attrEqState(type, stream, state) {
if (type == "equals") return attrValueState;
if (!Kludges.allowMissing) setStyle = "error";
if (!config.allowMissing) setStyle = "error";
return attrState(type, stream, state);
}
function attrValueState(type, stream, state) {
if (type == "string") return attrContinuedState;
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
setStyle = "error";
return attrState(type, stream, state);
}
Expand All @@ -297,12 +299,14 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
}

return {
startState: function() {
return {tokenize: inText,
state: baseState,
indented: 0,
tagName: null, tagStart: null,
context: null};
startState: function(baseIndent) {
var state = {tokenize: inText,
state: baseState,
indented: baseIndent || 0,
tagName: null, tagStart: null,
context: null}
if (baseIndent != null) state.baseIndent = baseIndent
return state
},

token: function(stream, state) {
Expand Down Expand Up @@ -335,45 +339,50 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
// Indent the starts of attribute names.
if (state.tagName) {
if (multilineTagIndentPastTag)
if (config.multilineTagIndentPastTag !== false)
return state.tagStart + state.tagName.length + 2;
else
return state.tagStart + indentUnit * multilineTagIndentFactor;
return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
}
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
if (tagAfter && tagAfter[1]) { // Closing tag spotted
while (context) {
if (context.tagName == tagAfter[2]) {
context = context.prev;
break;
} else if (Kludges.implicitlyClosed.hasOwnProperty(context.tagName)) {
} else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
context = context.prev;
} else {
break;
}
}
} else if (tagAfter) { // Opening tag spotted
while (context) {
var grabbers = Kludges.contextGrabbers[context.tagName];
var grabbers = config.contextGrabbers[context.tagName];
if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
context = context.prev;
else
break;
}
}
while (context && !context.startOfLine)
while (context && context.prev && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return 0;
else return state.baseIndent || 0;
},

electricInput: /<\/[\s\w:]+>$/,
blockCommentStart: "<!--",
blockCommentEnd: "-->",

configuration: parserConfig.htmlMode ? "html" : "xml",
helperType: parserConfig.htmlMode ? "html" : "xml"
configuration: config.htmlMode ? "html" : "xml",
helperType: config.htmlMode ? "html" : "xml",

skipAttribute: function(state) {
if (state.state == attrValueState)
state.state = attrState
}
};
});

Expand Down
2 changes: 1 addition & 1 deletion mode/yaml-frontmatter/yaml-frontmatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
state.state = FRONTMATTER
return yamlMode.token(stream, state.inner)
} else {
stream.state = BODY
state.state = BODY
state.inner = CodeMirror.startState(innerMode)
return innerMode.token(stream, state.inner)
}
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":"5.10.0",
"version":"5.11.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"license": "MIT",
Expand Down
2 changes: 2 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<script src="../mode/haml/haml.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/jsx/jsx.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/php/php.js"></script>
<script src="../mode/ruby/ruby.js"></script>
Expand Down Expand Up @@ -107,6 +108,7 @@ <h2>Test Suite</h2>
<script src="../mode/gfm/test.js"></script>
<script src="../mode/haml/test.js"></script>
<script src="../mode/javascript/test.js"></script>
<script src="../mode/jsx/test.js"></script>
<script src="../mode/markdown/test.js"></script>
<script src="../mode/php/test.js"></script>
<script src="../mode/ruby/test.js"></script>
Expand Down
2 changes: 1 addition & 1 deletion test/mode_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
for (var i = 0; i < output.length; ++i) {
var style = output[i].style, val = output[i].text;
s +=
'<td class="mt-token"' + (i == diffAt * 2 ? " style='background: pink'" : "") + '>' +
'<td class="mt-token"' + (i == diffAt ? " style='background: pink'" : "") + '>' +
'<span class="cm-' + esc(String(style)) + '">' +
esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT
'</span>' +
Expand Down
2 changes: 1 addition & 1 deletion theme/mbo.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@
.cm-s-mbo span.cm-qualifier { color: #ffffec; }

.cm-s-mbo .CodeMirror-activeline-background { background: #494b41; }
.cm-s-mbo .CodeMirror-matchingbracket { color: #222 !important; }
.cm-s-mbo .CodeMirror-matchingbracket { color: #ffb928 !important; }
.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); }
1 change: 1 addition & 0 deletions theme/monokai.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }

.cm-s-monokai span.cm-variable { color: #f8f8f2; }
Expand Down