diff --git a/AUTHORS b/AUTHORS index 741ba1e35e..ce2be8d6d2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -14,6 +14,7 @@ Ahmad Amireh Ahmad M. Zawawi ahoward Akeksandr Motsjonov +Alasdair Smith Alberto González Palomo Alberto Pose Albert Xing @@ -45,6 +46,7 @@ Andre von Houck Andrey Fedorov Andrey Klyuchnikov Andrey Lushnikov +Andrey Shchekin Andy Joslin Andy Kimball Andy Li @@ -75,8 +77,11 @@ benbro Beni Cherniavsky-Paskin Benjamin DeCoste Ben Keen +Ben Miller +Ben Mosher Bernhard Sirlinger Bert Chang +Bharad Billy Moon binny B Krishna Chaitanya @@ -93,6 +98,7 @@ Brian Sletten Bruce Mitchener Caitlin Potter Calin Barbat +Camilo Roca Chad Jolly Chandra Sekhar Pydi Charles Skelton @@ -103,6 +109,7 @@ Chris Granger Chris Houseknecht Chris Lohfink Chris Morgan +Chris Smith Christian Oyarzun Christian Petrov Christopher Brown @@ -134,9 +141,11 @@ Darius Roberts Dave Brondsema Dave Myers David Barnett +David H. Bronke David Mignot David Pathakjee David Vázquez +David Whittington deebugger Deep Thought Devin Abbott @@ -181,9 +190,11 @@ Gabriel Gheorghian Gabriel Horner Gabriel Nahmias galambalazs +Gary Sheng Gautam Mehta Gavin Douglas gekkoe +geowarin Gerard Braad Gergely Hegykozi Giovanni Calò @@ -196,18 +207,22 @@ Gordon Smith Grant Skinner greengiant Gregory Koberger +Grzegorz Mazur Guillaume Massé Guillaume Massé +guraga Gustavo Rodrigues Hakan Tunc Hans Engel Hardest +Harshvardhan Gupta Hasan Karahan Hector Oswaldo Caballero Herculano Campos Hiroyuki Makino hitsthings Hocdoc +Hugues Malphettes Ian Beck Ian Dickinson Ian Wehrman @@ -225,6 +240,7 @@ Jakob Miland Jakub Vrana Jakub Vrána James Campos +James Howard James Thorne Jamie Hill Jan Jongboom @@ -233,7 +249,9 @@ Jan Keromnes Jan Odvarko Jan Schär Jan T. Sott +Jared Dean Jared Forsyth +Jared Jacobs Jason Jason Barnabe Jason Grout @@ -243,6 +261,7 @@ Jason Siefken Jaydeep Solanki Jean Boussier Jeff Blaisdell +Jeff Jenkins jeffkenton Jeff Pickhardt jem (graphite) @@ -251,6 +270,7 @@ Jim JobJob jochenberger Jochen Berger +joelpinheiro Johan Ask John Connor John Engler @@ -266,6 +286,7 @@ Jon Sangster Joost-Wim Boekesteijn Joseph Pecoraro Josh Cohen +Josh Soref Joshua Newman Josh Watzman jots @@ -274,6 +295,7 @@ ju1ius Juan Benavides Romero Jucovschi Constantin Juho Vuori +Julien Rebetez Justin Andresen Justin Hileman jwallers@gmail.com @@ -281,6 +303,7 @@ kaniga karevn Kayur Patel Ken Newman +ken restivo Ken Rockot Kevin Earls Kevin Sawicki @@ -290,6 +313,7 @@ Koh Zi Han, Cliff komakino Konstantin Lopuhin koops +Kris Ciccarello ks-ifware kubelsmieci KwanEsq @@ -307,6 +331,7 @@ LM lochel Lorenzo Stoakes Luciano Longo +Lu Fangjian Luke Stagner lynschinzer M1cha @@ -314,6 +339,7 @@ Madhura Jayaratne Maksim Lin Maksym Taran Malay Majithia +Manideep Manuel Rego Casasnovas Marat Dreizin Marcel Gerber @@ -341,6 +367,7 @@ mats cronqvist Matt Gaide Matthew Bauer Matthew Beale +matthewhayes Matthew Rathbone Matthias Bussonnier Matthias BUSSONNIER @@ -419,6 +446,7 @@ Paul Garvin Paul Ivanov Pavel Pavel Feldman +Pavel Petržela Pavel Strashkin Paweł Bartkiewicz peteguhl @@ -426,7 +454,9 @@ peter Peter Flynn peterkroon Peter Kroon +Philipp A Philip Stadermann +Pierre Gerold Piët Delport prasanthj Prasanth J @@ -453,6 +483,7 @@ Ruslan Osmanov Ryan Prior sabaca Samuel Ainsworth +Sam Wilson sandeepshetty Sander AKA Redsandro santec @@ -466,6 +497,7 @@ Scott Aikin Scott Goodhew Sebastian Zaha Sergey Goder +Sergey Tselovalnikov Se-Won Kim shaund shaun gilchrist @@ -476,6 +508,7 @@ Shiv Deepak Shmuel Englard Shubham Jain silverwind +sinkuu snasa soliton4 sonson @@ -509,6 +542,7 @@ Thomas Schmid Tim Alby Tim Baumann Timothy Farrell +Timothy Gu Timothy Hatcher TobiasBg Tomas-A @@ -518,13 +552,16 @@ Tom MacWright Tony Jian Travis Heppe Triangle717 +Tristan Tarrant TSUYUSATO Kitsune twifkak +VapidWorx Vestimir Markov vf Victor Bocharsky Vincent Woo Volker Mische +Weiyan Shao wenli Wes Cossick Wesley Wiser diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eb70e9c53..a34cf82ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,130 @@ -## 5.12.0 (2015-02-19) +## 5.16.0 (2016-06-20) + +### Bugfixes + +Fix glitches when dragging content caused by the drop indicator receiving mouse events. + +Make Control-drag work on Firefox. + +Make clicking or selection-dragging at the end of a wrapped line select the right position. + +[show-hint addon](http://codemirror.net/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text. + +[rulers addon](http://codemirror.net/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar. + +### New features + +[search addon](http://codemirror.net/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog. + +[sublime keymap](http://codemirror.net/demo/sublime.html): Add a multi-cursor aware smart backspace binding. + +## 5.15.2 (2016-05-20) + +### Bugfixes + +Fix a critical document corruption bug that occurs when a document is gradually grown. + +## 5.15.0 (2016-05-20) + +### Bugfixes + +Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode. + +Fix issue where not all ASCII control characters were being replaced by placeholders. + +Remove the assumption that all modes have a `startState` method from several wrapping modes. + +Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any. + +Optimize document tree building when loading or pasting huge chunks of content. + +[markdown mode](http://codemirror.net/mode/markdown/): Fix several issues in matching link targets. + +[clike mode](http://codemirror.net/mode/clike/): Improve indentation of C++ template declarations. + +### New features + +Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected. + +Pasting [linewise-copied](http://codemirror.net/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line. + +[javascript mode](http://codemirror.net/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax. + +## 5.14.2 (2016-04-20) + +### Bugfixes + +Push a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0. + +Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon. + +Add the [mbox mode](http://codemirror.net/mode/mbox/index.html) to `mode/meta.js`. + +## 5.14.0 (2016-04-20) + +### Bugfixes + +[`posFromIndex`](http://codemirror.net/doc/manual.html#posFromIndex) and [`indexFromPos`](http://codemirror.net/doc/manual.html#indexFromPos) now take [`lineSeparator`](http://codemirror.net/doc/manual.html#option_lineSeparator) into account. + +[vim bindings](http://codemirror.net/demo/vim.html): Only call `.save()` when it is actually available. + +[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings. + +[Python mode](http://codemirror.net/mode/python/index.html): Improve distinguishing of decorators from `@` operators. + +[`findMarks`](http://codemirror.net/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range. + +### New features + +[vim bindings](http://codemirror.net/demo/vim.html): Add yank command. + +[match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace. + +[PowerShell mode](http://codemirror.net/mode/powershell/index.html): Added. + +[Yacas mode](http://codemirror.net/mode/yacas/index.html): Added. + +[Web IDL mode](http://codemirror.net/mode/webidl/index.html): Added. + +[SAS mode](http://codemirror.net/mode/sas/index.html): Added. + +[mbox mode](http://codemirror.net/mode/mbox/index.html): Added. + +## 5.13.2 (2016-03-23) + +### Bugfixes + +Solves a problem where the gutter would sometimes not extend all the way to the end of the document. + +## 5.13.0 (2016-03-21) + +### New features + +New DOM event forwarded: [`"dragleave"`](http://codemirror.net/doc/manual.html#event_dom). + +[protobuf mode](http://codemirror.net/mode/protobuf/index.html): Newly added. + +### Bugfixes + +Fix problem where [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks. + +Fix crash that showed up when atomic ranges and bidi text were combined. + +[show-hint addon](http://codemirror.net/demo/complete.html): Completion widgets no longer close when the line indented or dedented. + +[merge addon](http://codemirror.net/demo/merge.html): Fix bug when merging chunks at the end of the file. + +[placeholder addon](http://codemirror.net/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](http://codemirror.net/doc/manual.html#swapDoc). + +[simplescrollbars addon](http://codemirror.net/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document. + +[clike mode](http://codemirror.net/mode/clike/index.html): No longer gets confused when a comment starts after an operator. + +[markdown mode](http://codemirror.net/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation. + +[dylan mode](http://codemirror.net/mode/dylan/index.html): Several improvements and fixes. + +## 5.12.0 (2016-02-19) ### New features @@ -458,9 +584,9 @@ Integrate some bugfixes, enhancements to the vim keymap, and new modes ([D](http * New mode: [Common Lisp](http://codemirror.net/mode/commonlisp/index.html). * Fix right-click select-all on most browsers. -* Change the way highlighting happens: - Saves memory and CPU cycles. - `compareStates` is no longer needed. +* Change the way highlighting happens: + Saves memory and CPU cycles. + `compareStates` is no longer needed. `onHighlightComplete` no longer works. * Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite. * Add a [`CodeMirror.version`](http://codemirror.net/doc/manual.html#version) property. diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 3aa468089e..2c4f975d08 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -44,9 +44,17 @@ } }); + // Rough heuristic to try and detect lines that are part of multi-line string + function probablyInsideString(cm, pos, line) { + return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"`]/.test(line) + } + CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; var self = this, mode = self.getModeAt(from); + var firstLine = self.getLine(from.line); + if (firstLine == null || probablyInsideString(self, from, firstLine)) return; + var commentString = options.lineComment || mode.lineComment; if (!commentString) { if (options.blockCommentStart || mode.blockCommentStart) { @@ -55,8 +63,7 @@ } return; } - var firstLine = self.getLine(from.line); - if (firstLine == null) return; + var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); var pad = options.padding == null ? " " : options.padding; var blankLines = options.commentBlankLines || from.line == to.line; diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index babddfb1fe..2f8b1f84ae 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -14,10 +14,12 @@ if (val && !prev) { cm.on("blur", onBlur); cm.on("change", onChange); + cm.on("swapDoc", onChange); onChange(cm); } else if (!val && prev) { cm.off("blur", onBlur); cm.off("change", onChange); + cm.off("swapDoc", onChange); clearPlaceholder(cm); var wrapper = cm.getWrapperElement(); wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); diff --git a/addon/display/rulers.js b/addon/display/rulers.js index 01f65667c4..730054473a 100644 --- a/addon/display/rulers.js +++ b/addon/display/rulers.js @@ -11,30 +11,26 @@ })(function(CodeMirror) { "use strict"; - CodeMirror.defineOption("rulers", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) { - clearRulers(cm); - cm.off("refresh", refreshRulers); + CodeMirror.defineOption("rulers", false, function(cm, val) { + if (cm.state.rulerDiv) { + cm.display.lineSpace.removeChild(cm.state.rulerDiv) + cm.state.rulerDiv = null + cm.off("refresh", drawRulers) } if (val && val.length) { - setRulers(cm); - cm.on("refresh", refreshRulers); + cm.state.rulerDiv = cm.display.lineSpace.insertBefore(document.createElement("div"), cm.display.cursorDiv) + cm.state.rulerDiv.className = "CodeMirror-rulers" + drawRulers(cm) + cm.on("refresh", drawRulers) } }); - function clearRulers(cm) { - for (var i = cm.display.lineSpace.childNodes.length - 1; i >= 0; i--) { - var node = cm.display.lineSpace.childNodes[i]; - if (/(^|\s)CodeMirror-ruler($|\s)/.test(node.className)) - node.parentNode.removeChild(node); - } - } - - function setRulers(cm) { + function drawRulers(cm) { + cm.state.rulerDiv.textContent = "" var val = cm.getOption("rulers"); var cw = cm.defaultCharWidth(); var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; - var minH = cm.display.scroller.offsetHeight + 30; + cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px"; for (var i = 0; i < val.length; i++) { var elt = document.createElement("div"); elt.className = "CodeMirror-ruler"; @@ -49,15 +45,7 @@ if (conf.width) elt.style.borderLeftWidth = conf.width; } elt.style.left = (left + col * cw) + "px"; - elt.style.top = "-50px"; - elt.style.bottom = "-20px"; - elt.style.minHeight = minH + "px"; - cm.display.lineSpace.insertBefore(elt, cm.display.cursorDiv); + cm.state.rulerDiv.appendChild(elt) } } - - function refreshRulers(cm) { - clearRulers(cm); - setRulers(cm); - } }); diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 3eb9d8eae7..af7fce2a82 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -109,7 +109,7 @@ var ranges = cm.listSelections(); var opening = pos % 2 == 0; - var type, next; + var type; for (var i = 0; i < ranges.length; i++) { var range = ranges[i], cur = range.head, curType; var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index 1605f6c2a9..13c0f0cd88 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -13,7 +13,7 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { var line = start.line, lineText = cm.getLine(line); - var startCh, tokenType; + var tokenType; function findOpening(openCh) { for (var at = start.ch, pass = 0;;) { @@ -72,15 +72,15 @@ CodeMirror.registerHelper("fold", "import", function(cm, start) { } } - var start = start.line, has = hasImport(start), prev; - if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1)) + var startLine = start.line, has = hasImport(startLine), prev; + if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1)) return null; for (var end = has.end;;) { var next = hasImport(end.line + 1); if (next == null) break; end = next.end; } - return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end}; + return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end}; }); CodeMirror.registerHelper("fold", "include", function(cm, start) { @@ -91,14 +91,14 @@ CodeMirror.registerHelper("fold", "include", function(cm, start) { if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8; } - var start = start.line, has = hasInclude(start); - if (has == null || hasInclude(start - 1) != null) return null; - for (var end = start;;) { + var startLine = start.line, has = hasInclude(startLine); + if (has == null || hasInclude(startLine - 1) != null) return null; + for (var end = startLine;;) { var next = hasInclude(end + 1); if (next == null) break; ++end; } - return {from: CodeMirror.Pos(start, has + 1), + return {from: CodeMirror.Pos(startLine, has + 1), to: cm.clipPos(CodeMirror.Pos(end))}; }); diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js index 60fa3e43df..e8d800eb59 100644 --- a/addon/fold/comment-fold.js +++ b/addon/fold/comment-fold.js @@ -29,7 +29,7 @@ CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { } if (pass == 1 && found < start.ch) return; if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) && - (lineText.slice(found - endToken.length, found) == endToken || + (found == 0 || lineText.slice(found - endToken.length, found) == endToken || !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) { startCh = found + startToken.length; break; diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 62911f9352..78b36c4641 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -49,7 +49,7 @@ }); var myRange = cm.markText(range.from, range.to, { replacedWith: myWidget, - clearOnEnter: true, + clearOnEnter: getOption(cm, options, "clearOnEnter"), __isFold: true }); myRange.on("clear", function(from, to) { @@ -129,7 +129,8 @@ rangeFinder: CodeMirror.fold.auto, widget: "\u2194", minFoldSize: 0, - scanUp: false + scanUp: false, + clearOnEnter: true }; CodeMirror.defineOption("foldOptions", null); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index f101e26709..9d32326566 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -50,7 +50,7 @@ } function isFolded(cm, line) { - var marks = cm.findMarksAt(Pos(line)); + var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); for (var i = 0; i < marks.length; ++i) if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i]; } diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 504727f38c..f8c67b8976 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -140,9 +140,9 @@ var openTag = toNextTag(iter), end; if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return; if (!openTag[1] && end != "selfClose") { - var start = Pos(iter.line, iter.ch); - var close = findMatchingClose(iter, openTag[2]); - return close && {from: start, to: close.from}; + var startPos = Pos(iter.line, iter.ch); + var endPos = findMatchingClose(iter, openTag[2]); + return endPos && {from: startPos, to: endPos.from}; } } }); diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css index 924e638f7f..5617ccca2b 100644 --- a/addon/hint/show-hint.css +++ b/addon/hint/show-hint.css @@ -25,8 +25,6 @@ margin: 0; padding: 0 4px; border-radius: 2px; - max-width: 19em; - overflow: hidden; white-space: pre; color: black; cursor: pointer; diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index cbe3b39a30..604bd3b715 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -108,24 +108,22 @@ }, update: function(first) { - if (this.tick == null) return; - if (!this.options.hint.async) { - this.finishUpdate(this.options.hint(this.cm, this.options), first); - } else { - var myTick = ++this.tick, self = this; - this.options.hint(this.cm, function(data) { - if (self.tick == myTick) self.finishUpdate(data, first); - }, this.options); - } + if (this.tick == null) return + var self = this, myTick = ++this.tick + fetchHints(this.options.hint, this.cm, this.options, function(data) { + if (self.tick == myTick) self.finishUpdate(data, first) + }) }, finishUpdate: function(data, first) { if (this.data) CodeMirror.signal(this.data, "update"); - if (data && this.data && CodeMirror.cmpPos(data.from, this.data.from)) data = null; - this.data = data; var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); if (this.widget) this.widget.close(); + + if (data && this.data && isNewCompletion(this.data, data)) return; + this.data = data; + if (data && data.list.length) { if (picked && data.list.length == 1) { this.pick(data, 0); @@ -137,6 +135,11 @@ } }; + function isNewCompletion(old, nw) { + var moved = CodeMirror.cmpPos(nw.from, old.from) + return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch + } + function parseOptions(cm, pos, options) { var editor = cm.options.hintOptions; var out = {}; @@ -226,6 +229,9 @@ var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); (completion.options.container || document.body).appendChild(hints); var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; + var scrolls = hints.scrollHeight > hints.clientHeight + 1 + var startScroll = cm.getScrollInfo(); + if (overlapY > 0) { var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); if (curTop - height > 0) { // Fits above cursor @@ -250,6 +256,8 @@ } hints.style.left = (left = pos.left - overlapX) + "px"; } + if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) + node.style.paddingRight = cm.display.nativeBarWidth + "px" cm.addKeyMap(this.keyMap = buildKeyMap(completion, { moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, @@ -267,7 +275,6 @@ cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); } - var startScroll = cm.getScrollInfo(); cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); var newTop = top + startScroll.top - curScroll.top; @@ -355,40 +362,31 @@ return result } + function fetchHints(hint, cm, options, callback) { + if (hint.async) { + hint(cm, callback, options) + } else { + var result = hint(cm, options) + if (result && result.then) result.then(callback) + else callback(result) + } + } + function resolveAutoHints(cm, pos) { var helpers = cm.getHelpers(pos, "hint"), words if (helpers.length) { - var async = false, resolved - for (var i = 0; i < helpers.length; i++) if (helpers[i].async) async = true - if (async) { - resolved = function(cm, callback, options) { - var app = applicableHelpers(cm, helpers) - function run(i, result) { - if (i == app.length) return callback(null) - var helper = app[i] - if (helper.async) { - helper(cm, function(result) { - if (result) callback(result) - else run(i + 1) - }, options) - } else { - var result = helper(cm, options) - if (result) callback(result) - else run(i + 1) - } - } - run(0) - } - resolved.async = true - } else { - resolved = function(cm, options) { - var app = applicableHelpers(cm, helpers) - for (var i = 0; i < app.length; i++) { - var cur = app[i](cm, options) - if (cur && cur.list.length) return cur - } + var resolved = function(cm, callback, options) { + var app = applicableHelpers(cm, helpers); + function run(i) { + if (i == app.length) return callback(null) + fetchHints(app[i], cm, options, function(result) { + if (result && result.list.length > 0) callback(result) + else run(i + 1) + }) } + run(0) } + resolved.async = true resolved.supportsSelection = true return resolved } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index 22124b58f7..1ee4f0bbed 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -18,7 +18,9 @@ QUERY_DIV: ";", ALIAS_KEYWORD: "AS" }; - var Pos = CodeMirror.Pos; + var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos; + + function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" } function getKeywords(editor) { var mode = editor.doc.modeOption; @@ -30,10 +32,28 @@ return typeof item == "string" ? item : item.text; } - function getItem(list, item) { - if (!list.slice) return list[item]; - for (var i = list.length - 1; i >= 0; i--) if (getText(list[i]) == item) - return list[i]; + function wrapTable(name, value) { + if (isArray(value)) value = {columns: value} + if (!value.text) value.text = name + return value + } + + function parseTables(input) { + var result = {} + if (isArray(input)) { + for (var i = input.length - 1; i >= 0; i--) { + var item = input[i] + result[getText(item).toUpperCase()] = wrapTable(getText(item), item) + } + } else if (input) { + for (var name in input) + result[name.toUpperCase()] = wrapTable(name, input[name]) + } + return result + } + + function getTable(name) { + return tables[name.toUpperCase()] } function shallowClone(object) { @@ -50,11 +70,18 @@ } function addMatches(result, search, wordlist, formatter) { - for (var word in wordlist) { - if (!wordlist.hasOwnProperty(word)) continue; - if (wordlist.slice) word = wordlist[word]; - - if (match(search, word)) result.push(formatter(word)); + if (isArray(wordlist)) { + for (var i = 0; i < wordlist.length; i++) + if (match(search, wordlist[i])) result.push(formatter(wordlist[i])) + } else { + for (var word in wordlist) if (wordlist.hasOwnProperty(word)) { + var val = wordlist[word] + if (!val || val === true) + val = word + else + val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text + if (match(search, val)) result.push(formatter(val)) + } } } @@ -78,7 +105,7 @@ } function nameCompletion(cur, token, result, editor) { - // Try to complete table, colunm names and return start position of completion + // Try to complete table, column names and return start position of completion var useBacktick = false; var nameParts = []; var start = token.start; @@ -115,13 +142,13 @@ var alias = false; var aliasTable = table; // Check if table is available. If not, find table by Alias - if (!getItem(tables, table)) { + if (!getTable(table)) { var oldTable = table; table = findTableByAlias(table, editor); if (table !== oldTable) alias = true; } - var columns = getItem(tables, table); + var columns = getTable(table); if (columns && columns.columns) columns = columns.columns; @@ -151,15 +178,6 @@ } } - function convertCurToNumber(cur) { - // max characters of a line is 999,999. - return cur.line + cur.ch / Math.pow(10, 6); - } - - function convertNumberToCur(num) { - return Pos(Math.floor(num), +num.toString().split('.').pop()); - } - function findTableByAlias(alias, editor) { var doc = editor.doc; var fullQuery = doc.getValue(); @@ -182,15 +200,14 @@ separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length)); //find valid range - var prevItem = 0; - var current = convertCurToNumber(editor.getCursor()); - for (var i=0; i< separator.length; i++) { - var _v = convertCurToNumber(separator[i]); - if (current > prevItem && current <= _v) { - validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) }; + var prevItem = null; + var current = editor.getCursor() + for (var i = 0; i < separator.length; i++) { + if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) { + validRange = {start: prevItem, end: separator[i]}; break; } - prevItem = _v; + prevItem = separator[i]; } var query = doc.getRange(validRange.start, validRange.end, false); @@ -199,7 +216,7 @@ var lineText = query[i]; eachWord(lineText, function(word) { var wordUpperCase = word.toUpperCase(); - if (wordUpperCase === aliasUpperCase && getItem(tables, previousWord)) + if (wordUpperCase === aliasUpperCase && getTable(previousWord)) table = previousWord; if (wordUpperCase !== CONS.ALIAS_KEYWORD) previousWord = word; @@ -210,11 +227,11 @@ } CodeMirror.registerHelper("hint", "sql", function(editor, options) { - tables = (options && options.tables) || {}; + tables = parseTables(options && options.tables) var defaultTableName = options && options.defaultTable; var disableKeywords = options && options.disableKeywords; - defaultTable = defaultTableName && getItem(tables, defaultTableName); - keywords = keywords || getKeywords(editor); + defaultTable = defaultTableName && getTable(defaultTableName); + keywords = getKeywords(editor); if (defaultTableName && !defaultTable) defaultTable = findTableByAlias(defaultTableName, editor); diff --git a/addon/lint/lint.css b/addon/lint/lint.css index 414a9a0e06..f097cfe345 100644 --- a/addon/lint/lint.css +++ b/addon/lint/lint.css @@ -4,10 +4,10 @@ } .CodeMirror-lint-tooltip { - background-color: infobackground; + background-color: #ffd; border: 1px solid black; border-radius: 4px 4px 4px 4px; - color: infotext; + color: black; font-family: monospace; font-size: 10pt; overflow: hidden; diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 5afe49d0fc..e3a452766d 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -186,9 +186,14 @@ state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); } - function popupSpanTooltip(ann, e) { + function popupTooltips(annotations, e) { var target = e.target || e.srcElement; - showTooltipFor(e, annotationTooltip(ann), target); + var tooltip = document.createDocumentFragment(); + for (var i = 0; i < annotations.length; i++) { + var ann = annotations[i]; + tooltip.appendChild(annotationTooltip(ann)); + } + showTooltipFor(e, tooltip, target); } function onMouseOver(cm, e) { @@ -196,10 +201,13 @@ if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); + + var annotations = []; for (var i = 0; i < spans.length; ++i) { var ann = spans[i].__annotation; - if (ann) return popupSpanTooltip(ann, e); + if (ann) annotations.push(ann); } + if (annotations.length) popupTooltips(annotations, e); } CodeMirror.defineOption("lint", false, function(cm, val, old) { diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 129fafff56..d67b760cae 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -427,9 +427,9 @@ function copyChunk(dv, to, from, chunk) { if (dv.diffOutOfDate) return; - var start = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) - to.replaceRange(from.getRange(Pos(chunk.origFrom, 0), Pos(chunk.origTo, 0)), - start, Pos(chunk.editTo, 0)); + var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) + var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0) + to.replaceRange(from.getRange(origStart, Pos(chunk.origTo, 0)), editStart, Pos(chunk.editTo, 0)) } // Merge view, containing 0, 1, or 2 diff views. diff --git a/addon/scroll/scrollpastend.js b/addon/scroll/scrollpastend.js index 008ae4c7ba..a2ed089b48 100644 --- a/addon/scroll/scrollpastend.js +++ b/addon/scroll/scrollpastend.js @@ -40,7 +40,9 @@ if (cm.state.scrollPastEndPadding != padding) { cm.state.scrollPastEndPadding = padding; cm.display.lineSpace.parentNode.style.paddingBottom = padding; + cm.off("refresh", updateBottomMargin); cm.setSize(); + cm.on("refresh", updateBottomMargin); } } }); diff --git a/addon/scroll/simplescrollbars.js b/addon/scroll/simplescrollbars.js index f78353a130..23f3e03f81 100644 --- a/addon/scroll/simplescrollbars.js +++ b/addon/scroll/simplescrollbars.js @@ -59,22 +59,29 @@ CodeMirror.on(this.node, "DOMMouseScroll", onWheel); } - Bar.prototype.moveTo = function(pos, update) { + Bar.prototype.setPos = function(pos, force) { if (pos < 0) pos = 0; if (pos > this.total - this.screen) pos = this.total - this.screen; - if (pos == this.pos) return; + if (!force && pos == this.pos) return false; this.pos = pos; this.inner.style[this.orientation == "horizontal" ? "left" : "top"] = (pos * (this.size / this.total)) + "px"; - if (update !== false) this.scroll(pos, this.orientation); + return true }; + Bar.prototype.moveTo = function(pos) { + if (this.setPos(pos)) this.scroll(pos, this.orientation); + } + var minButtonSize = 10; Bar.prototype.update = function(scrollSize, clientSize, barSize) { - this.screen = clientSize; - this.total = scrollSize; - this.size = barSize; + var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize + if (sizeChanged) { + this.screen = clientSize; + this.total = scrollSize; + this.size = barSize; + } var buttonSize = this.screen * (this.size / this.total); if (buttonSize < minButtonSize) { @@ -83,8 +90,7 @@ } this.inner.style[this.orientation == "horizontal" ? "width" : "height"] = buttonSize + "px"; - this.inner.style[this.orientation == "horizontal" ? "left" : "top"] = - this.pos * (this.size / this.total) + "px"; + this.setPos(this.pos, sizeChanged); }; function SimpleScrollbars(cls, place, scroll) { @@ -111,7 +117,6 @@ if (needsV) { this.vert.update(measure.scrollHeight, measure.clientHeight, measure.viewHeight - (needsH ? width : 0)); - this.vert.node.style.display = "block"; this.vert.node.style.bottom = needsH ? width + "px" : "0"; } if (needsH) { @@ -125,11 +130,11 @@ }; SimpleScrollbars.prototype.setScrollTop = function(pos) { - this.vert.moveTo(pos, false); + this.vert.setPos(pos); }; SimpleScrollbars.prototype.setScrollLeft = function(pos) { - this.horiz.moveTo(pos, false); + this.horiz.setPos(pos); }; SimpleScrollbars.prototype.clear = function() { diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 8f02f01c87..2c2914a9d0 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -16,7 +16,7 @@ // highlighted only if the selected text is a word. showToken, when enabled, // will cause the current token to be highlighted when nothing is selected. // delay is used to specify how much time to wait, in milliseconds, before -// highlighting the matches. If annotateScrollbar is enabled, the occurances +// highlighting the matches. If annotateScrollbar is enabled, the occurences // will be highlighted on the scrollbar via the matchesonscrollbar addon. (function(mod) { @@ -29,24 +29,20 @@ })(function(CodeMirror) { "use strict"; - var DEFAULT_MIN_CHARS = 2; - var DEFAULT_TOKEN_STYLE = "matchhighlight"; - var DEFAULT_DELAY = 100; - var DEFAULT_WORDS_ONLY = false; + var defaults = { + style: "matchhighlight", + minChars: 2, + delay: 100, + wordsOnly: false, + annotateScrollbar: false, + showToken: false, + trim: true + } function State(options) { - if (typeof options == "object") { - this.minChars = options.minChars; - this.style = options.style; - this.showToken = options.showToken; - this.delay = options.delay; - this.wordsOnly = options.wordsOnly; - this.annotateScrollbar = options.annotateScrollbar; - } - if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; - if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; - if (this.delay == null) this.delay = DEFAULT_DELAY; - if (this.wordsOnly == null) this.wordsOnly = DEFAULT_WORDS_ONLY; + this.options = {} + for (var name in defaults) + this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name] this.overlay = this.timeout = null; this.matchesonscroll = null; } @@ -68,13 +64,13 @@ function cursorActivity(cm) { var state = cm.state.matchHighlighter; clearTimeout(state.timeout); - state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay); } function addOverlay(cm, query, hasBoundary, style) { var state = cm.state.matchHighlighter; cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); - if (state.annotateScrollbar) { + if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query; state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, true, {className: "CodeMirror-selection-highlight-scrollbar"}); @@ -86,7 +82,7 @@ if (state.overlay) { cm.removeOverlay(state.overlay); state.overlay = null; - if (state.annotateScrollbar) { + if (state.matchesonscroll) { state.matchesonscroll.clear(); state.matchesonscroll = null; } @@ -97,21 +93,22 @@ cm.operation(function() { var state = cm.state.matchHighlighter; removeOverlay(cm); - if (!cm.somethingSelected() && state.showToken) { - var re = state.showToken === true ? /[\w$]/ : state.showToken; + if (!cm.somethingSelected() && state.options.showToken) { + var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken; var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; while (start && re.test(line.charAt(start - 1))) --start; while (end < line.length && re.test(line.charAt(end))) ++end; if (start < end) - addOverlay(cm, line.slice(start, end), re, state.style); + addOverlay(cm, line.slice(start, end), re, state.options.style); return; } var from = cm.getCursor("from"), to = cm.getCursor("to"); if (from.line != to.line) return; - if (state.wordsOnly && !isWord(cm, from, to)) return; - var selection = cm.getRange(from, to).replace(/^\s+|\s+$/g, ""); - if (selection.length >= state.minChars) - addOverlay(cm, selection, false, state.style); + if (state.options.wordsOnly && !isWord(cm, from, to)) return; + var selection = cm.getRange(from, to) + if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "") + if (selection.length >= state.options.minChars) + addOverlay(cm, selection, false, state.options.style); }); } diff --git a/addon/search/search.js b/addon/search/search.js index 93e90b36ed..c005866f16 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -57,12 +57,13 @@ return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); } - function persistentDialog(cm, text, deflt, f) { - cm.openDialog(text, f, { + function persistentDialog(cm, text, deflt, onEnter, onKeyDown) { + cm.openDialog(text, onEnter, { value: deflt, selectValueOnOpen: true, closeOnEnter: false, - onClose: function() { clearSearch(cm); } + onClose: function() { clearSearch(cm); }, + onKeyDown: onKeyDown }); } @@ -112,16 +113,19 @@ } } - function doSearch(cm, rev, persistent) { + function doSearch(cm, rev, persistent, immediate) { var state = getSearchState(cm); if (state.query) return findNext(cm, rev); var q = cm.getSelection() || state.lastQuery; if (persistent && cm.openDialog) { var hiding = null - persistentDialog(cm, queryDialog, q, function(query, event) { + var searchNext = function(query, event) { CodeMirror.e_stop(event); if (!query) return; - if (query != state.queryText) startSearch(cm, state, query); + if (query != state.queryText) { + startSearch(cm, state, query); + state.posFrom = state.posTo = cm.getCursor(); + } if (hiding) hiding.style.opacity = 1 findNext(cm, event.shiftKey, function(_, to) { var dialog @@ -130,7 +134,22 @@ dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top) (hiding = dialog).style.opacity = .4 }) + }; + persistentDialog(cm, queryDialog, q, searchNext, function(event, query) { + var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][CodeMirror.keyName(event)]; + if (cmd == "findNext" || cmd == "findPrev") { + CodeMirror.e_stop(event); + startSearch(cm, getSearchState(cm), query); + cm.execCommand(cmd); + } else if (cmd == "find" || cmd == "findPersistent") { + CodeMirror.e_stop(event); + searchNext(query, event); + } }); + if (immediate) { + startSearch(cm, state, q); + findNext(cm, rev); + } } else { dialog(cm, queryDialog, "Search for:", q, function(query) { if (query && !state.query) cm.operation(function() { @@ -193,7 +212,7 @@ replaceAll(cm, query, text) } else { clearSearch(cm); - var cursor = getSearchCursor(cm, query, cm.getCursor()); + var cursor = getSearchCursor(cm, query, cm.getCursor("from")); var advance = function() { var start = cursor.from(), match; if (!(match = cursor.findNext())) { @@ -220,6 +239,8 @@ CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);}; + CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);}; + CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);}; CodeMirror.commands.findNext = doSearch; CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; CodeMirror.commands.clearSearch = clearSearch; diff --git a/addon/tern/tern.js b/addon/tern/tern.js index c345c49781..efdf2ed628 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -179,7 +179,7 @@ var data = findDoc(ts, doc); var argHints = ts.cachedArgHints; - if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0) + if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0) ts.cachedArgHints = null; var changed = data.changed; @@ -306,7 +306,7 @@ ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { if (error || !data.type || !(/^fn\(/).test(data.type)) return; ts.cachedArgHints = { - start: pos, + start: start, type: parseFnType(data.type), name: data.exprName || data.name || "fn", guess: data.guess, diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js index 8806fbe2f2..04851f99ff 100644 --- a/addon/wrap/hardwrap.js +++ b/addon/wrap/hardwrap.js @@ -30,7 +30,9 @@ } function findBreakPoint(text, column, wrapOn, killTrailingSpace) { - for (var at = column; at > 0; --at) + var at = column + while (at < text.length && text.charAt(at) == " ") at++ + for (; at > 0; --at) if (wrapOn.test(text.slice(at - 1, at + 1))) break; for (var first = true;; first = false) { var endOfText = at; diff --git a/bin/compress b/bin/compress index 809fbe83d4..d358f9c3a0 100755 --- a/bin/compress +++ b/bin/compress @@ -18,7 +18,7 @@ // Script files are specified without .js ending. Prefixing them with // their full (local) path is optional. So you may say lib/codemirror // or mode/xml/xml to be more precise. In fact, even the .js suffix -// may be speficied, if wanted. +// may be specified, if wanted. "use strict"; @@ -68,7 +68,7 @@ walk("mode/"); if (!local && !blob) help(false); if (files.length) { - console.log("Some speficied files were not found: " + + console.log("Some specified files were not found: " + files.map(function(a){return a.name;}).join(", ")); process.exit(1); } diff --git a/bin/release b/bin/release index b782739fa8..18c5cdafc7 100755 --- a/bin/release +++ b/bin/release @@ -32,14 +32,12 @@ if (bumpOnly) process.exit(0); child.exec("bash bin/authors.sh", function(){}); -var simple = number.slice(0, number.lastIndexOf(".")); - rewrite("doc/compress.html", function(cmp) { return cmp.replace(/\n "); + "\n "); }); rewrite("index.html", function(index) { - return index.replace(/\.zip">\d+\.\d+<\/a>/, - ".zip\">" + simple + ""); + return index.replace(/\.zip">\d+\.\d+\.\d+<\/a>/, + ".zip\">" + number + ""); }); diff --git a/bin/upload-release.js b/bin/upload-release.js new file mode 100644 index 0000000000..7c8551468c --- /dev/null +++ b/bin/upload-release.js @@ -0,0 +1,35 @@ +"use strict" + +let version = process.argv[2] +let auth = process.argv[3] + +if (!auth) { + console.log("Usage: upload-release.js [TAG] [github-user:password]") + process.exit(1) +} + +require('child_process').exec("git --no-pager show -s --format='%s' " + version, (error, stdout) => { + if (error) throw error + let message = stdout.split("\n").slice(2) + message = message.slice(0, message.indexOf("-----BEGIN PGP SIGNATURE-----")).join("\n") + + let req = require("https").request({ + host: "api.github.com", + auth: auth, + headers: {"user-agent": "Release uploader"}, + path: "/repos/codemirror/codemirror/releases", + method: "POST" + }, res => { + if (res.statusCode >= 300) { + console.error(res.statusMessage) + res.on("data", d => console.log(d.toString())) + res.on("end", process.exit(1)) + } + }) + req.write(JSON.stringify({ + tag_name: version, + name: version, + body: message + })) + req.end() +}) diff --git a/demo/complete.html b/demo/complete.html index cdf49dbeb9..18e17c8126 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -10,6 +10,7 @@ + + + + + + +
+

