Upgrade to CodeMirror 4.8

This commit is contained in:
Liang Ding 2014-11-30 10:24:24 +08:00
parent b5b859d569
commit bea92c98f8
296 changed files with 2011 additions and 1328 deletions

View File

@ -38,7 +38,7 @@ const (
const ( const (
WideVersion = "1.0.1" // wide version WideVersion = "1.0.1" // wide version
CodeMirrorVer = "4.7" // editor version CodeMirrorVer = "4.8" // editor version
) )
// The latest session content. // The latest session content.

View File

@ -1,262 +0,0 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
/*
* Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
* Licence: MIT
*/
(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("stex", function() {
"use strict";
function pushCommand(state, command) {
state.cmdState.push(command);
}
function peekCommand(state) {
if (state.cmdState.length > 0) {
return state.cmdState[state.cmdState.length - 1];
} else {
return null;
}
}
function popCommand(state) {
var plug = state.cmdState.pop();
if (plug) {
plug.closeBracket();
}
}
// returns the non-default plugin closest to the end of the list
function getMostPowerful(state) {
var context = state.cmdState;
for (var i = context.length - 1; i >= 0; i--) {
var plug = context[i];
if (plug.name == "DEFAULT") {
continue;
}
return plug;
}
return { styleIdentifier: function() { return null; } };
}
function addPluginPattern(pluginName, cmdStyle, styles) {
return function () {
this.name = pluginName;
this.bracketNo = 0;
this.style = cmdStyle;
this.styles = styles;
this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin
this.styleIdentifier = function() {
return this.styles[this.bracketNo - 1] || null;
};
this.openBracket = function() {
this.bracketNo++;
return "bracket";
};
this.closeBracket = function() {};
};
}
var plugins = {};
plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]);
plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]);
plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]);
plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]);
plugins["end"] = addPluginPattern("end", "tag", ["atom"]);
plugins["DEFAULT"] = function () {
this.name = "DEFAULT";
this.style = "tag";
this.styleIdentifier = this.openBracket = this.closeBracket = function() {};
};
function setState(state, f) {
state.f = f;
}
// called when in a normal (no environment) context
function normal(source, state) {
var plug;
// Do we look like '\command' ? If so, attempt to apply the plugin 'command'
if (source.match(/^\\[a-zA-Z@]+/)) {
var cmdName = source.current().slice(1);
plug = plugins[cmdName] || plugins["DEFAULT"];
plug = new plug();
pushCommand(state, plug);
setState(state, beginParams);
return plug.style;
}
// escape characters
if (source.match(/^\\[$&%#{}_]/)) {
return "tag";
}
// white space control characters
if (source.match(/^\\[,;!\/\\]/)) {
return "tag";
}
// find if we're starting various math modes
if (source.match("\\[")) {
setState(state, function(source, state){ return inMathMode(source, state, "\\]"); });
return "keyword";
}
if (source.match("$$")) {
setState(state, function(source, state){ return inMathMode(source, state, "$$"); });
return "keyword";
}
if (source.match("$")) {
setState(state, function(source, state){ return inMathMode(source, state, "$"); });
return "keyword";
}
var ch = source.next();
if (ch == "%") {
// special case: % at end of its own line; stay in same state
if (!source.eol()) {
setState(state, inCComment);
}
return "comment";
}
else if (ch == '}' || ch == ']') {
plug = peekCommand(state);
if (plug) {
plug.closeBracket(ch);
setState(state, beginParams);
} else {
return "error";
}
return "bracket";
} else if (ch == '{' || ch == '[') {
plug = plugins["DEFAULT"];
plug = new plug();
pushCommand(state, plug);
return "bracket";
}
else if (/\d/.test(ch)) {
source.eatWhile(/[\w.%]/);
return "atom";
}
else {
source.eatWhile(/[\w\-_]/);
plug = getMostPowerful(state);
if (plug.name == 'begin') {
plug.argument = source.current();
}
return plug.styleIdentifier();
}
}
function inCComment(source, state) {
source.skipToEnd();
setState(state, normal);
return "comment";
}
function inMathMode(source, state, endModeSeq) {
if (source.eatSpace()) {
return null;
}
if (source.match(endModeSeq)) {
setState(state, normal);
return "keyword";
}
if (source.match(/^\\[a-zA-Z@]+/)) {
return "tag";
}
if (source.match(/^[a-zA-Z]+/)) {
return "variable-2";
}
// escape characters
if (source.match(/^\\[$&%#{}_]/)) {
return "tag";
}
// white space control characters
if (source.match(/^\\[,;!\/]/)) {
return "tag";
}
// special math-mode characters
if (source.match(/^[\^_&]/)) {
return "tag";
}
// non-special characters
if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) {
return null;
}
if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) {
return "number";
}
var ch = source.next();
if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") {
return "bracket";
}
// eat comments here, because inCComment returns us to normal state!
if (ch == "%") {
if (!source.eol()) {
source.skipToEnd();
}
return "comment";
}
return "error";
}
function beginParams(source, state) {
var ch = source.peek(), lastPlug;
if (ch == '{' || ch == '[') {
lastPlug = peekCommand(state);
lastPlug.openBracket(ch);
source.eat(ch);
setState(state, normal);
return "bracket";
}
if (/[ \t\r]/.test(ch)) {
source.eat(ch);
return null;
}
setState(state, normal);
popCommand(state);
return normal(source, state);
}
return {
startState: function() {
return {
cmdState: [],
f: normal
};
},
copyState: function(s) {
return {
cmdState: s.cmdState.slice(),
f: s.f
};
},
token: function(stream, state) {
return state.f(stream, state);
},
lineComment: "%"
};
});
CodeMirror.defineMIME("text/x-stex", "stex");
CodeMirror.defineMIME("text/x-latex", "stex");
});

View File

