51 changes: 42 additions & 9 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,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 4.5.0</span>
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 4.6.0</span>
</h2>

<p>CodeMirror is a code-editor component that can be embedded in
Expand Down Expand Up @@ -1312,6 +1312,10 @@ <h3 id="api_selection">Cursor and selection methods</h3>
be <code>"line"</code> or <code>"page"</code>. The other
arguments and the returned value have the same interpretation as
they have in <code>findPosH</code>.</dd>

<dt id="findWordAt"><code><strong>cm.findWordAt</strong>(pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}</code></dt>
<dd>Returns the start and end of the 'word' (the stretch of
letters, whitespace, or punctuation) at the given position.</dd>
</dl>

<h3 id="api_configuration">Configuration methods</h3>
Expand Down Expand Up @@ -2053,14 +2057,43 @@ <h2>Addons</h2>
<dl>
<dt id="addon_dialog"><a href="../addon/dialog/dialog.js"><code>dialog/dialog.js</code></a></dt>
<dd>Provides a very simple way to query users for text input.
Adds an <strong><code>openDialog</code></strong> method to
CodeMirror instances, which can be called with an HTML fragment
or a detached DOM node that provides the prompt (should include
an <code>input</code> tag), and a callback function that is called
when text has been entered. Also adds
an <strong><code>openNotification</code></strong> function that
simply shows an HTML fragment as a notification. Depends
on <code>addon/dialog/dialog.css</code>.</dd>
Adds the <strong><code>openDialog(template, callback, options) →
closeFunction</code></strong> method to CodeMirror instances,
which can be called with an HTML fragment or a detached DOM
node that provides the prompt (should include an <code>input</code>
or <code>button</code> tag), and a callback function that is called
when the user presses enter. It returns a function <code>closeFunction</code>
which, if called, will close the dialog immediately.
<strong><code>openDialog</code></strong> takes the following options:
<dl>
<dt><code><strong>closeOnEnter</strong></code>:</dt>
<dd>If true, the dialog will be closed when the user presses
enter in the input. Defaults to <code>true</code>.</dd>
<dt><code><strong>onKeyDown</strong></code>:</dt>
<dd>An event handler of the signature <code>(event, value, closeFunction)</code>
that will be called whenever <code>keydown</code> fires in the
dialog's input. If your callback returns <code>true</code>,
the dialog will not do any further processing of the event.</dd>
<dt><code><strong>onKeyUp</strong></code>:</dt>
<dd>Same as <code>onKeyDown</code> but for the
<code>keyup</code> event.</dd>
<dt><code><strong>onInput</strong></code>:</dt>
<dd>Same as <code>onKeyDown</code> but for the
<code>input</code> event.</dd>
<dt><code><strong>onClose</strong></code>:</dt>
<dd>A callback of the signature <code>(dialogInstance)</code>
that will be called after the dialog has been closed and
removed from the DOM. No return value.</dd>
</dl>

<p>Also adds an <strong><code>openNotification(template, options) →
closeFunction</code></strong> function that simply shows an HTML
fragment as a notification at the top of the editor. It takes a
single option: <code>duration</code>, the amount of time after
which the notification will be automatically closed. If <code>
duration</code> is zero, the dialog will not be closed automatically.</p>

<p>Depends on <code>addon/dialog/dialog.css</code>.</p></dd>

<dt id="addon_searchcursor"><a href="../addon/search/searchcursor.js"><code>search/searchcursor.js</code></a></dt>
<dd>Adds the <code>getSearchCursor(query, start, caseFold) →
Expand Down
1 change: 1 addition & 0 deletions 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://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="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>
<li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li>
<li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
Expand Down
19 changes: 16 additions & 3 deletions doc/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,18 @@

<h2>Release notes and version history</h2>

<section id=v3 class=first>
<section id=v4 class=first>

<h2 id="v4">Version 4.x</h2>
<h2>Version 4.x</h2>

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

<ul class="rel-note">
<li>New mode: <a href="../mode/modelica/index.html">Modelica</a></li>
<li>New method: <a href="manual.html#findWordAt"><code>findWordAt</code></a></li>
<li>Make it easier to <a href="../demo/markselection.html">use text background styling</a></li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/4.5.0...4.6.0">list of patches</a>.</li>
</ul>

