27 changes: 24 additions & 3 deletions keymap/vim.js
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,8 @@
mapCommand: mapCommand,
_mapCommand: _mapCommand,

defineRegister: defineRegister,

exitVisualMode: exitVisualMode,
exitInsertMode: exitInsertMode
};
Expand Down Expand Up @@ -940,6 +942,25 @@
}
};

/**
* Defines an external register.
*
* The name should be a single character that will be used to reference the register.
* The register should support setText, pushText, clear, and toString(). See Register
* for a reference implementation.
*/
function defineRegister(name, register) {
var registers = vimGlobalState.registerController.registers[name];
if (!name || name.length != 1) {
throw Error('Register name must be 1 character');
}
if (registers[name]) {
throw Error('Register already defined ' + name);
}
registers[name] = register;
validRegisters.push(name);
}

/*
* vim registers allow you to keep many independent copy and paste buffers.
* See http://usevim.com/2012/04/13/registers/ for an introduction.
Expand Down Expand Up @@ -4726,7 +4747,7 @@
}

function _mapCommand(command) {
defaultKeymap.push(command);
defaultKeymap.unshift(command);
}

function mapCommand(keys, type, name, args, extra) {
Expand Down Expand Up @@ -4815,7 +4836,7 @@
if (macroModeState.isPlaying) { return; }
var registerName = macroModeState.latestRegister;
var register = vimGlobalState.registerController.getRegister(registerName);
if (register) {
if (register && register.pushInsertModeChanges) {
register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
}
}
Expand All @@ -4824,7 +4845,7 @@
if (macroModeState.isPlaying) { return; }
var registerName = macroModeState.latestRegister;
var register = vimGlobalState.registerController.getRegister(registerName);
if (register) {
if (register && register.pushSearchQuery) {
register.pushSearchQuery(query);
}
}
Expand Down
18 changes: 9 additions & 9 deletions lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}

/* DEFAULT THEME */

.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}

.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
Expand All @@ -111,18 +120,9 @@ div.CodeMirror-overwrite div.CodeMirror-cursor {}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}

.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}

.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}

Expand Down
62 changes: 26 additions & 36 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -1081,9 +1081,10 @@
cm.display.shift = false;
if (!sel) sel = doc.sel;

var paste = cm.state.pasteIncoming || origin == "paste";
var textLines = splitLines(inserted), multiPaste = null;
// When pasing N lines into N selections, insert one line per selection
if (cm.state.pasteIncoming && sel.ranges.length > 1) {
if (paste && sel.ranges.length > 1) {
if (lastCopied && lastCopied.join("\n") == inserted)
multiPaste = sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
else if (textLines.length == sel.ranges.length)
Expand All @@ -1097,16 +1098,16 @@
if (range.empty()) {
if (deleted && deleted > 0) // Handle deletion
from = Pos(from.line, from.ch - deleted);
else if (cm.state.overwrite && !cm.state.pasteIncoming) // Handle overwrite
else if (cm.state.overwrite && !paste) // Handle overwrite
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
}
var updateInput = cm.curOp.updateInput;
var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
origin: origin || (cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")};
makeChange(cm.doc, changeEvent);
signalLater(cm, "inputRead", cm, changeEvent);
}
if (inserted && !cm.state.pasteIncoming)
if (inserted && !paste)
triggerElectric(cm, inserted);

ensureCursorVisible(cm);
Expand All @@ -1115,6 +1116,15 @@
cm.state.pasteIncoming = cm.state.cutIncoming = false;
}

function handlePaste(e, cm) {
var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
if (pasted) {
e.preventDefault();
runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); });
return true;
}
}

function triggerElectric(cm, inserted) {
// When an 'electric' character is inserted, immediately trigger a reindent
if (!cm.options.electricChars || !cm.options.smartIndent) return;
Expand Down Expand Up @@ -1211,21 +1221,9 @@
input.poll();
});

on(te, "paste", function() {
// Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206
// Add a char to the end of textarea before paste occur so that
// selection doesn't span to the end of textarea.
if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) {
var start = te.selectionStart, end = te.selectionEnd;
te.value += "$";
// The selection end needs to be set before the start, otherwise there
// can be an intermediate non-empty selection between the two, which
// can override the middle-click paste buffer on linux and cause the
// wrong thing to get pasted.
te.selectionEnd = end;
te.selectionStart = start;
cm.state.fakedLastChar = true;
}
on(te, "paste", function(e) {
if (handlePaste(e, cm)) return true;

cm.state.pasteIncoming = true;
input.fastPoll();
});
Expand Down Expand Up @@ -1389,14 +1387,11 @@
// possible when it is clear that nothing happened. hasSelection
// will be the case when there is a lot of text in the textarea,
// in which case reading its value would be expensive.
if (!cm.state.focused || (hasSelection(input) && !prevInput) ||
if (this.contextMenuPending || !cm.state.focused ||
(hasSelection(input) && !prevInput) ||
isReadOnly(cm) || cm.options.disableInput || cm.state.keySeq)
return false;
// See paste handler for more on the fakedLastChar kludge
if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
input.value = input.value.substring(0, input.value.length - 1);
cm.state.fakedLastChar = false;
}

var text = input.value;
// If nothing changed, bail.
if (text == prevInput && !cm.somethingSelected()) return false;
Expand Down Expand Up @@ -1542,13 +1537,7 @@
div.contentEditable = "true";
disableBrowserMagic(div);

on(div, "paste", function(e) {
var pasted = e.clipboardData && e.clipboardData.getData("text/plain");
if (pasted) {
e.preventDefault();
cm.replaceSelection(pasted, null, "paste");
}
});
on(div, "paste", function(e) { handlePaste(e, cm); })

on(div, "compositionstart", function(e) {
var data = e.data;
Expand Down Expand Up @@ -1761,7 +1750,7 @@
var toIndex = findViewIndex(cm, to.line);
if (toIndex == display.view.length - 1) {
var toLine = display.viewTo - 1;
var toNode = display.view[toIndex].node;
var toNode = display.lineDiv.lastChild;
} else {
var toLine = lineNo(display.view[toIndex + 1].line) - 1;
var toNode = display.view[toIndex + 1].node.previousSibling;
Expand Down Expand Up @@ -3567,7 +3556,8 @@
var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained;
if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) &&
type == "single" && (contained = sel.contains(start)) > -1 &&
!sel.ranges[contained].empty())
(cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) &&
(cmp(contained.to(), start) > 0 || start.xRel < 0))
leftButtonStartDrag(cm, e, start, modifier);
else
leftButtonSelect(cm, e, start, type, modifier);
Expand Down Expand Up @@ -7587,7 +7577,7 @@
Doc.prototype.eachLine = Doc.prototype.iter;

// Set up methods on CodeMirror's prototype to redirect to the editor's document.
var dontDelegate = "iter insert remove copy getEditor".split(" ");
var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
CodeMirror.prototype[prop] = (function(method) {
return function() {return method.apply(this.doc, arguments);};
Expand Down Expand Up @@ -8739,7 +8729,7 @@

// THE END

CodeMirror.version = "5.3.0";
CodeMirror.version = "5.4.0";

return CodeMirror;
});
85 changes: 60 additions & 25 deletions mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
hooks = parserConfig.hooks || {},
multiLineStrings = parserConfig.multiLineStrings,
indentStatements = parserConfig.indentStatements !== false,
indentSwitch = parserConfig.indentSwitch !== false;
indentSwitch = parserConfig.indentSwitch !== false,
namespaceSeparator = parserConfig.namespaceSeparator;
var isOperatorChar = /[+\-*&%=<>!?|\/]/;

var curPunc, isDefKeyword;
Expand Down Expand Up @@ -62,6 +63,9 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
return "operator";
}
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
if (namespaceSeparator) while (stream.match(namespaceSeparator))
stream.eatWhile(/[\w\$_\xa1-\uffff]/);