@ -71,7 +71,7 @@
}; };
var closingBrackets = ""; var closingBrackets = "";
for (var i = 0; i < pairs.length; i += 2) (function(left, right) { for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
if (left != right) closingBrackets += right; closingBrackets += right;
map["'" + left + "'"] = function(cm) { map["'" + left + "'"] = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass; if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), type, next; var ranges = cm.listSelections(), type, next;

View File

@ -93,7 +93,7 @@
"if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
function getCompletions(token, context, keywords, options) { function getCompletions(token, context, keywords, options) {
var found = [], start = token.string; var found = [], start = token.string, global = options && options.globalScope || window;
function maybeAdd(str) { function maybeAdd(str) {
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
} }
@ -112,28 +112,28 @@
if (options && options.additionalContext) if (options && options.additionalContext)
base = options.additionalContext[obj.string]; base = options.additionalContext[obj.string];
if (!options || options.useGlobalScope !== false) if (!options || options.useGlobalScope !== false)
base = base || window[obj.string]; base = base || global[obj.string];
} else if (obj.type == "string") { } else if (obj.type == "string") {
base = ""; base = "";
} else if (obj.type == "atom") { } else if (obj.type == "atom") {
base = 1; base = 1;
} else if (obj.type == "function") { } else if (obj.type == "function") {
if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
(typeof window.jQuery == 'function')) (typeof global.jQuery == 'function'))
base = window.jQuery(); base = global.jQuery();
else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function')) else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
base = window._(); base = global._();
} }
while (base != null && context.length) while (base != null && context.length)
base = base[context.pop().string]; base = base[context.pop().string];
if (base != null) gatherCompletions(base); if (base != null) gatherCompletions(base);
} else { } else {
// If not, just look in the window object and any local scope // If not, just look in the global object and any local scope
// (reading into JS mode internals to get at the local and global variables) // (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name); for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
if (!options || options.useGlobalScope !== false) if (!options || options.useGlobalScope !== false)
gatherCompletions(window); gatherCompletions(global);
forEach(keywords, maybeAdd); forEach(keywords, maybeAdd);
} }
return found; return found;

View File