<p class="rel">21-08-2014: <a href="http://codemirror.net/codemirror-4.5.zip">Version 4.5</a>:</p>

Expand Down Expand Up @@ -114,7 +123,11 @@ <h2 id="v4">Version 4.x</h2>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/3.23.0...4.0.3">list of patches</a>.</li>
</ul>

<h2 id="v3">Version 3.x</h2>
</section>

<section id=v3>

<h2>Version 3.x</h2>

<p class="rel">22-04-2014: <a href="http://codemirror.net/codemirror-3.24.zip">Version 3.24</a>:</p>

Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ <h2>This is CodeMirror</h2>
</script>
<div style="position: relative; margin: 1em 0;">
<a class="bigbutton left" href="http://codemirror.net/codemirror.zip">DOWNLOAD LATEST RELEASE</a>
<div><strong>version 4.5</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div><strong>version 4.6</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div>or use the <a href="doc/compress.html">minification helper</a></div>
<div style="position: absolute; top: 0; right: 0; text-align: right">
<span class="bigbutton right" onclick="document.getElementById('paypal').submit();">DONATE WITH PAYPAL</span>
Expand Down
10 changes: 9 additions & 1 deletion lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
border: 0;
background: #7e7;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}

.cm-animate-fat-cursor {
width: auto;
border: 0;
Expand Down Expand Up @@ -213,6 +217,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}

.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
Expand Down Expand Up @@ -272,7 +277,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 1;
z-index: 3;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
Expand All @@ -299,3 +304,6 @@ div.CodeMirror-cursors {
visibility: hidden;
}
}

/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }
64 changes: 32 additions & 32 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,7 @@

var rect;
if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
for (;;) {
for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned
while (start && isExtendingChar(prepared.line.text.charAt(mStart + start))) --start;
while (mStart + end < mEnd && isExtendingChar(prepared.line.text.charAt(mStart + end))) ++end;
if (ie && ie_version < 9 && start == 0 && end == mEnd - mStart) {
Expand All @@ -1665,6 +1665,7 @@
start = start - 1;
collapse = "right";
}
if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
} else { // If it is a widget, simply get the box for the whole widget.
if (start > 0) collapse = bias = "right";
var rects;
Expand All @@ -1681,8 +1682,6 @@
rect = nullRect;
}

if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);

var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
var mid = (rtop + rbot) / 2;
var heights = prepared.view.measure.heights;
Expand Down Expand Up @@ -2090,11 +2089,11 @@
display.wheelStartX = display.wheelStartY = null;

// Propagate the scroll position to the actual DOM scroller
if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) {
if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) {
var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop));
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
}
if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) {
if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) {
var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft));
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left;
alignHorizontally(cm);
Expand Down Expand Up @@ -2457,16 +2456,16 @@
cm.options.smartIndent && range.head.ch < 100 &&
(!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
var mode = cm.getModeAt(range.head);
var end = changeEnd(changeEvent);
if (mode.electricChars) {
for (var j = 0; j < mode.electricChars.length; j++)
if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
indentLine(cm, range.head.line, "smart");
indentLine(cm, end.line, "smart");
break;
}
} else if (mode.electricInput) {
var end = changeEnd(changeEvent);
if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch)))
indentLine(cm, range.head.line, "smart");
indentLine(cm, end.line, "smart");
}
}
}
Expand Down Expand Up @@ -2528,7 +2527,7 @@
var pos = posFromMouse(cm, e);
if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return;
e_preventDefault(e);
var word = findWordAt(cm, pos);
var word = cm.findWordAt(pos);
extendSelection(cm.doc, word.anchor, word.head);
}));
else
Expand Down Expand Up @@ -2807,7 +2806,7 @@
start = posFromMouse(cm, e, true, true);
ourIndex = -1;
} else if (type == "double") {
var word = findWordAt(cm, start);
var word = cm.findWordAt(start);
if (cm.display.shift || doc.extend)
ourRange = extendRange(doc, ourRange, word.anchor, word.head);
else
Expand Down Expand Up @@ -2861,7 +2860,7 @@
var anchor = oldRange.anchor, head = pos;
if (type != "single") {
if (type == "double")
var range = findWordAt(cm, pos);
var range = cm.findWordAt(pos);
else
var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0)));
if (cmp(range.anchor, anchor) > 0) {
Expand Down Expand Up @@ -3999,24 +3998,6 @@
return target;
}