var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
Expand Down Expand Up @@ -109,12 +113,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
this.align = align;
this.prev = prev;
}
function isStatement(context) {
return context.type == "statement" || context.type == "switchstatement";
function isStatement(type) {
return type == "statement" || type == "switchstatement" || type == "namespace";
}
function pushContext(state, col, type) {
var indent = state.indented;
if (state.context && isStatement(state.context))
if (state.context && isStatement(state.context.type) && !isStatement(type))
indent = state.context.indented;
return state.context = new Context(indent, col, type, null, state.context);
}
Expand All @@ -127,7 +131,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {

function typeBefore(stream, state) {
if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
if (/\S[>*\]]\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
}

function isTopScope(context) {
for (;;) {
if (!context || context.type == "top") return true;
if (context.type == "}" && context.prev.type != "namespace") return false;
context = context.prev;
}
}

// Interface
Expand Down Expand Up @@ -156,43 +168,53 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
if (style == "comment" || style == "meta") return style;
if (ctx.align == null) ctx.align = true;

if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && isStatement(ctx)) popContext(state);
if ((curPunc == ";" || curPunc == ":" || curPunc == ","))
while (isStatement(state.context.type)) popContext(state);
else if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}") {
while (isStatement(ctx)) ctx = popContext(state);
while (isStatement(ctx.type)) ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (isStatement(ctx)) ctx = popContext(state);
while (isStatement(ctx.type)) ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
else if (indentStatements &&
(((ctx.type == "}" || ctx.type == "top") && curPunc != ';') ||
(isStatement(ctx) && curPunc == "newstatement"))) {
var type = "statement"
(((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
(isStatement(ctx.type) && curPunc == "newstatement"))) {
var type = "statement";
if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch")
type = "switchstatement"
type = "switchstatement";
else if (style == "keyword" && stream.current() == "namespace")
type = "namespace";
pushContext(state, stream.column(), type);
}

if (style == "variable" &&
((state.prevToken == "def" ||
(parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
stream.match(/^\s*\(/, false)))))
isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
style = "def";

if (hooks.token) {
var result = hooks.token(stream, state, style);
if (result !== undefined) style = result;
}

if (style == "def" && parserConfig.styleDefs === false) style = "variable";

state.startOfLine = false;
state.prevToken = isDefKeyword ? "def" : style;
state.prevToken = isDefKeyword ? "def" : style || curPunc;
return style;
},

indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (isStatement(ctx) && firstChar == "}") ctx = ctx.prev;
if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
var closing = firstChar == ctx.type;
var switchBlock = ctx.prev && ctx.prev.type == "switchstatement";
if (isStatement(ctx))
if (isStatement(ctx.type))
return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
return ctx.column + (closing ? 0 : 1);
Expand All @@ -203,7 +225,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
(!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
},

electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{|\})$/ : /^\s*[{}]$/,
electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//",
Expand All @@ -218,7 +240,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
}
var cKeywords = "auto if break case register continue return default do sizeof " +
"static else struct switch extern typedef float union for " +
"goto while enum const volatile true false";
"goto while enum const volatile";
var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";

function cppHook(stream, state) {
Expand Down Expand Up @@ -268,6 +290,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
return false;
}

function cppLooksLikeConstructor(word) {
var lastTwo = /(\w+)::(\w+)$/.exec(word);
return lastTwo && lastTwo[1] == lastTwo[2];
}

// C#-style strings where "" escapes a quote.
function tokenAtString(stream, state) {
var next;
Expand Down Expand Up @@ -322,19 +349,19 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
blockKeywords: words("case do else for if switch while struct"),
defKeywords: words("struct"),
typeFirstDefinitions: true,
atoms: words("null"),
atoms: words("null true false"),
hooks: {"#": cppHook, "*": pointerHook},
modeProps: {fold: ["brace", "include"]}
});

def(["text/x-c++src", "text/x-c++hdr"], {
name: "clike",
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
"static_cast typeid catch operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected " +
"wchar_t alignas alignof constexpr decltype nullptr noexcept thread_local final " +
"alignas alignof constexpr decltype nullptr noexcept thread_local final " +
"static_assert override"),
types: words(cTypes + "bool wchar_t"),
types: words(cTypes + " bool wchar_t"),
blockKeywords: words("catch class do else finally for if struct switch try while"),
defKeywords: words("class namespace struct enum union"),
typeFirstDefinitions: true,
Expand All @@ -345,8 +372,16 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"u": cpp11StringHook,
"U": cpp11StringHook,
"L": cpp11StringHook,
"R": cpp11StringHook
"R": cpp11StringHook,
token: function(stream, state, style) {
if (style == "variable" && stream.peek() == "(" &&
(state.prevToken == ";" || state.prevToken == null ||
state.prevToken == "}") &&
cppLooksLikeConstructor(stream.current()))
return "def";
}
},
namespaceSeparator: "::",
modeProps: {fold: ["brace", "include"]}
});

Expand Down Expand Up @@ -532,7 +567,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
"signal task uses abstract extends"),
types: words(cTypes),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
atoms: words("null true false"),
hooks: {"#": cppHook},
modeProps: {fold: ["brace", "include"]}
});
Expand All @@ -542,7 +577,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
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"),
types: words(cTypes),
atoms: words("YES NO NULL NILL ON OFF"),
atoms: words("YES NO NULL NILL ON OFF true false"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$]/);
Expand Down
6 changes: 6 additions & 0 deletions mode/clike/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@
"[variable-3 void] [def foo]() {}",
"[keyword struct] [def bar]{}",
"[variable-3 int] [variable-3 *][def baz]() {}");

MT("double_block",
"[keyword for] (;;)",
" [keyword for] (;;)",
" [variable x][operator ++];",
"[keyword return];");
})();
205 changes: 205 additions & 0 deletions mode/elm/elm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// 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"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("elm", function() {

function switchState(source, setState, f) {
setState(f);
return f(source, setState);
}