@ -18,10 +18,17 @@
var quote = (options && options.quoteChar) || '"'; var quote = (options && options.quoteChar) || '"';
if (!tags) return; if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur); var cur = cm.getCursor(), token = cm.getTokenAt(cur);
if (/^<\/?$/.test(token.string) && token.end == cur.ch) {
var nextToken = cm.getTokenAt(Pos(cur.line, cur.ch + 1));
if (nextToken.start == cur.ch && /\btag\b/.test(nextToken.type))
token = nextToken;
}
var inner = CodeMirror.innerMode(cm.getMode(), token.state); var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return; if (inner.mode.name != "xml") return;
var result = [], replaceToken = false, prefix; var result = [], replaceToken = false, prefix;
var tag = /\btag\b/.test(token.type), tagName = tag && /^\w/.test(token.string), tagStart; var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
var tagName = tag && /^\w/.test(token.string), tagStart;
if (tagName) { if (tagName) {
var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start); var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null; var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
@ -31,6 +38,7 @@
} else if (tag && token.string == "</") { } else if (tag && token.string == "</") {
tagType = "close"; tagType = "close";
} }
if (!tag && !inner.state.tagName || tagType) { if (!tag && !inner.state.tagName || tagType) {
if (tagName) if (tagName)
prefix = token.string; prefix = token.string;

View File

@ -3,12 +3,12 @@
(function(mod) { (function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror")); mod(require("../../lib/codemirror"), "cjs");
else if (typeof define == "function" && define.amd) // AMD else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod); define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); });
else // Plain browser env else // Plain browser env
mod(CodeMirror); mod(CodeMirror, "plain");
})(function(CodeMirror) { })(function(CodeMirror, env) {
if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
var loading = {}; var loading = {};
@ -35,21 +35,24 @@
if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
var script = document.createElement("script"); var file = CodeMirror.modeURL.replace(/%N/g, mode);
script.src = CodeMirror.modeURL.replace(/%N/g, mode); if (env == "plain") {
var others = document.getElementsByTagName("script")[0]; var script = document.createElement("script");
others.parentNode.insertBefore(script, others); script.src = file;
var list = loading[mode] = [cont]; var others = document.getElementsByTagName("script")[0];
var count = 0, poll = setInterval(function() { var list = loading[mode] = [cont];
if (++count > 100) return clearInterval(poll); CodeMirror.on(script, "load", function() {
if (CodeMirror.modes.hasOwnProperty(mode)) {
clearInterval(poll);
loading[mode] = null;
ensureDeps(mode, function() { ensureDeps(mode, function() {
for (var i = 0; i < list.length; ++i) list[i](); for (var i = 0; i < list.length; ++i) list[i]();
}); });
} });
}, 200); others.parentNode.insertBefore(script, others);
} else if (env == "cjs") {
require(file);
cont();
} else if (env == "amd") {
requirejs([file], cont);
}
}; };
CodeMirror.autoLoadMode = function(instance, mode) { CodeMirror.autoLoadMode = function(instance, mode) {

View File

@ -28,7 +28,7 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
overlay: CodeMirror.startState(overlay), overlay: CodeMirror.startState(overlay),
basePos: 0, baseCur: null, basePos: 0, baseCur: null,
overlayPos: 0, overlayCur: null, overlayPos: 0, overlayCur: null,
lineSeen: null streamSeen: null
}; };
}, },
copyState: function(state) { copyState: function(state) {
@ -41,9 +41,9 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
}, },
token: function(stream, state) { token: function(stream, state) {
if (stream.sol() || stream.string != state.lineSeen || if (stream != state.streamSeen ||
Math.min(state.basePos, state.overlayPos) < stream.start) { Math.min(state.basePos, state.overlayPos) < stream.start) {
state.lineSeen = stream.string; state.streamSeen = stream;
state.basePos = state.overlayPos = stream.start; state.basePos = state.overlayPos = stream.start;
} }

View File

@ -52,12 +52,12 @@
.CodeMirror div.CodeMirror-secondarycursor { .CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver; border-left: 1px solid silver;
} }
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
width: auto; width: auto;
border: 0; border: 0;
background: #7e7; background: #7e7;
} }
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursors { .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1; z-index: 1;
} }
@ -125,6 +125,7 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.cm-header, .cm-strong {font-weight: bold;} .cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;} .cm-em {font-style: italic;}
.cm-link {text-decoration: underline;} .cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-error {color: #f00;} .cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;} .cm-invalidchar {color: #f00;}
@ -209,6 +210,11 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
*zoom:1; *zoom:1;
*display:inline; *display:inline;
} }
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
height: 100%;
}
.CodeMirror-gutter-elt { .CodeMirror-gutter-elt {
position: absolute; position: absolute;
cursor: default; cursor: default;
@ -305,5 +311,8 @@ div.CodeMirror-cursors {
} }
} }
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */ /* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; } span.CodeMirror-selectedtext { background: none; }

View File

@ -86,7 +86,8 @@
suppressEdits: false, // used to disable editing during key handlers when in readOnly mode suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in readInput
draggingText: false, draggingText: false,
highlight: new Delayed() // stores highlight worker timeout highlight: new Delayed(), // stores highlight worker timeout
keySeq: null // Unfinished key sequence
}; };
// Override magic textarea content restore that IE sometimes does // Override magic textarea content restore that IE sometimes does
@ -184,8 +185,10 @@
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
if (ie && ie_version < 8) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px"; if (ie && ie_version < 8) d.scrollbarH.style.minHeight = d.scrollbarV.style.minWidth = "18px";
if (place.appendChild) place.appendChild(d.wrapper); if (place) {
else place(d.wrapper); if (place.appendChild) place.appendChild(d.wrapper);
else place(d.wrapper);
}
// Current rendered range (may be bigger than the view window). // Current rendered range (may be bigger than the view window).
d.viewFrom = d.viewTo = doc.first; d.viewFrom = d.viewTo = doc.first;
@ -196,7 +199,7 @@
d.externalMeasured = null; d.externalMeasured = null;
// Empty space (in pixels) above the view // Empty space (in pixels) above the view
d.viewOffset = 0; d.viewOffset = 0;
d.lastSizeC = 0; d.lastWrapHeight = d.lastWrapWidth = 0;
d.updateLineNumbers = null; d.updateLineNumbers = null;
// Used to only resize the line number gutter when necessary (when // Used to only resize the line number gutter when necessary (when
@ -301,12 +304,6 @@
}); });
} }
function keyMapChanged(cm) {
var map = keyMap[cm.options.keyMap], style = map.style;
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
(style ? " cm-keymap-" + style : "");
}
function themeChanged(cm) { function themeChanged(cm) {
cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
@ -551,6 +548,7 @@
this.visible = visibleLines(display, cm.doc, viewport); this.visible = visibleLines(display, cm.doc, viewport);
this.editorIsHidden = !display.wrapper.offsetWidth; this.editorIsHidden = !display.wrapper.offsetWidth;
this.wrapperHeight = display.wrapper.clientHeight; this.wrapperHeight = display.wrapper.clientHeight;
this.wrapperWidth = display.wrapper.clientWidth;
this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo; this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
this.oldScrollerWidth = display.scroller.clientWidth; this.oldScrollerWidth = display.scroller.clientWidth;
this.force = force; this.force = force;
@ -591,7 +589,7 @@
} }
var different = from != display.viewFrom || to != display.viewTo || var different = from != display.viewFrom || to != display.viewTo ||
display.lastSizeC != update.wrapperHeight; display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
adjustView(cm, from, to); adjustView(cm, from, to);
display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
@ -619,7 +617,8 @@
removeChildren(display.selectionDiv); removeChildren(display.selectionDiv);
if (different) { if (different) {
display.lastSizeC = update.wrapperHeight; display.lastWrapHeight = update.wrapperHeight;
display.lastWrapWidth = update.wrapperWidth;
startWorker(cm, 400); startWorker(cm, 400);
} }
@ -868,9 +867,12 @@
if (cm.options.lineNumbers || markers) { if (cm.options.lineNumbers || markers) {
var wrap = ensureLineWrapped(lineView); var wrap = ensureLineWrapped(lineView);
var gutterWrap = lineView.gutter = var gutterWrap = lineView.gutter =
wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "position: absolute; left: " + wrap.insertBefore(elt("div", null, "CodeMirror-gutter-wrapper", "left: " +
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"), (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) +
"px; width: " + dims.gutterTotalWidth + "px"),
lineView.text); lineView.text);
if (lineView.line.gutterClass)
gutterWrap.className += " " + lineView.line.gutterClass;
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
lineView.lineNumber = gutterWrap.appendChild( lineView.lineNumber = gutterWrap.appendChild(
elt("div", lineNumberFor(cm.options, lineN), elt("div", lineNumberFor(cm.options, lineN),
@ -2397,7 +2399,7 @@
// possible when it is clear that nothing happened. hasSelection // possible when it is clear that nothing happened. hasSelection
// will be the case when there is a lot of text in the textarea, // will be the case when there is a lot of text in the textarea,
// in which case reading its value would be expensive. // in which case reading its value would be expensive.
if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput) if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
return false; return false;
// See paste handler for more on the fakedLastChar kludge // See paste handler for more on the fakedLastChar kludge
if (cm.state.pasteIncoming && cm.state.fakedLastChar) { if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
@ -2652,8 +2654,10 @@
// Called when the window resizes // Called when the window resizes
function onResize(cm) { function onResize(cm) {
// Might be a text scaling operation, clear size caches.
var d = cm.display; var d = cm.display;
if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth)
return;
// Might be a text scaling operation, clear size caches.
d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
cm.setSize(); cm.setSize();
} }
@ -3167,62 +3171,70 @@
return done; return done;
} }
// Collect the currently active keymaps. function lookupKeyForEditor(cm, name, handle) {
function allKeyMaps(cm) { for (var i = 0; i < cm.state.keyMaps.length; i++) {
var maps = cm.state.keyMaps.slice(0); var result = lookupKey(name, cm.state.keyMaps[i], handle);
if (cm.options.extraKeys) maps.push(cm.options.extraKeys); if (result) return result;
maps.push(cm.options.keyMap); }
return maps; return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle))
|| lookupKey(name, cm.options.keyMap, handle);
}
var stopSeq = new Delayed;
function dispatchKey(cm, name, e, handle) {
var seq = cm.state.keySeq;
if (seq) {
if (isModifierKey(name)) return "handled";
stopSeq.set(50, function() {
if (cm.state.keySeq == seq) {
cm.state.keySeq = null;
resetInput(cm);
}
});
name = seq + " " + name;
}
var result = lookupKeyForEditor(cm, name, handle);
if (result == "multi")
cm.state.keySeq = name;
if (result == "handled")
signalLater(cm, "keyHandled", cm, name, e);
if (result == "handled" || result == "multi") {
e_preventDefault(e);
restartBlink(cm);
}
if (seq && !result && /\'$/.test(name)) {
e_preventDefault(e);
return true;
}
return !!result;
} }
var maybeTransition;
// Handle a key from the keydown event. // Handle a key from the keydown event.
function handleKeyBinding(cm, e) { function handleKeyBinding(cm, e) {
// Handle automatic keymap transitions var name = keyName(e, true);
var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto;
clearTimeout(maybeTransition);
if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() {
if (getKeyMap(cm.options.keyMap) == startMap) {
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
keyMapChanged(cm);
}
}, 50);
var name = keyName(e, true), handled = false;
if (!name) return false; if (!name) return false;
var keymaps = allKeyMaps(cm);
if (e.shiftKey) { if (e.shiftKey && !cm.state.keySeq) {
// First try to resolve full name (including 'Shift-'). Failing // First try to resolve full name (including 'Shift-'). Failing
// that, see if there is a cursor-motion command (starting with // that, see if there is a cursor-motion command (starting with
// 'go') bound to the keyname without 'Shift-'. // 'go') bound to the keyname without 'Shift-'.
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);}) return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);})
|| lookupKey(name, keymaps, function(b) { || dispatchKey(cm, name, e, function(b) {
if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
return doHandleBinding(cm, b); return doHandleBinding(cm, b);
}); });
} else { } else {
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); }); return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); });
} }
if (handled) {
e_preventDefault(e);
restartBlink(cm);
signalLater(cm, "keyHandled", cm, name, e);
}
return handled;
} }
// Handle a key from the keypress event // Handle a key from the keypress event
function handleCharBinding(cm, e, ch) { function handleCharBinding(cm, e, ch) {
var handled = lookupKey("'" + ch + "'", allKeyMaps(cm), return dispatchKey(cm, "'" + ch + "'", e,
function(b) { return doHandleBinding(cm, b, true); }); function(b) { return doHandleBinding(cm, b, true); });
if (handled) {
e_preventDefault(e);
restartBlink(cm);
signalLater(cm, "keyHandled", cm, "'" + ch + "'", e);
}
return handled;
} }
var lastStoppedKey = null; var lastStoppedKey = null;
@ -3701,6 +3713,8 @@
// If an editor sits on the top or bottom of the window, partially // If an editor sits on the top or bottom of the window, partially
// scrolled out of view, this ensures that the cursor is visible. // scrolled out of view, this ensures that the cursor is visible.
function maybeScrollWindow(cm, coords) { function maybeScrollWindow(cm, coords) {
if (signalDOMEvent(cm, "scrollCursorIntoView")) return;
var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
if (coords.top + box.top < 0) doScroll = true; if (coords.top + box.top < 0) doScroll = true;
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
@ -4024,12 +4038,12 @@
getDoc: function() {return this.doc;}, getDoc: function() {return this.doc;},
addKeyMap: function(map, bottom) { addKeyMap: function(map, bottom) {
this.state.keyMaps[bottom ? "push" : "unshift"](map); this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
}, },
removeKeyMap: function(map) { removeKeyMap: function(map) {
var maps = this.state.keyMaps; var maps = this.state.keyMaps;
for (var i = 0; i < maps.length; ++i) for (var i = 0; i < maps.length; ++i)
if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) { if (maps[i] == map || maps[i].name == map) {
maps.splice(i, 1); maps.splice(i, 1);
return true; return true;
} }
@ -4086,20 +4100,11 @@
// Fetch the parser token for a given character. Useful for hacks // Fetch the parser token for a given character. Useful for hacks
// that want to inspect the mode state (say, for completion). // that want to inspect the mode state (say, for completion).
getTokenAt: function(pos, precise) { getTokenAt: function(pos, precise) {
var doc = this.doc; return takeToken(this, pos, precise);
pos = clipPos(doc, pos); },
var state = getStateBefore(this, pos.line, precise), mode = this.doc.mode;
var line = getLine(doc, pos.line); getLineTokens: function(line, precise) {
var stream = new StringStream(line.text, this.options.tabSize); return takeToken(this, Pos(line), precise, true);
while (stream.pos < pos.ch && !stream.eol()) {
stream.start = stream.pos;
var style = readToken(mode, stream, state);
}
return {start: stream.start,
end: stream.pos,
string: stream.current(),
type: style || null,
state: state};
}, },
getTokenTypeAt: function(pos) { getTokenTypeAt: function(pos) {
@ -4502,7 +4507,12 @@
themeChanged(cm); themeChanged(cm);
guttersChanged(cm); guttersChanged(cm);
}, true); }, true);
option("keyMap", "default", keyMapChanged); option("keyMap", "default", function(cm, val, old) {
var next = getKeyMap(val);
var prev = old != CodeMirror.Init && getKeyMap(old);
if (prev && prev.detach) prev.detach(cm, next);
if (next.attach) next.attach(cm, prev || null);
});
option("extraKeys", null); option("extraKeys", null);
option("lineWrapping", false, wrappingChanged, true); option("lineWrapping", false, wrappingChanged, true);
@ -4847,9 +4857,11 @@
toggleOverwrite: function(cm) {cm.toggleOverwrite();} toggleOverwrite: function(cm) {cm.toggleOverwrite();}
}; };
// STANDARD KEYMAPS // STANDARD KEYMAPS
var keyMap = CodeMirror.keyMap = {}; var keyMap = CodeMirror.keyMap = {};
keyMap.basic = { keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
@ -4871,6 +4883,13 @@
"Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
fallthrough: "basic" fallthrough: "basic"
}; };
// Very basic readline/emacs-style bindings, which are standard on Mac.
keyMap.emacsy = {
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
};
keyMap.macDefault = { keyMap.macDefault = {
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
"Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
@ -4881,70 +4900,100 @@
"Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
fallthrough: ["basic", "emacsy"] fallthrough: ["basic", "emacsy"]
}; };
// Very basic readline/emacs-style bindings, which are standard on Mac.
keyMap.emacsy = {
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
"Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
"Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars"
};
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
// KEYMAP DISPATCH // KEYMAP DISPATCH
function getKeyMap(val) { function normalizeKeyName(name) {
if (typeof val == "string") return keyMap[val]; var parts = name.split(/-(?!$)/), name = parts[parts.length - 1];
else return val; var alt, ctrl, shift, cmd;
for (var i = 0; i < parts.length - 1; i++) {
var mod = parts[i];
if (/^(cmd|meta|m)$/i.test(mod)) cmd = true;
else if (/^a(lt)?$/i.test(mod)) alt = true;
else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true;
else if (/^s(hift)$/i.test(mod)) shift = true;
else throw new Error("Unrecognized modifier name: " + mod);
}
if (alt) name = "Alt-" + name;
if (ctrl) name = "Ctrl-" + name;
if (cmd) name = "Cmd-" + name;
if (shift) name = "Shift-" + name;
return name;
} }
// Given an array of keymaps and a key name, call handle on any // This is a kludge to keep keymaps mostly working as raw objects
// bindings found, until that returns a truthy value, at which point // (backwards compatibility) while at the same time support features
// we consider the key handled. Implements things like binding a key // like normalization and multi-stroke key bindings. It compiles a
// to false stopping further handling and keymap fallthrough. // new normalized keymap, and then updates the old object to reflect
var lookupKey = CodeMirror.lookupKey = function(name, maps, handle) { // this.
function lookup(map) { CodeMirror.normalizeKeyMap = function(keymap) {
map = getKeyMap(map); var copy = {};
var found = map[name]; for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) {
if (found === false) return "stop"; var value = keymap[keyname];
if (found != null && handle(found)) return true; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue;
if (map.nofallthrough) return "stop"; if (value == "...") { delete keymap[keyname]; continue; }
var fallthrough = map.fallthrough; var keys = map(keyname.split(" "), normalizeKeyName);
if (fallthrough == null) return false; for (var i = 0; i < keys.length; i++) {
if (Object.prototype.toString.call(fallthrough) != "[object Array]") var val, name;
return lookup(fallthrough); if (i == keys.length - 1) {
for (var i = 0; i < fallthrough.length; ++i) { name = keyname;
var done = lookup(fallthrough[i]); val = value;
if (done) return done; } else {
name = keys.slice(0, i + 1).join(" ");
val = "...";
}
var prev = copy[name];
if (!prev) copy[name] = val;
else if (prev != val) throw new Error("Inconsistent bindings for " + name);
} }
return false; delete keymap[keyname];
} }
for (var prop in copy) keymap[prop] = copy[prop];
return keymap;
};
for (var i = 0; i < maps.length; ++i) { var lookupKey = CodeMirror.lookupKey = function(key, map, handle) {
var done = lookup(maps[i]); map = getKeyMap(map);
if (done) return done != "stop"; var found = map.call ? map.call(key) : map[key];
if (found === false) return "nothing";
if (found === "...") return "multi";
if (found != null && handle(found)) return "handled";
if (map.fallthrough) {
if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
return lookupKey(key, map.fallthrough, handle);
for (var i = 0; i < map.fallthrough.length; i++) {
var result = lookupKey(key, map.fallthrough[i], handle);
if (result) return result;
}
} }
}; };
// Modifier key presses don't count as 'real' key presses for the // Modifier key presses don't count as 'real' key presses for the
// purpose of keymap fallthrough. // purpose of keymap fallthrough.
var isModifierKey = CodeMirror.isModifierKey = function(event) { var isModifierKey = CodeMirror.isModifierKey = function(value) {
var name = keyNames[event.keyCode]; var name = typeof value == "string" ? value : keyNames[value.keyCode];
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
}; };
// Look up the name of a key as indicated by an event object. // Look up the name of a key as indicated by an event object.
var keyName = CodeMirror.keyName = function(event, noShift) { var keyName = CodeMirror.keyName = function(event, noShift) {
if (presto && event.keyCode == 34 && event["char"]) return false; if (presto && event.keyCode == 34 && event["char"]) return false;
var name = keyNames[event.keyCode]; var base = keyNames[event.keyCode], name = base;
if (name == null || event.altGraphKey) return false; if (name == null || event.altGraphKey) return false;
if (event.altKey) name = "Alt-" + name; if (event.altKey && base != "Alt") name = "Alt-" + name;
if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name; if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name;
if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name; if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name;
if (!noShift && event.shiftKey) name = "Shift-" + name; if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name;
return name; return name;
}; };
function getKeyMap(val) {
return typeof val == "string" ? keyMap[val] : val;
}
// FROMTEXTAREA // FROMTEXTAREA
CodeMirror.fromTextArea = function(textarea, options) { CodeMirror.fromTextArea = function(textarea, options) {
@ -5794,20 +5843,44 @@
if (inner.mode.blankLine) return inner.mode.blankLine(inner.state); if (inner.mode.blankLine) return inner.mode.blankLine(inner.state);
} }
function readToken(mode, stream, state) { function readToken(mode, stream, state, inner) {
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
if (inner) inner[0] = CodeMirror.innerMode(mode, state).mode;
var style = mode.token(stream, state); var style = mode.token(stream, state);
if (stream.pos > stream.start) return style; if (stream.pos > stream.start) return style;
} }
throw new Error("Mode " + mode.name + " failed to advance stream."); throw new Error("Mode " + mode.name + " failed to advance stream.");
} }
// Utility for getTokenAt and getLineTokens
function takeToken(cm, pos, precise, asArray) {
function getObj(copy) {
return {start: stream.start, end: stream.pos,
string: stream.current(),
type: style || null,
state: copy ? copyState(doc.mode, state) : state};
}
var doc = cm.doc, mode = doc.mode, style;
pos = clipPos(doc, pos);
var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise);
var stream = new StringStream(line.text, cm.options.tabSize), tokens;
if (asArray) tokens = [];
while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
stream.start = stream.pos;
style = readToken(mode, stream, state);
if (asArray) tokens.push(getObj(true));
}
return asArray ? tokens : getObj();
}
// Run the given mode's parser over a line, calling f for each token. // Run the given mode's parser over a line, calling f for each token.
function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) {
var flattenSpans = mode.flattenSpans; var flattenSpans = mode.flattenSpans;
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
var curStart = 0, curStyle = null; var curStart = 0, curStyle = null;
var stream = new StringStream(text, cm.options.tabSize), style; var stream = new StringStream(text, cm.options.tabSize), style;
var inner = cm.options.addModeClass && [null];
if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses); if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses);
while (!stream.eol()) { while (!stream.eol()) {
if (stream.pos > cm.options.maxHighlightLength) { if (stream.pos > cm.options.maxHighlightLength) {
@ -5816,10 +5889,10 @@
stream.pos = text.length; stream.pos = text.length;
style = null; style = null;
} else { } else {
style = extractLineClasses(readToken(mode, stream, state), lineClasses); style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses);
} }
if (cm.options.addModeClass) { if (inner) {
var mName = CodeMirror.innerMode(mode, state).mode.name; var mName = inner[0].name;
if (mName) style = "m-" + (style ? mName + " " + style : mName); if (mName) style = "m-" + (style ? mName + " " + style : mName);
} }
if (!flattenSpans || curStyle != style) { if (!flattenSpans || curStyle != style) {
@ -5878,12 +5951,13 @@
return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null};
} }
function getLineStyles(cm, line) { function getLineStyles(cm, line, updateFrontier) {
if (!line.styles || line.styles[0] != cm.state.modeGen) { if (!line.styles || line.styles[0] != cm.state.modeGen) {
var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); var result = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
line.styles = result.styles; line.styles = result.styles;
if (result.classes) line.styleClasses = result.classes; if (result.classes) line.styleClasses = result.classes;
else if (line.styleClasses) line.styleClasses = null; else if (line.styleClasses) line.styleClasses = null;
if (updateFrontier === cm.doc.frontier) cm.doc.frontier++;
} }
return line.styles; return line.styles;
} }
@ -5938,7 +6012,8 @@
if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line)))
builder.addToken = buildTokenBadBidi(builder.addToken, order); builder.addToken = buildTokenBadBidi(builder.addToken, order);
builder.map = []; builder.map = [];
insertLineContent(line, builder, getLineStyles(cm, line)); var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
if (line.styleClasses) { if (line.styleClasses) {
if (line.styleClasses.bgClass) if (line.styleClasses.bgClass)
builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "");
@ -5960,9 +6035,14 @@
} }
} }
// See issue #2901
if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className))
builder.content.className = "cm-tab-wrap-hack";
signal(cm, "renderLine", cm, lineView.line, builder.pre); signal(cm, "renderLine", cm, lineView.line, builder.pre);
if (builder.pre.className) if (builder.pre.className)
builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); builder.textClass = joinClasses(builder.pre.className, builder.textClass || "");
return builder; return builder;
} }
@ -6531,22 +6611,26 @@
}, },
addLineClass: docMethodOp(function(handle, where, cls) { addLineClass: docMethodOp(function(handle, where, cls) {
return changeLine(this, handle, "class", function(line) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass"; var prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
: where == "gutter" ? "gutterClass" : "wrapClass";
if (!line[prop]) line[prop] = cls; if (!line[prop]) line[prop] = cls;
else if (new RegExp("(?:^|\\s)" + cls + "(?:$|\\s)").test(line[prop])) return false; else if (classTest(cls).test(line[prop])) return false;
else line[prop] += " " + cls; else line[prop] += " " + cls;
return true; return true;
}); });
}), }),
removeLineClass: docMethodOp(function(handle, where, cls) { removeLineClass: docMethodOp(function(handle, where, cls) {
return changeLine(this, handle, "class", function(line) { return changeLine(this, handle, "class", function(line) {
var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : "wrapClass"; var prop = where == "text" ? "textClass"
: where == "background" ? "bgClass"
: where == "gutter" ? "gutterClass" : "wrapClass";
var cur = line[prop]; var cur = line[prop];
if (!cur) return false; if (!cur) return false;
else if (cls == null) line[prop] = null; else if (cls == null) line[prop] = null;
else { else {
var found = cur.match(new RegExp("(?:^|\\s+)" + cls + "(?:$|\\s+)")); var found = cur.match(classTest(cls));
if (!found) return false; if (!found) return false;
var end = found.index + found[0].length; var end = found.index + found[0].length;
line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
@ -7165,6 +7249,8 @@
// registering a (non-DOM) handler on the editor for the event name, // registering a (non-DOM) handler on the editor for the event name,
// and preventDefault-ing the event in that handler. // and preventDefault-ing the event in that handler.
function signalDOMEvent(cm, e, override) { function signalDOMEvent(cm, e, override) {
if (typeof e == "string")
e = {type: e, preventDefault: function() { this.defaultPrevented = true; }};
signal(cm, override || e.type, cm, e); signal(cm, override || e.type, cm, e);
return e_defaultPrevented(e) || e.codemirrorIgnore; return e_defaultPrevented(e) || e.codemirrorIgnore;
} }
@ -7338,7 +7424,8 @@
}; };
else range = function(node, start, end) { else range = function(node, start, end) {
var r = document.body.createTextRange(); var r = document.body.createTextRange();
r.moveToElementText(node.parentNode); try { r.moveToElementText(node.parentNode); }
catch(e) { return r; }
r.collapse(true); r.collapse(true);
r.moveEnd("character", end); r.moveEnd("character", end);
r.moveStart("character", start); r.moveStart("character", start);
@ -7370,14 +7457,19 @@
catch(e) { return document.body; } catch(e) { return document.body; }
}; };
function classTest(cls) { return new RegExp("\\b" + cls + "\\b\\s*"); } function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); }
function rmClass(node, cls) { var rmClass = CodeMirror.rmClass = function(node, cls) {
var test = classTest(cls); var current = node.className;
if (test.test(node.className)) node.className = node.className.replace(test, ""); var match = classTest(cls).exec(current);
} if (match) {
function addClass(node, cls) { var after = current.slice(match.index + match[0].length);
if (!classTest(cls).test(node.className)) node.className += " " + cls; node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
} }
};
var addClass = CodeMirror.addClass = function(node, cls) {
var current = node.className;
if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls;
};
function joinClasses(a, b) { function joinClasses(a, b) {
var as = a.split(" "); var as = a.split(" ");
for (var i = 0; i < as.length; i++) for (var i = 0; i < as.length; i++)
@ -7824,7 +7916,7 @@
// THE END // THE END
CodeMirror.version = "4.7.0"; CodeMirror.version = "4.8.0";
return CodeMirror; return CodeMirror;
}); });

View File

@ -256,7 +256,7 @@
// Actual keymap // Actual keymap
var keyMap = CodeMirror.keyMap.emacs = { var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
"Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));}, "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));},
"Ctrl-K": repeated(function(cm) { "Ctrl-K": repeated(function(cm) {
var start = cm.getCursor(), end = cm.clipPos(Pos(start.line)); var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
@ -353,27 +353,7 @@
"Alt-/": "autocomplete", "Alt-/": "autocomplete",
"Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto", "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto",
"Alt-G": function(cm) {cm.setOption("keyMap", "emacs-Alt-G");}, "Alt-G G": function(cm) {
"Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");},
"Ctrl-Q": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-Q");},
"Ctrl-U": addPrefixMap
};
CodeMirror.keyMap["emacs-Ctrl-X"] = {
"Tab": function(cm) {
cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
},
"Ctrl-X": function(cm) {
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
},
"Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": repeated("undo"), "K": "close",
"Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
auto: "emacs", nofallthrough: true, disableInput: true
};
CodeMirror.keyMap["emacs-Alt-G"] = {
"G": function(cm) {
var prefix = getPrefix(cm, true); var prefix = getPrefix(cm, true);
if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1); if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
@ -383,13 +363,24 @@
cm.setCursor(num - 1); cm.setCursor(num - 1);
}); });
}, },
auto: "emacs", nofallthrough: true, disableInput: true
};
CodeMirror.keyMap["emacs-Ctrl-Q"] = { "Ctrl-X Tab": function(cm) {
"Tab": repeated("insertTab"), cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
auto: "emacs", nofallthrough: true },
}; "Ctrl-X Ctrl-X": function(cm) {
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
},
"Ctrl-X Ctrl-S": "save",
"Ctrl-X Ctrl-W": "save",
"Ctrl-X S": "saveAll",
"Ctrl-X F": "open",
"Ctrl-X U": repeated("undo"),
"Ctrl-X K": "close",
"Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); },
"Ctrl-Q Tab": repeated("insertTab"),
"Ctrl-U": addPrefixMap
});
var prefixMap = {"Ctrl-G": clearPrefix}; var prefixMap = {"Ctrl-G": clearPrefix};
function regPrefix(d) { function regPrefix(d) {

View File

@ -386,9 +386,7 @@
map["Alt-Q"] = "wrapLines"; map["Alt-Q"] = "wrapLines";
var mapK = CodeMirror.keyMap["sublime-Ctrl-K"] = {auto: "sublime", nofallthrough: true}; var cK = ctrl + "K ";
map[ctrl + "K"] = function(cm) {cm.setOption("keyMap", "sublime-Ctrl-K");};
function modifyWordOrSelection(cm, mod) { function modifyWordOrSelection(cm, mod) {
cm.operation(function() { cm.operation(function() {
@ -409,9 +407,9 @@
}); });
} }
mapK[ctrl + "Backspace"] = "delLineLeft"; map[cK + ctrl + "Backspace"] = "delLineLeft";
cmds[mapK[ctrl + "K"] = "delLineRight"] = function(cm) { cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) {
cm.operation(function() { cm.operation(function() {
var ranges = cm.listSelections(); var ranges = cm.listSelections();
for (var i = ranges.length - 1; i >= 0; i--) for (var i = ranges.length - 1; i >= 0; i--)
@ -420,22 +418,22 @@
}); });
}; };
cmds[mapK[ctrl + "U"] = "upcaseAtCursor"] = function(cm) { cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) {
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); }); modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
}; };
cmds[mapK[ctrl + "L"] = "downcaseAtCursor"] = function(cm) { cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) {
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); }); modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
}; };
cmds[mapK[ctrl + "Space"] = "setSublimeMark"] = function(cm) { cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) {
if (cm.state.sublimeMark) cm.state.sublimeMark.clear(); if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
cm.state.sublimeMark = cm.setBookmark(cm.getCursor()); cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
}; };
cmds[mapK[ctrl + "A"] = "selectToSublimeMark"] = function(cm) { cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) cm.setSelection(cm.getCursor(), found); if (found) cm.setSelection(cm.getCursor(), found);
}; };
cmds[mapK[ctrl + "W"] = "deleteToSublimeMark"] = function(cm) { cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) { if (found) {
var from = cm.getCursor(), to = found; var from = cm.getCursor(), to = found;
@ -444,7 +442,7 @@
cm.replaceRange("", from, to); cm.replaceRange("", from, to);
} }
}; };
cmds[mapK[ctrl + "X"] = "swapWithSublimeMark"] = function(cm) { cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) { if (found) {
cm.state.sublimeMark.clear(); cm.state.sublimeMark.clear();
@ -452,13 +450,13 @@
cm.setCursor(found); cm.setCursor(found);
} }
}; };
cmds[mapK[ctrl + "Y"] = "sublimeYank"] = function(cm) { cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) {
if (cm.state.sublimeKilled != null) if (cm.state.sublimeKilled != null)
cm.replaceSelection(cm.state.sublimeKilled, null, "paste"); cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
}; };
mapK[ctrl + "G"] = "clearBookmarks"; map[cK + ctrl + "G"] = "clearBookmarks";
cmds[mapK[ctrl + "C"] = "showInCenter"] = function(cm) { cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) {
var pos = cm.cursorCoords(null, "local"); var pos = cm.cursorCoords(null, "local");
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2); cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
}; };
@ -530,7 +528,7 @@
map["Shift-" + ctrl + "["] = "fold"; map["Shift-" + ctrl + "["] = "fold";
map["Shift-" + ctrl + "]"] = "unfold"; map["Shift-" + ctrl + "]"] = "unfold";
mapK[ctrl + "0"] = mapK[ctrl + "j"] = "unfoldAll"; map[cK + ctrl + "0"] = map[cK + ctrl + "j"] = "unfoldAll";
map[ctrl + "I"] = "findIncremental"; map[ctrl + "I"] = "findIncremental";
map["Shift-" + ctrl + "I"] = "findIncrementalReverse"; map["Shift-" + ctrl + "I"] = "findIncrementalReverse";
@ -538,4 +536,5 @@
map["F3"] = "findNext"; map["F3"] = "findNext";
map["Shift-F3"] = "findPrev"; map["Shift-F3"] = "findPrev";
CodeMirror.normalizeKeyMap(map);
}); });

View File

@ -471,4 +471,19 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
modeProps: {fold: ["brace", "include"]} modeProps: {fold: ["brace", "include"]}
}); });
def("text/x-objectivec", {
name: "clike",
keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " +
"inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
atoms: words("YES NO NULL NILL ON OFF"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$]/);
return "keyword";
},
"#": cppHook
},
modeProps: {fold: "brace"}
});
}); });

View File

@ -139,6 +139,26 @@ void Class::Method2(MyType<T, V>* value) {
} }
</textarea></div> </textarea></div>
<h2>Objective-C example</h2>
<div><textarea id="objectivec-code">
/*
This is a longer comment
That spans two lines
*/
#import <Test/Test.h>
@implementation YourAppDelegate
// This is a one-line comment
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
char myString[] = "This is a C character array";
int test = 5;
return YES;
}
</textarea></div>
<h2>Java example</h2> <h2>Java example</h2>
<div><textarea id="java-code"> <div><textarea id="java-code">
@ -202,6 +222,11 @@ object FilterTest extends App {
matchBrackets: true, matchBrackets: true,
mode: "text/x-java" mode: "text/x-java"
}); });
var objectivecEditor = CodeMirror.fromTextArea(document.getElementById("objectivec-code"), {
lineNumbers: true,
matchBrackets: true,
mode: "text/x-objectivec"
});
var scalaEditor = CodeMirror.fromTextArea(document.getElementById("scala-code"), { var scalaEditor = CodeMirror.fromTextArea(document.getElementById("scala-code"), {
lineNumbers: true, lineNumbers: true,
matchBrackets: true, matchBrackets: true,
@ -220,6 +245,7 @@ object FilterTest extends App {
<p><strong>MIME types defined:</strong> <code>text/x-csrc</code> <p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
(C), <code>text/x-c++src</code> (C++), <code>text/x-java</code> (C), <code>text/x-c++src</code> (C++), <code>text/x-java</code>
(Java), <code>text/x-csharp</code> (C#), (Java), <code>text/x-csharp</code> (C#),
<code>text/x-objectivec</code> (Objective-C),
<code>text/x-scala</code> (Scala), <code>text/x-vertex</code> <code>text/x-scala</code> (Scala), <code>text/x-vertex</code>
and <code>x-shader/x-fragment</code> (shader programs).</p> and <code>x-shader/x-fragment</code> (shader programs).</p>
</article> </article>

View File

@ -182,7 +182,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
override = "string-2"; override = "string-2";
return "maybeprop"; return "maybeprop";
} else if (allowNested) { } else if (allowNested) {
override = stream.match(/^\s*:/, false) ? "property" : "tag"; override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
return "block"; return "block";
} else { } else {
override += " error"; override += " error";

View File

@ -0,0 +1,80 @@
// 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("../../addon/mode/simple"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// Collect all Dockerfile directives
var instructions = ["from", "maintainer", "run", "cmd", "expose", "env",
"add", "copy", "entrypoint", "volume", "user",
"workdir", "onbuild"],
instructionRegex = "(" + instructions.join('|') + ")",
instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
CodeMirror.defineSimpleMode("dockerfile", {
start: [
// Block comment: This is a line starting with a comment
{
regex: /#.*$/,
token: "comment",
next: "start"
},
// Highlight an instruction without any arguments (for convenience)
{
regex: instructionOnlyLine,
token: "variable-2",
next: "start"
},
// Highlight an instruction followed by arguments
{
regex: instructionWithArguments,
token: ["variable-2", null],
next: "arguments"
},
// Fail-safe return to start
{
token: null,
next: "start"
}
],
arguments: [
{
// Line comment without instruction arguments is an error
regex: /#.*$/,
token: "error",
next: "start"
},
{
regex: /[^#]+\\$/,
token: null,
next: "arguments"
},
{
// Match everything except for the inline comment
regex: /[^#]+/,
token: null,
next: "start"
},
{
regex: /$/,
token: null,
next: "start"
},
// Fail safe return to start
{
token: null,
next: "start"
}
]
});
CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");
});

View File

@ -0,0 +1,72 @@
<!doctype html>
<title>CodeMirror: Dockerfile 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/mode/simple.js"></script>
<script src="dockerfile.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="#">Dockerfile</a>
</ul>
</div>
<article>
<h2>Dockerfile mode</h2>
<form><textarea id="code" name="code"># Install Ghost blogging platform and run development environment
#
# VERSION 1.0.0
FROM ubuntu:12.10
MAINTAINER Amer Grgic "amer@livebyt.es"
WORKDIR /data/ghost
# Install dependencies for nginx installation
RUN apt-get update
RUN apt-get install -y python g++ make software-properties-common --force-yes
RUN add-apt-repository ppa:chris-lea/node.js
RUN apt-get update
# Install unzip
RUN apt-get install -y unzip
# Install curl
RUN apt-get install -y curl
# Install nodejs & npm
RUN apt-get install -y rlwrap
RUN apt-get install -y nodejs
# Download Ghost v0.4.1
RUN curl -L https://ghost.org/zip/ghost-latest.zip -o /tmp/ghost.zip
# Unzip Ghost zip to /data/ghost
RUN unzip -uo /tmp/ghost.zip -d /data/ghost
# Add custom config js to /data/ghost
ADD ./config.example.js /data/ghost/config.js
# Install Ghost with NPM
RUN cd /data/ghost/ && npm install --production
# Expose port 2368
EXPOSE 2368
# Run Ghost
CMD ["npm","start"]
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "dockerfile"
});
</script>
<p>Dockerfile syntac highlighting for CodeMirror.</p>
<p><strong>MIME types defined:</strong> <code>text/x-dockerfile</code></p>
</article>

Some files were not shown because too many files have changed in this diff Show More