// Find the word at the given position (as returned by coordsChar).
function findWordAt(cm, pos) {
var doc = cm.doc, line = getLine(doc, pos.line).text;
var start = pos.ch, end = pos.ch;
if (line) {
var helper = cm.getHelper(pos, "wordChars");
if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
var startChar = line.charAt(start);
var check = isWordChar(startChar, helper)
? function(ch) { return isWordChar(ch, helper); }
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
: function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
while (start > 0 && check(line.charAt(start - 1))) --start;
while (end < line.length && check(line.charAt(end))) ++end;
}
return new Range(Pos(pos.line, start), Pos(pos.line, end));
}

// EDITOR METHODS

// The publicly visible API. Note that methodOp(f) means
Expand Down Expand Up @@ -4358,6 +4339,24 @@
doc.sel.ranges[i].goalColumn = goals[i];
}),

// Find the word at the given position (as returned by coordsChar).
findWordAt: function(pos) {
var doc = this.doc, line = getLine(doc, pos.line).text;
var start = pos.ch, end = pos.ch;
if (line) {
var helper = this.getHelper(pos, "wordChars");
if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end;
var startChar = line.charAt(start);
var check = isWordChar(startChar, helper)
? function(ch) { return isWordChar(ch, helper); }
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
: function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
while (start > 0 && check(line.charAt(start - 1))) --start;
while (end < line.length && check(line.charAt(end))) ++end;
}
return new Range(Pos(pos.line, start), Pos(pos.line, end));
},

toggleOverwrite: function(value) {
if (value != null && value == this.state.overwrite) return;
if (this.state.overwrite = !this.state.overwrite)
Expand Down Expand Up @@ -4444,6 +4443,7 @@
clearCaches(this);
resetInput(this);
this.scrollTo(doc.scrollLeft, doc.scrollTop);
this.curOp.forceScroll = true;
signalLater(this, "swapDoc", this, old);
return old;
}),
Expand Down Expand Up @@ -7295,7 +7295,7 @@
return function(){return f.apply(null, args);};
}

var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
var isWordCharBasic = CodeMirror.isWordChar = function(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
Expand Down Expand Up @@ -7462,7 +7462,7 @@
if (badBidiRects != null) return badBidiRects;
var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
var r0 = range(txt, 0, 1).getBoundingClientRect();
if (r0.left == r0.right) return false;
if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780)
var r1 = range(txt, 1, 2).getBoundingClientRect();
return badBidiRects = (r1.right - r0.right < 3);
}
Expand Down Expand Up @@ -7825,7 +7825,7 @@

// THE END

CodeMirror.version = "4.5.0";
CodeMirror.version = "4.6.0";