// These should all be Unicode extended, as per the Haskell 2010 report
var smallRE = /[a-z_]/;
var largeRE = /[A-Z]/;
var digitRE = /[0-9]/;
var hexitRE = /[0-9A-Fa-f]/;
var octitRE = /[0-7]/;
var idRE = /[a-z_A-Z0-9\']/;
var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/;
var specialRE = /[(),;[\]`{}]/;
var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer

function normal() {
return function (source, setState) {
if (source.eatWhile(whiteCharRE)) {
return null;
}

var ch = source.next();
if (specialRE.test(ch)) {
if (ch == '{' && source.eat('-')) {
var t = "comment";
if (source.eat('#')) t = "meta";
return switchState(source, setState, ncomment(t, 1));
}
return null;
}

if (ch == '\'') {
if (source.eat('\\'))
source.next(); // should handle other escapes here
else
source.next();

if (source.eat('\''))
return "string";
return "error";
}

if (ch == '"') {
return switchState(source, setState, stringLiteral);
}

if (largeRE.test(ch)) {
source.eatWhile(idRE);
if (source.eat('.'))
return "qualifier";
return "variable-2";
}

if (smallRE.test(ch)) {
var isDef = source.pos === 1;
source.eatWhile(idRE);
return isDef ? "variable-3" : "variable";
}

if (digitRE.test(ch)) {
if (ch == '0') {
if (source.eat(/[xX]/)) {
source.eatWhile(hexitRE); // should require at least 1
return "integer";
}
if (source.eat(/[oO]/)) {
source.eatWhile(octitRE); // should require at least 1
return "number";
}
}
source.eatWhile(digitRE);
var t = "number";
if (source.eat('.')) {
t = "number";
source.eatWhile(digitRE); // should require at least 1
}
if (source.eat(/[eE]/)) {
t = "number";
source.eat(/[-+]/);
source.eatWhile(digitRE); // should require at least 1
}
return t;
}

if (symbolRE.test(ch)) {
if (ch == '-' && source.eat(/-/)) {
source.eatWhile(/-/);
if (!source.eat(symbolRE)) {
source.skipToEnd();
return "comment";
}
}
source.eatWhile(symbolRE);
return "builtin";
}

return "error";
}
}

function ncomment(type, nest) {
if (nest == 0) {
return normal();
}
return function(source, setState) {
var currNest = nest;
while (!source.eol()) {
var ch = source.next();
if (ch == '{' && source.eat('-')) {
++currNest;
} else if (ch == '-' && source.eat('}')) {
--currNest;
if (currNest == 0) {
setState(normal());
return type;
}
}
}
setState(ncomment(type, currNest));
return type;
}
}

function stringLiteral(source, setState) {
while (!source.eol()) {
var ch = source.next();
if (ch == '"') {
setState(normal());
return "string";
}
if (ch == '\\') {
if (source.eol() || source.eat(whiteCharRE)) {
setState(stringGap);
return "string";
}
if (!source.eat('&')) source.next(); // should handle other escapes here
}
}
setState(normal());
return "error";
}

function stringGap(source, setState) {
if (source.eat('\\')) {
return switchState(source, setState, stringLiteral);
}
source.next();
setState(normal());
return "error";
}


var wellKnownWords = (function() {
var wkw = {};

var keywords = [
"case", "of", "as",
"if", "then", "else",
"let", "in",
"infix", "infixl", "infixr",
"type", "alias",
"input", "output", "foreign", "loopback",
"module", "where", "import", "exposing",
"_", "..", "|", ":", "=", "\\", "\"", "->", "<-"
];

for (var i = keywords.length; i--;)
wkw[keywords[i]] = "keyword";

return wkw;
})();



return {
startState: function () { return { f: normal() }; },
copyState: function (s) { return { f: s.f }; },

token: function(stream, state) {
var t = state.f(stream, function(s) { state.f = s; });
var w = stream.current();
return (wellKnownWords.hasOwnProperty(w)) ? wellKnownWords[w] : t;
}
};

});

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

<title>CodeMirror: Elm 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="elm.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="#">Elm</a>
</ul>
</div>

<article>
<h2>Elm mode</h2>

<div><textarea id="code" name="code">
import Color exposing (..)
import Graphics.Collage exposing (..)
import Graphics.Element exposing (..)
import Time exposing (..)

main =
Signal.map clock (every second)

clock t =
collage 400 400
[ filled lightGrey (ngon 12 110)
, outlined (solid grey) (ngon 12 110)
, hand orange 100 t
, hand charcoal 100 (t/60)
, hand charcoal 60 (t/720)
]

hand clr len time =
let angle = degrees (90 - 6 * inSeconds time)
in
segment (0,0) (fromPolar (len,angle))
|> traced (solid clr)
</textarea></div>

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

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

// Factor syntax highlight - simple mode
//
// by Dimage Sapelkin (https://github.com/kerabromsmu)

(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";

CodeMirror.defineSimpleMode("factor", {
// The start state contains the rules that are intially used
start: [
// comments
{regex: /#?!.*/, token: "comment"},
// strings """, multiline --> state
{regex: /"""/, token: "string", next: "string3"},
{regex: /"/, token: "string", next: "string"},
// numbers: dec, hex, unicode, bin, fractional, complex
{regex: /(?:[+-]?)(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\d+.?\d*)/, token: "number"},
//{regex: /[+-]?/} //fractional
// definition: defining word, defined word, etc
{regex: /(\:)(\s+)(\S+)(\s+)(\()/, token: ["keyword", null, "def", null, "keyword"], next: "stack"},
// vocabulary using --> state
{regex: /USING\:/, token: "keyword", next: "vocabulary"},
// vocabulary definition/use
{regex: /(USE\:|IN\:)(\s+)(\S+)/, token: ["keyword", null, "variable-2"]},
// <constructors>
{regex: /<\S+>/, token: "builtin"},
// "keywords", incl. ; t f . [ ] { } defining words
{regex: /;|t|f|if|\.|\[|\]|\{|\}|MAIN:/, token: "keyword"},
// any id (?)
{regex: /\S+/, token: "variable"},

{
regex: /./,
token: null
}
],
vocabulary: [
{regex: /;/, token: "keyword", next: "start"},
{regex: /\S+/, token: "variable-2"},
{
regex: /./,
token: null
}
],
string: [
{regex: /(?:[^\\]|\\.)*?"/, token: "string", next: "start"},
{regex: /.*/, token: "string"}
],
string3: [
{regex: /(?:[^\\]|\\.)*?"""/, token: "string", next: "start"},
{regex: /.*/, token: "string"}
],
stack: [
{regex: /\)/, token: "meta", next: "start"},
{regex: /--/, token: "meta"},
{regex: /\S+/, token: "variable-3"},
{
regex: /./,
token: null
}
],
// The meta property contains global information about the mode. It
// can contain properties like lineComment, which are supported by
// all modes, and also directives like dontIndentStates, which are
// specific to simple modes.
meta: {
dontIndentStates: ["start", "vocabulary", "string", "string3", "stack"],
lineComment: [ "!", "#!" ]
}
});

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

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

<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/mode/simple.js"></script>
<script src="factor.js"></script>
<style>
.CodeMirror {
font-family: 'Droid Sans Mono', monospace;
font-size: 14px;
}
</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="#">Factor</a>
</ul>
</div>

<article>

<h2>Factor mode</h2>

<form><textarea id="code" name="code">
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.

! A simple time server

USING: accessors calendar calendar.format io io.encodings.ascii
io.servers kernel threads ;
IN: time-server

: handle-time-client ( -- )
now timestamp>rfc822 print ;

: <time-server> ( -- threaded-server )
ascii <threaded-server>
"time-server" >>name
1234 >>insecure
[ handle-time-client ] >>handler ;

: start-time-server ( -- )
<time-server> start-server drop ;

MAIN: start-time-server
</textarea>
</form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
lineWrapping: true,
indentUnit: 2,
tabSize: 2,
autofocus: true,
mode: "text/x-factor"
});
</script>
<p/>
<p>Simple mode that handles Factor Syntax (<a href="http://en.wikipedia.org/wiki/Factor_(programming_language)">Factor on WikiPedia</a>).</p>

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

</article>
13 changes: 8 additions & 5 deletions mode/groovy/groovy.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ CodeMirror.defineMode("groovy", function(config) {
"short static strictfp super switch synchronized threadsafe throw throws transient " +
"try void volatile while");
var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
var standaloneKeywords = words("return break continue");
var atoms = words("null true false this");

var curPunc;
Expand All @@ -50,7 +51,7 @@ CodeMirror.defineMode("groovy", function(config) {
stream.skipToEnd();
return "comment";
}
if (expectExpression(state.lastToken)) {
if (expectExpression(state.lastToken, false)) {
return startString(ch, stream, state);
}
}
Expand All @@ -70,6 +71,7 @@ CodeMirror.defineMode("groovy", function(config) {
if (atoms.propertyIsEnumerable(cur)) { return "atom"; }
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
else if (standaloneKeywords.propertyIsEnumerable(cur)) curPunc = "standalone";
return "keyword";
}
return "variable";
Expand Down Expand Up @@ -132,9 +134,10 @@ CodeMirror.defineMode("groovy", function(config) {
return "comment";
}

function expectExpression(last) {
function expectExpression(last, newline) {
return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) ||
last == "newstatement" || last == "keyword" || last == "proplabel";
last == "newstatement" || last == "keyword" || last == "proplabel" ||
(last == "standalone" && !newline);
}

function Context(indented, column, type, align, prev) {
Expand Down Expand Up @@ -174,7 +177,7 @@ CodeMirror.defineMode("groovy", function(config) {
state.indented = stream.indentation();
state.startOfLine = true;
// Automatic semicolon insertion
if (ctx.type == "statement" && !expectExpression(state.lastToken)) {
if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) {
popContext(state); ctx = state.context;
}
}
Expand Down Expand Up @@ -209,7 +212,7 @@ CodeMirror.defineMode("groovy", function(config) {
indent: function(state, textAfter) {
if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev;
if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;
var closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit);
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
Expand Down
4 changes: 4 additions & 0 deletions mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ <h2>Language modes</h2>
<li><a href="ebnf/index.html">EBNF</a></li>
<li><a href="ecl/index.html">ECL</a></li>
<li><a href="eiffel/index.html">Eiffel</a></li>
<li><a href="elm/index.html">Elm</a></li>
<li><a href="erlang/index.html">Erlang</a></li>
<li><a href="factor/index.html">Factor</a></li>
<li><a href="forth/index.html">Forth</a></li>
<li><a href="fortran/index.html">Fortran</a></li>
<li><a href="mllike/index.html">F#</a></li>
Expand Down Expand Up @@ -117,6 +119,7 @@ <h2>Language modes</h2>
<li><a href="stylus/index.html">Stylus</a></li>
<li><a href="sql/index.html">SQL</a> (several dialects)</li>
<li><a href="sparql/index.html">SPARQL</a></li>
<li><a href="swift/index.html">Swift</a></li>
<li><a href="stex/index.html">sTeX, LaTeX</a></li>
<li><a href="tcl/index.html">Tcl</a></li>
<li><a href="textile/index.html">Textile</a></li>
Expand All @@ -128,6 +131,7 @@ <h2>Language modes</h2>
<li><a href="ttcn/index.html">TTCN</a></li>
<li><a href="ttcn-cfg/index.html">TTCN Configuration</a></li>
<li><a href="turtle/index.html">Turtle</a></li>
<li><a href="twig/index.html">Twig</a></li>
<li><a href="vb/index.html">VB.NET</a></li>
<li><a href="vbscript/index.html">VBScript</a></li>
<li><a href="velocity/index.html">Velocity</a></li>
Expand Down
7 changes: 5 additions & 2 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -482,8 +482,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function maybetype(type) {
if (isTS && type == ":") return cont(typedef);
}
function maybedefault(_, value) {
if (value == "=") return cont(expressionNoComma);
}
function typedef(type) {
if (type == "variable"){cx.marked = "variable-3"; return cont();}
if (type == "variable") {cx.marked = "variable-3"; return cont();}
}
function vardef() {
return pass(pattern, maybetype, maybeAssign, vardefCont);
Expand Down Expand Up @@ -538,7 +541,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function funarg(type) {
if (type == "spread") return cont(funarg);
return pass(pattern, maybetype);
return pass(pattern, maybetype, maybedefault);
}
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
Expand Down
5 changes: 5 additions & 0 deletions mode/javascript/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@
"]];",
"[number 10];");

MT("param_default",
"[keyword function] [variable foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
" [keyword return] [variable-2 x];",
"}");

var jsonld_mode = CodeMirror.getMode(
{indentUnit: 2},
{name: "javascript", jsonld: true}
Expand Down
7 changes: 5 additions & 2 deletions mode/kotlin/kotlin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ CodeMirror.defineMode("kotlin", function (config, parserConfig) {
var multiLineStrings = parserConfig.multiLineStrings;

var keywords = words(
"package continue return object while break class data trait throw super" +
"package continue return object while break class data trait interface throw super" +
" when type this else This try val var fun for is in if do as true false null get set");
var softKeywords = words("import" +
" where by get set abstract enum open annotation override private public internal" +
Expand Down Expand Up @@ -272,7 +272,10 @@ CodeMirror.defineMode("kotlin", function (config, parserConfig) {
},

closeBrackets: {triples: "'\""},
electricChars: "{}"
electricChars: "{}",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//"
};
});

Expand Down
60 changes: 37 additions & 23 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
, strong = 'strong'
, strikethrough = 'strikethrough';

var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
, ulRE = /^[*\-+]\s+/
, olRE = /^[0-9]+\.\s+/
, olRE = /^[0-9]+([.)])\s+/
, taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
, atxHeaderRE = /^#+ ?/
, setextHeaderRE = /^(?:\={1,}|-{1,})$/
, atxHeaderRE = /^(#+)(?: |$)/
, setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
, textRE = /^[^#!\[\]*_\\<>` "'(~]+/;

function switchInline(stream, state, f) {
Expand All @@ -100,6 +100,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.strikethrough = false;
// Reset state.quote
state.quote = 0;
// Reset state.indentedCode
state.indentedCode = false;
if (!htmlFound && state.f == htmlBlock) {
state.f = inlineNormal;
state.block = blockNormal;
Expand All @@ -116,7 +118,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

var sol = stream.sol();

var prevLineIsList = state.list !== false;
var prevLineIsList = state.list !== false,
prevLineIsIndentedCode = state.indentedCode;

state.indentedCode = false;

if (prevLineIsList) {
if (state.indentationDiff >= 0) { // Continued list
if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
Expand All @@ -134,30 +140,35 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {

var match = null;
if (state.indentationDiff >= 4) {
state.indentation -= 4;
stream.skipToEnd();
return code;
if (prevLineIsIndentedCode || !state.prevLineHasContent) {
state.indentation -= 4;
state.indentedCode = true;
return code;
} else {
return null;
}
} else if (stream.eatSpace()) {
return null;
} else if (match = stream.match(atxHeaderRE)) {
state.header = Math.min(6, match[0].indexOf(" ") !== -1 ? match[0].length - 1 : match[0].length);
} else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
state.header = match[1].length;
if (modeCfg.highlightFormatting) state.formatting = "header";
state.f = state.inline;
return getType(state);
} else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) {
} else if (state.prevLineHasContent && !state.quote && !prevLineIsList && !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
state.header = match[0].charAt(0) == '=' ? 1 : 2;
if (modeCfg.highlightFormatting) state.formatting = "header";
state.f = state.inline;
return getType(state);
} else if (stream.eat('>')) {
state.indentation++;
state.quote = sol ? 1 : state.quote + 1;
if (modeCfg.highlightFormatting) state.formatting = "quote";
stream.eatSpace();
return getType(state);
} else if (stream.peek() === '[') {
return switchInline(stream, state, footnoteLink);
} else if (stream.match(hrRE, true)) {
state.hr = true;
return hr;
} else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
var listType = null;
Expand Down Expand Up @@ -262,17 +273,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}

if (state.linkHref) {
styles.push(linkhref);
return styles.length ? styles.join(' ') : null;
}
styles.push(linkhref, "url");
} else { // Only apply inline styles to non-url text
if (state.strong) { styles.push(strong); }
if (state.em) { styles.push(em); }
if (state.strikethrough) { styles.push(strikethrough); }

if (state.strong) { styles.push(strong); }
if (state.em) { styles.push(em); }
if (state.strikethrough) { styles.push(strikethrough); }
if (state.linkText) { styles.push(linktext); }

if (state.linkText) { styles.push(linktext); }

if (state.code) { styles.push(code); }
if (state.code) { styles.push(code); }
}

if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }

Expand Down Expand Up @@ -626,7 +636,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
}
state.f = state.inline = inlineNormal;
return linkhref;
return linkhref + " url";
}