PowerShell mode

+ +
+ + +

MIME types defined: application/x-powershell.

+
+ + diff --git a/mode/powershell/powershell.js b/mode/powershell/powershell.js new file mode 100644 index 0000000000..c443e7232d --- /dev/null +++ b/mode/powershell/powershell.js @@ -0,0 +1,396 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + 'use strict'; + if (typeof exports == 'object' && typeof module == 'object') // CommonJS + mod(require('codemirror')); + else if (typeof define == 'function' && define.amd) // AMD + define(['codemirror'], mod); + else // Plain browser env + mod(window.CodeMirror); +})(function(CodeMirror) { +'use strict'; + +CodeMirror.defineMode('powershell', function() { + function buildRegexp(patterns, options) { + options = options || {}; + var prefix = options.prefix !== undefined ? options.prefix : '^'; + var suffix = options.suffix !== undefined ? options.suffix : '\\b'; + + for (var i = 0; i < patterns.length; i++) { + if (patterns[i] instanceof RegExp) { + patterns[i] = patterns[i].source; + } + else { + patterns[i] = patterns[i].replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + } + + return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i'); + } + + var notCharacterOrDash = '(?=[^A-Za-z\\d\\-_]|$)'; + var varNames = /[\w\-:]/ + var keywords = buildRegexp([ + /begin|break|catch|continue|data|default|do|dynamicparam/, + /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/, + /param|process|return|switch|throw|trap|try|until|where|while/ + ], { suffix: notCharacterOrDash }); + + var punctuation = /[\[\]{},;`\.]|@[({]/; + var wordOperators = buildRegexp([ + 'f', + /b?not/, + /[ic]?split/, 'join', + /is(not)?/, 'as', + /[ic]?(eq|ne|[gl][te])/, + /[ic]?(not)?(like|match|contains)/, + /[ic]?replace/, + /b?(and|or|xor)/ + ], { prefix: '-' }); + var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=!|\/]|<(?!#)|(?!#)>/; + var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' }); + + var numbers = /^((0x[\da-f]+)|((\d+\.\d+|\d\.|\.\d+|\d+)(e[\+\-]?\d+)?))[ld]?([kmgtp]b)?/i; + + var identifiers = /^[A-Za-z\_][A-Za-z\-\_\d]*\b/; + + var symbolBuiltins = /[A-Z]:|%|\?/i; + var namedBuiltins = buildRegexp([ + /Add-(Computer|Content|History|Member|PSSnapin|Type)/, + /Checkpoint-Computer/, + /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/, + /Compare-Object/, + /Complete-Transaction/, + /Connect-PSSession/, + /ConvertFrom-(Csv|Json|SecureString|StringData)/, + /Convert-Path/, + /ConvertTo-(Csv|Html|Json|SecureString|Xml)/, + /Copy-Item(Property)?/, + /Debug-Process/, + /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, + /Disconnect-PSSession/, + /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, + /(Enter|Exit)-PSSession/, + /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/, + /ForEach-Object/, + /Format-(Custom|List|Table|Wide)/, + new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential' + + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job' + + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration' + + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'), + /Group-Object/, + /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/, + /ImportSystemModules/, + /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/, + /Join-Path/, + /Limit-EventLog/, + /Measure-(Command|Object)/, + /Move-Item(Property)?/, + new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile' + + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'), + /Out-(Default|File|GridView|Host|Null|Printer|String)/, + /Pause/, + /(Pop|Push)-Location/, + /Read-Host/, + /Receive-(Job|PSSession)/, + /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/, + /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/, + /Rename-(Computer|Item(Property)?)/, + /Reset-ComputerMachinePassword/, + /Resolve-Path/, + /Restart-(Computer|Service)/, + /Restore-Computer/, + /Resume-(Job|Service)/, + /Save-Help/, + /Select-(Object|String|Xml)/, + /Send-MailMessage/, + new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' + + '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'), + /Show-(Command|ControlPanelItem|EventLog)/, + /Sort-Object/, + /Split-Path/, + /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/, + /Stop-(Computer|Job|Process|Service|Transcript)/, + /Suspend-(Job|Service)/, + /TabExpansion2/, + /Tee-Object/, + /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/, + /Trace-Command/, + /Unblock-File/, + /Undo-Transaction/, + /Unregister-(Event|PSSessionConfiguration)/, + /Update-(FormatData|Help|List|TypeData)/, + /Use-Transaction/, + /Wait-(Event|Job|Process)/, + /Where-Object/, + /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/, + /cd|help|mkdir|more|oss|prompt/, + /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/, + /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/, + /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/, + /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/, + /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/, + /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/ + ], { prefix: '', suffix: '' }); + var variableBuiltins = buildRegexp([ + /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/, + /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/, + /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/, + /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/, + /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/, + /WarningPreference|WhatIfPreference/, + + /Event|EventArgs|EventSubscriber|Sender/, + /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/, + /true|false|null/ + ], { prefix: '\\$', suffix: '' }); + + var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash }); + + var grammar = { + keyword: keywords, + number: numbers, + operator: operators, + builtin: builtins, + punctuation: punctuation, + identifier: identifiers + }; + + // tokenizers + function tokenBase(stream, state) { + // Handle Comments + //var ch = stream.peek(); + + var parent = state.returnStack[state.returnStack.length - 1]; + if (parent && parent.shouldReturnFrom(state)) { + state.tokenize = parent.tokenize; + state.returnStack.pop(); + return state.tokenize(stream, state); + } + + if (stream.eatSpace()) { + return null; + } + + if (stream.eat('(')) { + state.bracketNesting += 1; + return 'punctuation'; + } + + if (stream.eat(')')) { + state.bracketNesting -= 1; + return 'punctuation'; + } + + for (var key in grammar) { + if (stream.match(grammar[key])) { + return key; + } + } + + var ch = stream.next(); + + // single-quote string + if (ch === "'") { + return tokenSingleQuoteString(stream, state); + } + + if (ch === '$') { + return tokenVariable(stream, state); + } + + // double-quote string + if (ch === '"') { + return tokenDoubleQuoteString(stream, state); + } + + if (ch === '<' && stream.eat('#')) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + + if (ch === '#') { + stream.skipToEnd(); + return 'comment'; + } + + if (ch === '@') { + var quoteMatch = stream.eat(/["']/); + if (quoteMatch && stream.eol()) { + state.tokenize = tokenMultiString; + state.startQuote = quoteMatch[0]; + return tokenMultiString(stream, state); + } else if (stream.peek().match(/[({]/)) { + return 'punctuation'; + } else if (stream.peek().match(varNames)) { + // splatted variable + return tokenVariable(stream, state); + } + } + return 'error'; + } + + function tokenSingleQuoteString(stream, state) { + var ch; + while ((ch = stream.peek()) != null) { + stream.next(); + + if (ch === "'" && !stream.eat("'")) { + state.tokenize = tokenBase; + return 'string'; + } + } + + return 'error'; + } + + function tokenDoubleQuoteString(stream, state) { + var ch; + while ((ch = stream.peek()) != null) { + if (ch === '$') { + state.tokenize = tokenStringInterpolation; + return 'string'; + } + + stream.next(); + if (ch === '`') { + stream.next(); + continue; + } + + if (ch === '"' && !stream.eat('"')) { + state.tokenize = tokenBase; + return 'string'; + } + } + + return 'error'; + } + + function tokenStringInterpolation(stream, state) { + return tokenInterpolation(stream, state, tokenDoubleQuoteString); + } + + function tokenMultiStringReturn(stream, state) { + state.tokenize = tokenMultiString; + state.startQuote = '"' + return tokenMultiString(stream, state); + } + + function tokenHereStringInterpolation(stream, state) { + return tokenInterpolation(stream, state, tokenMultiStringReturn); + } + + function tokenInterpolation(stream, state, parentTokenize) { + if (stream.match('$(')) { + var savedBracketNesting = state.bracketNesting; + state.returnStack.push({ + /*jshint loopfunc:true */ + shouldReturnFrom: function(state) { + return state.bracketNesting === savedBracketNesting; + }, + tokenize: parentTokenize + }); + state.tokenize = tokenBase; + state.bracketNesting += 1; + return 'punctuation'; + } else { + stream.next(); + state.returnStack.push({ + shouldReturnFrom: function() { return true; }, + tokenize: parentTokenize + }); + state.tokenize = tokenVariable; + return state.tokenize(stream, state); + } + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == '>') { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch === '#'); + } + return 'comment'; + } + + function tokenVariable(stream, state) { + var ch = stream.peek(); + if (stream.eat('{')) { + state.tokenize = tokenVariableWithBraces; + return tokenVariableWithBraces(stream, state); + } else if (ch != undefined && ch.match(varNames)) { + stream.eatWhile(varNames); + state.tokenize = tokenBase; + return 'variable-2'; + } else { + state.tokenize = tokenBase; + return 'error'; + } + } + + function tokenVariableWithBraces(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch === '}') { + state.tokenize = tokenBase; + break; + } + } + return 'variable-2'; + } + + function tokenMultiString(stream, state) { + var quote = state.startQuote; + if (stream.sol() && stream.match(new RegExp(quote + '@'))) { + state.tokenize = tokenBase; + } + else if (quote === '"') { + while (!stream.eol()) { + var ch = stream.peek(); + if (ch === '$') { + state.tokenize = tokenHereStringInterpolation; + return 'string'; + } + + stream.next(); + if (ch === '`') { + stream.next(); + } + } + } + else { + stream.skipToEnd(); + } + + return 'string'; + } + + var external = { + startState: function() { + return { + returnStack: [], + bracketNesting: 0, + tokenize: tokenBase + }; + }, + + token: function(stream, state) { + return state.tokenize(stream, state); + }, + + blockCommentStart: '<#', + blockCommentEnd: '#>', + lineComment: '#', + fold: 'brace' + }; + return external; +}); + +CodeMirror.defineMIME('application/x-powershell', 'powershell'); +}); diff --git a/mode/powershell/test.js b/mode/powershell/test.js new file mode 100644 index 0000000000..59b8e6fca9 --- /dev/null +++ b/mode/powershell/test.js @@ -0,0 +1,72 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "powershell"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT('comment', '[number 1][comment # A]'); + MT('comment_multiline', '[number 1][comment <#]', + '[comment ABC]', + '[comment #>][number 2]'); + + [ + '0', '1234', + '12kb', '12mb', '12Gb', '12Tb', '12PB', '12L', '12D', '12lkb', '12dtb', + '1.234', '1.234e56', '1.', '1.e2', '.2', '.2e34', + '1.2MB', '1.kb', '.1dTB', '1.e1gb', '.2', '.2e34', + '0x1', '0xabcdef', '0x3tb', '0xelmb' + ].forEach(function(number) { + MT("number_" + number, "[number " + number + "]"); + }); + + MT('string_literal_escaping', "[string 'a''']"); + MT('string_literal_variable', "[string 'a $x']"); + MT('string_escaping_1', '[string "a `""]'); + MT('string_escaping_2', '[string "a """]'); + MT('string_variable_escaping', '[string "a `$x"]'); + MT('string_variable', '[string "a ][variable-2 $x][string b"]'); + MT('string_variable_spaces', '[string "a ][variable-2 ${x y}][string b"]'); + MT('string_expression', '[string "a ][punctuation $(][variable-2 $x][operator +][number 3][punctuation )][string b"]'); + MT('string_expression_nested', '[string "A][punctuation $(][string "a][punctuation $(][string "w"][punctuation )][string b"][punctuation )][string B"]'); + + MT('string_heredoc', '[string @"]', + '[string abc]', + '[string "@]'); + MT('string_heredoc_quotes', '[string @"]', + '[string abc "\']', + '[string "@]'); + MT('string_heredoc_variable', '[string @"]', + '[string a ][variable-2 $x][string b]', + '[string "@]'); + MT('string_heredoc_nested_string', '[string @"]', + '[string a][punctuation $(][string "w"][punctuation )][string b]', + '[string "@]'); + MT('string_heredoc_literal_quotes', "[string @']", + '[string abc "\']', + "[string '@]"); + + MT('array', "[punctuation @(][string 'a'][punctuation ,][string 'b'][punctuation )]"); + MT('hash', "[punctuation @{][string 'key'][operator :][string 'value'][punctuation }]"); + + MT('variable', "[variable-2 $test]"); + MT('variable_global', "[variable-2 $global:test]"); + MT('variable_spaces', "[variable-2 ${test test}]"); + MT('operator_splat', "[variable-2 @x]"); + MT('variable_builtin', "[builtin $ErrorActionPreference]"); + MT('variable_builtin_symbols', "[builtin $$]"); + + MT('operator', "[operator +]"); + MT('operator_unary', "[operator +][number 3]"); + MT('operator_long', "[operator -match]"); + + [ + '(', ')', '[[', ']]', '{', '}', ',', '`', ';', '.' + ].forEach(function(punctuation) { + MT("punctuation_" + punctuation.replace(/^[\[\]]/,''), "[punctuation " + punctuation + "]"); + }); + + MT('keyword', "[keyword if]"); + + MT('call_builtin', "[builtin Get-ChildItem]"); +})(); diff --git a/mode/properties/properties.js b/mode/properties/properties.js index 9da5baf420..ef8bf37eea 100644 --- a/mode/properties/properties.js +++ b/mode/properties/properties.js @@ -34,7 +34,7 @@ CodeMirror.defineMode("properties", function() { } if (sol) { - while(stream.eatSpace()); + while(stream.eatSpace()) {} } var ch = stream.next(); diff --git a/mode/protobuf/index.html b/mode/protobuf/index.html new file mode 100644 index 0000000000..cfe7b9dcd3 --- /dev/null +++ b/mode/protobuf/index.html @@ -0,0 +1,64 @@ + + +CodeMirror: ProtoBuf mode + + + + + + + + + +
+