return CodeMirror;
});
1 change: 1 addition & 0 deletions mode/htmlmixed/htmlmixed.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {

function html(stream, state) {
var tagName = state.htmlState.tagName;
if (tagName) tagName = tagName.toLowerCase();
var style = htmlMode.token(stream, state.htmlState);
if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") {
// Script block: mode to change to depends on type attribute
Expand Down
1 change: 1 addition & 0 deletions mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ <h2>Language modes</h2>
<li><a href="lua/index.html">Lua</a></li>
<li><a href="markdown/index.html">Markdown</a> (<a href="gfm/index.html">GitHub-flavour</a>)</li>
<li><a href="mirc/index.html">mIRC</a></li>
<li><a href="modelica/index.html">Modelica</a></li>
<li><a href="nginx/index.html">Nginx</a></li>
<li><a href="ntriples/index.html">NTriples</a></li>
<li><a href="mllike/index.html">OCaml</a></li>
Expand Down
8 changes: 3 additions & 5 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybeoperatorNoComma(type, value, noComma) {
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
var expr = noComma == false ? expression : expressionNoComma;
if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me);
if (value == "?") return cont(expression, expect(":"), expr);
Expand All @@ -417,13 +417,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function arrowBody(type) {
findFatArrow(cx.stream, cx.state);
if (type == "{") return pass(statement);
return pass(expression);
return pass(type == "{" ? statement : expression);
}
function arrowBodyNoComma(type) {
findFatArrow(cx.stream, cx.state);
if (type == "{") return pass(statement);
return pass(expressionNoComma);
return pass(type == "{" ? statement : expressionNoComma);
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
Expand Down
18 changes: 5 additions & 13 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,15 +360,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

var ch = stream.next();

if (state.escape) {
state.escape = false;
return getType(state);
}

if (ch === '\\') {
if (modeCfg.highlightFormatting) state.formatting = "escape";
state.escape = true;
return getType(state);
stream.next();
if (modeCfg.highlightFormatting) {
var type = getType(state);
return type ? type + " formatting-escape" : "formatting-escape";
}
}

// Matches link titles present on next line
Expand Down Expand Up @@ -650,7 +647,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
inline: inlineNormal,
text: handleText,

escape: false,
formatting: false,
linkText: false,
linkHref: false,
Expand Down Expand Up @@ -683,7 +679,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

inline: s.inline,
text: s.text,
escape: false,
formatting: false,
linkTitle: s.linkTitle,
em: s.em,
Expand Down Expand Up @@ -718,9 +713,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.thisLineHasContent = true;
}

// Reset state.escape
state.escape = false;

// Reset state.taskList
state.taskList = false;

Expand Down
2 changes: 1 addition & 1 deletion mode/markdown/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]");

FT("formatting_escape",
"[formatting&formatting-escape \\]*");
"[formatting-escape \\*]");