var savedInlineRE = [];
Expand Down Expand Up @@ -663,6 +673,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
em: false,
strong: false,
header: 0,
hr: false,
taskList: false,
list: false,
listDepth: 0,
Expand Down Expand Up @@ -695,10 +706,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
strong: s.strong,
strikethrough: s.strikethrough,
header: s.header,
hr: s.hr,
taskList: s.taskList,
list: s.list,
listDepth: s.listDepth,
quote: s.quote,
indentedCode: s.indentedCode,
trailingSpace: s.trailingSpace,
trailingSpaceNewLine: s.trailingSpaceNewLine,
md_inside: s.md_inside
Expand All @@ -711,10 +724,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.formatting = false;

if (stream.sol()) {
var forceBlankLine = !!state.header;
var forceBlankLine = !!state.header || state.hr;

// Reset state.header
// Reset state.header and state.hr
state.header = 0;
state.hr = false;

if (stream.match(/^\s*$/, true) || forceBlankLine) {
state.prevLineHasContent = false;
Expand Down
122 changes: 80 additions & 42 deletions mode/markdown/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@
"[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]");

FT("formatting_link",
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string (][string http://example.com/][string&formatting&formatting-link-string )]");
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url (][string&url http://example.com/][string&formatting&formatting-link-string&url )]");

FT("formatting_linkReference",
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string [][string bar][string&formatting&formatting-link-string ]]]",
"[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string http://example.com/]");
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string&url [][string&url bar][string&formatting&formatting-link-string&url ]]]",
"[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string&url http://example.com/]");