ProtoBuf mode

+
+ + +

MIME types defined: text/x-protobuf.

+ +
diff --git a/mode/protobuf/protobuf.js b/mode/protobuf/protobuf.js new file mode 100644 index 0000000000..bcae276e8d --- /dev/null +++ b/mode/protobuf/protobuf.js @@ -0,0 +1,68 @@ +// 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"; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); + }; + + var keywordArray = [ + "package", "message", "import", "syntax", + "required", "optional", "repeated", "reserved", "default", "extensions", "packed", + "bool", "bytes", "double", "enum", "float", "string", + "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64" + ]; + var keywords = wordRegexp(keywordArray); + + CodeMirror.registerHelper("hintWords", "protobuf", keywordArray); + + var identifiers = new RegExp("^[_A-Za-z\xa1-\uffff][_A-Za-z0-9\xa1-\uffff]*"); + + function tokenBase(stream) { + // whitespaces + if (stream.eatSpace()) return null; + + // Handle one line Comments + if (stream.match("//")) { + stream.skipToEnd(); + return "comment"; + } + + // Handle Number Literals + if (stream.match(/^[0-9\.+-]/, false)) { + if (stream.match(/^[+-]?0x[0-9a-fA-F]+/)) + return "number"; + if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?/)) + return "number"; + if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?/)) + return "number"; + } + + // Handle Strings + if (stream.match(/^"([^"]|(""))*"/)) { return "string"; } + if (stream.match(/^'([^']|(''))*'/)) { return "string"; } + + // Handle words + if (stream.match(keywords)) { return "keyword"; } + if (stream.match(identifiers)) { return "variable"; } ; + + // Handle non-detected items + stream.next(); + return null; + }; + + CodeMirror.defineMode("protobuf", function() { + return {token: tokenBase}; + }); + + CodeMirror.defineMIME("text/x-protobuf", "protobuf"); +}); diff --git a/mode/puppet/puppet.js b/mode/puppet/puppet.js index e7f799f78a..57041300af 100644 --- a/mode/puppet/puppet.js +++ b/mode/puppet/puppet.js @@ -126,7 +126,7 @@ CodeMirror.defineMode("puppet", function () { if (word && words.hasOwnProperty(word)) { // Negates the initial next() stream.backUp(1); - // Acutally move the stream + // rs move the stream stream.match(/[\w]+/); // We want to process these words differently // do to the importance they have in Puppet diff --git a/mode/python/index.html b/mode/python/index.html index 86eb3d52f7..6116a13b6b 100644 --- a/mode/python/index.html +++ b/mode/python/index.html @@ -186,7 +186,7 @@