MT("plainText",
"foo");
Expand Down
1 change: 1 addition & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ CodeMirror.modeInfo = [
{name: "Lua", mime: "text/x-lua", mode: "lua"},
{name: "Markdown (GitHub-flavour)", mime: "text/x-markdown", mode: "markdown"},
{name: "mIRC", mime: "text/mirc", mode: "mirc"},
{name: "Modelica", mime: "text/x-modelica", mode: "modelica"},
{name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx"},
{name: "NTriples", mime: "text/n-triples", mode: "ntriples"},
{name: "OCaml", mime: "text/x-ocaml", mode: "mllike"},
Expand Down
67 changes: 67 additions & 0 deletions mode/modelica/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!doctype html>

<title>CodeMirror: Modelica 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="../../addon/edit/matchbrackets.js"></script>
<link rel="stylesheet" href="../../addon/hint/show-hint.css">
<script src="../../addon/hint/show-hint.js"></script>
<script src="modelica.js"></script>
<style>.CodeMirror {border: 2px inset #dee;}</style>
<div id=nav>
<a href="http://codemirror.net"><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/marijnh/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Modelica</a>
</ul>
</div>

<article>
<h2>Modelica mode</h2>

<div><textarea id="modelica">
model BouncingBall
parameter Real e = 0.7;
parameter Real g = 9.81;
Real h(start=1);
Real v;
Boolean flying(start=true);
Boolean impact;
Real v_new;
equation
impact = h <= 0.0;
der(v) = if flying then -g else 0;
der(h) = v;
when {h <= 0.0 and v <= 0.0, impact} then
v_new = if edge(impact) then -e*pre(v) else 0;
flying = v_new > 0;
reinit(v, v_new);
end when;
annotation (uses(Modelica(version="3.2")));
end BouncingBall;
</textarea></div>

<script>
var modelicaEditor = CodeMirror.fromTextArea(document.getElementById("modelica"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/x-modelica"
});
var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
</script>

<p>Simple mode that tries to handle Modelica as well as it can.</p>

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

// Modelica support for CodeMirror, copyright (c) by Lennart Ochel

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

(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("modelica", function(config, parserConfig) {

var indentUnit = config.indentUnit;
var keywords = parserConfig.keywords || {};
var builtin = parserConfig.builtin || {};
var atoms = parserConfig.atoms || {};

var isSingleOperatorChar = /[;=\(:\),{}.*<>+\-\/^\[\]]/;
var isDoubleOperatorChar = /(:=|<=|>=|==|<>|\.\+|\.\-|\.\*|\.\/|\.\^)/;
var isDigit = /[0-9]/;
var isNonDigit = /[_a-zA-Z]/;

function tokenLineComment(stream, state) {
stream.skipToEnd();
state.tokenize = null;
return "comment";
}

function tokenBlockComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (maybeEnd && ch == "/") {
state.tokenize = null;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}

function tokenString(stream, state) {
var escaped = false, ch;
while ((ch = stream.next()) != null) {
if (ch == '"' && !escaped) {
state.tokenize = null;
state.sol = false;
break;
}
escaped = !escaped && ch == "\\";
}

return "string";
}

function tokenIdent(stream, state) {
stream.eatWhile(isDigit);
while (stream.eat(isDigit) || stream.eat(isNonDigit)) { }


var cur = stream.current();

if(state.sol && (cur == "package" || cur == "model" || cur == "when" || cur == "connector")) state.level++;
else if(state.sol && cur == "end" && state.level > 0) state.level--;

state.tokenize = null;
state.sol = false;

if (keywords.propertyIsEnumerable(cur)) return "keyword";
else if (builtin.propertyIsEnumerable(cur)) return "builtin";
else if (atoms.propertyIsEnumerable(cur)) return "atom";
else return "variable";
}

function tokenQIdent(stream, state) {
while (stream.eat(/[^']/)) { }

state.tokenize = null;
state.sol = false;

if(stream.eat("'"))
return "variable";
else
return "error";
}

function tokenUnsignedNuber(stream, state) {
stream.eatWhile(isDigit);
if (stream.eat('.')) {
stream.eatWhile(isDigit);
}
if (stream.eat('e') || stream.eat('E')) {
if (!stream.eat('-'))
stream.eat('+');
stream.eatWhile(isDigit);
}

state.tokenize = null;
state.sol = false;
return "number";
}

// Interface
return {
startState: function() {
return {
tokenize: null,
level: 0,
sol: true
};
},

token: function(stream, state) {
if(state.tokenize != null) {
return state.tokenize(stream, state);
}

if(stream.sol()) {
state.sol = true;
}

// WHITESPACE
if(stream.eatSpace()) {
state.tokenize = null;
return null;
}

var ch = stream.next();

// LINECOMMENT
if(ch == '/' && stream.eat('/')) {
state.tokenize = tokenLineComment;
}
// BLOCKCOMMENT
else if(ch == '/' && stream.eat('*')) {
state.tokenize = tokenBlockComment;
}
// TWO SYMBOL TOKENS
else if(isDoubleOperatorChar.test(ch+stream.peek())) {
stream.next();
state.tokenize = null;
return "operator";
}
// SINGLE SYMBOL TOKENS
else if(isSingleOperatorChar.test(ch)) {
state.tokenize = null;
return "operator";
}
// IDENT
else if(isNonDigit.test(ch)) {
state.tokenize = tokenIdent;
}
// Q-IDENT
else if(ch == "'" && stream.peek() && stream.peek() != "'") {
state.tokenize = tokenQIdent;
}
// STRING
else if(ch == '"') {
state.tokenize = tokenString;
}
// UNSIGNED_NUBER
else if(isDigit.test(ch)) {
state.tokenize = tokenUnsignedNuber;
}
// ERROR
else {
state.tokenize = null;
return "error";
}

return state.tokenize(stream, state);
},

indent: function(state, textAfter) {
if (state.tokenize != null) return CodeMirror.Pass;

var level = state.level;
if(/(algorithm)/.test(textAfter)) level--;
if(/(equation)/.test(textAfter)) level--;
if(/(initial algorithm)/.test(textAfter)) level--;
if(/(initial equation)/.test(textAfter)) level--;
if(/(end)/.test(textAfter)) level--;

if(level > 0)
return indentUnit*level;
else
return 0;
},

blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});

function words(str) {
var obj = {}, words = str.split(" ");
for (var i=0; i<words.length; ++i)
obj[words[i]] = true;
return obj;
}

var modelicaKeywords = "algorithm and annotation assert block break class connect connector constant constrainedby der discrete each else elseif elsewhen encapsulated end enumeration equation expandable extends external false final flow for function if import impure in initial inner input loop model not operator or outer output package parameter partial protected public pure record redeclare replaceable return stream then true type when while within";
var modelicaBuiltin = "abs acos actualStream asin atan atan2 cardinality ceil cos cosh delay div edge exp floor getInstanceName homotopy inStream integer log log10 mod pre reinit rem semiLinear sign sin sinh spatialDistribution sqrt tan tanh";
var modelicaAtoms = "Real Boolean Integer String";

function def(mimes, mode) {
if (typeof mimes == "string")
mimes = [mimes];

var words = [];

function add(obj) {
if (obj)
for (var prop in obj)
if (obj.hasOwnProperty(prop))
words.push(prop);
}

add(mode.keywords);
add(mode.builtin);
add(mode.atoms);

if (words.length) {
mode.helperType = mimes[0];
CodeMirror.registerHelper("hintWords", mimes[0], words);
}

for (var i=0; i<mimes.length; ++i)
CodeMirror.defineMIME(mimes[i], mode);
}

def(["text/x-modelica"], {
name: "modelica",
keywords: words(modelicaKeywords),
builtin: words(modelicaBuiltin),
atoms: words(modelicaAtoms)
});
});
4 changes: 2 additions & 2 deletions mode/python/python.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
return new RegExp("^((" + words.join(")|(") + "))\\b");
}

var wordOperators = wordRegexp(["and", "or", "not", "is", "in"]);
var wordOperators = wordRegexp(["and", "or", "not", "is"]);
var commonKeywords = ["as", "assert", "break", "class", "continue",
"def", "del", "elif", "else", "except", "finally",
"for", "from", "global", "if", "import",
"lambda", "pass", "raise", "return",
"try", "while", "with", "yield"];
"try", "while", "with", "yield", "in"];
var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr",
"classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod",
"enumerate", "eval", "filter", "float", "format", "frozenset",
Expand Down
79 changes: 29 additions & 50 deletions mode/rst/rst.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ CodeMirror.defineMode('rst-base', function (config) {
});
}

function AssertException(message) {
this.message = message;
}

AssertException.prototype.toString = function () {
return 'AssertException: ' + this.message;
};

function assert(expression, message) {
if (!expression) throw new AssertException(message);
return expression;
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -185,12 +172,12 @@ CodeMirror.defineMode('rst-base', function (config) {
switch (stage(state)) {
case 0:
change(state, to_normal, context(rx_role_pre, 1));
assert(stream.match(/^:/));
stream.match(/^:/);
token = 'meta';
break;
case 1:
change(state, to_normal, context(rx_role_pre, 2));
assert(stream.match(rx_NAME));
stream.match(rx_NAME);
token = 'keyword';

if (stream.current().match(/^(?:math|latex)/)) {
Expand All @@ -199,7 +186,7 @@ CodeMirror.defineMode('rst-base', function (config) {
break;
case 2:
change(state, to_normal, context(rx_role_pre, 3));
assert(stream.match(/^:`/));
stream.match(/^:`/);
token = 'meta';
break;
case 3:
Expand All @@ -221,92 +208,89 @@ CodeMirror.defineMode('rst-base', function (config) {
}

change(state, to_normal, context(rx_role_pre, 4));
assert(stream.match(rx_TEXT2));
stream.match(rx_TEXT2);
token = 'string';
break;
case 4:
change(state, to_normal, context(rx_role_pre, 5));
assert(stream.match(/^`/));
stream.match(/^`/);
token = 'meta';
break;
case 5:
change(state, to_normal, context(rx_role_pre, 6));
assert(stream.match(rx_TAIL));
stream.match(rx_TAIL);
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (phase(state) == rx_role_suf ||
stream.match(rx_role_suf, false)) {

switch (stage(state)) {
case 0:
change(state, to_normal, context(rx_role_suf, 1));
assert(stream.match(/^`/));
stream.match(/^`/);
token = 'meta';
break;
case 1:
change(state, to_normal, context(rx_role_suf, 2));
assert(stream.match(rx_TEXT2));
stream.match(rx_TEXT2);
token = 'string';
break;
case 2:
change(state, to_normal, context(rx_role_suf, 3));
assert(stream.match(/^`:/));
stream.match(/^`:/);
token = 'meta';
break;
case 3:
change(state, to_normal, context(rx_role_suf, 4));
assert(stream.match(rx_NAME));
stream.match(rx_NAME);
token = 'keyword';
break;
case 4:
change(state, to_normal, context(rx_role_suf, 5));
assert(stream.match(/^:/));
stream.match(/^:/);
token = 'meta';
break;
case 5:
change(state, to_normal, context(rx_role_suf, 6));
assert(stream.match(rx_TAIL));
stream.match(rx_TAIL);
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (phase(state) == rx_role || stream.match(rx_role, false)) {

switch (stage(state)) {
case 0:
change(state, to_normal, context(rx_role, 1));
assert(stream.match(/^:/));
stream.match(/^:/);
token = 'meta';
break;
case 1:
change(state, to_normal, context(rx_role, 2));
assert(stream.match(rx_NAME));
stream.match(rx_NAME);
token = 'keyword';
break;
case 2:
change(state, to_normal, context(rx_role, 3));
assert(stream.match(/^:/));
stream.match(/^:/);
token = 'meta';
break;
case 3:
change(state, to_normal, context(rx_role, 4));
assert(stream.match(rx_TAIL));
stream.match(rx_TAIL);
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (phase(state) == rx_substitution_ref ||
stream.match(rx_substitution_ref, false)) {

switch (stage(state)) {
case 0:
change(state, to_normal, context(rx_substitution_ref, 1));
assert(stream.match(rx_substitution_text));
stream.match(rx_substitution_text);
token = 'variable-2';
break;
case 1:
Expand All @@ -315,7 +299,6 @@ CodeMirror.defineMode('rst-base', function (config) {
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (stream.match(rx_footnote_ref)) {
change(state, to_normal);
Expand All @@ -341,21 +324,20 @@ CodeMirror.defineMode('rst-base', function (config) {
break;
case 1:
change(state, to_normal, context(rx_link_ref2, 2));
assert(stream.match(/^`/));
stream.match(/^`/);
token = 'link';
break;
case 2:
change(state, to_normal, context(rx_link_ref2, 3));
assert(stream.match(rx_TEXT2));
stream.match(rx_TEXT2);
break;
case 3:
change(state, to_normal, context(rx_link_ref2, 4));
assert(stream.match(/^`_/));
stream.match(/^`_/);
token = 'link';
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (stream.match(rx_verbatim)) {
change(state, to_verbatim);
Expand All @@ -380,34 +362,33 @@ CodeMirror.defineMode('rst-base', function (config) {
switch (stage(state)) {
case 0:
change(state, to_explicit, context(rx_substitution, 1));
assert(stream.match(rx_substitution_text));
stream.match(rx_substitution_text);
token = 'variable-2';
break;
case 1:
change(state, to_explicit, context(rx_substitution, 2));
assert(stream.match(rx_substitution_sepa));
stream.match(rx_substitution_sepa);
break;
case 2:
change(state, to_explicit, context(rx_substitution, 3));
assert(stream.match(rx_substitution_name));
stream.match(rx_substitution_name);
token = 'keyword';
break;
case 3:
change(state, to_explicit, context(rx_substitution, 4));
assert(stream.match(rx_substitution_tail));
stream.match(rx_substitution_tail);
token = 'meta';
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (phase(state) == rx_directive ||
stream.match(rx_directive, false)) {

switch (stage(state)) {
case 0:
change(state, to_explicit, context(rx_directive, 1));
assert(stream.match(rx_directive_name));
stream.match(rx_directive_name);
token = 'keyword';

if (stream.current().match(/^(?:math|latex)/))
Expand All @@ -417,7 +398,7 @@ CodeMirror.defineMode('rst-base', function (config) {
break;
case 1:
change(state, to_explicit, context(rx_directive, 2));
assert(stream.match(rx_directive_tail));
stream.match(rx_directive_tail);
token = 'meta';

if (stream.match(/^latex\s*$/) || state.tmp_stex) {
Expand All @@ -436,25 +417,23 @@ CodeMirror.defineMode('rst-base', function (config) {
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (phase(state) == rx_link || stream.match(rx_link, false)) {

switch (stage(state)) {
case 0:
change(state, to_explicit, context(rx_link, 1));
assert(stream.match(rx_link_head));
assert(stream.match(rx_link_name));
stream.match(rx_link_head);
stream.match(rx_link_name);
token = 'link';
break;
case 1:
change(state, to_explicit, context(rx_link, 2));
assert(stream.match(rx_link_tail));
stream.match(rx_link_tail);
token = 'meta';
break;
default:
change(state, to_normal);
assert(stream.current() == '');
}
} else if (stream.match(rx_footnote)) {
change(state, to_normal);
Expand Down
1 change: 1 addition & 0 deletions mode/sass/sass.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ CodeMirror.defineMode("sass", function(config) {
return {
tokenizer: tokenBase,
scopes: [{offset: 0, type: "sass"}],
indentCount: 0,
definedVars: [],
definedMixins: []
};
Expand Down
3 changes: 2 additions & 1 deletion mode/shell/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ CodeMirror.defineMode('shell', function() {
startState: function() {return {tokens:[]};},
token: function(stream, state) {
return tokenize(stream, state);
}
},
lineComment: '#'
};
});

Expand Down
4 changes: 2 additions & 2 deletions mode/smalltalk/smalltalk.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ CodeMirror.defineMode('smalltalk', function(config) {
stream.next();
token = nextSymbol(stream, new Context(nextSymbol, context));
} else {
if (stream.eatWhile(/[^ .{}\[\]()]/))
if (stream.eatWhile(/[^\s.{}\[\]()]/))
token.name = 'string-2';
else
token.name = 'meta';
}

} else if (aChar === '$') {
if (stream.next() === '<') {
stream.eatWhile(/[^ >]/);
stream.eatWhile(/[^\s>]/);
stream.next();
}
token.name = 'string-2';
Expand Down
2 changes: 1 addition & 1 deletion mode/xml/xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
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},
'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},
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":"4.5.0",
"version":"4.6.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
24 changes: 10 additions & 14 deletions theme/mbo.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* Based on mbonaci's Brackets mbo theme */
/****************************************************************/
/* Based on mbonaci's Brackets mbo theme */
/* https://github.com/mbonaci/global/blob/master/Mbo.tmTheme */
/* Create your own: http://tmtheme-editor.herokuapp.com */
/****************************************************************/

.cm-s-mbo.CodeMirror {background: #2c2c2c; color: #ffffe9;}
.cm-s-mbo.CodeMirror {background: #2c2c2c; color: #ffffec;}
.cm-s-mbo div.CodeMirror-selected {background: #716C62 !important;}
.cm-s-mbo .CodeMirror-gutters {background: #4e4e4e; border-right: 0px;}
.cm-s-mbo .CodeMirror-guttermarker { color: white; }
Expand All @@ -15,6 +19,7 @@
.cm-s-mbo span.cm-property, .cm-s-mbo span.cm-attribute {color: #9ddfe9;}
.cm-s-mbo span.cm-keyword {color: #ffb928;}
.cm-s-mbo span.cm-string {color: #ffcf6c;}
.cm-s-mbo span.cm-string.cm-property {color: #ffffec;}

.cm-s-mbo span.cm-variable {color: #ffffec;}
.cm-s-mbo span.cm-variable-2 {color: #00a8c6;}
Expand All @@ -23,17 +28,8 @@
.cm-s-mbo span.cm-tag {color: #9ddfe9;}
.cm-s-mbo span.cm-link {color: #f54b07;}
.cm-s-mbo span.cm-error {border-bottom: #636363; color: #ffffec;}
.cm-s-mbo span.cm-qualifier {color: #ffffec;}

.cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;}
.cm-s-mbo .CodeMirror-matchingbracket {
text-decoration: underline;
color: #f5e107 !important;
}

.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); }

.cm-s-mbo span.cm-searching {
background-color: none;
background: none;
box-shadow: 0 0 0 1px #ffffec;
}
.cm-s-mbo .CodeMirror-matchingbracket {color: #222 !important;}
.cm-s-mbo .CodeMirror-matchingtag {background: rgba(255, 255, 255, .37);}