FT("formatting_linkWeb",
"[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]");
Expand Down Expand Up @@ -85,13 +85,6 @@
" [comment foo]",
"bar");

// Code blocks using 4 spaces with internal indentation
MT("codeBlocksUsing4SpacesIndentation",
" foo",
" [comment bar]",
" [comment hello]",
" [comment world]");

// Code blocks should end even after extra indented lines
MT("codeBlocksWithTrailingIndentedLine",
" [comment foo]",
Expand All @@ -104,6 +97,12 @@
MT("codeBlocksUsing1Tab",
"\t[comment foo]");

// No code blocks directly after paragraph
// http://spec.commonmark.org/0.19/#example-65
MT("noCodeBlocksAfterParagraph",
"Foo",
" Bar");

// Inline code using backticks
MT("inlineCodeUsingBackticks",
"foo [comment `bar`]");
Expand Down Expand Up @@ -166,10 +165,13 @@
MT("atxH6",
"[header&header-6 ###### foo]");

// H6 - 7x '#' should still be H6, per Dingus
// http://daringfireball.net/projects/markdown/dingus
MT("atxH6NotH7",
"[header&header-6 ####### foo]");
// http://spec.commonmark.org/0.19/#example-24
MT("noAtxH7",
"####### foo");

// http://spec.commonmark.org/0.19/#example-25
MT("noAtxH1WithoutSpace",
"#5 bolt");

// Inline styles should be parsed inside headers
MT("atxH1inline",
Expand Down Expand Up @@ -202,6 +204,25 @@
"foo",
"[header&header-2 ---]");

// http://spec.commonmark.org/0.19/#example-45
MT("setextH2AllowSpaces",
"foo",
" [header&header-2 ---- ]");

// http://spec.commonmark.org/0.19/#example-44
MT("noSetextAfterIndentedCodeBlock",
" [comment foo]",
"[hr ---]");

// http://spec.commonmark.org/0.19/#example-51
MT("noSetextAfterQuote",
"[quote&quote-1 > foo]",
"[hr ---]");

MT("noSetextAfterList",
"[variable-2 - foo]",
"[hr ---]");

// Single-line blockquote with trailing space
MT("blockquoteSpace",
"[quote&quote-1 > foo]");
Expand Down Expand Up @@ -251,6 +272,13 @@
"",
"hello");

// Header with leading space after continued blockquote (#3287, negative indentation)
MT("headerAfterContinuedBlockquote",
"[quote&quote-1 > foo]",
"[quote&quote-1 bar]",
"",
" [header&header-1 # hello]");

// Check list types

MT("listAsterisk",
Expand Down Expand Up @@ -287,11 +315,21 @@
"1. bar",
"2. hello");

// List after hr
MT("listAfterHr",
"[hr ---]",
"[variable-2 - bar]");

// List after header
MT("listAfterHeader",
"[header&header-1 # foo]",
"[variable-2 - bar]");

// hr after list
MT("hrAfterList",
"[variable-2 - foo]",
"[hr -----]");

// Formatting in lists (*)
MT("listAsteriskFormatting",
"[variable-2 * ][variable-2&em *foo*][variable-2 bar]",
Expand Down Expand Up @@ -498,39 +536,39 @@

// Inline link with title
MT("linkTitle",
"[link [[foo]]][string (http://example.com/ \"bar\")] hello");
"[link [[foo]]][string&url (http://example.com/ \"bar\")] hello");

// Inline link without title
MT("linkNoTitle",
"[link [[foo]]][string (http://example.com/)] bar");
"[link [[foo]]][string&url (http://example.com/)] bar");

// Inline link with image
MT("linkImage",
"[link [[][tag ![[foo]]][string (http://example.com/)][link ]]][string (http://example.com/)] bar");
"[link [[][tag ![[foo]]][string&url (http://example.com/)][link ]]][string&url (http://example.com/)] bar");

// Inline link with Em
MT("linkEm",
"[link [[][link&em *foo*][link ]]][string (http://example.com/)] bar");
"[link [[][link&em *foo*][link ]]][string&url (http://example.com/)] bar");

// Inline link with Strong
MT("linkStrong",
"[link [[][link&strong **foo**][link ]]][string (http://example.com/)] bar");
"[link [[][link&strong **foo**][link ]]][string&url (http://example.com/)] bar");

// Inline link with EmStrong
MT("linkEmStrong",
"[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string (http://example.com/)] bar");
"[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string&url (http://example.com/)] bar");

// Image with title
MT("imageTitle",
"[tag ![[foo]]][string (http://example.com/ \"bar\")] hello");
"[tag ![[foo]]][string&url (http://example.com/ \"bar\")] hello");

// Image without title
MT("imageNoTitle",
"[tag ![[foo]]][string (http://example.com/)] bar");
"[tag ![[foo]]][string&url (http://example.com/)] bar");

// Image with asterisks
MT("imageAsterisks",
"[tag ![[*foo*]]][string (http://example.com/)] bar");
"[tag ![[*foo*]]][string&url (http://example.com/)] bar");

// Not a link. Should be normal text due to square brackets being used
// regularly in text, especially in quoted material, and no space is allowed
Expand All @@ -540,79 +578,79 @@

// Reference-style links
MT("linkReference",
"[link [[foo]]][string [[bar]]] hello");
"[link [[foo]]][string&url [[bar]]] hello");

// Reference-style links with Em
MT("linkReferenceEm",
"[link [[][link&em *foo*][link ]]][string [[bar]]] hello");
"[link [[][link&em *foo*][link ]]][string&url [[bar]]] hello");

// Reference-style links with Strong
MT("linkReferenceStrong",
"[link [[][link&strong **foo**][link ]]][string [[bar]]] hello");
"[link [[][link&strong **foo**][link ]]][string&url [[bar]]] hello");

// Reference-style links with EmStrong
MT("linkReferenceEmStrong",
"[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string [[bar]]] hello");
"[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string&url [[bar]]] hello");

// Reference-style links with optional space separator (per docuentation)
// "You can optionally use a space to separate the sets of brackets"
MT("linkReferenceSpace",
"[link [[foo]]] [string [[bar]]] hello");
"[link [[foo]]] [string&url [[bar]]] hello");

// Should only allow a single space ("...use *a* space...")
MT("linkReferenceDoubleSpace",
"[[foo]] [[bar]] hello");

// Reference-style links with implicit link name
MT("linkImplicit",
"[link [[foo]]][string [[]]] hello");
"[link [[foo]]][string&url [[]]] hello");

// @todo It would be nice if, at some point, the document was actually
// checked to see if the referenced link exists