Advanced Configuration Options:

  • singleOperators - RegEx - Regular Expression for single operator matching, default :
    ^[\\+\\-\\*/%&|\\^~<>!]
    including
    @
    on Python 3
  • singleDelimiters - RegEx - Regular Expression for single delimiter matching, default :
    ^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]
  • doubleOperators - RegEx - Regular Expression for double operators matching, default :
    ^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))
  • -
  • doubleDelimiters - RegEx - Regular Expressoin for double delimiters matching, default :
    ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
  • +
  • doubleDelimiters - RegEx - Regular Expression for double delimiters matching, default :
    ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
  • tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default :
    ^((//=)|(>>=)|(<<=)|(\\*\\*=))
  • identifiers - RegEx - Regular Expression for identifier, default :
    ^[_A-Za-z][_A-Za-z0-9]*
    on Python 2 and
    ^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*
    on Python 3.
  • extra_keywords - list of string - List of extra words ton consider as keywords
  • diff --git a/mode/python/python.js b/mode/python/python.js index 196b026c69..ec662b1a95 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -32,13 +32,6 @@ "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__", "NotImplemented", "Ellipsis", "__debug__"]; - var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile", - "file", "intern", "long", "raw_input", "reduce", "reload", - "unichr", "unicode", "xrange", "False", "True", "None"], - keywords: ["exec", "print"]}; - var py3 = {builtins: ["ascii", "bytes", "exec", "print"], - keywords: ["nonlocal", "False", "True", "None", "async", "await"]}; - CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); function top(state) { @@ -53,15 +46,6 @@ var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/; var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/; - if (parserConf.version && parseInt(parserConf.version, 10) == 3) { - // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; - var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; - } else { - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; - var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; - } - var hangingIndent = parserConf.hangingIndent || conf.indentUnit; var myKeywords = commonKeywords, myBuiltins = commonBuiltins; @@ -71,13 +55,21 @@ if (parserConf.extra_builtins != undefined) myBuiltins = myBuiltins.concat(parserConf.extra_builtins); - if (parserConf.version && parseInt(parserConf.version, 10) == 3) { - myKeywords = myKeywords.concat(py3.keywords); - myBuiltins = myBuiltins.concat(py3.builtins); - var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i"); + var py3 = parserConf.version && parseInt(parserConf.version, 10) == 3 + if (py3) { + // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; + myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); + myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); + var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i"); } else { - myKeywords = myKeywords.concat(py2.keywords); - myBuiltins = myBuiltins.concat(py2.builtins); + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; + myKeywords = myKeywords.concat(["exec", "print"]); + myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", + "file", "intern", "long", "raw_input", "reduce", "reload", + "unichr", "unicode", "xrange", "False", "True", "None"]); var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); } var keywords = wordRegexp(myKeywords); @@ -249,16 +241,16 @@ } function tokenLexer(stream, state) { + if (stream.sol()) state.beginningOfLine = true; + var style = state.tokenize(stream, state); var current = stream.current(); // Handle decorators - if (current == "@") { - if (parserConf.version && parseInt(parserConf.version, 10) == 3) - return stream.match(identifiers, false) ? "meta" : "operator"; - else - return stream.match(identifiers, false) ? "meta" : ERRORCLASS; - } + if (state.beginningOfLine && current == "@") + return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS; + + if (/\S/.test(current)) state.beginningOfLine = false; if ((style == "variable" || style == "builtin") && state.lastToken == "meta") diff --git a/mode/python/test.js b/mode/python/test.js new file mode 100644 index 0000000000..c1a9c6a990 --- /dev/null +++ b/mode/python/test.js @@ -0,0 +1,30 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 4}, + {name: "python", + version: 3, + singleLineStringErrors: false}); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Error, because "foobarhello" is neither a known type or property, but + // property was expected (after "and"), and it should be in parentheses. + MT("decoratorStartOfLine", + "[meta @dec]", + "[keyword def] [def function]():", + " [keyword pass]"); + + MT("decoratorIndented", + "[keyword class] [def Foo]:", + " [meta @dec]", + " [keyword def] [def function]():", + " [keyword pass]"); + + MT("matmulWithSpace:", "[variable a] [operator @] [variable b]"); + MT("matmulWithoutSpace:", "[variable a][operator @][variable b]"); + MT("matmulSpaceBefore:", "[variable a] [operator @][variable b]"); + + MT("fValidStringPrefix", "[string f'this is a {formatted} string']"); + MT("uValidStringPrefix", "[string u'this is an unicode string']"); +})(); diff --git a/mode/r/r.js b/mode/r/r.js index 1ab4a9565a..d41d1c54c1 100644 --- a/mode/r/r.js +++ b/mode/r/r.js @@ -11,6 +11,8 @@ })(function(CodeMirror) { "use strict"; +CodeMirror.registerHelper("wordChars", "r", /[\w.]/); + CodeMirror.defineMode("r", function(config) { function wordObj(str) { var words = str.split(" "), res = {}; diff --git a/mode/sas/index.html b/mode/sas/index.html new file mode 100644 index 0000000000..636e06594b --- /dev/null +++ b/mode/sas/index.html @@ -0,0 +1,81 @@ + + +CodeMirror: SAS mode + + + + + + + + + + +
    +

    SAS mode

    +
    + + + +

    MIME types defined: text/x-sas.

    + +
    diff --git a/mode/sas/sas.js b/mode/sas/sas.js new file mode 100755 index 0000000000..fe114827c0 --- /dev/null +++ b/mode/sas/sas.js @@ -0,0 +1,315 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + + +// SAS mode copyright (c) 2016 Jared Dean, SAS Institute +// Created by Jared Dean + +// TODO +// indent and de-indent +// identify macro variables + + +//Definitions +// comment -- text withing * ; or /* */ +// keyword -- SAS language variable +// variable -- macro variables starts with '&' or variable formats +// variable-2 -- DATA Step, proc, or macro names +// string -- text within ' ' or " " +// operator -- numeric operator + / - * ** le eq ge ... and so on +// builtin -- proc %macro data run mend +// atom +// def + +(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("sas", function () { + var words = {}; + var isDoubleOperatorSym = { + eq: 'operator', + lt: 'operator', + le: 'operator', + gt: 'operator', + ge: 'operator', + "in": 'operator', + ne: 'operator', + or: 'operator' + }; + var isDoubleOperatorChar = /(<=|>=|!=|<>)/; + var isSingleOperatorChar = /[=\(:\),{}.*<>+\-\/^\[\]]/; + + // Takes a string of words separated by spaces and adds them as + // keys with the value of the first argument 'style' + function define(style, string, context) { + if (context) { + var split = string.split(' '); + for (var i = 0; i < split.length; i++) { + words[split[i]] = {style: style, state: context}; + } + } + } + //datastep + define('def', 'stack pgm view source debug nesting nolist', ['inDataStep']); + define('def', 'if while until for do do; end end; then else cancel', ['inDataStep']); + define('def', 'label format _n_ _error_', ['inDataStep']); + define('def', 'ALTER BUFNO BUFSIZE CNTLLEV COMPRESS DLDMGACTION ENCRYPT ENCRYPTKEY EXTENDOBSCOUNTER GENMAX GENNUM INDEX LABEL OBSBUF OUTREP PW PWREQ READ REPEMPTY REPLACE REUSE ROLE SORTEDBY SPILL TOBSNO TYPE WRITE FILECLOSE FIRSTOBS IN OBS POINTOBS WHERE WHEREUP IDXNAME IDXWHERE DROP KEEP RENAME', ['inDataStep']); + define('def', 'filevar finfo finv fipname fipnamel fipstate first firstobs floor', ['inDataStep']); + define('def', 'varfmt varinfmt varlabel varlen varname varnum varray varrayx vartype verify vformat vformatd vformatdx vformatn vformatnx vformatw vformatwx vformatx vinarray vinarrayx vinformat vinformatd vinformatdx vinformatn vinformatnx vinformatw vinformatwx vinformatx vlabel vlabelx vlength vlengthx vname vnamex vnferr vtype vtypex weekday', ['inDataStep']); + define('def', 'zipfips zipname zipnamel zipstate', ['inDataStep']); + define('def', 'put putc putn', ['inDataStep']); + define('builtin', 'data run', ['inDataStep']); + + + //proc + define('def', 'data', ['inProc']); + + // flow control for macros + define('def', '%if %end %end; %else %else; %do %do; %then', ['inMacro']); + + //everywhere + define('builtin', 'proc run; quit; libname filename %macro %mend option options', ['ALL']); + + define('def', 'footnote title libname ods', ['ALL']); + define('def', '%let %put %global %sysfunc %eval ', ['ALL']); + // automatic macro variables http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a003167023.htm + define('variable', '&sysbuffr &syscc &syscharwidth &syscmd &sysdate &sysdate9 &sysday &sysdevic &sysdmg &sysdsn &sysencoding &sysenv &syserr &syserrortext &sysfilrc &syshostname &sysindex &sysinfo &sysjobid &syslast &syslckrc &syslibrc &syslogapplname &sysmacroname &sysmenv &sysmsg &sysncpu &sysodspath &sysparm &syspbuff &sysprocessid &sysprocessname &sysprocname &sysrc &sysscp &sysscpl &sysscpl &syssite &sysstartid &sysstartname &systcpiphostname &systime &sysuserid &sysver &sysvlong &sysvlong4 &syswarningtext', ['ALL']); + + //footnote[1-9]? title[1-9]? + + //options statement + define('def', 'source2 nosource2 page pageno pagesize', ['ALL']); + + //proc and datastep + define('def', '_all_ _character_ _cmd_ _freq_ _i_ _infile_ _last_ _msg_ _null_ _numeric_ _temporary_ _type_ abort abs addr adjrsq airy alpha alter altlog altprint and arcos array arsin as atan attrc attrib attrn authserver autoexec awscontrol awsdef awsmenu awsmenumerge awstitle backward band base betainv between blocksize blshift bnot bor brshift bufno bufsize bxor by byerr byline byte calculated call cards cards4 catcache cbufno cdf ceil center cexist change chisq cinv class cleanup close cnonct cntllev coalesce codegen col collate collin column comamid comaux1 comaux2 comdef compbl compound compress config continue convert cos cosh cpuid create cross crosstab css curobs cv daccdb daccdbsl daccsl daccsyd dacctab dairy datalines datalines4 datejul datepart datetime day dbcslang dbcstype dclose ddm delete delimiter depdb depdbsl depsl depsyd deptab dequote descending descript design= device dflang dhms dif digamma dim dinfo display distinct dkricond dkrocond dlm dnum do dopen doptname doptnum dread drop dropnote dsname dsnferr echo else emaildlg emailid emailpw emailserver emailsys encrypt end endsas engine eof eov erf erfc error errorcheck errors exist exp fappend fclose fcol fdelete feedback fetch fetchobs fexist fget file fileclose fileexist filefmt filename fileref fmterr fmtsearch fnonct fnote font fontalias fopen foptname foptnum force formatted formchar formdelim formdlim forward fpoint fpos fput fread frewind frlen from fsep fuzz fwrite gaminv gamma getoption getvarc getvarn go goto group gwindow hbar hbound helpenv helploc hms honorappearance hosthelp hostprint hour hpct html hvar ibessel ibr id if index indexc indexw initcmd initstmt inner input inputc inputn inr insert int intck intnx into intrr invaliddata irr is jbessel join juldate keep kentb kurtosis label lag last lbound leave left length levels lgamma lib library libref line linesize link list log log10 log2 logpdf logpmf logsdf lostcard lowcase lrecl ls macro macrogen maps mautosource max maxdec maxr mdy mean measures median memtype merge merror min minute missing missover mlogic mod mode model modify month mopen mort mprint mrecall msglevel msymtabmax mvarsize myy n nest netpv new news nmiss no nobatch nobs nocaps nocardimage nocenter nocharcode nocmdmac nocol nocum nodate nodbcs nodetails nodmr nodms nodmsbatch nodup nodupkey noduplicates noechoauto noequals noerrorabend noexitwindows nofullstimer noicon noimplmac noint nolist noloadlist nomiss nomlogic nomprint nomrecall nomsgcase nomstored nomultenvappl nonotes nonumber noobs noovp nopad nopercent noprint noprintinit normal norow norsasuser nosetinit nosplash nosymbolgen note notes notitle notitles notsorted noverbose noxsync noxwait npv null number numkeys nummousekeys nway obs on open order ordinal otherwise out outer outp= output over ovp p(1 5 10 25 50 75 90 95 99) pad pad2 paired parm parmcards path pathdll pathname pdf peek peekc pfkey pmf point poisson poke position printer probbeta probbnml probchi probf probgam probhypr probit probnegb probnorm probsig probt procleave prt ps pw pwreq qtr quote r ranbin rancau ranexp rangam range ranks rannor ranpoi rantbl rantri ranuni read recfm register regr remote remove rename repeat replace resolve retain return reuse reverse rewind right round rsquare rtf rtrace rtraceloc s s2 samploc sasautos sascontrol sasfrscr sasmsg sasmstore sasscript sasuser saving scan sdf second select selection separated seq serror set setcomm setot sign simple sin sinh siteinfo skewness skip sle sls sortedby sortpgm sortseq sortsize soundex spedis splashlocation split spool sqrt start std stderr stdin stfips stimer stname stnamel stop stopover subgroup subpopn substr sum sumwgt symbol symbolgen symget symput sysget sysin sysleave sysmsg sysparm sysprint sysprintfont sysprod sysrc system t table tables tan tanh tapeclose tbufsize terminal test then timepart tinv tnonct to today tol tooldef totper transformout translate trantab tranwrd trigamma trim trimn trunc truncover type unformatted uniform union until upcase update user usericon uss validate value var weight when where while wincharset window work workinit workterm write wsum xsync xwait yearcutoff yes yyq min max', ['inDataStep', 'inProc']); + define('operator', 'and not ', ['inDataStep', 'inProc']); + + // Main function + function tokenize(stream, state) { + // Finally advance the stream + var ch = stream.next(); + + // BLOCKCOMMENT + if (ch === '/' && stream.eat('*')) { + state.continueComment = true; + return "comment"; + } else if (state.continueComment === true) { // in comment block + //comment ends at the beginning of the line + if (ch === '*' && stream.peek() === '/') { + stream.next(); + state.continueComment = false; + } else if (stream.skipTo('*')) { //comment is potentially later in line + stream.skipTo('*'); + stream.next(); + if (stream.eat('/')) + state.continueComment = false; + } else { + stream.skipToEnd(); + } + return "comment"; + } + + // DoubleOperator match + var doubleOperator = ch + stream.peek(); + + // Match all line comments. + var myString = stream.string; + var myRegexp = /(?:^\s*|[;]\s*)(\*.*?);/ig; + var match = myRegexp.exec(myString); + if (match !== null) { + if (match.index === 0 && (stream.column() !== (match.index + match[0].length - 1))) { + stream.backUp(stream.column()); + stream.skipTo(';'); + stream.next(); + return 'comment'; + } else if (match.index + 1 < stream.column() && stream.column() < match.index + match[0].length - 1) { + // the ';' triggers the match so move one past it to start + // the comment block that is why match.index+1 + stream.backUp(stream.column() - match.index - 1); + stream.skipTo(';'); + stream.next(); + return 'comment'; + } + } else if (!state.continueString && (ch === '"' || ch === "'")) { + // Have we found a string? + state.continueString = ch; //save the matching quote in the state + return "string"; + } else if (state.continueString !== null) { + if (stream.skipTo(state.continueString)) { + // quote found on this line + stream.next(); + state.continueString = null; + } else { + stream.skipToEnd(); + } + return "string"; + } else if (state.continueString !== null && stream.eol()) { + stream.skipTo(state.continueString) || stream.skipToEnd(); + return "string"; + } else if (/[\d\.]/.test(ch)) { //find numbers + if (ch === ".") + stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/); + else if (ch === "0") + stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/); + else + stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/); + return "number"; + } else if (isDoubleOperatorChar.test(ch + stream.peek())) { // TWO SYMBOL TOKENS + stream.next(); + return "operator"; + } else if (isDoubleOperatorSym.hasOwnProperty(doubleOperator)) { + stream.next(); + if (stream.peek() === ' ') + return isDoubleOperatorSym[doubleOperator.toLowerCase()]; + } else if (isSingleOperatorChar.test(ch)) { // SINGLE SYMBOL TOKENS + return "operator"; + } + + // Matches one whole word -- even if the word is a character + var word; + if (stream.match(/[%&;\w]+/, false) != null) { + word = ch + stream.match(/[%&;\w]+/, true); + if (/&/.test(word)) return 'variable' + } else { + word = ch; + } + // the word after DATA PROC or MACRO + if (state.nextword) { + stream.match(/[\w]+/); + // match memname.libname + if (stream.peek() === '.') stream.skipTo(' '); + state.nextword = false; + return 'variable-2'; + + } + + // Are we in a DATA Step? + if (state.inDataStep) { + if (word.toLowerCase() === 'run;' || stream.match(/run\s;/)) { + state.inDataStep = false; + return 'builtin'; + } + // variable formats + if ((word) && stream.next() === '.') { + //either a format or libname.memname + if (/\w/.test(stream.peek())) return 'variable-2'; + else return 'variable'; + } + // do we have a DATA Step keyword + if (word && words.hasOwnProperty(word.toLowerCase()) && + (words[word.toLowerCase()].state.indexOf("inDataStep") !== -1 || + words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + //backup to the start of the word + if (stream.start < stream.pos) + stream.backUp(stream.pos - stream.start); + //advance the length of the word and return + for (var i = 0; i < word.length; ++i) stream.next(); + return words[word.toLowerCase()].style; + } + } + // Are we in an Proc statement? + if (state.inProc) { + if (word.toLowerCase() === 'run;' || word.toLowerCase() === 'quit;') { + state.inProc = false; + return 'builtin'; + } + // do we have a proc keyword + if (word && words.hasOwnProperty(word.toLowerCase()) && + (words[word.toLowerCase()].state.indexOf("inProc") !== -1 || + words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word].style; + } + } + // Are we in a Macro statement? + if (state.inMacro) { + if (word.toLowerCase() === '%mend') { + if (stream.peek() === ';') stream.next(); + state.inMacro = false; + return 'builtin'; + } + if (word && words.hasOwnProperty(word.toLowerCase()) && + (words[word.toLowerCase()].state.indexOf("inMacro") !== -1 || + words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word.toLowerCase()].style; + } + + return 'atom'; + } + // Do we have Keywords specific words? + if (word && words.hasOwnProperty(word.toLowerCase())) { + // Negates the initial next() + stream.backUp(1); + // Actually move the stream + stream.match(/[\w]+/); + if (word.toLowerCase() === 'data' && /=/.test(stream.peek()) === false) { + state.inDataStep = true; + state.nextword = true; + return 'builtin'; + } + if (word.toLowerCase() === 'proc') { + state.inProc = true; + state.nextword = true; + return 'builtin'; + } + if (word.toLowerCase() === '%macro') { + state.inMacro = true; + state.nextword = true; + return 'builtin'; + } + if (/title[1-9]/i.test(word)) return 'def'; + + if (word.toLowerCase() === 'footnote') { + stream.eat(/[1-9]/); + return 'def'; + } + + // Returns their value as state in the prior define methods + if (state.inDataStep === true && words[word.toLowerCase()].state.indexOf("inDataStep") !== -1) + return words[word.toLowerCase()].style; + if (state.inProc === true && words[word.toLowerCase()].state.indexOf("inProc") !== -1) + return words[word.toLowerCase()].style; + if (state.inMacro === true && words[word.toLowerCase()].state.indexOf("inMacro") !== -1) + return words[word.toLowerCase()].style; + if (words[word.toLowerCase()].state.indexOf("ALL") !== -1) + return words[word.toLowerCase()].style; + return null; + } + // Unrecognized syntax + return null; + } + + return { + startState: function () { + return { + inDataStep: false, + inProc: false, + inMacro: false, + nextword: false, + continueString: null, + continueComment: false + }; + }, + token: function (stream, state) { + // Strip the spaces, but regex will account for them either way + if (stream.eatSpace()) return null; + // Go through the main process + return tokenize(stream, state); + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/" + }; + + }); + + CodeMirror.defineMIME("text/x-sas", "sas"); +}); diff --git a/mode/slim/slim.js b/mode/slim/slim.js index 164464d066..991a97efcd 100644 --- a/mode/slim/slim.js +++ b/mode/slim/slim.js @@ -165,7 +165,7 @@ }; return function(stream, state) { rubyState = state.rubyState; - state.rubyState = rubyMode.startState(); + state.rubyState = CodeMirror.startState(rubyMode); state.tokenize = runSplat; return ruby(stream, state); }; @@ -317,7 +317,7 @@ function startSubMode(mode, state) { var subMode = getMode(mode); - var subState = subMode.startState && subMode.startState(); + var subState = CodeMirror.startState(subMode); state.subMode = subMode; state.subState = subState; @@ -507,8 +507,8 @@ var mode = { // default to html mode startState: function() { - var htmlState = htmlMode.startState(); - var rubyState = rubyMode.startState(); + var htmlState = CodeMirror.startState(htmlMode); + var rubyState = CodeMirror.startState(rubyMode); return { htmlState: htmlState, rubyState: rubyState, diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 79bfc24dfd..580c306f15 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -121,10 +121,11 @@ return tokenUntil(stream, state, /\{\/literal}/); case "string": - if (stream.match(/^.*?"/)) { - state.soyState.pop(); - } else { + var match = stream.match(/^.*?("|\\[\s\S])/); + if (!match) { stream.skipToEnd(); + } else if (match[1] == "\"") { + state.soyState.pop(); } return "string"; } diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index 90b08e18f2..095dcca653 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -25,7 +25,7 @@ CodeMirror.defineMode("sparql", function(config) { "strbefore", "strafter", "year", "month", "day", "hours", "minutes", "seconds", "timezone", "tz", "now", "uuid", "struuid", "md5", "sha1", "sha256", "sha384", "sha512", "coalesce", "if", "strlang", "strdt", "isnumeric", "regex", "exists", - "isblank", "isliteral", "a"]); + "isblank", "isliteral", "a", "bind"]); var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe", "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional", "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group", diff --git a/mode/sql/index.html b/mode/sql/index.html index 7ea4b6934a..dba069dc81 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -56,8 +56,9 @@

    SQL Mode for CodeMirror

    text/x-cassandra, text/x-plsql, text/x-mssql, - text/x-hive. - text/x-pgsql. + text/x-hive, + text/x-pgsql, + text/x-gql.

    + + + + + + +
    +

    Web IDL mode

    + +
    + +
    + + + +

    MIME type defined: text/x-webidl.

    +
    diff --git a/mode/webidl/webidl.js b/mode/webidl/webidl.js new file mode 100644 index 0000000000..814333620a --- /dev/null +++ b/mode/webidl/webidl.js @@ -0,0 +1,195 @@ +// 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"; + +function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); +}; + +var builtinArray = [ + "Clamp", + "Constructor", + "EnforceRange", + "Exposed", + "ImplicitThis", + "Global", "PrimaryGlobal", + "LegacyArrayClass", + "LegacyUnenumerableNamedProperties", + "LenientThis", + "NamedConstructor", + "NewObject", + "NoInterfaceObject", + "OverrideBuiltins", + "PutForwards", + "Replaceable", + "SameObject", + "TreatNonObjectAsNull", + "TreatNullAs", + "EmptyString", + "Unforgeable", + "Unscopeable" +]; +var builtins = wordRegexp(builtinArray); + +var typeArray = [ + "unsigned", "short", "long", // UnsignedIntegerType + "unrestricted", "float", "double", // UnrestrictedFloatType + "boolean", "byte", "octet", // Rest of PrimitiveType + "Promise", // PromiseType + "ArrayBuffer", "DataView", "Int8Array", "Int16Array", "Int32Array", + "Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray", + "Float32Array", "Float64Array", // BufferRelatedType + "ByteString", "DOMString", "USVString", "sequence", "object", "RegExp", + "Error", "DOMException", "FrozenArray", // Rest of NonAnyType + "any", // Rest of SingleType + "void" // Rest of ReturnType +]; +var types = wordRegexp(typeArray); + +var keywordArray = [ + "attribute", "callback", "const", "deleter", "dictionary", "enum", "getter", + "implements", "inherit", "interface", "iterable", "legacycaller", "maplike", + "partial", "required", "serializer", "setlike", "setter", "static", + "stringifier", "typedef", // ArgumentNameKeyword except + // "unrestricted" + "optional", "readonly", "or" +]; +var keywords = wordRegexp(keywordArray); + +var atomArray = [ + "true", "false", // BooleanLiteral + "Infinity", "NaN", // FloatLiteral + "null" // Rest of ConstValue +]; +var atoms = wordRegexp(atomArray); + +CodeMirror.registerHelper("hintWords", "webidl", + builtinArray.concat(typeArray).concat(keywordArray).concat(atomArray)); + +var startDefArray = ["callback", "dictionary", "enum", "interface"]; +var startDefs = wordRegexp(startDefArray); + +var endDefArray = ["typedef"]; +var endDefs = wordRegexp(endDefArray); + +var singleOperators = /^[:<=>?]/; +var integers = /^-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/; +var floats = /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/; +var identifiers = /^_?[A-Za-z][0-9A-Z_a-z-]*/; +var identifiersEnd = /^_?[A-Za-z][0-9A-Z_a-z-]*(?=\s*;)/; +var strings = /^"[^"]*"/; +var multilineComments = /^\/\*.*?\*\//; +var multilineCommentsStart = /^\/\*.*/; +var multilineCommentsEnd = /^.*?\*\//; + +function readToken(stream, state) { + // whitespace + if (stream.eatSpace()) return null; + + // comment + if (state.inComment) { + if (stream.match(multilineCommentsEnd)) { + state.inComment = false; + return "comment"; + } + stream.skipToEnd(); + return "comment"; + } + if (stream.match("//")) { + stream.skipToEnd(); + return "comment"; + } + if (stream.match(multilineComments)) return "comment"; + if (stream.match(multilineCommentsStart)) { + state.inComment = true; + return "comment"; + } + + // integer and float + if (stream.match(/^-?[0-9\.]/, false)) { + if (stream.match(integers) || stream.match(floats)) return "number"; + } + + // string + if (stream.match(strings)) return "string"; + + // identifier + if (state.startDef && stream.match(identifiers)) return "def"; + + if (state.endDef && stream.match(identifiersEnd)) { + state.endDef = false; + return "def"; + } + + if (stream.match(keywords)) return "keyword"; + + if (stream.match(types)) { + var lastToken = state.lastToken; + var nextToken = (stream.match(/^\s*(.+?)\b/, false) || [])[1]; + + if (lastToken === ":" || lastToken === "implements" || + nextToken === "implements" || nextToken === "=") { + // Used as identifier + return "builtin"; + } else { + // Used as type + return "variable-3"; + } + } + + if (stream.match(builtins)) return "builtin"; + if (stream.match(atoms)) return "atom"; + if (stream.match(identifiers)) return "variable"; + + // other + if (stream.match(singleOperators)) return "operator"; + + // unrecognized + stream.next(); + return null; +}; + +CodeMirror.defineMode("webidl", function() { + return { + startState: function() { + return { + // Is in multiline comment + inComment: false, + // Last non-whitespace, matched token + lastToken: "", + // Next token is a definition + startDef: false, + // Last token of the statement is a definition + endDef: false + }; + }, + token: function(stream, state) { + var style = readToken(stream, state); + + if (style) { + var cur = stream.current(); + state.lastToken = cur; + if (style === "keyword") { + state.startDef = startDefs.test(cur); + state.endDef = state.endDef || endDefs.test(cur); + } else { + state.startDef = false; + } + } + + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-webidl", "webidl"); +}); diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index c642ee58fa..75dcbee3ea 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -17,7 +17,7 @@ CodeMirror.defineMode("xquery", function() { // function. Each keyword is a property of the keywords object whose // value is {type: atype, style: astyle} var keywords = function(){ - // conveinence functions used to build keywords object + // convenience functions used to build keywords object function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a") , B = kw("keyword b") diff --git a/mode/yacas/index.html b/mode/yacas/index.html new file mode 100644 index 0000000000..8e52cafb10 --- /dev/null +++ b/mode/yacas/index.html @@ -0,0 +1,87 @@ + + +CodeMirror: yacas mode + + + + + + + + + + +
    +

    yacas mode

    + + + + + + +

    MIME types defined: text/x-yacas (yacas).

    +
    diff --git a/mode/yacas/yacas.js b/mode/yacas/yacas.js new file mode 100644 index 0000000000..30bd60b2ff --- /dev/null +++ b/mode/yacas/yacas.js @@ -0,0 +1,204 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Yacas mode copyright (c) 2015 by Grzegorz Mazur +// Loosely based on mathematica mode by Calin Barbat + +(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('yacas', function(_config, _parserConfig) { + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " + + "FromString Function Integrate InverseTaylor Limit " + + "LocalSymbols Macro MacroRule MacroRulePattern " + + "NIntegrate Rule RulePattern Subst TD TExplicitSum " + + "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " + + "ToStdout ToString TraceRule Until While"); + + // patterns + var pFloatForm = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)"; + var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)"; + + // regular expressions + var reFloatForm = new RegExp(pFloatForm); + var reIdentifier = new RegExp(pIdentifier); + var rePattern = new RegExp(pIdentifier + "?_" + pIdentifier); + var reFunctionLike = new RegExp(pIdentifier + "\\s*\\("); + + function tokenBase(stream, state) { + var ch; + + // get next character + ch = stream.next(); + + // string + if (ch === '"') { + state.tokenize = tokenString; + return state.tokenize(stream, state); + } + + // comment + if (ch === '/') { + if (stream.eat('*')) { + state.tokenize = tokenComment; + return state.tokenize(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + + // go back one character + stream.backUp(1); + + // update scope info + var m = stream.match(/^(\w+)\s*\(/, false); + if (m !== null && bodiedOps.hasOwnProperty(m[1])) + state.scopes.push('bodied'); + + var scope = currentScope(state); + + if (scope === 'bodied' && ch === '[') + state.scopes.pop(); + + if (ch === '[' || ch === '{' || ch === '(') + state.scopes.push(ch); + + scope = currentScope(state); + + if (scope === '[' && ch === ']' || + scope === '{' && ch === '}' || + scope === '(' && ch === ')') + state.scopes.pop(); + + if (ch === ';') { + while (scope === 'bodied') { + state.scopes.pop(); + scope = currentScope(state); + } + } + + // look for ordered rules + if (stream.match(/\d+ *#/, true, false)) { + return 'qualifier'; + } + + // look for numbers + if (stream.match(reFloatForm, true, false)) { + return 'number'; + } + + // look for placeholders + if (stream.match(rePattern, true, false)) { + return 'variable-3'; + } + + // match all braces separately + if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) { + return 'bracket'; + } + + // literals looking like function calls + if (stream.match(reFunctionLike, true, false)) { + stream.backUp(1); + return 'variable'; + } + + // all other identifiers + if (stream.match(reIdentifier, true, false)) { + return 'variable-2'; + } + + // operators; note that operators like @@ or /; are matched separately for each symbol. + if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) { + return 'operator'; + } + + // everything else is an error + return 'error'; + } + + function tokenString(stream, state) { + var next, end = false, escaped = false; + while ((next = stream.next()) != null) { + if (next === '"' && !escaped) { + end = true; + break; + } + escaped = !escaped && next === '\\'; + } + if (end && !escaped) { + state.tokenize = tokenBase; + } + return 'string'; + }; + + function tokenComment(stream, state) { + var prev, next; + while((next = stream.next()) != null) { + if (prev === '*' && next === '/') { + state.tokenize = tokenBase; + break; + } + prev = next; + } + return 'comment'; + } + + function currentScope(state) { + var scope = null; + if (state.scopes.length > 0) + scope = state.scopes[state.scopes.length - 1]; + return scope; + } + + return { + startState: function() { + return { + tokenize: tokenBase, + scopes: [] + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + }, + indent: function(state, textAfter) { + if (state.tokenize !== tokenBase && state.tokenize !== null) + return CodeMirror.Pass; + + var delta = 0; + if (textAfter === ']' || textAfter === '];' || + textAfter === '}' || textAfter === '};' || + textAfter === ');') + delta = -1; + + return (state.scopes.length + delta) * _config.indentUnit; + }, + electricChars: "{}[]();", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" + }; +}); + +CodeMirror.defineMIME('text/x-yacas', { + name: 'yacas' +}); + +}); diff --git a/mode/yaml-frontmatter/index.html b/mode/yaml-frontmatter/index.html index 30bed2f855..30cb294e80 100644 --- a/mode/yaml-frontmatter/index.html +++ b/mode/yaml-frontmatter/index.html @@ -78,7 +78,7 @@

    YAML front matter mode

    GFM adds syntax to strikethrough text, which is missing from standard Markdown. ~~Mistaken text.~~ -~~**works with other fomatting**~~ +~~**works with other formatting**~~ ~~spans across lines~~ diff --git a/mode/yaml-frontmatter/yaml-frontmatter.js b/mode/yaml-frontmatter/yaml-frontmatter.js index 9f081b009a..5f49772370 100644 --- a/mode/yaml-frontmatter/yaml-frontmatter.js +++ b/mode/yaml-frontmatter/yaml-frontmatter.js @@ -65,4 +65,4 @@ } } }) -}) +}); diff --git a/package.json b/package.json index 1b4f32e523..276c2d29e9 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "codemirror", - "version":"5.12.1", + "version":"5.16.2", "main": "lib/codemirror.js", - "description": "In-browser code editing made bearable", + "description": "Full-featured in-browser code editor", "license": "MIT", "directories": {"lib": "./lib"}, "scripts": { @@ -19,5 +19,10 @@ "email": "marijnh@gmail.com", "web": "http://marijnhaverbeke.nl"}], "repository": {"type": "git", - "url": "https://github.com/codemirror/CodeMirror.git"} + "url": "https://github.com/codemirror/CodeMirror.git"}, + "jspm": { + "directories": {}, + "dependencies": {}, + "devDependencies": {} + } } diff --git a/test/index.html b/test/index.html index 3e227a061d..90c0335171 100644 --- a/test/index.html +++ b/test/index.html @@ -8,6 +8,7 @@ + @@ -26,6 +27,8 @@ + + @@ -40,6 +43,7 @@ +