diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 2c265420457a966bda236e97e784b3cd0aa126e7..586b7370dc46b50cab92abbb07e85a91a1992c75 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -46,6 +46,7 @@ CodeMirror.on(inp, "keydown", function(e) { if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { + inp.blur(); CodeMirror.e_stop(e); close(); me.focus(); diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index 1570664c4fda9b7c7f79c71801ba36b6cc7900d6..3ef979b5240770f931a1d571126697ba735ce98a 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -22,8 +22,8 @@ var list = [], seen = {}; var re = new RegExp(word.source, "g"); for (var dir = -1; dir <= 1; dir += 2) { - var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; - for (; line != end; line += dir) { + var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; + for (; line != endLine; line += dir) { var text = editor.getLine(line), m; while (m = re.exec(text)) { if (line == cur.line && m[0] === curWord) continue; diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 6a4d74e3603a0eee19efab0b7bf82b7f925d32ff..084b34750526c528cd65c02fa6498b5f9330345f 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -200,8 +200,24 @@ var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); (options.container || document.body).appendChild(hints); - var box = hints.getBoundingClientRect(); - var overlapX = box.right - winW, overlapY = box.bottom - winH; + var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; + if (overlapY > 0) { + var height = box.bottom - box.top, curTop = box.top - (pos.bottom - pos.top); + if (curTop - height > 0) { // Fits above cursor + hints.style.top = (top = curTop - height) + "px"; + below = false; + } else if (height > winH) { + hints.style.height = (winH - 5) + "px"; + hints.style.top = (top = pos.bottom - box.top) + "px"; + var cursor = cm.getCursor(); + if (data.from.ch != cursor.ch) { + pos = cm.cursorCoords(cursor); + hints.style.left = (left = pos.left) + "px"; + box = hints.getBoundingClientRect(); + } + } + } + var overlapX = box.left - winW; if (overlapX > 0) { if (box.right - box.left > winW) { hints.style.width = (winW - 5) + "px"; @@ -209,17 +225,6 @@ } hints.style.left = (left = pos.left - overlapX) + "px"; } - if (overlapY > 0) { - var height = box.bottom - box.top; - if (box.top - (pos.bottom - pos.top) - height > 0) { - overlapY = height + (pos.bottom - pos.top); - below = false; - } else if (height > winH) { - hints.style.height = (winH - 5) + "px"; - overlapY -= height - winH; - } - hints.style.top = (top = pos.bottom - overlapY) + "px"; - } cm.addKeyMap(this.keyMap = buildKeyMap(options, { moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, @@ -227,7 +232,8 @@ menuSize: function() { return widget.screenAmount(); }, length: completions.length, close: function() { completion.close(); }, - pick: function() { widget.pick(); } + pick: function() { widget.pick(); }, + data: data })); if (options.closeOnUnfocus !== false) { diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js index c7cc8486575eb048e5d8d3766262c15c75c68754..3bfd11a93e5882211f81dd276bb7e40f3d16fd4c 100644 --- a/addon/runmode/runmode.node.js +++ b/addon/runmode/runmode.node.js @@ -94,7 +94,7 @@ exports.resolveMode = function(spec) { else return spec || {name: "null"}; }; exports.getMode = function(options, spec) { - spec = exports.resolveMode(mimeModes[spec]); + spec = exports.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) throw new Error("Unknown mode: " + spec); return mfactory(options, spec); diff --git a/bin/source-highlight b/bin/source-highlight index 7596ed776ca04d432d05b52867cdfbd99ce62cc2..1373f6626f6ac74b78f6f04f94704a97f7b8694c 100755 --- a/bin/source-highlight +++ b/bin/source-highlight @@ -40,7 +40,7 @@ function ensureMode(name) { ensureMode(modeName); function esc(str) { - return str.replace(/[<&]/, function(ch) { return ch == "&" ? "&" : "<"; }); + return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&" : "<"; }); } var code = fs.readFileSync("/dev/stdin", "utf8"); diff --git a/demo/variableheight.html b/demo/variableheight.html index ab241c6d28ae13fcd8dd8d3f1ab2903ba4f21435..98f2fd29d054eb0c6617a54ff3fe05ca2d52f332 100644 --- a/demo/variableheight.html +++ b/demo/variableheight.html @@ -11,12 +11,12 @@ <style type="text/css"> .CodeMirror {border: 1px solid silver; border-width: 1px 2px; } .cm-header { font-family: arial; } - .cm-header1 { font-size: 150%; } - .cm-header2 { font-size: 130%; } - .cm-header3 { font-size: 120%; } - .cm-header4 { font-size: 110%; } - .cm-header5 { font-size: 100%; } - .cm-header6 { font-size: 90%; } + .cm-header-1 { font-size: 150%; } + .cm-header-2 { font-size: 130%; } + .cm-header-3 { font-size: 120%; } + .cm-header-4 { font-size: 110%; } + .cm-header-5 { font-size: 100%; } + .cm-header-6 { font-size: 90%; } .cm-strong { font-size: 140%; } </style> <div id=nav> diff --git a/doc/manual.html b/doc/manual.html index 25db83a1403d709801fa4a5bc1003bf3bccdbc51..7ec8dc657af1a0e9ab87cf82c704e2a9bdf1b46d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2162,7 +2162,11 @@ has <code>moveFocus(n)</code>, <code>setFocus(n)</code>, <code>pick()</code>, and <code>close()</code> methods (see the source for details), that can be used to change the focused element, pick the - current element or close the menu.</dd> + current element or close the menu. Additionnaly <code>menuSize()</code> + can give you access to the size of the current dropdown menu, + <code>length</code> give you the number of availlable completions, and + <code>data</code> give you full access to the completion returned by the + hinting function.</dd> <dt><code><strong>extraKeys</strong>: keymap</code></dt> <dd>Like <code>customKeys</code> above, but the bindings will be added to the set of default bindings, instead of replacing diff --git a/doc/realworld.html b/doc/realworld.html index b9e35f2dfc3653126c745354ac0df23cc625358a..96d4df19564a9a0c530b68ef8a7378f4febea52c 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -74,6 +74,7 @@ <li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li> <li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li> <li><a href="http://icecoder.net">ICEcoder</a> (web IDE)</li> + <li><a href="http://i-mos.org/imos/">i-MOS</a> (modeling and simulation platform)</li> <li><a href="http://www.janvas.com/">Janvas</a> (vector graphics editor)</li> <li><a href="http://extensions.joomla.org/extensions/edition/editors/8723">Joomla plugin</a></li> <li><a href="http://jqfundamentals.com/">jQuery fundamentals</a> (interactive tutorial)</li> diff --git a/keymap/vim.js b/keymap/vim.js index 1c41f8f671a80f393afb880b20a6bb2b4f8472d5..74d711cda80bf6c3f0ab92150c99de8ba96d7c38 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -81,7 +81,7 @@ { keys: ['<S-BS>'], type: 'keyToKey', toKeys: ['b'] }, { keys: ['<C-n>'], type: 'keyToKey', toKeys: ['j'] }, { keys: ['<C-p>'], type: 'keyToKey', toKeys: ['k'] }, - { keys: ['C-['], type: 'keyToKey', toKeys: ['<Esc>'] }, + { keys: ['<C-[>'], type: 'keyToKey', toKeys: ['<Esc>'] }, { keys: ['<C-c>'], type: 'keyToKey', toKeys: ['<Esc>'] }, { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' }, { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'}, @@ -1542,22 +1542,44 @@ findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum))); }, textObjectManipulation: function(cm, motionArgs) { + // TODO: lots of possible exceptions that can be thrown here. Try da( + // outside of a () block. + + // TODO: adding <> >< to this map doesn't work, presumably because + // they're operators + var mirroredPairs = {'(': ')', ')': '(', + '{': '}', '}': '{', + '[': ']', ']': '['}; + var selfPaired = {'\'': true, '"': true}; + var character = motionArgs.selectedCharacter; + // Inclusive is the difference between a and i // TODO: Instead of using the additional text object map to perform text // object operations, merge the map into the defaultKeyMap and use // motionArgs to define behavior. Define separate entries for 'aw', // 'iw', 'a[', 'i[', etc. var inclusive = !motionArgs.textObjectInner; - if (!textObjects[character]) { + + var tmp; + if (mirroredPairs[character]) { + tmp = selectCompanionObject(cm, mirroredPairs[character], inclusive); + } else if (selfPaired[character]) { + tmp = findBeginningAndEnd(cm, character, inclusive); + } else if (character === 'W') { + tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, + true /** bigWord */); + } else if (character === 'w') { + tmp = expandWordUnderCursor(cm, inclusive, true /** forward */, + false /** bigWord */); + } else { // No text object defined for this, don't move. return null; } - var tmp = textObjects[character](cm, inclusive); - var start = tmp.start; - var end = tmp.end; - return [start, end]; + + return [tmp.start, tmp.end]; }, + repeatLastCharacterSearch: function(cm, motionArgs) { var lastSearch = vimGlobalState.lastChararacterSearch; var repeat = motionArgs.repeat; @@ -2025,36 +2047,6 @@ } }; - var textObjects = { - // TODO: lots of possible exceptions that can be thrown here. Try da( - // outside of a () block. - // TODO: implement text objects for the reverse like }. Should just be - // an additional mapping after moving to the defaultKeyMap. - 'w': function(cm, inclusive) { - return expandWordUnderCursor(cm, inclusive, true /** forward */, - false /** bigWord */); - }, - 'W': function(cm, inclusive) { - return expandWordUnderCursor(cm, inclusive, - true /** forward */, true /** bigWord */); - }, - '{': function(cm, inclusive) { - return selectCompanionObject(cm, '}', inclusive); - }, - '(': function(cm, inclusive) { - return selectCompanionObject(cm, ')', inclusive); - }, - '[': function(cm, inclusive) { - return selectCompanionObject(cm, ']', inclusive); - }, - '\'': function(cm, inclusive) { - return findBeginningAndEnd(cm, "'", inclusive); - }, - '"': function(cm, inclusive) { - return findBeginningAndEnd(cm, '"', inclusive); - } - }; - /* * Below are miscellaneous utility functions used by vim.js */ @@ -2644,13 +2636,25 @@ return cur; } + // TODO: perhaps this finagling of start and end positions belonds + // in codmirror/replaceRange? function selectCompanionObject(cm, revSymb, inclusive) { var cur = cm.getCursor(); - var end = findMatchedSymbol(cm, cur, revSymb); var start = findMatchedSymbol(cm, end); - start.ch += inclusive ? 1 : 0; - end.ch += inclusive ? 0 : 1; + + if((start.line == end.line && start.ch > end.ch) + || (start.line > end.line)) { + var tmp = start; + start = end; + end = tmp; + } + + if(inclusive) { + end.ch += 1; + } else { + start.ch += 1; + } return { start: start, end: end }; } diff --git a/lib/codemirror.css b/lib/codemirror.css index 110693bed9ea549bd5d2b9e068ff506e5ad3b5fd..fcc023b753c51ea957da071df416e21636a7bb96 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -36,6 +36,8 @@ min-width: 20px; text-align: right; color: #999; + -moz-box-sizing: content-box; + box-sizing: content-box; } /* CURSOR */ diff --git a/lib/codemirror.js b/lib/codemirror.js index 6b6a0e9ca3f8746cd7b82e4736132363e1e85de1..497f68e89aeef2e06403b53fded5460ce63011a4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2409,6 +2409,7 @@ } if (clickInGutter(cm, e)) return; var start = posFromMouse(cm, e); + window.focus(); switch (e_button(e)) { case 1: diff --git a/mode/julia/julia.js b/mode/julia/julia.js index b60371304066c19159c9155d77775d54c980018f..d37ab503ed658c9694106befbaea79ad6f42e899 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -15,21 +15,22 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } - var operators = parserConf.operators || /^(?:\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|<:|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b|\.{3})/; + var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/; var delimiters = parserConf.delimiters || /^[;,()[\]{}]/; var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*!*/; - var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch"]; + var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"]; var blockClosers = ["end", "else", "elseif", "catch", "finally"]; var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall']; - var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf']; + var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf']; //var stringPrefixes = new RegExp("^[br]?('|\")") - var stringPrefixes = /^[br]?('|"{3}|")/; + var stringPrefixes = /^(`|'|"{3}|([br]?"))/; var keywords = wordRegexp(keywordList); var builtins = wordRegexp(builtinList); var openers = wordRegexp(blockOpeners); var closers = wordRegexp(blockClosers); - var macro = /@[_A-Za-z][_A-Za-z0-9]*!*/; + var macro = /^@[_A-Za-z][_A-Za-z0-9]*/; + var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/; var indentInfo = null; function in_array(state) { @@ -53,14 +54,19 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { function tokenBase(stream, state) { // Handle scope changes var leaving_expr = state.leaving_expr; + if(stream.sol()) { + leaving_expr = false; + } state.leaving_expr = false; if(leaving_expr) { if(stream.match(/^'+/)) { return 'operator'; } - if(stream.match("...")) { - return 'operator'; - } + + } + + if(stream.match(/^\.{2,3}/)) { + return 'operator'; } if (stream.eatSpace()) { @@ -93,8 +99,12 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { state.leaving_expr=true; } + if(ch===')') { + state.leaving_expr = true; + } + var match; - if(match=stream.match(openers, false)) { + if(!in_array(state) && (match=stream.match(openers, false))) { state.scopes.push(match); } @@ -103,25 +113,29 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { } if(in_array(state)) { - if(stream.match("end")) { + if(stream.match(/^end/)) { return 'number'; } } - if(stream.match("=>")) { + + if(stream.match(/^=>/)) { return 'operator'; } + + // Handle Number Literals if (stream.match(/^[0-9\.]/, false)) { var imMatcher = RegExp(/^im\b/); var floatLiteral = false; // Floats - if (stream.match(/^\d*\.\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; } - if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; } if (stream.match(/^\.\d+/)) { floatLiteral = true; } if (floatLiteral) { // Float literals may be "imaginary" stream.match(imMatcher); + state.leaving_expr = true; return 'number'; } // Integers @@ -134,9 +148,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } // Decimal if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { - // Decimal literals may be "imaginary" - stream.eat(/J/i); - // TODO - Can you have imaginary longs? intLiteral = true; } // Zero by itself with no other piece of number. @@ -144,21 +155,37 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (intLiteral) { // Integer literals may be "long" stream.match(imMatcher); + state.leaving_expr = true; return 'number'; } } + if(stream.match(/^(::)|(<:)/)) { + return 'operator'; + } + + // Handle symbols + if(!leaving_expr && stream.match(symbol)) { + return 'string'; + } + + // Handle operators and Delimiters + if (stream.match(operators)) { + return 'operator'; + } + + // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } - // Handle operators and Delimiters - if (stream.match(operators)) { - return 'operator'; + if (stream.match(macro)) { + return 'meta'; } + if (stream.match(delimiters)) { return null; } @@ -171,9 +198,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return 'builtin'; } - if (stream.match(macro)) { - return 'meta'; - } if (stream.match(identifiers)) { state.leaving_expr=true; @@ -258,7 +282,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") { delta = -1; } - return (state.scopes.length + delta) * 2; + return (state.scopes.length + delta) * 4; }, lineComment: "#", diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index cfa56b911d2e0b86dd653a71b0e84069ce7b29e1..cc831ee0d88154a7caeb0850840d9783b1d4fc6c 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -249,7 +249,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { styles.push(formatting + "-" + state.formatting[i]); if (state.formatting[i] === "header") { - styles.push(formatting + "-" + state.formatting[i] + state.header); + styles.push(formatting + "-" + state.formatting[i] + "-" + state.header); } // Add `formatting-quote` and `formatting-quote-#` for blockquotes @@ -285,7 +285,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (state.code) { styles.push(code); } - if (state.header) { styles.push(header); styles.push(header + state.header); } + if (state.header) { styles.push(header); styles.push(header + "-" + state.header); } if (state.quote) { styles.push(quote); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 30e82ca6541fcdb9b97515050633975c990db19e..c3016d3a6a476950d5b8c8fc13b5687168b6cb9b 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -23,11 +23,11 @@ "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]"); FT("formatting_atxHeader", - "[header&header1&formatting&formatting-header&formatting-header1 #][header&header1 foo # bar ][header&header1&formatting&formatting-header&formatting-header1 #]"); + "[header&header-1&formatting&formatting-header&formatting-header-1 #][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]"); FT("formatting_setextHeader", "foo", - "[header&header1&formatting&formatting-header&formatting-header1 =]"); + "[header&header-1&formatting&formatting-header&formatting-header-1 =]"); FT("formatting_blockquote", "[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]"); @@ -138,31 +138,31 @@ // http://daringfireball.net/projects/markdown/syntax#header MT("atxH1", - "[header&header1 # foo]"); + "[header&header-1 # foo]"); MT("atxH2", - "[header&header2 ## foo]"); + "[header&header-2 ## foo]"); MT("atxH3", - "[header&header3 ### foo]"); + "[header&header-3 ### foo]"); MT("atxH4", - "[header&header4 #### foo]"); + "[header&header-4 #### foo]"); MT("atxH5", - "[header&header5 ##### foo]"); + "[header&header-5 ##### foo]"); MT("atxH6", - "[header&header6 ###### foo]"); + "[header&header-6 ###### foo]"); // H6 - 7x '#' should still be H6, per Dingus // http://daringfireball.net/projects/markdown/dingus MT("atxH6NotH7", - "[header&header6 ####### foo]"); + "[header&header-6 ####### foo]"); // Inline styles should be parsed inside headers MT("atxH1inline", - "[header&header1 # foo ][header&header1&em *bar*]"); + "[header&header-1 # foo ][header&header-1&em *bar*]"); // Setext headers - H1, H2 // Per documentation, "Any number of underlining =’s or -’s will work." @@ -174,22 +174,22 @@ // Check if single underlining = works MT("setextH1", "foo", - "[header&header1 =]"); + "[header&header-1 =]"); // Check if 3+ ='s work MT("setextH1", "foo", - "[header&header1 ===]"); + "[header&header-1 ===]"); // Check if single underlining - works MT("setextH2", "foo", - "[header&header2 -]"); + "[header&header-2 -]"); // Check if 3+ -'s work MT("setextH2", "foo", - "[header&header2 ---]"); + "[header&header-2 ---]"); // Single-line blockquote with trailing space MT("blockquoteSpace", @@ -278,7 +278,7 @@ // List after header MT("listAfterHeader", - "[header&header1 # foo]", + "[header&header-1 # foo]", "[variable-2 - bar]"); // Formatting in lists (*) diff --git a/mode/rst/index.html b/mode/rst/index.html index 78030ebe94cb34566de3931703f332f66294ae35..cb8269751a22cc43377441a529eedc56778c9408 100644 --- a/mode/rst/index.html +++ b/mode/rst/index.html @@ -6,6 +6,7 @@ <link rel="stylesheet" href="../../lib/codemirror.css"> <script src="../../lib/codemirror.js"></script> +<script src="../../addon/mode/overlay.js"></script> <script src="rst.js"></script> <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> <div id=nav> diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 01c6a056505aae60f7c84d6ace5f2173a46b371f..0763d4b999eece835395eff3fedcc712690a8af5 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -8,557 +8,560 @@ })(function(CodeMirror) { "use strict"; -CodeMirror.defineMode('rst-base', function (config) { - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// +CodeMirror.defineMode('rst', function (config, options) { - function format(string) { - var args = Array.prototype.slice.call(arguments, 1); - return string.replace(/{(\d+)}/g, function (match, n) { - return typeof args[n] != 'undefined' ? args[n] : match; - }); + var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/; + var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/; + var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/; + + var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/; + var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/; + var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/; + + var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://"; + var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})"; + var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*"; + var rx_uri = new RegExp("^" + rx_uri_protocol + rx_uri_domain + rx_uri_path); + + var overlay = { + token: function (stream) { + + if (stream.match(rx_strong) && stream.match (/\W+|$/, false)) + return 'strong'; + if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false)) + return 'em'; + if (stream.match(rx_literal) && stream.match (/\W+|$/, false)) + return 'string-2'; + if (stream.match(rx_number)) + return 'number'; + if (stream.match(rx_positive)) + return 'positive'; + if (stream.match(rx_negative)) + return 'negative'; + if (stream.match(rx_uri)) + return 'link'; + + while (stream.next() != null) { + if (stream.match(rx_strong, false)) break; + if (stream.match(rx_emphasis, false)) break; + if (stream.match(rx_literal, false)) break; + if (stream.match(rx_number, false)) break; + if (stream.match(rx_positive, false)) break; + if (stream.match(rx_negative, false)) break; + if (stream.match(rx_uri, false)) break; + } + + return null; } + }; - function AssertException(message) { - this.message = message; - } + var mode = CodeMirror.getMode( + config, options.backdrop || 'rst-base' + ); - AssertException.prototype.toString = function () { - return 'AssertException: ' + this.message; - }; + return CodeMirror.overlayMode(mode, overlay, true); // combine +}, 'python', 'stex'); - function assert(expression, message) { - if (!expression) throw new AssertException(message); - return expression; - } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - var mode_python = CodeMirror.getMode(config, 'python'); - var mode_stex = CodeMirror.getMode(config, 'stex'); - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - var SEPA = "\\s+"; - var TAIL = "(?:\\s*|\\W|$)", - rx_TAIL = new RegExp(format('^{0}', TAIL)); - - var NAME = - "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)", - rx_NAME = new RegExp(format('^{0}', NAME)); - var NAME_WWS = - "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)"; - var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS); - - var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)"; - var TEXT2 = "(?:[^\\`]+)", - rx_TEXT2 = new RegExp(format('^{0}', TEXT2)); - - var rx_section = new RegExp( - "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$"); - var rx_explicit = new RegExp( - format('^\\.\\.{0}', SEPA)); - var rx_link = new RegExp( - format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL)); - var rx_directive = new RegExp( - format('^{0}::{1}', REF_NAME, TAIL)); - var rx_substitution = new RegExp( - format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL)); - var rx_footnote = new RegExp( - format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL)); - var rx_citation = new RegExp( - format('^\\[{0}\\]{1}', REF_NAME, TAIL)); - - var rx_substitution_ref = new RegExp( - format('^\\|{0}\\|', TEXT1)); - var rx_footnote_ref = new RegExp( - format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME)); - var rx_citation_ref = new RegExp( - format('^\\[{0}\\]_', REF_NAME)); - var rx_link_ref1 = new RegExp( - format('^{0}__?', REF_NAME)); - var rx_link_ref2 = new RegExp( - format('^`{0}`_', TEXT2)); - - var rx_role_pre = new RegExp( - format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL)); - var rx_role_suf = new RegExp( - format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL)); - var rx_role = new RegExp( - format('^:{0}:{1}', NAME, TAIL)); - - var rx_directive_name = new RegExp(format('^{0}', REF_NAME)); - var rx_directive_tail = new RegExp(format('^::{0}', TAIL)); - var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1)); - var rx_substitution_sepa = new RegExp(format('^{0}', SEPA)); - var rx_substitution_name = new RegExp(format('^{0}', REF_NAME)); - var rx_substitution_tail = new RegExp(format('^::{0}', TAIL)); - var rx_link_head = new RegExp("^_"); - var rx_link_name = new RegExp(format('^{0}|_', REF_NAME)); - var rx_link_tail = new RegExp(format('^:{0}', TAIL)); - - var rx_verbatim = new RegExp('^::\\s*$'); - var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s'); - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - function to_normal(stream, state) { - var token = null; - - if (stream.sol() && stream.match(rx_examples, false)) { - change(state, to_mode, { - mode: mode_python, local: mode_python.startState() - }); - } else if (stream.sol() && stream.match(rx_explicit)) { - change(state, to_explicit); - token = 'meta'; - } else if (stream.sol() && stream.match(rx_section)) { - change(state, to_normal); - token = 'header'; - } else if (phase(state) == rx_role_pre || - stream.match(rx_role_pre, false)) { - - switch (stage(state)) { - case 0: - change(state, to_normal, context(rx_role_pre, 1)); - assert(stream.match(/^:/)); - token = 'meta'; - break; - case 1: - change(state, to_normal, context(rx_role_pre, 2)); - assert(stream.match(rx_NAME)); - token = 'keyword'; - - if (stream.current().match(/^(?:math|latex)/)) { - state.tmp_stex = true; - } - break; - case 2: - change(state, to_normal, context(rx_role_pre, 3)); - assert(stream.match(/^:`/)); - token = 'meta'; - break; - case 3: - if (state.tmp_stex) { - state.tmp_stex = undefined; state.tmp = { - mode: mode_stex, local: mode_stex.startState() - }; - } - - if (state.tmp) { - if (stream.peek() == '`') { - change(state, to_normal, context(rx_role_pre, 4)); - state.tmp = undefined; - break; - } - - token = state.tmp.mode.token(stream, state.tmp.local); - break; - } - - change(state, to_normal, context(rx_role_pre, 4)); - assert(stream.match(rx_TEXT2)); - token = 'string'; - break; - case 4: - change(state, to_normal, context(rx_role_pre, 5)); - assert(stream.match(/^`/)); - token = 'meta'; - break; - case 5: - change(state, to_normal, context(rx_role_pre, 6)); - assert(stream.match(rx_TAIL)); - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (phase(state) == rx_role_suf || - stream.match(rx_role_suf, false)) { - - switch (stage(state)) { - case 0: - change(state, to_normal, context(rx_role_suf, 1)); - assert(stream.match(/^`/)); - token = 'meta'; - break; - case 1: - change(state, to_normal, context(rx_role_suf, 2)); - assert(stream.match(rx_TEXT2)); - token = 'string'; - break; - case 2: - change(state, to_normal, context(rx_role_suf, 3)); - assert(stream.match(/^`:/)); - token = 'meta'; - break; - case 3: - change(state, to_normal, context(rx_role_suf, 4)); - assert(stream.match(rx_NAME)); - token = 'keyword'; - break; - case 4: - change(state, to_normal, context(rx_role_suf, 5)); - assert(stream.match(/^:/)); - token = 'meta'; - break; - case 5: - change(state, to_normal, context(rx_role_suf, 6)); - assert(stream.match(rx_TAIL)); - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (phase(state) == rx_role || stream.match(rx_role, false)) { - - switch (stage(state)) { - case 0: - change(state, to_normal, context(rx_role, 1)); - assert(stream.match(/^:/)); - token = 'meta'; - break; - case 1: - change(state, to_normal, context(rx_role, 2)); - assert(stream.match(rx_NAME)); - token = 'keyword'; - break; - case 2: - change(state, to_normal, context(rx_role, 3)); - assert(stream.match(/^:/)); - token = 'meta'; - break; - case 3: - change(state, to_normal, context(rx_role, 4)); - assert(stream.match(rx_TAIL)); - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (phase(state) == rx_substitution_ref || - stream.match(rx_substitution_ref, false)) { - - switch (stage(state)) { - case 0: - change(state, to_normal, context(rx_substitution_ref, 1)); - assert(stream.match(rx_substitution_text)); - token = 'variable-2'; - break; - case 1: - change(state, to_normal, context(rx_substitution_ref, 2)); - if (stream.match(/^_?_?/)) token = 'link'; - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (stream.match(rx_footnote_ref)) { - change(state, to_normal); - token = 'quote'; - } else if (stream.match(rx_citation_ref)) { - change(state, to_normal); - token = 'quote'; - } else if (stream.match(rx_link_ref1)) { - change(state, to_normal); - if (!stream.peek() || stream.peek().match(/^\W$/)) { - token = 'link'; - } - } else if (phase(state) == rx_link_ref2 || - stream.match(rx_link_ref2, false)) { - - switch (stage(state)) { - case 0: - if (!stream.peek() || stream.peek().match(/^\W$/)) { - change(state, to_normal, context(rx_link_ref2, 1)); - } else { - stream.match(rx_link_ref2); - } - break; - case 1: - change(state, to_normal, context(rx_link_ref2, 2)); - assert(stream.match(/^`/)); - token = 'link'; - break; - case 2: - change(state, to_normal, context(rx_link_ref2, 3)); - assert(stream.match(rx_TEXT2)); - break; - case 3: - change(state, to_normal, context(rx_link_ref2, 4)); - assert(stream.match(/^`_/)); - token = 'link'; - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (stream.match(rx_verbatim)) { - change(state, to_verbatim); - } +CodeMirror.defineMode('rst-base', function (config) { - else { - if (stream.next()) change(state, to_normal); + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function format(string) { + var args = Array.prototype.slice.call(arguments, 1); + return string.replace(/{(\d+)}/g, function (match, n) { + return typeof args[n] != 'undefined' ? args[n] : match; + }); + } + + function AssertException(message) { + this.message = message; + } + + AssertException.prototype.toString = function () { + return 'AssertException: ' + this.message; + }; + + function assert(expression, message) { + if (!expression) throw new AssertException(message); + return expression; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + var mode_python = CodeMirror.getMode(config, 'python'); + var mode_stex = CodeMirror.getMode(config, 'stex'); + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + var SEPA = "\\s+"; + var TAIL = "(?:\\s*|\\W|$)", + rx_TAIL = new RegExp(format('^{0}', TAIL)); + + var NAME = + "(?:[^\\W\\d_](?:[\\w!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)", + rx_NAME = new RegExp(format('^{0}', NAME)); + var NAME_WWS = + "(?:[^\\W\\d_](?:[\\w\\s!\"#$%&'()\\*\\+,\\-\\.\/:;<=>\\?]*[^\\W_])?)"; + var REF_NAME = format('(?:{0}|`{1}`)', NAME, NAME_WWS); + + var TEXT1 = "(?:[^\\s\\|](?:[^\\|]*[^\\s\\|])?)"; + var TEXT2 = "(?:[^\\`]+)", + rx_TEXT2 = new RegExp(format('^{0}', TEXT2)); + + var rx_section = new RegExp( + "^([!'#$%&\"()*+,-./:;<=>?@\\[\\\\\\]^_`{|}~])\\1{3,}\\s*$"); + var rx_explicit = new RegExp( + format('^\\.\\.{0}', SEPA)); + var rx_link = new RegExp( + format('^_{0}:{1}|^__:{1}', REF_NAME, TAIL)); + var rx_directive = new RegExp( + format('^{0}::{1}', REF_NAME, TAIL)); + var rx_substitution = new RegExp( + format('^\\|{0}\\|{1}{2}::{3}', TEXT1, SEPA, REF_NAME, TAIL)); + var rx_footnote = new RegExp( + format('^\\[(?:\\d+|#{0}?|\\*)]{1}', REF_NAME, TAIL)); + var rx_citation = new RegExp( + format('^\\[{0}\\]{1}', REF_NAME, TAIL)); + + var rx_substitution_ref = new RegExp( + format('^\\|{0}\\|', TEXT1)); + var rx_footnote_ref = new RegExp( + format('^\\[(?:\\d+|#{0}?|\\*)]_', REF_NAME)); + var rx_citation_ref = new RegExp( + format('^\\[{0}\\]_', REF_NAME)); + var rx_link_ref1 = new RegExp( + format('^{0}__?', REF_NAME)); + var rx_link_ref2 = new RegExp( + format('^`{0}`_', TEXT2)); + + var rx_role_pre = new RegExp( + format('^:{0}:`{1}`{2}', NAME, TEXT2, TAIL)); + var rx_role_suf = new RegExp( + format('^`{1}`:{0}:{2}', NAME, TEXT2, TAIL)); + var rx_role = new RegExp( + format('^:{0}:{1}', NAME, TAIL)); + + var rx_directive_name = new RegExp(format('^{0}', REF_NAME)); + var rx_directive_tail = new RegExp(format('^::{0}', TAIL)); + var rx_substitution_text = new RegExp(format('^\\|{0}\\|', TEXT1)); + var rx_substitution_sepa = new RegExp(format('^{0}', SEPA)); + var rx_substitution_name = new RegExp(format('^{0}', REF_NAME)); + var rx_substitution_tail = new RegExp(format('^::{0}', TAIL)); + var rx_link_head = new RegExp("^_"); + var rx_link_name = new RegExp(format('^{0}|_', REF_NAME)); + var rx_link_tail = new RegExp(format('^:{0}', TAIL)); + + var rx_verbatim = new RegExp('^::\\s*$'); + var rx_examples = new RegExp('^\\s+(?:>>>|In \\[\\d+\\]:)\\s'); + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_normal(stream, state) { + var token = null; + + if (stream.sol() && stream.match(rx_examples, false)) { + change(state, to_mode, { + mode: mode_python, local: CodeMirror.startState(mode_python) + }); + } else if (stream.sol() && stream.match(rx_explicit)) { + change(state, to_explicit); + token = 'meta'; + } else if (stream.sol() && stream.match(rx_section)) { + change(state, to_normal); + token = 'header'; + } else if (phase(state) == rx_role_pre || + stream.match(rx_role_pre, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role_pre, 1)); + assert(stream.match(/^:/)); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role_pre, 2)); + assert(stream.match(rx_NAME)); + token = 'keyword'; + + if (stream.current().match(/^(?:math|latex)/)) { + state.tmp_stex = true; + } + break; + case 2: + change(state, to_normal, context(rx_role_pre, 3)); + assert(stream.match(/^:`/)); + token = 'meta'; + break; + case 3: + if (state.tmp_stex) { + state.tmp_stex = undefined; state.tmp = { + mode: mode_stex, local: CodeMirror.startState(mode_stex) + }; } - return token; - } + if (state.tmp) { + if (stream.peek() == '`') { + change(state, to_normal, context(rx_role_pre, 4)); + state.tmp = undefined; + break; + } - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - function to_explicit(stream, state) { - var token = null; - - if (phase(state) == rx_substitution || - stream.match(rx_substitution, false)) { - - switch (stage(state)) { - case 0: - change(state, to_explicit, context(rx_substitution, 1)); - assert(stream.match(rx_substitution_text)); - token = 'variable-2'; - break; - case 1: - change(state, to_explicit, context(rx_substitution, 2)); - assert(stream.match(rx_substitution_sepa)); - break; - case 2: - change(state, to_explicit, context(rx_substitution, 3)); - assert(stream.match(rx_substitution_name)); - token = 'keyword'; - break; - case 3: - change(state, to_explicit, context(rx_substitution, 4)); - assert(stream.match(rx_substitution_tail)); - token = 'meta'; - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (phase(state) == rx_directive || - stream.match(rx_directive, false)) { - - switch (stage(state)) { - case 0: - change(state, to_explicit, context(rx_directive, 1)); - assert(stream.match(rx_directive_name)); - token = 'keyword'; - - if (stream.current().match(/^(?:math|latex)/)) - state.tmp_stex = true; - else if (stream.current().match(/^python/)) - state.tmp_py = true; - break; - case 1: - change(state, to_explicit, context(rx_directive, 2)); - assert(stream.match(rx_directive_tail)); - token = 'meta'; - - if (stream.match(/^latex\s*$/) || state.tmp_stex) { - state.tmp_stex = undefined; change(state, to_mode, { - mode: mode_stex, local: mode_stex.startState() - }); - } - break; - case 2: - change(state, to_explicit, context(rx_directive, 3)); - if (stream.match(/^python\s*$/) || state.tmp_py) { - state.tmp_py = undefined; change(state, to_mode, { - mode: mode_python, local: mode_python.startState() - }); - } - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (phase(state) == rx_link || stream.match(rx_link, false)) { - - switch (stage(state)) { - case 0: - change(state, to_explicit, context(rx_link, 1)); - assert(stream.match(rx_link_head)); - assert(stream.match(rx_link_name)); - token = 'link'; - break; - case 1: - change(state, to_explicit, context(rx_link, 2)); - assert(stream.match(rx_link_tail)); - token = 'meta'; - break; - default: - change(state, to_normal); - assert(stream.current() == ''); - } - } else if (stream.match(rx_footnote)) { - change(state, to_normal); - token = 'quote'; - } else if (stream.match(rx_citation)) { - change(state, to_normal); - token = 'quote'; + token = state.tmp.mode.token(stream, state.tmp.local); + break; } - else { - stream.eatSpace(); - if (stream.eol()) { - change(state, to_normal); - } else { - stream.skipToEnd(); - change(state, to_comment); - token = 'comment'; - } + change(state, to_normal, context(rx_role_pre, 4)); + assert(stream.match(rx_TEXT2)); + token = 'string'; + break; + case 4: + change(state, to_normal, context(rx_role_pre, 5)); + assert(stream.match(/^`/)); + token = 'meta'; + break; + case 5: + change(state, to_normal, context(rx_role_pre, 6)); + assert(stream.match(rx_TAIL)); + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (phase(state) == rx_role_suf || + stream.match(rx_role_suf, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role_suf, 1)); + assert(stream.match(/^`/)); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role_suf, 2)); + assert(stream.match(rx_TEXT2)); + token = 'string'; + break; + case 2: + change(state, to_normal, context(rx_role_suf, 3)); + assert(stream.match(/^`:/)); + token = 'meta'; + break; + case 3: + change(state, to_normal, context(rx_role_suf, 4)); + assert(stream.match(rx_NAME)); + token = 'keyword'; + break; + case 4: + change(state, to_normal, context(rx_role_suf, 5)); + assert(stream.match(/^:/)); + token = 'meta'; + break; + case 5: + change(state, to_normal, context(rx_role_suf, 6)); + assert(stream.match(rx_TAIL)); + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (phase(state) == rx_role || stream.match(rx_role, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_role, 1)); + assert(stream.match(/^:/)); + token = 'meta'; + break; + case 1: + change(state, to_normal, context(rx_role, 2)); + assert(stream.match(rx_NAME)); + token = 'keyword'; + break; + case 2: + change(state, to_normal, context(rx_role, 3)); + assert(stream.match(/^:/)); + token = 'meta'; + break; + case 3: + change(state, to_normal, context(rx_role, 4)); + assert(stream.match(rx_TAIL)); + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (phase(state) == rx_substitution_ref || + stream.match(rx_substitution_ref, false)) { + + switch (stage(state)) { + case 0: + change(state, to_normal, context(rx_substitution_ref, 1)); + assert(stream.match(rx_substitution_text)); + token = 'variable-2'; + break; + case 1: + change(state, to_normal, context(rx_substitution_ref, 2)); + if (stream.match(/^_?_?/)) token = 'link'; + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (stream.match(rx_footnote_ref)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_citation_ref)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_link_ref1)) { + change(state, to_normal); + if (!stream.peek() || stream.peek().match(/^\W$/)) { + token = 'link'; + } + } else if (phase(state) == rx_link_ref2 || + stream.match(rx_link_ref2, false)) { + + switch (stage(state)) { + case 0: + if (!stream.peek() || stream.peek().match(/^\W$/)) { + change(state, to_normal, context(rx_link_ref2, 1)); + } else { + stream.match(rx_link_ref2); } - - return token; - } - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - function to_comment(stream, state) { - return as_block(stream, state, 'comment'); + break; + case 1: + change(state, to_normal, context(rx_link_ref2, 2)); + assert(stream.match(/^`/)); + token = 'link'; + break; + case 2: + change(state, to_normal, context(rx_link_ref2, 3)); + assert(stream.match(rx_TEXT2)); + break; + case 3: + change(state, to_normal, context(rx_link_ref2, 4)); + assert(stream.match(/^`_/)); + token = 'link'; + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (stream.match(rx_verbatim)) { + change(state, to_verbatim); } - function to_verbatim(stream, state) { - return as_block(stream, state, 'meta'); + else { + if (stream.next()) change(state, to_normal); } - function as_block(stream, state, token) { - if (stream.eol() || stream.eatSpace()) { - stream.skipToEnd(); - return token; - } else { - change(state, to_normal); - return null; + return token; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function to_explicit(stream, state) { + var token = null; + + if (phase(state) == rx_substitution || + stream.match(rx_substitution, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_substitution, 1)); + assert(stream.match(rx_substitution_text)); + token = 'variable-2'; + break; + case 1: + change(state, to_explicit, context(rx_substitution, 2)); + assert(stream.match(rx_substitution_sepa)); + break; + case 2: + change(state, to_explicit, context(rx_substitution, 3)); + assert(stream.match(rx_substitution_name)); + token = 'keyword'; + break; + case 3: + change(state, to_explicit, context(rx_substitution, 4)); + assert(stream.match(rx_substitution_tail)); + token = 'meta'; + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (phase(state) == rx_directive || + stream.match(rx_directive, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_directive, 1)); + assert(stream.match(rx_directive_name)); + token = 'keyword'; + + if (stream.current().match(/^(?:math|latex)/)) + state.tmp_stex = true; + else if (stream.current().match(/^python/)) + state.tmp_py = true; + break; + case 1: + change(state, to_explicit, context(rx_directive, 2)); + assert(stream.match(rx_directive_tail)); + token = 'meta'; + + if (stream.match(/^latex\s*$/) || state.tmp_stex) { + state.tmp_stex = undefined; change(state, to_mode, { + mode: mode_stex, local: CodeMirror.startState(mode_stex) + }); } - } - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - function to_mode(stream, state) { - - if (state.ctx.mode && state.ctx.local) { - - if (stream.sol()) { - if (!stream.eatSpace()) change(state, to_normal); - return null; - } - - return state.ctx.mode.token(stream, state.ctx.local); + break; + case 2: + change(state, to_explicit, context(rx_directive, 3)); + if (stream.match(/^python\s*$/) || state.tmp_py) { + state.tmp_py = undefined; change(state, to_mode, { + mode: mode_python, local: CodeMirror.startState(mode_python) + }); } - + break; + default: change(state, to_normal); - return null; - } - - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - - function context(phase, stage, mode, local) { - return {phase: phase, stage: stage, mode: mode, local: local}; - } - - function change(state, tok, ctx) { - state.tok = tok; - state.ctx = ctx || {}; + assert(stream.current() == ''); + } + } else if (phase(state) == rx_link || stream.match(rx_link, false)) { + + switch (stage(state)) { + case 0: + change(state, to_explicit, context(rx_link, 1)); + assert(stream.match(rx_link_head)); + assert(stream.match(rx_link_name)); + token = 'link'; + break; + case 1: + change(state, to_explicit, context(rx_link, 2)); + assert(stream.match(rx_link_tail)); + token = 'meta'; + break; + default: + change(state, to_normal); + assert(stream.current() == ''); + } + } else if (stream.match(rx_footnote)) { + change(state, to_normal); + token = 'quote'; + } else if (stream.match(rx_citation)) { + change(state, to_normal); + token = 'quote'; } - function stage(state) { - return state.ctx.stage || 0; + else { + stream.eatSpace(); + if (stream.eol()) { + change(state, to_normal); + } else { + stream.skipToEnd(); + change(state, to_comment); + token = 'comment'; + } } - function phase(state) { - return state.ctx.phase; - } + return token; + } - /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// - return { - startState: function () { - return {tok: to_normal, ctx: context(undefined, 0)}; - }, + function to_comment(stream, state) { + return as_block(stream, state, 'comment'); + } - copyState: function (state) { - return {tok: state.tok, ctx: state.ctx}; - }, + function to_verbatim(stream, state) { + return as_block(stream, state, 'meta'); + } - innerMode: function (state) { - return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode} - : state.ctx ? {state: state.ctx.local, mode: state.ctx.mode} - : null; - }, + function as_block(stream, state, token) { + if (stream.eol() || stream.eatSpace()) { + stream.skipToEnd(); + return token; + } else { + change(state, to_normal); + return null; + } + } - token: function (stream, state) { - return state.tok(stream, state); - } - }; -}, 'python', 'stex'); + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// + function to_mode(stream, state) { -CodeMirror.defineMode('rst', function (config, options) { + if (state.ctx.mode && state.ctx.local) { - var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/; - var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/; - var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/; - - var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/; - var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/; - var rx_negative = /^(?:\s\-[\d]+(?:[\.,]\d+)*)/; - - var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://"; - var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})"; - var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*"; - var rx_uri = new RegExp("^" + - rx_uri_protocol + rx_uri_domain + rx_uri_path - ); - - var overlay = { - token: function (stream) { - - if (stream.match(rx_strong) && stream.match (/\W+|$/, false)) - return 'strong'; - if (stream.match(rx_emphasis) && stream.match (/\W+|$/, false)) - return 'em'; - if (stream.match(rx_literal) && stream.match (/\W+|$/, false)) - return 'string-2'; - if (stream.match(rx_number)) - return 'number'; - if (stream.match(rx_positive)) - return 'positive'; - if (stream.match(rx_negative)) - return 'negative'; - if (stream.match(rx_uri)) - return 'link'; - - while (stream.next() != null) { - if (stream.match(rx_strong, false)) break; - if (stream.match(rx_emphasis, false)) break; - if (stream.match(rx_literal, false)) break; - if (stream.match(rx_number, false)) break; - if (stream.match(rx_positive, false)) break; - if (stream.match(rx_negative, false)) break; - if (stream.match(rx_uri, false)) break; - } - - return null; - } - }; + if (stream.sol()) { + if (!stream.eatSpace()) change(state, to_normal); + return null; + } - var mode = CodeMirror.getMode( - config, options.backdrop || 'rst-base' - ); + return state.ctx.mode.token(stream, state.ctx.local); + } - return CodeMirror.overlayMode(mode, overlay, true); // combine + change(state, to_normal); + return null; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + function context(phase, stage, mode, local) { + return {phase: phase, stage: stage, mode: mode, local: local}; + } + + function change(state, tok, ctx) { + state.tok = tok; + state.ctx = ctx || {}; + } + + function stage(state) { + return state.ctx.stage || 0; + } + + function phase(state) { + return state.ctx.phase; + } + + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + + return { + startState: function () { + return {tok: to_normal, ctx: context(undefined, 0)}; + }, + + copyState: function (state) { + var ctx = state.ctx, tmp = state.tmp; + if (ctx.local) + ctx = {mode: ctx.mode, local: CodeMirror.copyState(ctx.mode, ctx.local)}; + if (tmp) + tmp = {mode: tmp.mode, local: CodeMirror.copyState(tmp.mode, tmp.local)}; + return {tok: state.tok, ctx: ctx, tmp: tmp}; + }, + + innerMode: function (state) { + return state.tmp ? {state: state.tmp.local, mode: state.tmp.mode} + : state.ctx.mode ? {state: state.ctx.local, mode: state.ctx.mode} + : null; + }, + + token: function (stream, state) { + return state.tok(stream, state); + } + }; }, 'python', 'stex'); /////////////////////////////////////////////////////////////////////////////// diff --git a/test/vim_test.js b/test/vim_test.js index ee02dc09417ac60078b52c481bc86e051a924f69..8008f499fb8ce5347e21fda31ea56f78d5e626b4 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -997,6 +997,40 @@ testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t'); testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo'); testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t'); testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo'); +// Deleting text objects +// Open and close on same line +testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz'); +testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz'); +testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo baz'); +testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo baz'); + +testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz'); +testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz'); +testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo baz'); +testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo baz'); + +testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz'); +testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz'); +testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz'); +testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz'); + +// Open and close on different lines, equally indented +testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b'); +testEdit('di}_middle_spc', 'a{\n\tbar\n}b', /r/, 'di}', 'a{}b'); +testEdit('da{_middle_spc', 'a{\n\tbar\n}b', /r/, 'da{', 'ab'); +testEdit('da}_middle_spc', 'a{\n\tbar\n}b', /r/, 'da}', 'ab'); + +// open and close on diff lines, open indented less than close +testEdit('di{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di{', 'a{}b'); +testEdit('di}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di}', 'a{}b'); +testEdit('da{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da{', 'ab'); +testEdit('da}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da}', 'ab'); + +// open and close on diff lines, open indented more than close +testEdit('di[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di[', 'a\t[]b'); +testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b'); +testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb'); +testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb'); // Operator-motion tests testVim('D', function(cm, vim, helpers) {