// Link label, for reference-style links (taken from documentation)

MT("labelNoTitle",
"[link [[foo]]:] [string http://example.com/]");
"[link [[foo]]:] [string&url http://example.com/]");

MT("labelIndented",
" [link [[foo]]:] [string http://example.com/]");
" [link [[foo]]:] [string&url http://example.com/]");

MT("labelSpaceTitle",
"[link [[foo bar]]:] [string http://example.com/ \"hello\"]");
"[link [[foo bar]]:] [string&url http://example.com/ \"hello\"]");

MT("labelDoubleTitle",
"[link [[foo bar]]:] [string http://example.com/ \"hello\"] \"world\"");
"[link [[foo bar]]:] [string&url http://example.com/ \"hello\"] \"world\"");

MT("labelTitleDoubleQuotes",
"[link [[foo]]:] [string http://example.com/ \"bar\"]");
"[link [[foo]]:] [string&url http://example.com/ \"bar\"]");

MT("labelTitleSingleQuotes",
"[link [[foo]]:] [string http://example.com/ 'bar']");
"[link [[foo]]:] [string&url http://example.com/ 'bar']");

MT("labelTitleParenthese",
"[link [[foo]]:] [string http://example.com/ (bar)]");
"[link [[foo]]:] [string&url http://example.com/ (bar)]");

MT("labelTitleInvalid",
"[link [[foo]]:] [string http://example.com/] bar");
"[link [[foo]]:] [string&url http://example.com/] bar");

MT("labelLinkAngleBrackets",
"[link [[foo]]:] [string <http://example.com/> \"bar\"]");
"[link [[foo]]:] [string&url <http://example.com/> \"bar\"]");

MT("labelTitleNextDoubleQuotes",
"[link [[foo]]:] [string http://example.com/]",
"[link [[foo]]:] [string&url http://example.com/]",
"[string \"bar\"] hello");

MT("labelTitleNextSingleQuotes",
"[link [[foo]]:] [string http://example.com/]",
"[link [[foo]]:] [string&url http://example.com/]",
"[string 'bar'] hello");

MT("labelTitleNextParenthese",
"[link [[foo]]:] [string http://example.com/]",
"[link [[foo]]:] [string&url http://example.com/]",
"[string (bar)] hello");

MT("labelTitleNextMixed",
"[link [[foo]]:] [string http://example.com/]",
"[link [[foo]]:] [string&url http://example.com/]",
"(bar\" hello");

