diff --git a/AUTHORS b/AUTHORS
index 1e3ece23..741ba1e3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -27,6 +27,7 @@ Alex Piggott
Aliaksei Chapyzhenka
Allen Sarkisyan
Amin Shali
+Amin Ullah Khan
amshali@google.com
Amsul
amuntean
@@ -60,6 +61,7 @@ Anthony Grimes
Anton Kovalyov
AQNOUCH Mohammed
areos
+Arnab Bose
as3boyan
AtomicPages LLC
Atul Bhouraskar
@@ -91,10 +93,12 @@ Brian Sletten
Bruce Mitchener
Caitlin Potter
Calin Barbat
+Chad Jolly
Chandra Sekhar Pydi
Charles Skelton
Cheah Chu Yeow
Chris Coyier
+Chris Ford
Chris Granger
Chris Houseknecht
Chris Lohfink
@@ -104,9 +108,11 @@ Christian Petrov
Christopher Brown
Christopher Mitchell
Christopher Pfohl
+Chunliang Lyu
ciaranj
CodeAnimal
coderaiser
+Cole R Lawrence
ComFreek
Curtis Gagliardi
dagsta
@@ -118,6 +124,7 @@ Daniel, Dao Quang Minh
Daniele Di Sarli
Daniel Faust
Daniel Huigens
+Daniel Kesler
Daniel KJ
Daniel Neel
Daniel Parnell
@@ -132,6 +139,7 @@ David Pathakjee
David Vázquez
deebugger
Deep Thought
+Devin Abbott
Devon Carew
dignifiedquire
Dimage Sapelkin
@@ -143,6 +151,7 @@ Doug Wikle
Drew Bratcher
Drew Hintz
Drew Khoury
+Drini Cami
Dror BG
duralog
eborden
@@ -151,6 +160,7 @@ ekhaled
Elisée
Enam Mijbah Noor
Eric Allam
+Erik Welander
eustas
Fabien O'Carroll
Fabio Zendhi Nagao
@@ -172,13 +182,16 @@ Gabriel Horner
Gabriel Nahmias
galambalazs
Gautam Mehta
+Gavin Douglas
gekkoe
Gerard Braad
Gergely Hegykozi
Giovanni Calò
+Glebov Boris
Glenn Jorde
Glenn Ruehle
Golevka
+Google Inc.
Gordon Smith
Grant Skinner
greengiant
@@ -218,6 +231,7 @@ Jan Jongboom
jankeromnes
Jan Keromnes
Jan Odvarko
+Jan Schär
Jan T. Sott
Jared Forsyth
Jason
@@ -233,6 +247,9 @@ jeffkenton
Jeff Pickhardt
jem (graphite)
Jeremy Parmenter
+Jim
+JobJob
+jochenberger
Jochen Berger
Johan Ask
John Connor
@@ -240,6 +257,7 @@ John Engler
John Lees-Miller
John Snelson
John Van Der Loo
+Jon Ander Peñalba
Jonas Döbertin
Jonathan Malmaud
jongalloway
@@ -247,6 +265,7 @@ Jon Malmaud
Jon Sangster
Joost-Wim Boekesteijn
Joseph Pecoraro
+Josh Cohen
Joshua Newman
Josh Watzman
jots
@@ -255,10 +274,12 @@ ju1ius
Juan Benavides Romero
Jucovschi Constantin
Juho Vuori
+Justin Andresen
Justin Hileman
jwallers@gmail.com
kaniga
karevn
+Kayur Patel
Ken Newman
Ken Rockot
Kevin Earls
@@ -333,6 +354,7 @@ Max Kirsch
Max Schaefer
Max Xiantu
mbarkhau
+McBrainy
melpon
Metatheos
Micah Dubinko
@@ -372,6 +394,7 @@ Nicholas Bollweg
Nicholas Bollweg (Nick)
Nick Kreeger
Nick Small
+Nicolò Ribaudo
Niels van Groningen
nightwing
Nikita Beloglazov
@@ -384,6 +407,7 @@ noragrossman
Norman Rzepka
Oreoluwa Onatemowo
pablo
+pabloferz
Page
Panupong Pasupat
paris
@@ -393,6 +417,7 @@ Patrick Stoica
Patrick Strawderman
Paul Garvin
Paul Ivanov
+Pavel
Pavel Feldman
Pavel Strashkin
Paweł Bartkiewicz
@@ -402,10 +427,13 @@ Peter Flynn
peterkroon
Peter Kroon
Philip Stadermann
+Piët Delport
prasanthj
Prasanth J
+Prayag Verma
Radek Piórkowski
Rahul
+Rahul Anand
ramwin1
Randall Mason
Randy Burden
@@ -437,6 +465,7 @@ SCLINIC\jdecker
Scott Aikin
Scott Goodhew
Sebastian Zaha
+Sergey Goder
Se-Won Kim
shaund
shaun gilchrist
@@ -456,15 +485,19 @@ Stanislav Oaserele
Stas Kobzar
Stefan Borsje
Steffen Beyer
+Steffen Bruchmann
Stephen Lavelle
+Steve Champagne
Steve O'Hara
stoskov
+Stu Kennedy
Sungho Kim
sverweij
Taha Jahangir
Tako Schotanus
Takuji Shimokawa
Tarmil
+TDaglis
tel
tfjgeorge
Thaddee Tyl
@@ -485,6 +518,7 @@ Tom MacWright
Tony Jian
Travis Heppe
Triangle717
+TSUYUSATO Kitsune
twifkak
Vestimir Markov
vf
@@ -495,15 +529,18 @@ wenli
Wes Cossick
Wesley Wiser
Will Binns-Smith
+Will Dean
William Jamieson
William Stein
Willy
Wojtek Ptak
+Wu Cheng-Han
Xavier Mendez
Yassin N. Hassan
YNH Webdev
Yunchi Luo
Yuvi Panda
+Zac Anger
Zachary Dremann
Zhang Hao
zziuni
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..7eb70e9c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,684 @@
+## 5.12.0 (2015-02-19)
+
+### New features
+
+[Vim bindings](http://codemirror.net/demo/vim.html): Ctrl-Q is now an alias for Ctrl-V.
+
+[Vim bindings](http://codemirror.net/demo/vim.html): The Vim API now exposes an `unmap` method to unmap bindings.
+
+[active-line addon](http://codemirror.net/demo/activeline.html): This addon can now style the active line's gutter.
+
+[FCL mode](http://codemirror.net/mode/fcl/): Newly added.
+
+[SQL mode](http://codemirror.net/mode/sql/): Now has a Postgresql dialect.
+
+### Bugfixes
+
+Fix [issue](https://github.com/codemirror/CodeMirror/issues/3781) where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.
+
+Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a [problem](https://github.com/codemirror/CodeMirror/issues/3238) when the editor is inside a transformed parent container.
+
+Solve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox.
+
+Fix a [bug](https://github.com/codemirror/CodeMirror/issues/3834) that caused phantom scroll space under the text in some situations.
+
+[Sublime Text bindings](http://codemirror.net/demo/sublime.html): Bind delete-line to Shift-Ctrl-K on OS X.
+
+[Markdown mode](http://codemirror.net/mode/markdown/): Fix [issue](https://github.com/codemirror/CodeMirror/issues/3787) where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.
+
+[Markdown mode](http://codemirror.net/mode/markdown/): Ignore backslashes in code fragments.
+
+[Markdown mode](http://codemirror.net/mode/markdown/): Use whichever mode is registered as `text/html` to parse HTML.
+
+[Clike mode](http://codemirror.net/mode/clike/): Improve indentation of Scala `=>` functions.
+
+[Python mode](http://codemirror.net/mode/python/): Improve indentation of bracketed code.
+
+[HTMLMixed mode](http://codemirror.net/mode/htmlmixed/): Support multi-line opening tags for sub-languages (`
-
+
diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html
index c6010900..893d7213 100644
--- a/demo/matchhighlighter.html
+++ b/demo/matchhighlighter.html
@@ -6,6 +6,8 @@
+
+
CodeMirror
@@ -31,14 +35,66 @@
Match Highlighter Demo
-
+Select this text: hardtospot
+ And everywhere else in your code where hardtospot appears will
+automatically illuminate. Give it a try! No more hard to spot
+variables - stay in context of your code all the time.
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pharetra
+interdum dui eu pulvinar. Mauris maximus ligula venenatis tempus
+interdum. Cras hendrerit, ipsum sed ultrices pharetra, ligula diam
+porttitor lacus, ac tempor eros est a massa. Nam orci elit, vulputate
+in tristique quis, consectetur vitae metus. Pellentesque et enim
+elementum, lobortis augue in, lacinia sapien. Morbi eu nunc semper,
+sagittis felis a, pellentesque mauris. Lorem ipsum dolor sit amet,
+consectetur adipiscing elit. Aenean quis diam turpis.
+
+Fusce lobortis nisl quis aliquet euismod. Aenean vitae nulla non ipsum
+efficitur scelerisque. Curabitur auctor, lorem non rhoncus porttitor,
+augue ligula lacinia dolor, et vehicula magna lorem imperdiet velit.
+Fusce risus sem, hardtospot commodo eleifend hendrerit vitae, mollis
+quis risus. Cras tincidunt, justo vitae hendrerit venenatis, urna
+dolor placerat tortor, eu lobortis lectus dolor in ligula. Nullam non
+erat non nisl vulputate ultrices sit amet vestibulum dolor. Quisque in
+tortor porta, pellentesque odio nec, malesuada nibh.
+
+In a dui feugiat, ullamcorper urna in, accumsan magna. Donec egestas
+sem nec eros rhoncus, vel gravida purus ornare. Nulla orci mauris,
+porta nec pharetra sed, ornare et lorem. Donec luctus turpis nunc,
+eget dictum felis mollis et. Sed sodales hardtospot nunc vitae leo
+rhoncus imperdiet. Donec elementum malesuada velit quis placerat.
+Proin accumsan lorem id nisi volutpat ullamcorper. Vivamus laoreet
+dolor ac sem malesuada, ac scelerisque ex efficitur. Aliquam tempus
+libero velit, vel tristique augue vulputate nec.
+
+Mauris ultrices leo felis, sit amet congue augue aliquam condimentum.
+Vivamus purus leo, mattis vitae dignissim vel, ultricies ac ex. Mauris
+eu dolor eu purus ultricies ultrices. Sed euismod feugiat ex et
+mattis. Morbi cursus laoreet pharetra. Donec eu dolor sodales,
+ultricies nisi et, malesuada urna. Praesent sit amet fringilla felis.
+Nam rhoncus, est blandit auctor auctor, lorem ipsum laoreet ipsum,
+quis sodales libero odio in lorem. Phasellus odio dolor, elementum
+sagittis nibh non, fermentum semper libero. Mauris hendrerit
+hardtospot lectus sit amet commodo eleifend. Morbi pulvinar eget nisl
+at eleifend. Fusce eget porta erat, vitae lobortis libero.
+
+Phasellus sit amet massa in massa pharetra malesuada. Vestibulum at
+quam vel libero aliquam volutpat at ut dui. Praesent scelerisque vel
+mauris sit amet vehicula. Phasellus at mi nec ligula cursus interdum
+sit amet non quam. Aliquam tempus sollicitudin euismod. Nulla euismod
+mollis enim tincidunt placerat. Proin ac scelerisque enim, quis
+sollicitudin metus. Pellentesque congue nec sapien ut rhoncus. Sed
+eget ornare diam, ut consectetur ante. Aenean eleifend mauris quis
+ornare accumsan. In hac habitasse hardtospot platea dictumst.
+
+
diff --git a/demo/runmode.html b/demo/runmode.html
index 257f03d6..ab8938d8 100644
--- a/demo/runmode.html
+++ b/demo/runmode.html
@@ -43,7 +43,7 @@ Mode Runner Demo
Running a CodeMirror mode outside of the editor.
The CodeMirror.runMode function, defined
- in lib/runmode.js takes the following arguments:
+ in addon/runmode/runmode.js takes the following arguments:
text (string)
diff --git a/demo/search.html b/demo/search.html
index 21c34251..a5a88971 100644
--- a/demo/search.html
+++ b/demo/search.html
@@ -14,6 +14,7 @@
+
+
+
+
+
+Crystal mode
+
+# Features of Crystal
+# - Ruby-inspired syntax.
+# - Statically type-checked but without having to specify the type of variables or method arguments.
+# - Be able to call C code by writing bindings to it in Crystal.
+# - Have compile-time evaluation and generation of code, to avoid boilerplate code.
+# - Compile to efficient native code.
+
+# A very basic HTTP server
+require "http/server"
+
+server = HTTP::Server.new(8080) do |request|
+ HTTP::Response.ok "text/plain", "Hello world, got #{request.path}!"
+end
+
+puts "Listening on http://0.0.0.0:8080"
+server.listen
+
+module Foo
+ def initialize(@foo); end
+
+ abstract def abstract_method : String
+
+ @[AlwaysInline]
+ def with_foofoo
+ with Foo.new(self) yield
+ end
+
+ struct Foo
+ def initialize(@foo); end
+
+ def hello_world
+ @foo.abstract_method
+ end
+ end
+end
+
+class Bar
+ include Foo
+
+ @@foobar = 12345
+
+ def initialize(@bar)
+ super(@bar.not_nil! + 100)
+ end
+
+ macro alias_method(name, method)
+ def {{ name }}(*args)
+ {{ method }}(*args)
+ end
+ end
+
+ def a_method
+ "Hello, World"
+ end
+
+ alias_method abstract_method, a_method
+
+ macro def show_instance_vars : Nil
+ {% for var in @type.instance_vars %}
+ puts "@{{ var }} = #{ @{{ var }} }"
+ {% end %}
+ nil
+ end
+end
+
+class Baz < Bar; end
+
+lib LibC
+ fun c_puts = "puts"(str : Char*) : Int
+end
+
+$baz = Baz.new(100)
+$baz.show_instance_vars
+$baz.with_foofoo do
+ LibC.c_puts hello_world
+end
+
+
+
+MIME types defined: text/x-crystal.
+
diff --git a/mode/css/css.js b/mode/css/css.js
index b20b4907..e9656e3d 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -12,9 +12,8 @@
"use strict";
CodeMirror.defineMode("css", function(config, parserConfig) {
- var provided = parserConfig;
+ var inline = parserConfig.inline
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
- parserConfig.inline = provided.inline;
var indentUnit = config.indentUnit,
tokenHooks = parserConfig.tokenHooks,
@@ -368,9 +367,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
return {
startState: function(base) {
return {tokenize: null,
- state: parserConfig.inline ? "block" : "top",
+ state: inline ? "block" : "top",
stateArg: null,
- context: new Context(parserConfig.inline ? "block" : "top", base || 0, null)};
+ context: new Context(inline ? "block" : "top", base || 0, null)};
},
token: function(stream, state) {
@@ -453,8 +452,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"animation-direction", "animation-duration", "animation-fill-mode",
"animation-iteration-count", "animation-name", "animation-play-state",
"animation-timing-function", "appearance", "azimuth", "backface-visibility",
- "background", "background-attachment", "background-clip", "background-color",
- "background-image", "background-origin", "background-position",
+ "background", "background-attachment", "background-blend-mode", "background-clip",
+ "background-color", "background-image", "background-origin", "background-position",
"background-repeat", "background-size", "baseline-shift", "binding",
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
"bookmark-target", "border", "border-bottom", "border-bottom-color",
@@ -598,11 +597,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
"cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
- "col-resize", "collapse", "column", "column-reverse", "compact", "condensed", "contain", "content",
+ "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
+ "compact", "condensed", "contain", "content",
"content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
- "cross", "crosshair", "currentcolor", "cursive", "cyclic", "dashed", "decimal",
+ "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
"decimal-leading-zero", "default", "default-button", "destination-atop",
- "destination-in", "destination-out", "destination-over", "devanagari",
+ "destination-in", "destination-out", "destination-over", "devanagari", "difference",
"disc", "discard", "disclosure-closed", "disclosure-open", "document",
"dot-dash", "dot-dot-dash",
"dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
@@ -613,23 +613,23 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
- "ethiopic-numeric", "ew-resize", "expanded", "extends", "extra-condensed",
+ "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
- "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
- "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
"inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert",
"italic", "japanese-formal", "japanese-informal", "justify", "kannada",
"katakana", "katakana-iroha", "keep-all", "khmer",
"korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
- "landscape", "lao", "large", "larger", "left", "level", "lighter",
+ "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
"line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
- "lower-roman", "lowercase", "ltr", "malayalam", "match", "matrix", "matrix3d",
+ "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
"media-controls-background", "media-current-time-display",
"media-fullscreen-button", "media-mute-button", "media-play-button",
"media-return-to-realtime-button", "media-rewind-button",
@@ -638,7 +638,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
"menu", "menulist", "menulist-button", "menulist-text",
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
- "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
+ "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
"narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
"ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
@@ -652,7 +652,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
"rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
"rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
- "s-resize", "sans-serif", "scale", "scale3d", "scaleX", "scaleY", "scaleZ",
+ "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
"scroll", "scrollbar", "se-resize", "searchfield",
"searchfield-cancel-button", "searchfield-decoration",
"searchfield-results-button", "searchfield-results-decoration",
@@ -660,7 +660,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"simp-chinese-formal", "simp-chinese-informal", "single",
"skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
- "small", "small-caps", "small-caption", "smaller", "solid", "somali",
+ "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
"source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
"square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
"subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
diff --git a/mode/css/less_test.js b/mode/css/less_test.js
index 7b77f584..dd821558 100644
--- a/mode/css/less_test.js
+++ b/mode/css/less_test.js
@@ -26,10 +26,10 @@
MT("mixin",
"[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
- " [property color]: [variable darken]([variable-2 @color], [number 10%]);",
+ " [property color]: [atom darken]([variable-2 @color], [number 10%]);",
"}",
"[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
- " [property color]: [variable lighten]([variable-2 @color], [number 10%]);",
+ " [property color]: [atom lighten]([variable-2 @color], [number 10%]);",
"}",
"[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
" [property display]: [atom block];",
diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js
index 26c226a1..785921b3 100644
--- a/mode/css/scss_test.js
+++ b/mode/css/scss_test.js
@@ -95,7 +95,7 @@
MT('indent_parentheses',
"[tag foo] {",
- " [property color]: [variable darken]([variable-2 $blue],",
+ " [property color]: [atom darken]([variable-2 $blue],",
" [number 9%]);",
"}");
diff --git a/mode/dart/dart.js b/mode/dart/dart.js
index b1ee3776..d92eb519 100644
--- a/mode/dart/dart.js
+++ b/mode/dart/dart.js
@@ -15,7 +15,7 @@
"implements get native operator set typedef with enum throw rethrow " +
"assert break case continue default in return new deferred async await " +
"try catch finally do else for if switch while import library export " +
- "part of show hide is").split(" ");
+ "part of show hide is as").split(" ");
var blockKeywords = "try catch finally do else for if switch while".split(" ");
var atoms = "true false null".split(" ");
var builtins = "void bool num int double dynamic var String".split(" ");
@@ -46,7 +46,7 @@
atoms: set(atoms),
hooks: {
"@": function(stream) {
- stream.eatWhile(/[\w\$_]/);
+ stream.eatWhile(/[\w\$_\.]/);
return "meta";
},
diff --git a/mode/django/django.js b/mode/django/django.js
index 7fae876c..a8a7d831 100644
--- a/mode/django/django.js
+++ b/mode/django/django.js
@@ -35,11 +35,13 @@
"truncatechars_html", "truncatewords", "truncatewords_html",
"unordered_list", "upper", "urlencode", "urlize",
"urlizetrunc", "wordcount", "wordwrap", "yesno"],
- operators = ["==", "!=", "<", ">", "<=", ">=", "in", "not", "or", "and"];
+ operators = ["==", "!=", "<", ">", "<=", ">="],
+ wordOperators = ["in", "not", "or", "and"];
keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b");
filters = new RegExp("^\\b(" + filters.join("|") + ")\\b");
operators = new RegExp("^\\b(" + operators.join("|") + ")\\b");
+ wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b");
// We have to return "null" instead of null, in order to avoid string
// styling as the default, when using Django templates inside HTML
@@ -59,7 +61,7 @@
// Ignore completely any stream series that do not match the
// Django template opening tags.
- while (stream.next() != null && !stream.match("{{", false) && !stream.match("{%", false)) {}
+ while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {}
return null;
}
@@ -270,6 +272,11 @@
return "operator";
}
+ // Attempt to match a word operator
+ if (stream.match(wordOperators)) {
+ return "keyword";
+ }
+
// Attempt to match a keyword
var keywordMatch = stream.match(keywords);
if (keywordMatch) {
@@ -310,9 +317,8 @@
// Mark everything as comment inside the tag and the tag itself.
function inComment (stream, state) {
- if (stream.match("#}")) {
- state.tokenize = tokenBase;
- }
+ if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase
+ else stream.skipToEnd()
return "comment";
}
diff --git a/mode/fcl/fcl.js b/mode/fcl/fcl.js
new file mode 100644
index 00000000..51811697
--- /dev/null
+++ b/mode/fcl/fcl.js
@@ -0,0 +1,173 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("fcl", function(config) {
+ var indentUnit = config.indentUnit;
+
+ var keywords = {
+ "term": true,
+ "method": true, "accu": true,
+ "rule": true, "then": true, "is": true, "and": true, "or": true,
+ "if": true, "default": true
+ };
+
+ var start_blocks = {
+ "var_input": true,
+ "var_output": true,
+ "fuzzify": true,
+ "defuzzify": true,
+ "function_block": true,
+ "ruleblock": true
+ };
+
+ var end_blocks = {
+ "end_ruleblock": true,
+ "end_defuzzify": true,
+ "end_function_block": true,
+ "end_fuzzify": true,
+ "end_var": true
+ };
+
+ var atoms = {
+ "true": true, "false": true, "nan": true,
+ "real": true, "min": true, "max": true, "cog": true, "cogs": true
+ };
+
+ var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+
+ if (/[\d\.]/.test(ch)) {
+ 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";
+ }
+
+ if (ch == "/" || ch == "(") {
+ if (stream.eat("*")) {
+ state.tokenize = tokenComment;
+ return tokenComment(stream, state);
+ }
+ if (stream.eat("/")) {
+ stream.skipToEnd();
+ return "comment";
+ }
+ }
+ if (isOperatorChar.test(ch)) {
+ stream.eatWhile(isOperatorChar);
+ return "operator";
+ }
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+
+ var cur = stream.current().toLowerCase();
+ if (keywords.propertyIsEnumerable(cur) ||
+ start_blocks.propertyIsEnumerable(cur) ||
+ end_blocks.propertyIsEnumerable(cur)) {
+ return "keyword";
+ }
+ if (atoms.propertyIsEnumerable(cur)) return "atom";
+ return "variable";
+ }
+
+
+ function tokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if ((ch == "/" || ch == ")") && maybeEnd) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return "comment";
+ }
+
+ function Context(indented, column, type, align, prev) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.align = align;
+ this.prev = prev;
+ }
+
+ function pushContext(state, col, type) {
+ return state.context = new Context(state.indented, col, type, null, state.context);
+ }
+
+ function popContext(state) {
+ if (!state.context.prev) return;
+ var t = state.context.type;
+ if (t == "end_block")
+ state.indented = state.context.indented;
+ return state.context = state.context.prev;
+ }
+
+ // Interface
+
+ return {
+ startState: function(basecolumn) {
+ return {
+ tokenize: null,
+ context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
+ indented: 0,
+ startOfLine: true
+ };
+ },
+
+ token: function(stream, state) {
+ var ctx = state.context;
+ if (stream.sol()) {
+ if (ctx.align == null) ctx.align = false;
+ state.indented = stream.indentation();
+ state.startOfLine = true;
+ }
+ if (stream.eatSpace()) return null;
+
+ var style = (state.tokenize || tokenBase)(stream, state);
+ if (style == "comment") return style;
+ if (ctx.align == null) ctx.align = true;
+
+ var cur = stream.current().toLowerCase();
+
+ if (start_blocks.propertyIsEnumerable(cur)) pushContext(state, stream.column(), "end_block");
+ else if (end_blocks.propertyIsEnumerable(cur)) popContext(state);
+
+ state.startOfLine = false;
+ return style;
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != tokenBase && state.tokenize != null) return 0;
+ var ctx = state.context;
+
+ var closing = end_blocks.propertyIsEnumerable(textAfter);
+ if (ctx.align) return ctx.column + (closing ? 0 : 1);
+ else return ctx.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricChars: "ryk",
+ fold: "brace",
+ blockCommentStart: "(*",
+ blockCommentEnd: "*)",
+ lineComment: "//"
+ };
+});
+
+CodeMirror.defineMIME("text/x-fcl", "fcl");
+});
diff --git a/mode/fcl/index.html b/mode/fcl/index.html
new file mode 100644
index 00000000..3c18d0b3
--- /dev/null
+++ b/mode/fcl/index.html
@@ -0,0 +1,108 @@
+
+
+CodeMirror: FCL mode
+
+
+
+
+
+
+
+
+
+
+
+
+FCL mode
+
+ FUNCTION_BLOCK Fuzzy_FB
+ VAR_INPUT
+ TimeDay : REAL; (* RANGE(0 .. 23) *)
+ ApplicateHost: REAL;
+ TimeConfiguration: REAL;
+ TimeRequirements: REAL;
+ END_VAR
+
+ VAR_OUTPUT
+ ProbabilityDistribution: REAL;
+ ProbabilityAccess: REAL;
+ END_VAR
+
+ FUZZIFY TimeDay
+ TERM inside := (0, 0) (8, 1) (22,0);
+ TERM outside := (0, 1) (8, 0) (22, 1);
+ END_FUZZIFY
+
+ FUZZIFY ApplicateHost
+ TERM few := (0, 1) (100, 0) (200, 0);
+ TERM many := (0, 0) (100, 0) (200, 1);
+ END_FUZZIFY
+
+ FUZZIFY TimeConfiguration
+ TERM recently := (0, 1) (30, 1) (120, 0);
+ TERM long := (0, 0) (30, 0) (120, 1);
+ END_FUZZIFY
+
+ FUZZIFY TimeRequirements
+ TERM recently := (0, 1) (30, 1) (365, 0);
+ TERM long := (0, 0) (30, 0) (365, 1);
+ END_FUZZIFY
+
+ DEFUZZIFY ProbabilityAccess
+ TERM hight := 1;
+ TERM medium := 0.5;
+ TERM low := 0;
+ ACCU: MAX;
+ METHOD: COGS;
+ DEFAULT := 0;
+ END_DEFUZZIFY
+
+ DEFUZZIFY ProbabilityDistribution
+ TERM hight := 1;
+ TERM medium := 0.5;
+ TERM low := 0;
+ ACCU: MAX;
+ METHOD: COGS;
+ DEFAULT := 0;
+ END_DEFUZZIFY
+
+ RULEBLOCK No1
+ AND : MIN;
+ RULE 1 : IF TimeDay IS outside AND ApplicateHost IS few THEN ProbabilityAccess IS hight;
+ RULE 2 : IF ApplicateHost IS many THEN ProbabilityAccess IS hight;
+ RULE 3 : IF TimeDay IS inside AND ApplicateHost IS few THEN ProbabilityAccess IS low;
+ END_RULEBLOCK
+
+ RULEBLOCK No2
+ AND : MIN;
+ RULE 1 : IF ApplicateHost IS many THEN ProbabilityDistribution IS hight;
+ END_RULEBLOCK
+
+ END_FUNCTION_BLOCK
+
+
+
+
+ MIME type: text/x-fcl
+
diff --git a/mode/haml/haml.js b/mode/haml/haml.js
index 8fe63b02..03ce8335 100644
--- a/mode/haml/haml.js
+++ b/mode/haml/haml.js
@@ -85,8 +85,10 @@
state.tokenize = rubyInQuote(")");
return state.tokenize(stream, state);
} else if (ch == "{") {
- state.tokenize = rubyInQuote("}");
- return state.tokenize(stream, state);
+ if (!stream.match(/^\{%.*/)) {
+ state.tokenize = rubyInQuote("}");
+ return state.tokenize(stream, state);
+ }
}
}
diff --git a/mode/handlebars/handlebars.js b/mode/handlebars/handlebars.js
index 40dfea42..2174e538 100644
--- a/mode/handlebars/handlebars.js
+++ b/mode/handlebars/handlebars.js
@@ -3,15 +3,15 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"), require("../../addon/mode/simple"));
+ mod(require("../../lib/codemirror"), require("../../addon/mode/simple"), require("../../addon/mode/multiplex"));
else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror", "../../addon/mode/simple"], mod);
+ define(["../../lib/codemirror", "../../addon/mode/simple", "../../addon/mode/multiplex"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
- CodeMirror.defineSimpleMode("handlebars", {
+ CodeMirror.defineSimpleMode("handlebars-tags", {
start: [
{ regex: /\{\{!--/, push: "dash_comment", token: "comment" },
{ regex: /\{\{!/, push: "comment", token: "comment" },
@@ -21,8 +21,8 @@
{ regex: /\}\}/, pop: true, token: "tag" },
// Double and single quotes
- { regex: /"(?:[^\\]|\\.)*?"/, token: "string" },
- { regex: /'(?:[^\\]|\\.)*?'/, token: "string" },
+ { regex: /"(?:[^\\"]|\\.)*"?/, token: "string" },
+ { regex: /'(?:[^\\']|\\.)*'?/, token: "string" },
// Handlebars keywords
{ regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" },
@@ -49,5 +49,14 @@
]
});
+ CodeMirror.defineMode("handlebars", function(config, parserConfig) {
+ var handlebars = CodeMirror.getMode(config, "handlebars-tags");
+ if (!parserConfig || !parserConfig.base) return handlebars;
+ return CodeMirror.multiplexingMode(
+ CodeMirror.getMode(config, parserConfig.base),
+ {open: "{{", close: "}}", mode: handlebars, parseDelimiters: true}
+ );
+ });
+
CodeMirror.defineMIME("text/x-handlebars-template", "handlebars");
});
diff --git a/mode/handlebars/index.html b/mode/handlebars/index.html
index beaef87a..b1bfad1c 100644
--- a/mode/handlebars/index.html
+++ b/mode/handlebars/index.html
@@ -61,23 +61,19 @@ {{t 'article_list'}}
-
-
+
Handlebars syntax highlighting for CodeMirror.
MIME types defined: text/x-handlebars-template
+
+ Supported options: base to set the mode to
+ wrap. For example, use
+ mode: {name: "handlebars", base: "text/html"}
+ to highlight an HTML template.
diff --git a/mode/haskell-literate/haskell-literate.js b/mode/haskell-literate/haskell-literate.js
new file mode 100644
index 00000000..9358994d
--- /dev/null
+++ b/mode/haskell-literate/haskell-literate.js
@@ -0,0 +1,43 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function (mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../haskell/haskell"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../haskell/haskell"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function (CodeMirror) {
+ "use strict"
+
+ CodeMirror.defineMode("haskell-literate", function (config, parserConfig) {
+ var baseMode = CodeMirror.getMode(config, (parserConfig && parserConfig.base) || "haskell")
+
+ return {
+ startState: function () {
+ return {
+ inCode: false,
+ baseState: CodeMirror.startState(baseMode)
+ }
+ },
+ token: function (stream, state) {
+ if (stream.sol()) {
+ if (state.inCode = stream.eat(">"))
+ return "meta"
+ }
+ if (state.inCode) {
+ return baseMode.token(stream, state.baseState)
+ } else {
+ stream.skipToEnd()
+ return "comment"
+ }
+ },
+ innerMode: function (state) {
+ return state.inCode ? {state: state.baseState, mode: baseMode} : null
+ }
+ }
+ }, "haskell")
+
+ CodeMirror.defineMIME("text/x-literate-haskell", "haskell-literate")
+})
diff --git a/mode/haskell-literate/index.html b/mode/haskell-literate/index.html
new file mode 100644
index 00000000..8c9bc60d
--- /dev/null
+++ b/mode/haskell-literate/index.html
@@ -0,0 +1,282 @@
+
+
+
CodeMirror: Haskell-literate mode
+
+
+
+
+
+
+
+
+
+
+
+ Haskell literate mode
+
+
+> {-# LANGUAGE OverloadedStrings #-}
+> {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
+> import Control.Applicative ((<$>), (<*>))
+> import Data.Maybe (isJust)
+
+> import Data.Text (Text)
+> import Text.Blaze ((!))
+> import qualified Data.Text as T
+> import qualified Happstack.Server as Happstack
+> import qualified Text.Blaze.Html5 as H
+> import qualified Text.Blaze.Html5.Attributes as A
+
+> import Text.Digestive
+> import Text.Digestive.Blaze.Html5
+> import Text.Digestive.Happstack
+> import Text.Digestive.Util
+
+Simple forms and validation
+---------------------------
+
+Let's start by creating a very simple datatype to represent a user:
+
+> data User = User
+> { userName :: Text
+> , userMail :: Text
+> } deriving (Show)
+
+And dive in immediately to create a `Form` for a user. The `Form v m a` type
+has three parameters:
+
+- `v`: the type for messages and errors (usually a `String`-like type, `Text` in
+ this case);
+- `m`: the monad we are operating in, not specified here;
+- `a`: the return type of the `Form`, in this case, this is obviously `User`.
+
+> userForm :: Monad m => Form Text m User
+
+We create forms by using the `Applicative` interface. A few form types are
+provided in the `Text.Digestive.Form` module, such as `text`, `string`,
+`bool`...
+
+In the `digestive-functors` library, the developer is required to label each
+field using the `.:` operator. This might look like a bit of a burden, but it
+allows you to do some really useful stuff, like separating the `Form` from the
+actual HTML layout.
+
+> userForm = User
+> <$> "name" .: text Nothing
+> <*> "mail" .: check "Not a valid email address" checkEmail (text Nothing)
+
+The `check` function enables you to validate the result of a form. For example,
+we can validate the email address with a really naive `checkEmail` function.
+
+> checkEmail :: Text -> Bool
+> checkEmail = isJust . T.find (== '@')
+
+More validation
+---------------
+
+For our example, we also want descriptions of Haskell libraries, and in order to
+do that, we need package versions...
+
+> type Version = [Int]
+
+We want to let the user input a version number such as `0.1.0.0`. This means we
+need to validate if the input `Text` is of this form, and then we need to parse
+it to a `Version` type. Fortunately, we can do this in a single function:
+`validate` allows conversion between values, which can optionally fail.
+
+`readMaybe :: Read a => String -> Maybe a` is a utility function imported from
+`Text.Digestive.Util`.
+
+> validateVersion :: Text -> Result Text Version
+> validateVersion = maybe (Error "Cannot parse version") Success .
+> mapM (readMaybe . T.unpack) . T.split (== '.')
+
+A quick test in GHCi:
+
+ ghci> validateVersion (T.pack "0.3.2.1")
+ Success [0,3,2,1]
+ ghci> validateVersion (T.pack "0.oops")
+ Error "Cannot parse version"
+
+It works! This means we can now easily add a `Package` type and a `Form` for it:
+
+> data Category = Web | Text | Math
+> deriving (Bounded, Enum, Eq, Show)
+
+> data Package = Package Text Version Category
+> deriving (Show)
+
+> packageForm :: Monad m => Form Text m Package
+> packageForm = Package
+> <$> "name" .: text Nothing
+> <*> "version" .: validate validateVersion (text (Just "0.0.0.1"))
+> <*> "category" .: choice categories Nothing
+> where
+> categories = [(x, T.pack (show x)) | x <- [minBound .. maxBound]]
+
+Composing forms
+---------------
+
+A release has an author and a package. Let's use this to illustrate the
+composability of the digestive-functors library: we can reuse the forms we have
+written earlier on.
+
+> data Release = Release User Package
+> deriving (Show)
+
+> releaseForm :: Monad m => Form Text m Release
+> releaseForm = Release
+> <$> "author" .: userForm
+> <*> "package" .: packageForm
+
+Views
+-----
+
+As mentioned before, one of the advantages of using digestive-functors is
+separation of forms and their actual HTML layout. In order to do this, we have
+another type, `View`.
+
+We can get a `View` from a `Form` by supplying input. A `View` contains more
+information than a `Form`, it has:
+
+- the original form;
+- the input given by the user;
+- any errors that have occurred.
+
+It is this view that we convert to HTML. For this tutorial, we use the
+[blaze-html] library, and some helpers from the `digestive-functors-blaze`
+library.
+
+[blaze-html]: http://jaspervdj.be/blaze/
+
+Let's write a view for the `User` form. As you can see, we here refer to the
+different fields in the `userForm`. The `errorList` will generate a list of
+errors for the `"mail"` field.
+
+> userView :: View H.Html -> H.Html
+> userView view = do
+> label "name" view "Name: "
+> inputText "name" view
+> H.br
+>
+> errorList "mail" view
+> label "mail" view "Email address: "
+> inputText "mail" view
+> H.br
+
+Like forms, views are also composable: let's illustrate that by adding a view
+for the `releaseForm`, in which we reuse `userView`. In order to do this, we
+take only the parts relevant to the author from the view by using `subView`. We
+can then pass the resulting view to our own `userView`.
+We have no special view code for `Package`, so we can just add that to
+`releaseView` as well. `childErrorList` will generate a list of errors for each
+child of the specified form. In this case, this means a list of errors from
+`"package.name"` and `"package.version"`. Note how we use `foo.bar` to refer to
+nested forms.
+
+> releaseView :: View H.Html -> H.Html
+> releaseView view = do
+> H.h2 "Author"
+> userView $ subView "author" view
+>
+> H.h2 "Package"
+> childErrorList "package" view
+>
+> label "package.name" view "Name: "
+> inputText "package.name" view
+> H.br
+>
+> label "package.version" view "Version: "
+> inputText "package.version" view
+> H.br
+>
+> label "package.category" view "Category: "
+> inputSelect "package.category" view
+> H.br
+
+The attentive reader might have wondered what the type parameter for `View` is:
+it is the `String`-like type used for e.g. error messages.
+But wait! We have
+ releaseForm :: Monad m => Form Text m Release
+ releaseView :: View H.Html -> H.Html
+... doesn't this mean that we need a `View Text` rather than a `View Html`? The
+answer is yes -- but having `View Html` allows us to write these views more
+easily with the `digestive-functors-blaze` library. Fortunately, we will be able
+to fix this using the `Functor` instance of `View`.
+ fmap :: Monad m => (v -> w) -> View v -> View w
+A backend
+---------
+To finish this tutorial, we need to be able to actually run this code. We need
+an HTTP server for that, and we use [Happstack] for this tutorial. The
+`digestive-functors-happstack` library gives about everything we need for this.
+[Happstack]: http://happstack.com/
+
+> site :: Happstack.ServerPart Happstack.Response
+> site = do
+> Happstack.decodeBody $ Happstack.defaultBodyPolicy "/tmp" 4096 4096 4096
+> r <- runForm "test" releaseForm
+> case r of
+> (view, Nothing) -> do
+> let view' = fmap H.toHtml view
+> Happstack.ok $ Happstack.toResponse $
+> template $
+> form view' "/" $ do
+> releaseView view'
+> H.br
+> inputSubmit "Submit"
+> (_, Just release) -> Happstack.ok $ Happstack.toResponse $
+> template $ do
+> css
+> H.h1 "Release received"
+> H.p $ H.toHtml $ show release
+>
+> main :: IO ()
+> main = Happstack.simpleHTTP Happstack.nullConf site
+
+Utilities
+---------
+
+> template :: H.Html -> H.Html
+> template body = H.docTypeHtml $ do
+> H.head $ do
+> H.title "digestive-functors tutorial"
+> css
+> H.body body
+> css :: H.Html
+> css = H.style ! A.type_ "text/css" $ do
+> "label {width: 130px; float: left; clear: both}"
+> "ul.digestive-functors-error-list {"
+> " color: red;"
+> " list-style-type: none;"
+> " padding-left: 0px;"
+> "}"
+
+
+
+ MIME types
+ defined: text/x-literate-haskell.
+
+ Parser configuration parameters recognized: base to
+ set the base mode (defaults to "haskell").
+
+
+
+
diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js
index 21e74f16..6574fbd5 100644
--- a/mode/htmlmixed/htmlmixed.js
+++ b/mode/htmlmixed/htmlmixed.js
@@ -44,13 +44,9 @@
return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
}
- function getAttrValue(stream, attr) {
- var pos = stream.pos, match;
- while (pos >= 0 && stream.string.charAt(pos) !== "<") pos--;
- if (pos < 0) return pos;
- if (match = stream.string.slice(pos, stream.pos).match(getAttrRegexp(attr)))
- return match[2];
- return "";
+ function getAttrValue(text, attr) {
+ var match = text.match(getAttrRegexp(attr))
+ return match ? match[2] : ""
}
function getTagRegexp(tagName, anchored) {
@@ -66,10 +62,10 @@
}
}
- function findMatchingMode(tagInfo, stream) {
+ function findMatchingMode(tagInfo, tagText) {
for (var i = 0; i < tagInfo.length; i++) {
var spec = tagInfo[i];
- if (!spec[0] || spec[1].test(getAttrValue(stream, spec[0]))) return spec[2];
+ if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
}
}
@@ -89,15 +85,17 @@
tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
function html(stream, state) {
- var tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase();
- var tagInfo = tagName && tags.hasOwnProperty(tagName) && tags[tagName];
-
- var style = htmlMode.token(stream, state.htmlState), modeSpec;
-
- if (tagInfo && /\btag\b/.test(style) && stream.current() === ">" &&
- (modeSpec = findMatchingMode(tagInfo, stream))) {
- var mode = CodeMirror.getMode(config, modeSpec);
- var endTagA = getTagRegexp(tagName, true), endTag = getTagRegexp(tagName, false);
+ var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
+ if (tag && !/[<>\s\/]/.test(stream.current()) &&
+ (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
+ tags.hasOwnProperty(tagName)) {
+ state.inTag = tagName + " "
+ } else if (state.inTag && tag && />$/.test(stream.current())) {
+ var inTag = /^([\S]+) (.*)/.exec(state.inTag)
+ state.inTag = null
+ var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
+ var mode = CodeMirror.getMode(config, modeSpec)
+ var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
state.token = function (stream, state) {
if (stream.match(endTagA, false)) {
state.token = html;
@@ -108,6 +106,9 @@
};
state.localMode = mode;
state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
+ } else if (state.inTag) {
+ state.inTag += stream.current()
+ if (stream.eol()) state.inTag += " "
}
return style;
};
@@ -115,7 +116,7 @@
return {
startState: function () {
var state = htmlMode.startState();
- return {token: html, localMode: null, localState: null, htmlState: state};
+ return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
},
copyState: function (state) {
@@ -123,7 +124,8 @@
if (state.localState) {
local = CodeMirror.copyState(state.localMode, state.localState);
}
- return {token: state.token, localMode: state.localMode, localState: local,
+ return {token: state.token, inTag: state.inTag,
+ localMode: state.localMode, localState: local,
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
},
diff --git a/mode/index.html b/mode/index.html
index 044dee7f..08f5e97b 100644
--- a/mode/index.html
+++ b/mode/index.html
@@ -42,6 +42,7 @@
Language modes
COBOL
CoffeeScript
Common Lisp
+
Crystal
CSS
Cypher
Cython
@@ -58,6 +59,7 @@
Language modes
Elm
Erlang
Factor
+
FCL
Forth
Fortran
F#
@@ -67,7 +69,7 @@
Language modes
Groovy
HAML
Handlebars
-
Haskell
+
Haskell (Literate )
Haxe
HTML embedded (JSP, ASP.NET)
HTML mixed-mode
@@ -75,7 +77,7 @@
Language modes
IDL
Java
Jade
-
JavaScript
+
JavaScript (JSX )
Jinja2
Julia
Kotlin
@@ -86,6 +88,7 @@
Language modes
Mathematica
mIRC
Modelica
+
MscGen
MUMPS
Nginx
NSIS
@@ -147,6 +150,7 @@
Language modes
XML/HTML
XQuery
YAML
+
YAML frontmatter
Z80
diff --git a/mode/jade/jade.js b/mode/jade/jade.js
index 96fadb19..1db069a9 100644
--- a/mode/jade/jade.js
+++ b/mode/jade/jade.js
@@ -74,7 +74,7 @@ CodeMirror.defineMode('jade', function (config) {
res.javaScriptArguments = this.javaScriptArguments;
res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
res.isInterpolating = this.isInterpolating;
- res.interpolationNesting = this.intpolationNesting;
+ res.interpolationNesting = this.interpolationNesting;
res.jsState = CodeMirror.copyState(jsMode, this.jsState);
@@ -167,7 +167,7 @@ CodeMirror.defineMode('jade', function (config) {
if (state.interpolationNesting < 0) {
stream.next();
state.isInterpolating = false;
- return 'puncutation';
+ return 'punctuation';
}
} else if (stream.peek() === '{') {
state.interpolationNesting++;
@@ -583,7 +583,7 @@ CodeMirror.defineMode('jade', function (config) {
copyState: copyState,
token: nextToken
};
-});
+}, 'javascript', 'css', 'htmlmixed');
CodeMirror.defineMIME('text/x-jade', 'jade');
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index b961b890..fa5721d5 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -13,6 +13,11 @@
})(function(CodeMirror) {
"use strict";
+function expressionAllowed(stream, state, backUp) {
+ return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+ (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
+}
+
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
@@ -32,12 +37,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
- "async": kw("async"), "function": kw("function"), "catch": kw("catch"),
+ "function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
"this": kw("this"), "class": kw("class"), "super": kw("atom"),
- "await": C, "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
+ "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
};
// Extend the 'normal' keywords with the TypeScript language extensions
@@ -45,15 +50,20 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var type = {type: "variable", style: "variable-3"};
var tsKeywords = {
// object-like things
- "interface": kw("interface"),
- "extends": kw("extends"),
- "constructor": kw("constructor"),
+ "interface": kw("class"),
+ "implements": C,
+ "namespace": C,
+ "module": kw("module"),
+ "enum": kw("module"),
// scope modifiers
- "public": kw("public"),
- "private": kw("private"),
- "protected": kw("protected"),
- "static": kw("static"),
+ "public": kw("modifier"),
+ "private": kw("modifier"),
+ "protected": kw("modifier"),
+ "abstract": kw("modifier"),
+
+ // operators
+ "as": operator,
// types
"string": type, "number": type, "boolean": type, "any": type
@@ -121,8 +131,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
- } else if (state.lastType == "operator" || state.lastType == "keyword c" ||
- state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
+ } else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
return ret("regexp", "string-2");
@@ -356,6 +365,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+ if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
@@ -373,7 +383,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
- if (type == "async") return cont(expression);
if (type == "function") return cont(functiondef, maybeop);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
@@ -452,9 +461,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
function objprop(type, value) {
- if (type == "async") {
- return cont(objprop);
- } else if (type == "variable" || cx.style == "keyword") {
+ if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
return cont(afterprop);
@@ -463,8 +470,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return cont(afterprop);
} else if (type == "jsonld-keyword") {
return cont(afterprop);
+ } else if (type == "modifier") {
+ return cont(objprop)
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
+ } else if (type == "spread") {
+ return cont(expression);
}
}
function getterSetter(type) {
@@ -513,6 +524,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return pass(pattern, maybetype, maybeAssign, vardefCont);
}
function pattern(type, value) {
+ if (type == "modifier") return cont(pattern)
if (type == "variable") { register(value); return cont(); }
if (type == "spread") return cont(pattern);
if (type == "[") return contCommasep(pattern, "]");
@@ -525,6 +537,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern);
+ if (type == "}") return pass();
return cont(expect(":"), pattern, maybeAssign);
}
function maybeAssign(_type, value) {
@@ -647,7 +660,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
- indented: 0
+ indented: basecolumn || 0
};
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
state.globalVars = parserConfig.globalVars;
@@ -703,7 +716,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
helperType: jsonMode ? "json" : "javascript",
jsonldMode: jsonldMode,
- jsonMode: jsonMode
+ jsonMode: jsonMode,
+
+ expressionAllowed: expressionAllowed,
+ skipExpression: function(state) {
+ var top = state.cc[state.cc.length - 1]
+ if (top == expression || top == expressionNoComma) state.cc.pop()
+ }
};
});
diff --git a/mode/javascript/test.js b/mode/javascript/test.js
index 252e064d..cb43d089 100644
--- a/mode/javascript/test.js
+++ b/mode/javascript/test.js
@@ -17,6 +17,10 @@
" [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
"})();");
+ MT("destructure_trailing_comma",
+ "[keyword let] {[def a], [def b],} [operator =] [variable foo];",
+ "[keyword let] [def c];"); // Parser still in good state?
+
MT("class_body",
"[keyword class] [def Foo] {",
" [property constructor]() {}",
diff --git a/mode/jsx/index.html b/mode/jsx/index.html
new file mode 100644
index 00000000..cb51edb3
--- /dev/null
+++ b/mode/jsx/index.html
@@ -0,0 +1,89 @@
+
+
+CodeMirror: JSX mode
+
+
+
+
+
+
+
+
+
+
+
+
+JSX mode
+
+
+
+
+
+JSX Mode for React 's
+JavaScript syntax extension.
+
+MIME types defined: text/jsx.
+
+
diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js
new file mode 100644
index 00000000..bc2ea797
--- /dev/null
+++ b/mode/jsx/jsx.js
@@ -0,0 +1,147 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function(CodeMirror) {
+ "use strict"
+
+ // Depth means the amount of open braces in JS context, in XML
+ // context 0 means not in tag, 1 means in tag, and 2 means in tag
+ // and js block comment.
+ function Context(state, mode, depth, prev) {
+ this.state = state; this.mode = mode; this.depth = depth; this.prev = prev
+ }
+
+ function copyContext(context) {
+ return new Context(CodeMirror.copyState(context.mode, context.state),
+ context.mode,
+ context.depth,
+ context.prev && copyContext(context.prev))
+ }
+
+ CodeMirror.defineMode("jsx", function(config) {
+ var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+ var jsMode = CodeMirror.getMode(config, "javascript")
+
+ function flatXMLIndent(state) {
+ var tagName = state.tagName
+ state.tagName = null
+ var result = xmlMode.indent(state, "")
+ state.tagName = tagName
+ return result
+ }
+
+ function token(stream, state) {
+ if (state.context.mode == xmlMode)
+ return xmlToken(stream, state, state.context)
+ else
+ return jsToken(stream, state, state.context)
+ }
+
+ function xmlToken(stream, state, cx) {
+ if (cx.depth == 2) { // Inside a JS /* */ comment
+ if (stream.match(/^.*?\*\//)) cx.depth = 1
+ else stream.skipToEnd()
+ return "comment"
+ }
+
+ if (stream.peek() == "{") {
+ xmlMode.skipAttribute(cx.state)
+
+ var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context
+ // If JS starts on same line as tag
+ if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) {
+ while (xmlContext.prev && !xmlContext.startOfLine)
+ xmlContext = xmlContext.prev
+ // If tag starts the line, use XML indentation level
+ if (xmlContext.startOfLine) indent -= config.indentUnit
+ // Else use JS indentation level
+ else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented
+ // Else if inside of tag
+ } else if (cx.depth == 1) {
+ indent += config.indentUnit
+ }
+
+ state.context = new Context(CodeMirror.startState(jsMode, indent),
+ jsMode, 0, state.context)
+ return null
+ }
+
+ if (cx.depth == 1) { // Inside of tag
+ if (stream.peek() == "<") { // Tag inside of tag
+ xmlMode.skipAttribute(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),
+ xmlMode, 0, state.context)
+ return null
+ } else if (stream.match("//")) {
+ stream.skipToEnd()
+ return "comment"
+ } else if (stream.match("/*")) {
+ cx.depth = 2
+ return token(stream, state)
+ }
+ }
+
+ var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop
+ if (/\btag\b/.test(style)) {
+ if (/>$/.test(cur)) {
+ if (cx.state.context) cx.depth = 0
+ else state.context = state.context.prev
+ } else if (/^ -1) {
+ stream.backUp(cur.length - stop)
+ }
+ return style
+ }
+
+ function jsToken(stream, state, cx) {
+ if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
+ jsMode.skipExpression(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
+ xmlMode, 0, state.context)
+ return null
+ }
+
+ var style = jsMode.token(stream, cx.state)
+ if (!style && cx.depth != null) {
+ var cur = stream.current()
+ if (cur == "{") {
+ cx.depth++
+ } else if (cur == "}") {
+ if (--cx.depth == 0) state.context = state.context.prev
+ }
+ }
+ return style
+ }
+
+ return {
+ startState: function() {
+ return {context: new Context(CodeMirror.startState(jsMode), jsMode)}
+ },
+
+ copyState: function(state) {
+ return {context: copyContext(state.context)}
+ },
+
+ token: token,
+
+ indent: function(state, textAfter, fullLine) {
+ return state.context.mode.indent(state.context.state, textAfter, fullLine)
+ },
+
+ innerMode: function(state) {
+ return state.context
+ }
+ }
+ }, "xml", "javascript")
+
+ CodeMirror.defineMIME("text/jsx", "jsx")
+})
diff --git a/mode/jsx/test.js b/mode/jsx/test.js
new file mode 100644
index 00000000..c54a8b24
--- /dev/null
+++ b/mode/jsx/test.js
@@ -0,0 +1,69 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function() {
+ var mode = CodeMirror.getMode({indentUnit: 2}, "jsx")
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) }
+
+ MT("selfclose",
+ "[keyword var] [def x] [operator =] [bracket&tag <] [tag foo] [bracket&tag />] [operator +] [number 1];")
+
+ MT("openclose",
+ "([bracket&tag <][tag foo][bracket&tag >]hello [atom &][bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("attr",
+ "([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &][bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("braced_attr",
+ "([bracket&tag <][tag foo] [attribute abc]={[number 10]}[bracket&tag >]hello [atom &][bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("braced_text",
+ "([bracket&tag <][tag foo][bracket&tag >]hello {[number 10]} [atom &][bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("nested_tag",
+ "([bracket&tag <][tag foo][bracket&tag ><][tag bar][bracket&tag >][tag bar][bracket&tag >][tag foo][bracket&tag >][operator ++])")
+
+ MT("nested_jsx",
+ "[keyword return] (",
+ " [bracket&tag <][tag foo][bracket&tag >]",
+ " say {[number 1] [operator +] [bracket&tag <][tag bar] [attribute attr]={[number 10]}[bracket&tag />]}!",
+ " [bracket&tag ][tag foo][bracket&tag >][operator ++]",
+ ")")
+
+ MT("preserve_js_context",
+ "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]")
+
+ MT("line_comment",
+ "([bracket&tag <][tag foo] [comment // hello]",
+ " [bracket&tag >][tag foo][bracket&tag >][operator ++])")
+
+ MT("line_comment_not_in_tag",
+ "([bracket&tag <][tag foo][bracket&tag >] // hello",
+ " [bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("block_comment",
+ "([bracket&tag <][tag foo] [comment /* hello]",
+ "[comment line 2]",
+ "[comment line 3 */] [bracket&tag >][tag foo][bracket&tag >][operator ++])")
+
+ MT("block_comment_not_in_tag",
+ "([bracket&tag <][tag foo][bracket&tag >]/* hello",
+ " line 2",
+ " line 3 */ [bracket&tag ][tag foo][bracket&tag >][operator ++])")
+
+ MT("missing_attr",
+ "([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])")
+
+ MT("indent_js",
+ "([bracket&tag <][tag foo][bracket&tag >]",
+ " [bracket&tag <][tag bar] [attribute baz]={[keyword function]() {",
+ " [keyword return] [number 10]",
+ " }}[bracket&tag />]",
+ " [bracket&tag ][tag foo][bracket&tag >])")
+
+ MT("spread",
+ "([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])")
+
+ MT("tag_attribute",
+ "([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])")
+})()
diff --git a/mode/julia/julia.js b/mode/julia/julia.js
index 17710647..004de443 100644
--- a/mode/julia/julia.js
+++ b/mode/julia/julia.js
@@ -14,39 +14,45 @@
CodeMirror.defineMode("julia", function(_conf, parserConf) {
var ERRORCLASS = 'error';
- function wordRegexp(words) {
- return new RegExp("^((" + words.join(")|(") + "))\\b");
+ function wordRegexp(words, end) {
+ if (typeof end === 'undefined') { end = "\\b"; }
+ return new RegExp("^((" + words.join(")|(") + "))" + end);
}
- var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b/;
+ var octChar = "\\\\[0-7]{1,3}";
+ var hexChar = "\\\\x[A-Fa-f0-9]{1,2}";
+ var specialChar = "\\\\[abfnrtv0%?'\"\\\\]";
+ var singleChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])";
+ var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/;
var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
- var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*!*/;
+ var identifiers = parserConf.identifiers || /^[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/;
+ var charsList = [octChar, hexChar, specialChar, singleChar];
var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
var blockClosers = ["end", "else", "elseif", "catch", "finally"];
- var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall'];
- var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf'];
+ var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype'];
+ var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf'];
//var stringPrefixes = new RegExp("^[br]?('|\")")
- var stringPrefixes = /^(`|'|"{3}|([br]?"))/;
+ var stringPrefixes = /^(`|"{3}|([brv]?"))/;
+ var chars = wordRegexp(charsList, "'");
var keywords = wordRegexp(keywordList);
var builtins = wordRegexp(builtinList);
var openers = wordRegexp(blockOpeners);
var closers = wordRegexp(blockClosers);
- var macro = /^@[_A-Za-z][_A-Za-z0-9]*/;
- var symbol = /^:[_A-Za-z][_A-Za-z0-9]*/;
+ var macro = /^@[_A-Za-z][\w]*/;
+ var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/;
+ var typeAnnotation = /^::[^,;"{()=$\s]+({[^}]*}+)*/;
- function in_array(state) {
- var ch = cur_scope(state);
- if(ch=="[" || ch=="{") {
+ function inArray(state) {
+ var ch = currentScope(state);
+ if (ch == '[') {
return true;
}
- else {
- return false;
- }
+ return false;
}
- function cur_scope(state) {
- if(state.scopes.length==0) {
+ function currentScope(state) {
+ if (state.scopes.length == 0) {
return null;
}
return state.scopes[state.scopes.length - 1];
@@ -54,20 +60,25 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
// tokenizers
function tokenBase(stream, state) {
+ // Handle multiline comments
+ if (stream.match(/^#=/, false)) {
+ state.tokenize = tokenComment;
+ return state.tokenize(stream, state);
+ }
+
// Handle scope changes
- var leaving_expr = state.leaving_expr;
- if(stream.sol()) {
- leaving_expr = false;
+ var leavingExpr = state.leavingExpr;
+ if (stream.sol()) {
+ leavingExpr = false;
}
- state.leaving_expr = false;
- if(leaving_expr) {
- if(stream.match(/^'+/)) {
+ state.leavingExpr = false;
+ if (leavingExpr) {
+ if (stream.match(/^'+/)) {
return 'operator';
}
-
}
- if(stream.match(/^\.{2,3}/)) {
+ if (stream.match(/^\.{2,3}/)) {
return 'operator';
}
@@ -76,99 +87,95 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
}
var ch = stream.peek();
- // Handle Comments
+
+ // Handle single line comments
if (ch === '#') {
- stream.skipToEnd();
- return 'comment';
+ stream.skipToEnd();
+ return 'comment';
}
- if(ch==='[') {
- state.scopes.push("[");
+
+ if (ch === '[') {
+ state.scopes.push('[');
}
- if(ch==='{') {
- state.scopes.push("{");
+ if (ch === '(') {
+ state.scopes.push('(');
}
- var scope=cur_scope(state);
+ var scope = currentScope(state);
- if(scope==='[' && ch===']') {
+ if (scope == '[' && ch === ']') {
state.scopes.pop();
- state.leaving_expr=true;
+ state.leavingExpr = true;
}
- if(scope==='{' && ch==='}') {
+ if (scope == '(' && ch === ')') {
state.scopes.pop();
- state.leaving_expr=true;
- }
-
- if(ch===')') {
- state.leaving_expr = true;
+ state.leavingExpr = true;
}
var match;
- if(!in_array(state) && (match=stream.match(openers, false))) {
+ if (!inArray(state) && (match=stream.match(openers, false))) {
state.scopes.push(match);
}
- if(!in_array(state) && stream.match(closers, false)) {
+ if (!inArray(state) && stream.match(closers, false)) {
state.scopes.pop();
}
- if(in_array(state)) {
- if(stream.match(/^end/)) {
+ if (inArray(state)) {
+ if (state.lastToken == 'end' && stream.match(/^:/)) {
+ return 'operator';
+ }
+ if (stream.match(/^end/)) {
return 'number';
}
-
}
- if(stream.match(/^=>/)) {
+ if (stream.match(/^=>/)) {
return 'operator';
}
-
// Handle Number Literals
if (stream.match(/^[0-9\.]/, false)) {
var imMatcher = RegExp(/^im\b/);
- var floatLiteral = false;
+ var numberLiteral = false;
// Floats
- if (stream.match(/^\d*\.(?!\.)\d+([ef][\+\-]?\d+)?/i)) { floatLiteral = true; }
- if (stream.match(/^\d+\.(?!\.)\d*/)) { floatLiteral = true; }
- if (stream.match(/^\.\d+/)) { floatLiteral = true; }
- if (floatLiteral) {
- // Float literals may be "imaginary"
- stream.match(imMatcher);
- state.leaving_expr = true;
- return 'number';
- }
+ if (stream.match(/^\d*\.(?!\.)\d*([Eef][\+\-]?\d+)?/i)) { numberLiteral = true; }
+ if (stream.match(/^\d+\.(?!\.)\d*/)) { numberLiteral = true; }
+ if (stream.match(/^\.\d+/)) { numberLiteral = true; }
+ if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { numberLiteral = true; }
// Integers
- var intLiteral = false;
- // Hex
- if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
- // Binary
- if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
- // Octal
- if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
- // Decimal
- if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
- intLiteral = true;
- }
+ if (stream.match(/^0x[0-9a-f]+/i)) { numberLiteral = true; } // Hex
+ if (stream.match(/^0b[01]+/i)) { numberLiteral = true; } // Binary
+ if (stream.match(/^0o[0-7]+/i)) { numberLiteral = true; } // Octal
+ if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal
// Zero by itself with no other piece of number.
- if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
- if (intLiteral) {
+ if (stream.match(/^0(?![\dx])/i)) { numberLiteral = true; }
+ if (numberLiteral) {
// Integer literals may be "long"
stream.match(imMatcher);
- state.leaving_expr = true;
+ state.leavingExpr = true;
return 'number';
}
}
- if(stream.match(/^(::)|(<:)/)) {
+ if (stream.match(/^<:/)) {
return 'operator';
}
+ if (stream.match(typeAnnotation)) {
+ return 'builtin';
+ }
+
// Handle symbols
- if(!leaving_expr && stream.match(symbol)) {
- return 'string';
+ if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) {
+ return 'builtin';
+ }
+
+ // Handle parametric types
+ if (stream.match(/^{[^}]*}(?=\()/)) {
+ return 'builtin';
}
// Handle operators and Delimiters
@@ -176,6 +183,11 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return 'operator';
}
+ // Handle Chars
+ if (stream.match(/^'/)) {
+ state.tokenize = tokenChar;
+ return state.tokenize(stream, state);
+ }
// Handle Strings
if (stream.match(stringPrefixes)) {
@@ -187,7 +199,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return 'meta';
}
-
if (stream.match(delimiters)) {
return null;
}
@@ -200,43 +211,128 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return 'builtin';
}
+ var isDefinition = state.isDefinition ||
+ state.lastToken == 'function' ||
+ state.lastToken == 'macro' ||
+ state.lastToken == 'type' ||
+ state.lastToken == 'immutable';
if (stream.match(identifiers)) {
- state.leaving_expr=true;
+ if (isDefinition) {
+ if (stream.peek() === '.') {
+ state.isDefinition = true;
+ return 'variable';
+ }
+ state.isDefinition = false;
+ return 'def';
+ }
+ if (stream.match(/^({[^}]*})*\(/, false)) {
+ return callOrDef(stream, state);
+ }
+ state.leavingExpr = true;
return 'variable';
}
+
// Handle non-detected items
stream.next();
return ERRORCLASS;
}
+ function callOrDef(stream, state) {
+ var match = stream.match(/^(\(\s*)/);
+ if (match) {
+ if (state.firstParenPos < 0)
+ state.firstParenPos = state.scopes.length;
+ state.scopes.push('(');
+ state.charsAdvanced += match[1].length;
+ }
+ if (currentScope(state) == '(' && stream.match(/^\)/)) {
+ state.scopes.pop();
+ state.charsAdvanced += 1;
+ if (state.scopes.length <= state.firstParenPos) {
+ var isDefinition = stream.match(/^\s*?=(?!=)/, false);
+ stream.backUp(state.charsAdvanced);
+ state.firstParenPos = -1;
+ state.charsAdvanced = 0;
+ if (isDefinition)
+ return 'def';
+ return 'builtin';
+ }
+ }
+ // Unfortunately javascript does not support multiline strings, so we have
+ // to undo anything done upto here if a function call or definition splits
+ // over two or more lines.
+ if (stream.match(/^$/g, false)) {
+ stream.backUp(state.charsAdvanced);
+ while (state.scopes.length > state.firstParenPos)
+ state.scopes.pop();
+ state.firstParenPos = -1;
+ state.charsAdvanced = 0;
+ return 'builtin';
+ }
+ state.charsAdvanced += stream.match(/^([^()]*)/)[1].length;
+ return callOrDef(stream, state);
+ }
+
+ function tokenComment(stream, state) {
+ if (stream.match(/^#=/)) {
+ state.weakScopes++;
+ }
+ if (!stream.match(/.*?(?=(#=|=#))/)) {
+ stream.skipToEnd();
+ }
+ if (stream.match(/^=#/)) {
+ state.weakScopes--;
+ if (state.weakScopes == 0)
+ state.tokenize = tokenBase;
+ }
+ return 'comment';
+ }
+
+ function tokenChar(stream, state) {
+ var isChar = false, match;
+ if (stream.match(chars)) {
+ isChar = true;
+ } else if (match = stream.match(/\\u([a-f0-9]{1,4})(?=')/i)) {
+ var value = parseInt(match[1], 16);
+ if (value <= 55295 || value >= 57344) { // (U+0,U+D7FF), (U+E000,U+FFFF)
+ isChar = true;
+ stream.next();
+ }
+ } else if (match = stream.match(/\\U([A-Fa-f0-9]{5,8})(?=')/)) {
+ var value = parseInt(match[1], 16);
+ if (value <= 1114111) { // U+10FFFF
+ isChar = true;
+ stream.next();
+ }
+ }
+ if (isChar) {
+ state.leavingExpr = true;
+ state.tokenize = tokenBase;
+ return 'string';
+ }
+ if (!stream.match(/^[^']+(?=')/)) { stream.skipToEnd(); }
+ if (stream.match(/^'/)) { state.tokenize = tokenBase; }
+ return ERRORCLASS;
+ }
+
function tokenStringFactory(delimiter) {
- while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
+ while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
delimiter = delimiter.substr(1);
}
- var singleline = delimiter.length == 1;
var OUTCLASS = 'string';
function tokenString(stream, state) {
while (!stream.eol()) {
- stream.eatWhile(/[^'"\\]/);
+ stream.eatWhile(/[^"\\]/);
if (stream.eat('\\')) {
stream.next();
- if (singleline && stream.eol()) {
- return OUTCLASS;
- }
} else if (stream.match(delimiter)) {
state.tokenize = tokenBase;
+ state.leavingExpr = true;
return OUTCLASS;
} else {
- stream.eat(/['"]/);
- }
- }
- if (singleline) {
- if (parserConf.singleLineStringErrors) {
- return ERRORCLASS;
- } else {
- state.tokenize = tokenBase;
+ stream.eat(/["]/);
}
}
return OUTCLASS;
@@ -245,50 +341,47 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
return tokenString;
}
- function tokenLexer(stream, state) {
- var style = state.tokenize(stream, state);
- var current = stream.current();
-
- // Handle '.' connected identifiers
- if (current === '.') {
- style = stream.match(identifiers, false) ? null : ERRORCLASS;
- if (style === null && state.lastStyle === 'meta') {
- // Apply 'meta' style to '.' connected identifiers when
- // appropriate.
- style = 'meta';
- }
- return style;
- }
-
- return style;
- }
-
var external = {
startState: function() {
return {
tokenize: tokenBase,
scopes: [],
- leaving_expr: false
+ weakScopes: 0,
+ lastToken: null,
+ leavingExpr: false,
+ isDefinition: false,
+ charsAdvanced: 0,
+ firstParenPos: -1
};
},
token: function(stream, state) {
- var style = tokenLexer(stream, state);
- state.lastStyle = style;
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
+
+ if (current && style) {
+ state.lastToken = current;
+ }
+
+ // Handle '.' connected identifiers
+ if (current === '.') {
+ style = stream.match(identifiers, false) || stream.match(macro, false) ||
+ stream.match(/\(/, false) ? 'operator' : ERRORCLASS;
+ }
return style;
},
indent: function(state, textAfter) {
var delta = 0;
- if(textAfter=="end" || textAfter=="]" || textAfter=="}" || textAfter=="else" || textAfter=="elseif" || textAfter=="catch" || textAfter=="finally") {
+ if (textAfter == "]" || textAfter == ")" || textAfter == "end" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") {
delta = -1;
}
return (state.scopes.length + delta) * _conf.indentUnit;
},
+ electricInput: /(end|else(if)?|catch|finally)$/,
lineComment: "#",
- fold: "indent",
- electricChars: "edlsifyh]}"
+ fold: "indent"
};
return external;
});
diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js
index 2349ddf2..ed8452ab 100644
--- a/mode/markdown/markdown.js
+++ b/mode/markdown/markdown.js
@@ -13,8 +13,8 @@
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
- var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
- var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
+ var htmlMode = CodeMirror.getMode(cmCfg, "text/html");
+ var htmlModeMissing = htmlMode.name == "null"
function getMode(name) {
if (CodeMirror.findModeByName) {
@@ -55,8 +55,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (modeCfg.tokenTypeOverrides === undefined)
modeCfg.tokenTypeOverrides = {};
- var codeDepth = 0;
-
var tokenTypes = {
header: "header",
code: "comment",
@@ -121,7 +119,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.quote = 0;
// Reset state.indentedCode
state.indentedCode = false;
- if (!htmlFound && state.f == htmlBlock) {
+ if (htmlModeMissing && state.f == htmlBlock) {
state.f = inlineNormal;
state.block = blockNormal;
}
@@ -215,7 +213,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (state.localMode) state.localState = state.localMode.startState();
state.f = state.block = local;
if (modeCfg.highlightFormatting) state.formatting = "code-block";
- state.code = true;
+ state.code = -1
return getType(state);
}
@@ -224,18 +222,21 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
function htmlBlock(stream, state) {
var style = htmlMode.token(stream, state.htmlState);
- if ((htmlFound && state.htmlState.tagStart === null &&
- (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
- (state.md_inside && stream.current().indexOf(">") > -1)) {
- state.f = inlineNormal;
- state.block = blockNormal;
- state.htmlState = null;
+ if (!htmlModeMissing) {
+ var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
+ if ((inner.mode.name == "xml" && inner.state.tagStart === null &&
+ (!inner.state.context && inner.state.tokenize.isInText)) ||
+ (state.md_inside && stream.current().indexOf(">") > -1)) {
+ state.f = inlineNormal;
+ state.block = blockNormal;
+ state.htmlState = null;
+ }
}
return style;
}
function local(stream, state) {
- if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
+ if (state.fencedChars && stream.match(state.fencedChars, false)) {
state.localMode = state.localState = null;
state.f = state.block = leavingLocal;
return null;
@@ -253,9 +254,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
state.f = inlineNormal;
state.fencedChars = null;
if (modeCfg.highlightFormatting) state.formatting = "code-block";
- state.code = true;
+ state.code = 1
var returnType = getType(state);
- state.code = false;
+ state.code = 0
return returnType;
}
@@ -378,15 +379,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
var ch = stream.next();
- if (ch === '\\') {
- stream.next();
- if (modeCfg.highlightFormatting) {
- var type = getType(state);
- var formattingEscape = tokenTypes.formatting + "-escape";
- return type ? type + " " + formattingEscape : formattingEscape;
- }
- }
-
// Matches link titles present on next line
if (state.linkTitle) {
state.linkTitle = false;
@@ -405,26 +397,32 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
if (ch === '`') {
var previousFormatting = state.formatting;
if (modeCfg.highlightFormatting) state.formatting = "code";
- var t = getType(state);
- var before = stream.pos;
stream.eatWhile('`');
- var difference = 1 + stream.pos - before;
- if (!state.code) {
- codeDepth = difference;
- state.code = true;
- return getType(state);
+ var count = stream.current().length
+ if (state.code == 0) {
+ state.code = count
+ return getType(state)
+ } else if (count == state.code) { // Must be exact
+ var t = getType(state)
+ state.code = 0
+ return t
} else {
- if (difference === codeDepth) { // Must be exact
- state.code = false;
- return t;
- }
- state.formatting = previousFormatting;
- return getType(state);
+ state.formatting = previousFormatting
+ return getType(state)
}
} else if (state.code) {
return getType(state);
}
+ if (ch === '\\') {
+ stream.next();
+ if (modeCfg.highlightFormatting) {
+ var type = getType(state);
+ var formattingEscape = tokenTypes.formatting + "-escape";
+ return type ? type + " " + formattingEscape : formattingEscape;
+ }
+ }
+
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
stream.match(/\[[^\]]*\]/);
state.inline = state.f = linkHref;
@@ -620,7 +618,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
}
function footnoteLink(stream, state) {
- if (stream.match(/^[^\]]*\]:/, false)) {
+ if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
state.f = footnoteLinkInside;
stream.next(); // Consume [
if (modeCfg.highlightFormatting) state.formatting = "link";
@@ -639,7 +637,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return returnType;
}
- stream.match(/^[^\]]+/, true);
+ stream.match(/^([^\]\\]|\\.)+/, true);
return tokenTypes.linkText;
}
@@ -692,6 +690,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
linkText: false,
linkHref: false,
linkTitle: false,
+ code: 0,
em: false,
strong: false,
header: 0,
@@ -712,7 +711,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
f: s.f,
prevLine: s.prevLine,
- thisLine: s.this,
+ thisLine: s.thisLine,
block: s.block,
htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
diff --git a/mode/markdown/test.js b/mode/markdown/test.js
index f9cc27c3..6d7829fa 100644
--- a/mode/markdown/test.js
+++ b/mode/markdown/test.js
@@ -696,6 +696,15 @@
"[link [[foo]]:] [string&url http://example.com/]",
"(bar\" hello");
+ MT("labelEscape",
+ "[link [[foo \\]] ]]:] [string&url http://example.com/]");
+
+ MT("labelEscapeColon",
+ "[link [[foo \\]]: bar]]:] [string&url http://example.com/]");
+
+ MT("labelEscapeEnd",
+ "[[foo\\]]: http://example.com/");
+
MT("linkWeb",
"[link ] foo");
diff --git a/mode/meta.js b/mode/meta.js
index 7af51c1e..bc34cf83 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -22,12 +22,14 @@
{name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
{name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
{name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]},
+ {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
{name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
{name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
{name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
{name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
{name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
{name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
+ {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]},
{name: "CSS", mime: "text/css", mode: "css", ext: ["css"]},
{name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]},
{name: "D", mime: "text/x-d", mode: "d", ext: ["d"]},
@@ -45,6 +47,7 @@
{name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
{name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
{name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
+ {name: "FCL", mime: "text/x-fcl", mode: "fcl"},
{name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
{name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
{name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
@@ -52,9 +55,10 @@
{name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
{name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
{name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
- {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]},
+ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]},
{name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
{name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
+ {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
{name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
{name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
{name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
@@ -68,6 +72,7 @@
mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]},
{name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
{name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
+ {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
{name: "Jinja2", mime: "null", mode: "jinja2"},
{name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
{name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
diff --git a/mode/mscgen/index.html b/mode/mscgen/index.html
index 0517825f..8c28ee62 100644
--- a/mode/mscgen/index.html
+++ b/mode/mscgen/index.html
@@ -1,6 +1,6 @@
-CodeMirror: Oz mode
+CodeMirror: MscGen mode
@@ -24,11 +24,10 @@
MscGen mode
-
+
# Sample mscgen program
-# See http://www.mcternan.me.uk/mscgen or
+# See http://www.mcternan.me.uk/mscgen or
# https://sverweij.github.io/mscgen_js for more samples
-
msc {
# options
hscale="1.2";
@@ -54,12 +53,99 @@ MscGen mode
}
+Xù mode
+
+
+# Xù - expansions to MscGen to support inline expressions
+# https://github.com/sverweij/mscgen_js/blob/master/wikum/xu.md
+# More samples: https://sverweij.github.io/mscgen_js
+msc {
+ hscale="0.8",
+ width="700";
+
+ a,
+ b [label="change store"],
+ c,
+ d [label="necro queue"],
+ e [label="natalis queue"],
+ f;
+
+ a =>> b [label="get change list()"];
+ a alt f [label="changes found"] { /* alt is a xu specific keyword*/
+ b >> a [label="list of changes"];
+ a =>> c [label="cull old stuff (list of changes)"];
+ b loop e [label="for each change"] { // loop is xu specific as well...
+ /*
+ * Interesting stuff happens.
+ */
+ c =>> b [label="get change()"];
+ b >> c [label="change"];
+ c alt e [label="change too old"] {
+ c =>> d [label="queue(change)"];
+ --- [label="change newer than latest run"];
+ c =>> e [label="queue(change)"];
+ --- [label="all other cases"];
+ ||| [label="leave well alone"];
+ };
+ };
+
+ c >> a [label="done
+ processing"];
+
+ /* shucks! nothing found ...*/
+ --- [label="nothing found"];
+ b >> a [label="nothing"];
+ a note a [label="silent exit"];
+ };
+}
+
+
+MsGenny mode
+
+# MsGenny - simplified version of MscGen / Xù
+# https://github.com/sverweij/mscgen_js/blob/master/wikum/msgenny.md
+# More samples: https://sverweij.github.io/mscgen_js
+a -> b : a -> b (signal);
+a => b : a => b (method);
+b >> a : b >> a (return value);
+a =>> b : a =>> b (callback);
+a -x b : a -x b (lost);
+a :> b : a :> b (emphasis);
+a .. b : a .. b (dotted);
+a -- b : "a -- b straight line";
+a note a : a note a\n(note),
+b box b : b box b\n(action);
+a rbox a : a rbox a\n(reference),
+b abox b : b abox b\n(state/ condition);
+||| : ||| (empty row);
+... : ... (omitted row);
+--- : --- (comment);
+
+
+
+ Simple mode for highlighting MscGen and two derived sequence
+ chart languages.
+
+
- MIME types defined: text/x-mscgen
+ MIME types defined:
+ text/x-mscgen
+ text/x-xu
+ text/x-msgenny
+
+
diff --git a/mode/mscgen/index_msgenny.html b/mode/mscgen/index_msgenny.html
deleted file mode 100644
index 1664f305..00000000
--- a/mode/mscgen/index_msgenny.html
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
- CodeMirror: msgenny mode
-
-
-
-
-
-
- CodeMirror: msgenny mode
-
-
-# Sample msgenny program
-# https://sverweij.github.io/mscgen_js for more samples
-
-a -> b : a -> b (signal);
-a => b : a => b (method);
-b >> a : b >> a (return value);
-a =>> b : a =>> b (callback);
-a -x b : a -x b (lost);
-a :> b : a :> b (emphasis);
-a .. b : a .. b (dotted);
-a -- b : "a -- b straight line";
-a note a : a note a\n(note),
-b box b : b box b\n(action);
-a rbox a : a rbox a\n(reference),
-b abox b : b abox b\n(state/ condition);
-||| : ||| (empty row);
-... : ... (omitted row);
---- : --- (comment);
-
-
-
-
- MIME types defined: text/x-msgenny
-
-
diff --git a/mode/mscgen/index_xu.html b/mode/mscgen/index_xu.html
deleted file mode 100644
index 2f7bf9ec..00000000
--- a/mode/mscgen/index_xu.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
- CodeMirror: xu mode
-
-
-
-
-
-
- CodeMirror: xù mode
-
-
-# test50 - expansions to mscgen to support inline expressions
-# for now in a separate language: xù
-msc {
-
- hscale="0.8",
- width="700";
-
- a,
- b [label="change store"],
- c,
- d [label="necro queue"],
- e [label="natalis queue"],
- f;
-
- a =>> b [label="get change list()"];
- a alt f [label="changes found"] { /* alt is a xu specific keyword*/
- b >> a [label="list of changes"];
- a =>> c [label="cull old stuff (list of changes)"];
- b loop e [label="for each change"] { // loop is xu specific as well...
- /*
- * Here interesting stuff happens.
- * TBD
- */
- c =>> b [label="get change()"];
- b >> c [label="change"];
- c alt e [label="change too old"] {
- c =>> d [label="queue(change)"];
- --- [label="change newer than latest run"];
- c =>> e [label="queue(change)"];
- --- [label="all other cases"];
- ||| [label="leave well alone"];
- };
- };
-
-
- c >> a [label="done
- processing"];
-
- /* shucks! nothing found ...*/
- --- [label="nothing found"];
- b >> a [label="nothing"];
- a note a [label="silent exit"];
- };
-}
-
-
-
-
- MIME types defined: text/x-xu
-
-
diff --git a/mode/mscgen/mscgen.js b/mode/mscgen/mscgen.js
index 3cf4eb08..d61b4706 100644
--- a/mode/mscgen/mscgen.js
+++ b/mode/mscgen/mscgen.js
@@ -69,11 +69,11 @@
CodeMirror.defineMIME("text/x-msgenny", {name: "mscgen", language: "msgenny"});
function wordRegexpBoundary(pWords) {
- return new RegExp("\\b((" + pWords.join(")|(") + "))\\b", "i");
+ return new RegExp("\\b(" + pWords.join("|") + ")\\b", "i");
}
function wordRegexp(pWords) {
- return new RegExp("((" + pWords.join(")|(") + "))", "i");
+ return new RegExp("(" + pWords.join("|") + ")", "i");
}
function startStateFn() {
diff --git a/mode/mumps/index.html b/mode/mumps/index.html
index bd1f69ae..b1f92c21 100644
--- a/mode/mumps/index.html
+++ b/mode/mumps/index.html
@@ -1,4 +1,4 @@
-
+
CodeMirror: MUMPS mode
@@ -73,7 +73,7 @@ MUMPS mode
IF '$LENGTH($PIECE(XUSER(1),U,2)) QUIT 21 ;p419, p434
Q 0
;
-
+
- The XML mode supports two configuration parameters:
+ The XML mode supports these configuration parameters:
htmlMode (boolean)
This switches the mode to parse HTML instead of XML. This
means attributes do not have to be quoted, and some elements
(such as br) do not require a closing tag.
+ matchClosing (boolean)
+ Controls whether the mode checks that close tags match the
+ corresponding opening tag, and highlights mismatches as errors.
+ Defaults to true.
alignCDATA (boolean)
Setting this to true will force the opening tag of CDATA
blocks to not be indented.
diff --git a/mode/xml/xml.js b/mode/xml/xml.js
index 5ad21720..f987a3a3 100644
--- a/mode/xml/xml.js
+++ b/mode/xml/xml.js
@@ -11,54 +11,56 @@
})(function(CodeMirror) {
"use strict";
-CodeMirror.defineMode("xml", function(config, parserConfig) {
- var indentUnit = config.indentUnit;
- var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
- var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
- if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
+var htmlConfig = {
+ autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+ 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+ 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+ 'track': true, 'wbr': true, 'menuitem': true},
+ implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+ 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+ 'th': true, 'tr': true},
+ contextGrabbers: {
+ 'dd': {'dd': true, 'dt': true},
+ 'dt': {'dd': true, 'dt': true},
+ 'li': {'li': true},
+ 'option': {'option': true, 'optgroup': true},
+ 'optgroup': {'optgroup': true},
+ 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+ 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+ 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+ 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+ 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+ 'rp': {'rp': true, 'rt': true},
+ 'rt': {'rp': true, 'rt': true},
+ 'tbody': {'tbody': true, 'tfoot': true},
+ 'td': {'td': true, 'th': true},
+ 'tfoot': {'tbody': true},
+ 'th': {'td': true, 'th': true},
+ 'thead': {'tbody': true, 'tfoot': true},
+ 'tr': {'tr': true}
+ },
+ doNotIndent: {"pre": true},
+ allowUnquoted: true,
+ allowMissing: true,
+ caseFold: true
+}
- var Kludges = parserConfig.htmlMode ? {
- autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
- 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
- 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
- 'track': true, 'wbr': true, 'menuitem': true},
- implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
- 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
- 'th': true, 'tr': true},
- contextGrabbers: {
- 'dd': {'dd': true, 'dt': true},
- 'dt': {'dd': true, 'dt': true},
- 'li': {'li': true},
- 'option': {'option': true, 'optgroup': true},
- 'optgroup': {'optgroup': true},
- 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
- 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
- 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
- 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
- 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
- 'rp': {'rp': true, 'rt': true},
- 'rt': {'rp': true, 'rt': true},
- 'tbody': {'tbody': true, 'tfoot': true},
- 'td': {'td': true, 'th': true},
- 'tfoot': {'tbody': true},
- 'th': {'td': true, 'th': true},
- 'thead': {'tbody': true, 'tfoot': true},
- 'tr': {'tr': true}
- },
- doNotIndent: {"pre": true},
- allowUnquoted: true,
- allowMissing: true,
- caseFold: true
- } : {
- autoSelfClosers: {},
- implicitlyClosed: {},
- contextGrabbers: {},
- doNotIndent: {},
- allowUnquoted: false,
- allowMissing: false,
- caseFold: false
- };
- var alignCDATA = parserConfig.alignCDATA;
+var xmlConfig = {
+ autoSelfClosers: {},
+ implicitlyClosed: {},
+ contextGrabbers: {},
+ doNotIndent: {},
+ allowUnquoted: false,
+ allowMissing: false,
+ caseFold: false
+}
+
+CodeMirror.defineMode("xml", function(editorConf, config_) {
+ var indentUnit = editorConf.indentUnit
+ var config = {}
+ var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+ for (var prop in defaults) config[prop] = defaults[prop]
+ for (var prop in config_) config[prop] = config_[prop]
// Return variables for tokenizers
var type, setStyle;
@@ -188,7 +190,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
this.tagName = tagName;
this.indent = state.indented;
this.startOfLine = startOfLine;
- if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
+ if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
this.noIndent = true;
}
function popContext(state) {
@@ -201,8 +203,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
return;
}
parentTagName = state.context.tagName;
- if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
- !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+ if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
+ !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
return;
}
popContext(state);
@@ -233,9 +235,9 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
if (type == "word") {
var tagName = stream.current();
if (state.context && state.context.tagName != tagName &&
- Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName))
+ config.implicitlyClosed.hasOwnProperty(state.context.tagName))
popContext(state);
- if (state.context && state.context.tagName == tagName) {
+ if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
setStyle = "tag";
return closeState;
} else {
@@ -269,7 +271,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
var tagName = state.tagName, tagStart = state.tagStart;
state.tagName = state.tagStart = null;
if (type == "selfcloseTag" ||
- Kludges.autoSelfClosers.hasOwnProperty(tagName)) {
+ config.autoSelfClosers.hasOwnProperty(tagName)) {
maybePopContext(state, tagName);
} else {
maybePopContext(state, tagName);
@@ -282,12 +284,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
}
function attrEqState(type, stream, state) {
if (type == "equals") return attrValueState;
- if (!Kludges.allowMissing) setStyle = "error";
+ if (!config.allowMissing) setStyle = "error";
return attrState(type, stream, state);
}
function attrValueState(type, stream, state) {
if (type == "string") return attrContinuedState;
- if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
+ if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
setStyle = "error";
return attrState(type, stream, state);
}
@@ -297,12 +299,14 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
}
return {
- startState: function() {
- return {tokenize: inText,
- state: baseState,
- indented: 0,
- tagName: null, tagStart: null,
- context: null};
+ startState: function(baseIndent) {
+ var state = {tokenize: inText,
+ state: baseState,
+ indented: baseIndent || 0,
+ tagName: null, tagStart: null,
+ context: null}
+ if (baseIndent != null) state.baseIndent = baseIndent
+ return state
},
token: function(stream, state) {
@@ -335,19 +339,19 @@ CodeMirror.defineMode("xml", function(config, parserConfig) {
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
// Indent the starts of attribute names.
if (state.tagName) {
- if (multilineTagIndentPastTag)
+ if (config.multilineTagIndentPastTag !== false)
return state.tagStart + state.tagName.length + 2;
else
- return state.tagStart + indentUnit * multilineTagIndentFactor;
+ return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
}
- if (alignCDATA && /$/,
blockCommentStart: "",
- configuration: parserConfig.htmlMode ? "html" : "xml",
- helperType: parserConfig.htmlMode ? "html" : "xml"
+ configuration: config.htmlMode ? "html" : "xml",
+ helperType: config.htmlMode ? "html" : "xml",
+
+ skipAttribute: function(state) {
+ if (state.state == attrValueState)
+ state.state = attrState
+ }
};
});
diff --git a/mode/yaml-frontmatter/index.html b/mode/yaml-frontmatter/index.html
new file mode 100644
index 00000000..30bed2f8
--- /dev/null
+++ b/mode/yaml-frontmatter/index.html
@@ -0,0 +1,121 @@
+
+
+CodeMirror: YAML front matter mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+YAML front matter mode
+
+---
+receipt: Oz-Ware Purchase Invoice
+date: 2007-08-06
+customer:
+ given: Dorothy
+ family: Gale
+
+items:
+ - part_no: A4786
+ descrip: Water Bucket (Filled)
+ price: 1.47
+ quantity: 4
+
+ - part_no: E1628
+ descrip: High Heeled "Ruby" Slippers
+ size: 8
+ price: 100.27
+ quantity: 1
+
+bill-to: &id001
+ street: |
+ 123 Tornado Alley
+ Suite 16
+ city: East Centerville
+ state: KS
+
+ship-to: *id001
+
+specialDelivery: >
+ Follow the Yellow Brick
+ Road to the Emerald City.
+ Pay no attention to the
+ man behind the curtain.
+---
+
+GitHub Flavored Markdown
+========================
+
+Everything from markdown plus GFM features:
+
+## URL autolinking
+
+Underscores_are_allowed_between_words.
+
+## Strikethrough text
+
+GFM adds syntax to strikethrough text, which is missing from standard Markdown.
+
+~~Mistaken text.~~
+~~**works with other fomatting**~~
+
+~~spans across
+lines~~
+
+## Fenced code blocks (and syntax highlighting)
+
+```javascript
+for (var i = 0; i < items.length; i++) {
+ console.log(items[i], i); // log them
+}
+```
+
+## Task Lists
+
+- [ ] Incomplete task list item
+- [x] **Completed** task list item
+
+## A bit of GitHub spice
+
+* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
+* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
+* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
+* \#Num: #1
+* User/#Num: mojombo#1
+* User/Project#Num: mojombo/god#1
+
+See http://github.github.com/github-flavored-markdown/.
+
+
+Defines a mode that parses
+a YAML frontmatter
+at the start of a file, switching to a base mode at the end of that.
+Takes a mode configuration option base to configure the
+base mode, which defaults to "gfm".
+
+
+
+
diff --git a/mode/yaml-frontmatter/yaml-frontmatter.js b/mode/yaml-frontmatter/yaml-frontmatter.js
new file mode 100644
index 00000000..9f081b00
--- /dev/null
+++ b/mode/yaml-frontmatter/yaml-frontmatter.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"), require("../yaml/yaml"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../yaml/yaml"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function (CodeMirror) {
+
+ var START = 0, FRONTMATTER = 1, BODY = 2
+
+ // a mixed mode for Markdown text with an optional YAML front matter
+ CodeMirror.defineMode("yaml-frontmatter", function (config, parserConfig) {
+ var yamlMode = CodeMirror.getMode(config, "yaml")
+ var innerMode = CodeMirror.getMode(config, parserConfig && parserConfig.base || "gfm")
+
+ function curMode(state) {
+ return state.state == BODY ? innerMode : yamlMode
+ }
+
+ return {
+ startState: function () {
+ return {
+ state: START,
+ inner: CodeMirror.startState(yamlMode)
+ }
+ },
+ copyState: function (state) {
+ return {
+ state: state.state,
+ inner: CodeMirror.copyState(curMode(state), state.inner)
+ }
+ },
+ token: function (stream, state) {
+ if (state.state == START) {
+ if (stream.match(/---/, false)) {
+ state.state = FRONTMATTER
+ return yamlMode.token(stream, state.inner)
+ } else {
+ state.state = BODY
+ state.inner = CodeMirror.startState(innerMode)
+ return innerMode.token(stream, state.inner)
+ }
+ } else if (state.state == FRONTMATTER) {
+ var end = stream.sol() && stream.match(/---/, false)
+ var style = yamlMode.token(stream, state.inner)
+ if (end) {
+ state.state = BODY
+ state.inner = CodeMirror.startState(innerMode)
+ }
+ return style
+ } else {
+ return innerMode.token(stream, state.inner)
+ }
+ },
+ innerMode: function (state) {
+ return {mode: curMode(state), state: state.inner}
+ },
+ blankLine: function (state) {
+ var mode = curMode(state)
+ if (mode.blankLine) return mode.blankLine(state.inner)
+ }
+ }
+ })
+})
diff --git a/package.json b/package.json
index 934af6a0..1b4f32e5 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,14 @@
{
"name": "codemirror",
- "version":"5.8.1",
+ "version":"5.12.1",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"license": "MIT",
"directories": {"lib": "./lib"},
- "scripts": {"test": "node ./test/run.js"},
+ "scripts": {
+ "test": "node ./test/run.js",
+ "lint": "bin/lint"
+ },
"devDependencies": {"node-static": "0.6.0",
"phantomjs": "1.9.2-5",
"blint": ">=0.1.1"},
diff --git a/test/index.html b/test/index.html
index b0b1fa97..3e227a06 100644
--- a/test/index.html
+++ b/test/index.html
@@ -23,6 +23,7 @@
+
@@ -107,6 +108,7 @@ Test Suite
+
diff --git a/test/mode_test.js b/test/mode_test.js
index 2e16eba0..0aed50f7 100644
--- a/test/mode_test.js
+++ b/test/mode_test.js
@@ -170,7 +170,7 @@
for (var i = 0; i < output.length; ++i) {
var style = output[i].style, val = output[i].text;
s +=
- '' +
+ ' ' +
'' +
esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT
' ' +
diff --git a/test/test.js b/test/test.js
index 7bc2c8e9..01efbce8 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1657,6 +1657,8 @@ testCM("atomicMarker", function(cm) {
testCM("selectionBias", function(cm) {
cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true});
cm.setCursor(Pos(0, 2));
+ eqPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 2));
eqPos(cm.getCursor(), Pos(0, 3));
cm.setCursor(Pos(0, 2));
eqPos(cm.getCursor(), Pos(0, 1));
diff --git a/test/vim_test.js b/test/vim_test.js
index 855cb882..c75e003a 100644
--- a/test/vim_test.js
+++ b/test/vim_test.js
@@ -2148,9 +2148,18 @@ testVim('S_normal', function(cm, vim, helpers) {
cm.setCursor(0, 1);
helpers.doKeys('j', 'S');
helpers.doKeys('');
- helpers.assertCursorAt(1, 0);
- eq('aa\n\ncc', cm.getValue());
-}, { value: 'aa\nbb\ncc'});
+ helpers.assertCursorAt(1, 1);
+ eq('aa{\n \ncc', cm.getValue());
+ helpers.doKeys('j', 'S');
+ eq('aa{\n \n ', cm.getValue());
+ helpers.assertCursorAt(2, 2);
+ helpers.doKeys('');
+ helpers.doKeys('d', 'd', 'd', 'd');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('S');
+ is(vim.insertMode);
+ eq('', cm.getValue());
+}, { value: 'aa{\nbb\ncc'});
testVim('blockwise_paste', function(cm, vim, helpers) {
cm.setCursor(0, 0);
helpers.doKeys('', '3', 'j', 'l', 'y');
@@ -3110,6 +3119,25 @@ forEach(['zb','zz','zt','z-','z.','z'], function(e, idx){
return new Array(500).join('\n');
})()});
});
+testVim('zb_to_bottom', function(cm, vim, helpers){
+ var lineNum = 250;
+ cm.setSize(600, 35*cm.defaultTextHeight());
+ cm.setCursor(lineNum, 0);
+ helpers.doKeys('z', 'b');
+ var scrollInfo = cm.getScrollInfo();
+ eq(scrollInfo.top + scrollInfo.clientHeight, cm.charCoords(Pos(lineNum, 0), 'local').bottom);
+}, { value: (function(){
+ return new Array(500).join('\n');
+})()});
+testVim('zt_to_top', function(cm, vim, helpers){
+ var lineNum = 250;
+ cm.setSize(600, 35*cm.defaultTextHeight());
+ cm.setCursor(lineNum, 0);
+ helpers.doKeys('z', 't');
+ eq(cm.getScrollInfo().top, cm.charCoords(Pos(lineNum, 0), 'local').top);
+}, { value: (function(){
+ return new Array(500).join('\n');
+})()});
testVim('zb', 'gg', 'normal');
+ is(CodeMirror.Vim.handleKey(cm, "", "normal"), "Alt-X key is mapped");
+ CodeMirror.Vim.unmap("", "normal");
+ is(!CodeMirror.Vim.handleKey(cm, "", "normal"), "Alt-X key is unmapped");
+});
// Testing registration of functions as ex-commands and mapping to -keys
testVim('ex_api_test', function(cm, vim, helpers) {
diff --git a/theme/mbo.css b/theme/mbo.css
index 59ff5da7..e164fcf4 100644
--- a/theme/mbo.css
+++ b/theme/mbo.css
@@ -33,5 +33,5 @@
.cm-s-mbo span.cm-qualifier { color: #ffffec; }
.cm-s-mbo .CodeMirror-activeline-background { background: #494b41; }
-.cm-s-mbo .CodeMirror-matchingbracket { color: #222 !important; }
+.cm-s-mbo .CodeMirror-matchingbracket { color: #ffb928 !important; }
.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); }
diff --git a/theme/monokai.css b/theme/monokai.css
index 6910f793..7c8a4c5d 100644
--- a/theme/monokai.css
+++ b/theme/monokai.css
@@ -16,6 +16,7 @@
.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute { color: #a6e22e; }
.cm-s-monokai span.cm-keyword { color: #f92672; }
+.cm-s-monokai span.cm-builtin { color: #66d9ef; }
.cm-s-monokai span.cm-string { color: #e6db74; }
.cm-s-monokai span.cm-variable { color: #f8f8f2; }