diff --git a/AUTHORS b/AUTHORS
index acc10fe16d..362fd94a7e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -33,6 +33,7 @@ Anders Nawroth
Anderson Mesquita
Andreas Reischuck
Andre von Houck
+Andrey Fedorov
Andrey Lushnikov
Andy Joslin
Andy Kimball
@@ -84,6 +85,7 @@ dagsta
daines
Dan Heberden
Daniel, Dao Quang Minh
+Daniele Di Sarli
Daniel Faust
Daniel Huigens
Daniel KJ
@@ -145,6 +147,7 @@ Hakan Tunc
Hans Engel
Hardest
Hasan Karahan
+Hiroyuki Makino
hitsthings
Hocdoc
Ian Beck
@@ -215,6 +218,7 @@ Leonid Khachaturov
Leonya Khachaturov
Liam Newman
LM
+lochel
Lorenzo Stoakes
Luciano Longo
lynschinzer
@@ -223,6 +227,7 @@ Maksym Taran
Malay Majithia
Manuel Rego Casasnovas
Marat Dreizin
+Marcel Gerber
Marco Aurélio
Marco Munizaga
Marcus Bointon
@@ -316,6 +321,7 @@ Sascha Peilicke
satchmorun
sathyamoorthi
SCLINIC\jdecker
+Scott Aikin
Sebastian Zaha
shaund
shaun gilchrist
@@ -324,6 +330,7 @@ sheopory
Shiv Deepak
Shmuel Englard
Shubham Jain
+snasa
soliton4
sonson
spastorelli
diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js
index 946040cb91..35a6103c2d 100644
--- a/addon/dialog/dialog.js
+++ b/addon/dialog/dialog.js
@@ -129,8 +129,8 @@
CodeMirror.defineExtension("openNotification", function(template, options) {
closeNotification(this, close);
var dialog = dialogDiv(this, template, options && options.bottom);
- var duration = options && (options.duration === undefined ? 5000 : options.duration);
var closed = false, doneTimer;
+ var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
function close() {
if (closed) return;
@@ -143,7 +143,10 @@
CodeMirror.e_preventDefault(e);
close();
});
+
if (duration)
- doneTimer = setTimeout(close, options.duration);
+ doneTimer = setTimeout(close, duration);
+
+ return close;
});
});
diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js
index 69ea4446be..a0bec7dd43 100644
--- a/addon/edit/closetag.js
+++ b/addon/edit/closetag.js
@@ -102,11 +102,25 @@
var pos = ranges[i].head, tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
if (tok.type == "string" || tok.string.charAt(0) != "<" ||
- tok.start != pos.ch - 1 || inner.mode.name != "xml" ||
- !state.context || !state.context.tagName ||
- closingTagExists(cm, state.context.tagName, pos, state))
+ tok.start != pos.ch - 1)
return CodeMirror.Pass;
- replacements[i] = "/" + state.context.tagName + ">";
+ // Kludge to get around the fact that we are not in XML mode
+ // when completing in JS/CSS snippet in htmlmixed mode. Does not
+ // work for other XML embedded languages (there is no general
+ // way to go from a mixed mode to its current XML state).
+ if (inner.mode.name != "xml") {
+ if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript")
+ replacements[i] = "/script>";
+ else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css")
+ replacements[i] = "/style>";
+ else
+ return CodeMirror.Pass;
+ } else {
+ if (!state.context || !state.context.tagName ||
+ closingTagExists(cm, state.context.tagName, pos, state))
+ return CodeMirror.Pass;
+ replacements[i] = "/" + state.context.tagName + ">";
+ }
}
cm.replaceSelections(replacements);
ranges = cm.listSelections();
diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js
index addd9b7bc0..992218f288 100755
--- a/addon/hint/html-hint.js
+++ b/addon/hint/html-hint.js
@@ -3,9 +3,9 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("../../lib/codemirror", "./xml-hint"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["../../lib/codemirror", "./xml-hint"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css
index 8a4ff052e3..924e638f7f 100644
--- a/addon/hint/show-hint.css
+++ b/addon/hint/show-hint.css
@@ -32,7 +32,7 @@
cursor: pointer;
}
-.CodeMirror-hint-active {
+li.CodeMirror-hint-active {
background: #08f;
color: white;
}
diff --git a/addon/merge/merge.js b/addon/merge/merge.js
index d9b277664b..da3ea47ccf 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -1,17 +1,17 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
+// declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL
+
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
+ mod(require("../../lib/codemirror"), require("diff_match_patch"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
+ define(["../../lib/codemirror", "diff_match_patch"], mod);
else // Plain browser env
- mod(CodeMirror);
-})(function(CodeMirror) {
+ mod(CodeMirror, diff_match_patch);
+})(function(CodeMirror, diff_match_patch) {
"use strict";
- // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL
-
var Pos = CodeMirror.Pos;
var svgNS = "http://www.w3.org/2000/svg";
diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js
index 14dd4d4184..e9a22721f7 100644
--- a/addon/search/match-highlighter.js
+++ b/addon/search/match-highlighter.js
@@ -8,12 +8,15 @@
// document.
//
// The option can be set to true to simply enable it, or to a
-// {minChars, style, showToken} object to explicitly configure it.
-// minChars is the minimum amount of characters that should be
+// {minChars, style, wordsOnly, showToken, delay} object to explicitly
+// configure it. minChars is the minimum amount of characters that should be
// selected for the behavior to occur, and style is the token style to
// apply to the matches. This will be prefixed by "cm-" to create an
-// actual CSS class name. showToken, when enabled, will cause the
-// current token to be highlighted when nothing is selected.
+// actual CSS class name. If wordsOnly is enabled, the matches will be
+// highlighted only if the selected text is a word. showToken, when enabled,
+// will cause the current token to be highlighted when nothing is selected.
+// delay is used to specify how much time to wait, in milliseconds, before
+// highlighting the matches.
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -28,6 +31,7 @@
var DEFAULT_MIN_CHARS = 2;
var DEFAULT_TOKEN_STYLE = "matchhighlight";
var DEFAULT_DELAY = 100;
+ var DEFAULT_WORDS_ONLY = false;
function State(options) {
if (typeof options == "object") {
@@ -35,10 +39,12 @@
this.style = options.style;
this.showToken = options.showToken;
this.delay = options.delay;
+ this.wordsOnly = options.wordsOnly;
}
if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
if (this.delay == null) this.delay = DEFAULT_DELAY;
+ if (this.wordsOnly == null) this.wordsOnly = DEFAULT_WORDS_ONLY;
this.overlay = this.timeout = null;
}
@@ -81,12 +87,30 @@
}
var from = cm.getCursor("from"), to = cm.getCursor("to");
if (from.line != to.line) return;
+ if (state.wordsOnly && !isWord(cm, from, to)) return;
var selection = cm.getRange(from, to).replace(/^\s+|\s+$/g, "");
if (selection.length >= state.minChars)
cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
});
}
+ function isWord(cm, from, to) {
+ var str = cm.getRange(from, to);
+ if (str.match(/^\w+$/) !== null) {
+ if (from.ch > 0) {
+ var pos = {line: from.line, ch: from.ch - 1};
+ var chr = cm.getRange(pos, from);
+ if (chr.match(/\W/) === null) return false;
+ }
+ if (to.ch < cm.getLine(from.line).length) {
+ var pos = {line: to.line, ch: to.ch + 1};
+ var chr = cm.getRange(to, pos);
+ if (chr.match(/\W/) === null) return false;
+ }
+ return true;
+ } else return false;
+ }
+
function boundariesAround(stream, re) {
return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
diff --git a/bower.json b/bower.json
index 9cf2abecfe..638b9d0016 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"4.5.0",
+ "version":"4.6.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
diff --git a/demo/markselection.html b/demo/markselection.html
index 93e38ec833..d729515e58 100644
--- a/demo/markselection.html
+++ b/demo/markselection.html
@@ -12,6 +12,7 @@
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror-selected { background-color: blue !important; }
.CodeMirror-selectedtext { color: white; }
+ .styled-background { background-color: #ff7; }
@@ -28,18 +29,24 @@
Selection Marking Demo
-
+
+Select something from here. You'll see that the selection's foreground
+color changes to white! Since, by default, CodeMirror only puts an
+independent "marker" layer behind the text, you'll need something like
+this to change its colour.
+
+Also notice that turning this addon on (with the default style) allows
+you to safely give text a background color without screwing up the
+visibility of the selection.
- Simple addon to easily mark (and style) selected text.
+ Simple addon to easily mark (and style) selected text. Docs .
diff --git a/demo/requirejs.html b/demo/requirejs.html
new file mode 100644
index 0000000000..6399f8d769
--- /dev/null
+++ b/demo/requirejs.html
@@ -0,0 +1,52 @@
+
+
+
+
CodeMirror: HTML completion demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ RequireJS module loading demo
+
+ This demo does the same thing as
+ the HTML5 completion demo , but
+ loads its dependencies
+ with Require.js , rather than
+ explicitly. Press ctrl-space to activate
+ completion.
+
+
+
+
+
+
diff --git a/doc/compress.html b/doc/compress.html
index 46c102af47..4fdcc1331c 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@
Version:
HEAD
+ 4.6
4.5
4.4
4.3
@@ -131,6 +132,7 @@
markdown.js
mirc.js
mllike.js
+ modelica.js
nginx.js
ntriples.js
octave.js
diff --git a/doc/manual.html b/doc/manual.html
index a511d9bc5b..5520a738b5 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -63,7 +63,7 @@
User manual and reference guide
- version 4.5.0
+ version 4.6.0
CodeMirror is a code-editor component that can be embedded in
@@ -1312,6 +1312,10 @@
Cursor and selection methods
be "line" or "page". The other
arguments and the returned value have the same interpretation as
they have in findPosH.
+
+ cm.findWordAt (pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}
+ Returns the start and end of the 'word' (the stretch of
+ letters, whitespace, or punctuation) at the given position.
Configuration methods
@@ -2053,14 +2057,43 @@ Static properties
dialog/dialog.js
Provides a very simple way to query users for text input.
- Adds an openDialog method to
- CodeMirror instances, which can be called with an HTML fragment
- or a detached DOM node that provides the prompt (should include
- an input tag), and a callback function that is called
- when text has been entered. Also adds
- an openNotification function that
- simply shows an HTML fragment as a notification. Depends
- on addon/dialog/dialog.css.
+ Adds the openDialog(template, callback, options) →
+ closeFunction method to CodeMirror instances,
+ which can be called with an HTML fragment or a detached DOM
+ node that provides the prompt (should include an input
+ or button tag), and a callback function that is called
+ when the user presses enter. It returns a function closeFunction
+ which, if called, will close the dialog immediately.
+ openDialog takes the following options:
+
+ closeOnEnter :
+ If true, the dialog will be closed when the user presses
+ enter in the input. Defaults to true.
+ onKeyDown :
+ An event handler of the signature (event, value, closeFunction)
+ that will be called whenever keydown fires in the
+ dialog's input. If your callback returns true,
+ the dialog will not do any further processing of the event.
+ onKeyUp :
+ Same as onKeyDown but for the
+ keyup event.
+ onInput :
+ Same as onKeyDown but for the
+ input event.
+ onClose :
+ A callback of the signature (dialogInstance)
+ that will be called after the dialog has been closed and
+ removed from the DOM. No return value.
+
+
+ Also adds an openNotification(template, options) →
+ closeFunction function that simply shows an HTML
+ fragment as a notification at the top of the editor. It takes a
+ single option: duration, the amount of time after
+ which the notification will be automatically closed. If
+ duration is zero, the dialog will not be closed automatically.
+
+ Depends on addon/dialog/dialog.css.
search/searchcursor.js
Adds the getSearchCursor(query, start, caseFold) →
diff --git a/doc/realworld.html b/doc/realworld.html
index 899fcc3502..590e07b18a 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -85,6 +85,7 @@
Google Apps Script
Graphit (function graphing)
Handcraft (HTML prototyping)
+ Hawkee
Haxe (Haxe Playground)
HaxPad (editor for Win RT)
Histone template engine playground
diff --git a/doc/releases.html b/doc/releases.html
index 4b6d384b9e..f878850d39 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -25,9 +25,18 @@
Release notes and version history
-
+
+
+
+
+ Version 3.x
22-04-2014: Version 3.24 :
diff --git a/index.html b/index.html
index 391ee507cf..3b53e00dc0 100644
--- a/index.html
+++ b/index.html
@@ -85,7 +85,7 @@
DOWNLOAD LATEST RELEASE
-
+
DONATE WITH PAYPAL
diff --git a/lib/codemirror.css b/lib/codemirror.css
index c0897771aa..6b9ca0238d 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -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;
@@ -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 */
@@ -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;
@@ -299,3 +304,6 @@ div.CodeMirror-cursors {
visibility: hidden;
}
}
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 61e8c41eb3..97f6668e73 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -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) {
@@ -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;
@@ -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;
@@ -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);
@@ -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");
}
}
}
@@ -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
@@ -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
@@ -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) {
@@ -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
@@ -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)
@@ -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;
}),
@@ -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));
@@ -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);
}
@@ -7825,7 +7825,7 @@
// THE END
- CodeMirror.version = "4.5.0";
+ CodeMirror.version = "4.6.0";
return CodeMirror;
});
diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js
index dc48978eb2..250ef8cd24 100644
--- a/mode/htmlmixed/htmlmixed.js
+++ b/mode/htmlmixed/htmlmixed.js
@@ -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
diff --git a/mode/index.html b/mode/index.html
index 1c106ae8e0..e3339ad085 100644
--- a/mode/index.html
+++ b/mode/index.html
@@ -71,6 +71,7 @@
Lua
Markdown (GitHub-flavour )
mIRC
+
Modelica
Nginx
NTriples
OCaml
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 46616bc021..2e24d3a55b 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -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);
@@ -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);
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 81ed24e8d3..1bad78d65e 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -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
@@ -650,7 +647,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
inline: inlineNormal,
text: handleText,
- escape: false,
formatting: false,
linkText: false,
linkHref: false,
@@ -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,
@@ -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;
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index 77a3b659c0..96ca1aefc7 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -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");
diff --git a/mode/meta.js b/mode/meta.js
index e3c32b6f6c..d8ddd474c5 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -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"},
diff --git a/mode/modelica/index.html b/mode/modelica/index.html
new file mode 100644
index 0000000000..eb4f08c57d
--- /dev/null
+++ b/mode/modelica/index.html
@@ -0,0 +1,67 @@
+
+
+
CodeMirror: Modelica mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Modelica mode
+
+
+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;
+
+
+
+
+ Simple mode that tries to handle Modelica as well as it can.
+
+ MIME types defined: text/x-modelica
+ (Modlica code).
+
diff --git a/mode/modelica/modelica.js b/mode/modelica/modelica.js
new file mode 100644
index 0000000000..77ec7a3c18
--- /dev/null
+++ b/mode/modelica/modelica.js
@@ -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
]/);
+ stream.eatWhile(/[^\s>]/);
stream.next();
}
token.name = 'string-2';
diff --git a/mode/xml/xml.js b/mode/xml/xml.js
index 786507d2e4..2f3b8f87a0 100644
--- a/mode/xml/xml.js
+++ b/mode/xml/xml.js
@@ -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},
diff --git a/package.json b/package.json
index 47f05587aa..924c076f2c 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/theme/mbo.css b/theme/mbo.css
index 6cb2b18d9b..0ad6360b50 100644
--- a/theme/mbo.css
+++ b/theme/mbo.css
@@ -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; }
@@ -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;}
@@ -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);}