MT("linkWeb",
Expand Down
7 changes: 7 additions & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
CodeMirror.modeInfo = [
{name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
{name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
{name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn, asn1"]},
{name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
{name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
{name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
Expand All @@ -37,9 +38,11 @@
{name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"},
{name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]},
{name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]},
{name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]},
{name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
{name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
{name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
{name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
{name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
{name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
{name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
Expand Down Expand Up @@ -113,6 +116,7 @@
{name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
{name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
{name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
{name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
{name: "MariaDB", mime: "text/x-mariadb", mode: "sql"},
{name: "sTeX", mime: "text/x-stex", mode: "stex"},
{name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
Expand All @@ -124,8 +128,11 @@
{name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]},
{name: "Tornado", mime: "text/x-tornado", mode: "tornado"},
{name: "troff", mime: "troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]},
{name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]},
{name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
{name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
{name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
{name: "Twig", mime: "text/x-twig", mode: "twig"},
{name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
{name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]},
{name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
Expand Down
37 changes: 20 additions & 17 deletions mode/php/php.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,31 @@
return obj;
}

// Helper for stringWithEscapes
function matchSequence(list, end) {
if (list.length == 0) return stringWithEscapes(end);
// Helper for phpString
function matchSequence(list, end, escapes) {
if (list.length == 0) return phpString(end);
return function (stream, state) {
var patterns = list[0];
for (var i = 0; i < patterns.length; i++) if (stream.match(patterns[i][0])) {
state.tokenize = matchSequence(list.slice(1), end);
return patterns[i][1];
}
state.tokenize = stringWithEscapes(end);
state.tokenize = phpString(end, escapes);
return "string";
};
}
function stringWithEscapes(closing) {
return function(stream, state) { return stringWithEscapes_(stream, state, closing); };
function phpString(closing, escapes) {
return function(stream, state) { return phpString_(stream, state, closing, escapes); };
}
function stringWithEscapes_(stream, state, closing) {
function phpString_(stream, state, closing, escapes) {
// "Complex" syntax
if (stream.match("${", false) || stream.match("{$", false)) {
if (escapes !== false && stream.match("${", false) || stream.match("{$", false)) {
state.tokenize = null;
return "string";
}

// Simple syntax
if (stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
if (escapes !== false && stream.match(/^\$[a-zA-Z_][a-zA-Z0-9_]*/)) {
// After the variable name there may appear array or object operator.
if (stream.match("[", false)) {
// Match array operator
Expand All @@ -51,23 +51,24 @@
[/\$[a-zA-Z_][a-zA-Z0-9_]*/, "variable-2"],
[/[\w\$]+/, "variable"]],
[["]", null]]
], closing);
], closing, escapes);
}
if (stream.match(/\-\>\w/, false)) {
// Match object operator
state.tokenize = matchSequence([
[["->", null]],
[[/[\w]+/, "variable"]]
], closing);
], closing, escapes);
}
return "variable-2";
}

var escaped = false;
// Normal string
while (!stream.eol() &&
(escaped || (!stream.match("{$", false) &&
!stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
(escaped || escapes === false ||
(!stream.match("{$", false) &&
!stream.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*|\$\{)/, false)))) {
if (!escaped && stream.match(closing)) {
state.tokenize = null;
state.tokStack.pop(); state.tokStack.pop();
Expand Down Expand Up @@ -105,11 +106,13 @@
},
"<": function(stream, state) {
if (stream.match(/<</)) {
var nowDoc = stream.eat("'");
stream.eatWhile(/[\w\.]/);
var delim = stream.current().slice(3);
var delim = stream.current().slice(3 + (nowDoc ? 1 : 0));
if (nowDoc) stream.eat("'");
if (delim) {
(state.tokStack || (state.tokStack = [])).push(delim, 0);
state.tokenize = stringWithEscapes(delim);
state.tokenize = phpString(delim, nowDoc ? false : true);
return "string";
}
}
Expand All @@ -128,7 +131,7 @@
},
'"': function(_stream, state) {
(state.tokStack || (state.tokStack = [])).push('"', 0);
state.tokenize = stringWithEscapes('"');
state.tokenize = phpString('"');
return "string";
},
"{": function(_stream, state) {
Expand All @@ -139,7 +142,7 @@
"}": function(_stream, state) {
if (state.tokStack && state.tokStack.length > 0 &&
!--state.tokStack[state.tokStack.length - 1]) {
state.tokenize = stringWithEscapes(state.tokStack[state.tokStack.length - 2]);
state.tokenize = phpString(state.tokStack[state.tokStack.length - 2]);
}
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion mode/python/python.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"unichr", "unicode", "xrange", "False", "True", "None"],
keywords: ["exec", "print"]};
var py3 = {builtins: ["ascii", "bytes", "exec", "print"],
keywords: ["nonlocal", "False", "True", "None"]};
keywords: ["nonlocal", "False", "True", "None", "async", "await"]};

CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins));

Expand Down
88 changes: 88 additions & 0 deletions mode/swift/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!doctype html>

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

<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="./swift.js"></script>
<style>
.CodeMirror { border: 2px inset #dee; }
</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="#">Swift</a>
</ul>
</div>

<article>
<h2>Swift mode</h2>
<form><textarea id="code" name="code">
//
// TipCalculatorModel.swift
// TipCalculator
//
// Created by Main Account on 12/18/14.
// Copyright (c) 2014 Razeware LLC. All rights reserved.
//

import Foundation

class TipCalculatorModel {

var total: Double
var taxPct: Double
var subtotal: Double {
get {
return total / (taxPct + 1)
}
}

init(total: Double, taxPct: Double) {
self.total = total
self.taxPct = taxPct
}

func calcTipWithTipPct(tipPct: Double) -> Double {
return subtotal * tipPct
}

func returnPossibleTips() -> [Int: Double] {

let possibleTipsInferred = [0.15, 0.18, 0.20]
let possibleTipsExplicit:[Double] = [0.15, 0.18, 0.20]

var retval = [Int: Double]()
for possibleTip in possibleTipsInferred {
let intPct = Int(possibleTip*100)
retval[intPct] = calcTipWithTipPct(possibleTip)
}
return retval

}

}
</textarea></form>

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

<p>A simple mode for Swift</p>

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

// Swift mode created by Michael Kaminsky https://github.com/mkaminsky11

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

function trim(str) { return /^\s*(.*?)\s*$/.exec(str)[1] }

var separators = [" ","\\\+","\\\-","\\\(","\\\)","\\\*","/",":","\\\?","\\\<","\\\>"," ","\\\."]
var tokens = new RegExp(separators.join("|"),"g")

function getWord(string, pos) {
var index = -1, count = 1
var words = string.split(tokens)
for (var i = 0; i < words.length; i++) {
for(var j = 1; j <= words[i].length; j++) {
if (count==pos) index = i
count++
}
count++
}
var ret = ["", ""]
if (pos == 0) {
ret[1] = words[0]
ret[0] = null
} else {
ret[1] = words[index]
ret[0] = words[index-1]
}
return ret
}

CodeMirror.defineMode("swift", function() {
var keywords=["var","let","class","deinit","enum","extension","func","import","init","let","protocol","static","struct","subscript","typealias","var","as","dynamicType","is","new","super","self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case","continue","default","do","else","fallthrough","if","in","for","return","switch","where","while","associativity","didSet","get","infix","inout","left","mutating","none","nonmutating","operator","override","postfix","precedence","prefix","right","set","unowned","unowned(safe)","unowned(unsafe)","weak","willSet"]
var commonConstants=["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null","this","super"]
var types=["String","bool","int","string","double","Double","Int","Float","float","public","private","extension"]
var numbers=["0","1","2","3","4","5","6","7","8","9"]
var operators=["+","-","/","*","%","=","|","&","<",">"]
var punc=[";",",",".","(",")","{","}","[","]"]
var delimiters=/^(?:[()\[\]{},:`=;]|\.\.?\.?)/
var identifiers=/^[_A-Za-z$][_A-Za-z$0-9]*/
var properties=/^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/
var regexPrefixes=/^(\/{3}|\/)/

return {
startState: function() {
return {
prev: false,
string: false,
escape: false,
inner: false,
comment: false,
num_left: 0,
num_right: 0,
doubleString: false,
singleString: false
}
},
token: function(stream, state) {
if (stream.eatSpace()) return null

var ch = stream.next()
if (state.string) {
if (state.escape) {
state.escape = false
return "string"
} else {
if ((ch == "\"" && (state.doubleString && !state.singleString) ||
(ch == "'" && (!state.doubleString && state.singleString))) &&
!state.escape) {
state.string = false
state.doubleString = false
state.singleString = false
return "string"
} else if (ch == "\\" && stream.peek() == "(") {
state.inner = true
state.string = false
return "keyword"
} else if (ch == "\\" && stream.peek() != "(") {
state.escape = true
state.string = true
return "string"
} else {
return "string"
}
}
} else if (state.comment) {
if (ch == "*" && stream.peek() == "/") {
state.prev = "*"
return "comment"
} else if (ch == "/" && state.prev == "*") {
state.prev = false
state.comment = false
return "comment"
}
return "comment"
} else {
if (ch == "/") {
if (stream.peek() == "/") {
stream.skipToEnd()
return "comment"
}
if (stream.peek() == "*") {
state.comment = true
return "comment"
}
}
if (ch == "(" && state.inner) {
state.num_left++
return null
}
if (ch == ")" && state.inner) {
state.num_right++
if (state.num_left == state.num_right) {
state.inner=false
state.string=true
}
return null
}

var ret = getWord(stream.string, stream.pos)
var the_word = ret[1]
var prev_word = ret[0]

if (operators.indexOf(ch + "") > -1) return "operator"
if (punc.indexOf(ch) > -1) return "punctuation"

if (typeof the_word != "undefined") {
the_word = trim(the_word)
if (typeof prev_word != "undefined") prev_word = trim(prev_word)
if (the_word.charAt(0) == "#") return null

if (types.indexOf(the_word) > -1) return "def"
if (commonConstants.indexOf(the_word) > -1) return "atom"
if (numbers.indexOf(the_word) > -1) return "number"

if ((numbers.indexOf(the_word.charAt(0) + "") > -1 ||
operators.indexOf(the_word.charAt(0) + "") > -1) &&
numbers.indexOf(ch) > -1) {
return "number"
}

if (keywords.indexOf(the_word) > -1 ||
keywords.indexOf(the_word.split(tokens)[0]) > -1)
return "keyword"
if (keywords.indexOf(prev_word) > -1) return "def"
}
if (ch == '"' && !state.doubleString) {
state.string = true
state.doubleString = true
return "string"
}
if (ch == "'" && !state.singleString) {
state.string = true
state.singleString = true
return "string"
}
if (ch == "(" && state.inner)
state.num_left++
if (ch == ")" && state.inner) {
state.num_right++
if (state.num_left == state.num_right) {
state.inner = false
state.string = true
}
return null
}
if (stream.match(/^-?[0-9\.]/, false)) {
if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i) ||
stream.match(/^-?\d+\.\d*/) ||
stream.match(/^-?\.\d+/)) {
if (stream.peek() == ".") stream.backUp(1)
return "number"
}
if (stream.match(/^-?0x[0-9a-f]+/i) ||
stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/) ||
stream.match(/^-?0(?![\dx])/i))
return "number"
}
if (stream.match(regexPrefixes)) {
if (stream.current()!="/" || stream.match(/^.*\//,false)) return "string"
else stream.backUp(1)
}
if (stream.match(delimiters)) return "punctuation"
if (stream.match(identifiers)) return "variable"
if (stream.match(properties)) return "property"
return "variable"
}
}
}
})

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

<title>CodeMirror: Twig 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="twig.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="#">Twig</a>
</ul>
</div>

<article>
<h2>Twig mode</h2>
<form><textarea id="code" name="code">
{% extends "layout.twig" %}
{% block title %}CodeMirror: Twig mode{% endblock %}
{# this is a comment #}
{% block content %}
{% for foo in bar if foo.baz is divisible by(3) %}
Hello {{ foo.world }}
{% else %}
{% set msg = "Result not found" %}
{% include "empty.twig" with { message: msg } %}
{% endfor %}
{% endblock %}
</textarea></form>
<script>
var editor =
CodeMirror.fromTextArea(document.getElementById("code"), {mode:
{name: "twig", htmlMode: true}});
</script>
</article>
132 changes: 132 additions & 0 deletions mode/twig/twig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

CodeMirror.defineMode("twig", function() {
var keywords = ["and", "as", "autoescape", "endautoescape", "block", "do", "endblock", "else", "elseif", "extends", "for", "endfor", "embed", "endembed", "filter", "endfilter", "flush", "from", "if", "endif", "in", "is", "include", "import", "not", "or", "set", "spaceless", "endspaceless", "with", "endwith", "trans", "endtrans", "blocktrans", "endblocktrans", "macro", "endmacro", "use", "verbatim", "endverbatim"],
operator = /^[+\-*&%=<>!?|~^]/,
sign = /^[:\[\(\{]/,
atom = ["true", "false", "null", "empty", "defined", "divisibleby", "divisible by", "even", "odd", "iterable", "sameas", "same as"],
number = /^(\d[+\-\*\/])?\d+(\.\d+)?/;

keywords = new RegExp("((" + keywords.join(")|(") + "))\\b");
atom = new RegExp("((" + atom.join(")|(") + "))\\b");

function tokenBase (stream, state) {
var ch = stream.peek();

//Comment
if (state.incomment) {
if (!stream.skipTo("#}")) {
stream.skipToEnd();
} else {
stream.eatWhile(/\#|}/);
state.incomment = false;
}
return "comment";
//Tag
} else if (state.intag) {
//After operator
if (state.operator) {
state.operator = false;
if (stream.match(atom)) {
return "atom";
}
if (stream.match(number)) {
return "number";
}
}
//After sign
if (state.sign) {
state.sign = false;
if (stream.match(atom)) {
return "atom";
}
if (stream.match(number)) {
return "number";
}
}

if (state.instring) {
if (ch == state.instring) {
state.instring = false;
}
stream.next();
return "string";
} else if (ch == "'" || ch == '"') {
state.instring = ch;
stream.next();
return "string";
} else if (stream.match(state.intag + "}") || stream.eat("-") && stream.match(state.intag + "}")) {
state.intag = false;
return "tag";
} else if (stream.match(operator)) {
state.operator = true;
return "operator";
} else if (stream.match(sign)) {
state.sign = true;
} else {
if (stream.eat(" ") || stream.sol()) {
if (stream.match(keywords)) {
return "keyword";
}
if (stream.match(atom)) {
return "atom";
}
if (stream.match(number)) {
return "number";
}
if (stream.sol()) {
stream.next();
}
} else {
stream.next();
}

}
return "variable";
} else if (stream.eat("{")) {
if (ch = stream.eat("#")) {
state.incomment = true;
if (!stream.skipTo("#}")) {
stream.skipToEnd();
} else {
stream.eatWhile(/\#|}/);
state.incomment = false;
}
return "comment";
//Open tag
} else if (ch = stream.eat(/\{|%/)) {
//Cache close tag
state.intag = ch;
if (ch == "{") {
state.intag = "}";
}
stream.eat("-");
return "tag";
}
}
stream.next();
};

return {
startState: function () {
return {};
},
token: function (stream, state) {
return tokenBase(stream, state);
}
};
});

CodeMirror.defineMIME("text/x-twig", "twig");
});
3 changes: 2 additions & 1 deletion mode/vb/vb.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,9 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1);
if(state.currentIndent < 0) return 0;
return state.currentIndent * conf.indentUnit;
}
},

lineComment: "'"
};
return external;
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"5.3.0",
"version":"5.4.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"license": "MIT",
Expand Down
10 changes: 10 additions & 0 deletions test/sql-hint-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,16 @@
to: Pos(0, 9)
});

test("alias_complete_with_displayText", {
value: "SELECT t. FROM mytable t",
cursor: Pos(0, 9),
tables: displayTextTables,
list: [{text: "t.id", displayText: "id | Unique ID"},
{text: "t.name", displayText: "name | The name"}],
from: Pos(0, 7),
to: Pos(0, 9)
})

function deepCompare(a, b) {
if (!a || typeof a != "object")
return a === b;
Expand Down
5 changes: 3 additions & 2 deletions theme/ambiance.css
2 changes: 1 addition & 1 deletion theme/erlang-dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; }
.cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; }

.cm-s-erlang-dark span.cm-quote { color: #ccc; }
.cm-s-erlang-dark span.cm-atom { color: #f133f1; }
.cm-s-erlang-dark span.cm-attribute { color: #ff80e1; }
.cm-s-erlang-dark span.cm-bracket { color: #ff9d00; }
Expand All @@ -20,7 +21,6 @@
.cm-s-erlang-dark span.cm-operator { color: #d55; }
.cm-s-erlang-dark span.cm-property { color: #ccc; }
.cm-s-erlang-dark span.cm-qualifier { color: #ccc; }
.cm-s-erlang-dark span.cm-quote { color: #ccc; }
.cm-s-erlang-dark span.cm-special { color: #ffbbbb; }
.cm-s-erlang-dark span.cm-string { color: #3ad900; }
.cm-s-erlang-dark span.cm-string-2 { color: #ccc; }
Expand Down
4 changes: 2 additions & 2 deletions theme/lesser-dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Ported to CodeMirror by Peter Kroon
.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; }
.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; }

.cm-s-lesser-dark span.cm-header {color: #a0a;}
.cm-s-lesser-dark span.cm-quote {color: #090;}
.cm-s-lesser-dark span.cm-keyword { color: #599eff; }
.cm-s-lesser-dark span.cm-atom { color: #C2B470; }
.cm-s-lesser-dark span.cm-number { color: #B35E4D; }
Expand All @@ -37,8 +39,6 @@ Ported to CodeMirror by Peter Kroon
.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; }
.cm-s-lesser-dark span.cm-tag { color: #669199; }
.cm-s-lesser-dark span.cm-attribute {color: #00c;}
.cm-s-lesser-dark span.cm-header {color: #a0a;}
.cm-s-lesser-dark span.cm-quote {color: #090;}
.cm-s-lesser-dark span.cm-hr {color: #999;}
.cm-s-lesser-dark span.cm-link {color: #00c;}
.cm-s-lesser-dark span.cm-error { color: #9d1e15; }
Expand Down
1 change: 1 addition & 0 deletions theme/monokai.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
.cm-s-monokai span.cm-def {color: #fd971f;}
.cm-s-monokai span.cm-bracket {color: #f8f8f2;}
.cm-s-monokai span.cm-tag {color: #f92672;}
.cm-s-monokai span.cm-header {color: #ae81ff;}
.cm-s-monokai span.cm-link {color: #ae81ff;}
.cm-s-monokai span.cm-error {background: #f92672; color: #f8f8f0;}

Expand Down
4 changes: 2 additions & 2 deletions theme/solarized.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
text-shadow: none;
}

.cm-s-solarized .cm-header { color: #586e75; }
.cm-s-solarized .cm-quote { color: #93a1a1; }

.cm-s-solarized .cm-keyword { color: #cb4b16 }
.cm-s-solarized .cm-atom { color: #d33682; }
Expand All @@ -73,8 +75,6 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png
.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; }
.cm-s-solarized .cm-tag { color: #93a1a1 }
.cm-s-solarized .cm-attribute { color: #2aa198; }
.cm-s-solarized .cm-header { color: #586e75; }
.cm-s-solarized .cm-quote { color: #93a1a1; }
.cm-s-solarized .cm-hr {
color: transparent;
border-top: 1px solid #586e75;
Expand Down
19 changes: 9 additions & 10 deletions theme/ttcn.css
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
/* DEFAULT THEME */
.cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-header {color: #00f; font-weight: bold;}

.cm-atom {color: #219;}
.cm-attribute {color: #00c;}
.cm-bracket {color: #997;}
.cm-comment {color: #333333;}
.cm-def {color: #00f;}
.cm-em {font-style: italic;}
.cm-error {color: #f00;}
.cm-header {color: #00f; font-weight: bold;}
.cm-hr {color: #999;}
.cm-invalidchar {color: #f00;}
.cm-keyword {font-weight:bold}
Expand All @@ -15,7 +22,6 @@
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-qualifier {color: #555;}
.cm-quote {color: #090;}
.cm-strikethrough {text-decoration: line-through;}
.cm-string {color: #006400;}
.cm-string-2 {color: #f50;}
Expand All @@ -25,13 +31,6 @@
.cm-variable-2 {color: #05a;}
.cm-variable-3 {color: #085;}

.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}

.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}

Expand Down