diff --git a/demo/kitchen-sink/doclist.js b/demo/kitchen-sink/doclist.js
index e4f4833b..7e3feb47 100644
--- a/demo/kitchen-sink/doclist.js
+++ b/demo/kitchen-sink/doclist.js
@@ -132,6 +132,7 @@ var docs = {
"docs/toml.toml": "TOML",
"docs/typescript.ts": "Typescript",
"docs/vbscript.vbs": "VBScript",
+ "docs/velocity.vm": "Velocity",
"docs/xml.xml": "XML",
"docs/xquery.xq": "XQuery",
"docs/yaml.yaml": "YAML",
diff --git a/demo/kitchen-sink/docs/velocity.vm b/demo/kitchen-sink/docs/velocity.vm
new file mode 100644
index 00000000..9156020b
--- /dev/null
+++ b/demo/kitchen-sink/docs/velocity.vm
@@ -0,0 +1,44 @@
+#*
+ This is a sample comment block that
+ spans multiple lines.
+*#
+
+#macro ( outputItem $item )
+
${item}
+#end
+
+## Define the items to iterate
+#set ( $items = [1, 2, 3, 4] )
+
+
+ ## Iterate over the items and output the evens.
+ #foreach ( $item in $items )
+ #if ( $_MathTool.mod($item, 2) == 0 )
+ #outputItem ($item)
+ #end
+ #end
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/kitchen-sink/modelist.js b/demo/kitchen-sink/modelist.js
index 9c693f99..4611e406 100644
--- a/demo/kitchen-sink/modelist.js
+++ b/demo/kitchen-sink/modelist.js
@@ -25,7 +25,7 @@ var Mode = function(name, desc, extensions) {
}) + "$";
} else {
var re = "^.*\\.(" + extensions + ")$";
- }
+ }
this.extRe = new RegExp(re, "gi");
};
@@ -103,6 +103,7 @@ var modesByName = {
toml: ["toml" , "toml"],
typescript: ["Typescript" , "typescript|ts|str"],
vbscript: ["VBScript" , "vbs"],
+ velocity: ["Velocity" , "vm"],
xml: ["XML" , "xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl"],
xquery: ["XQuery" , "xq"],
yaml: ["YAML" , "yaml"]
diff --git a/lib/ace/mode/folding/velocity.js b/lib/ace/mode/folding/velocity.js
new file mode 100644
index 00000000..ce235b49
--- /dev/null
+++ b/lib/ace/mode/folding/velocity.js
@@ -0,0 +1,120 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("../../lib/oop");
+var BaseFoldMode = require("./fold_mode").FoldMode;
+var Range = require("../../range").Range;
+
+var FoldMode = exports.FoldMode = function() {};
+oop.inherits(FoldMode, BaseFoldMode);
+
+(function() {
+
+ this.getFoldWidgetRange = function(session, foldStyle, row) {
+ var range = this.indentationBlock(session, row);
+ if (range)
+ return range;
+
+ var re = /\S/;
+ var line = session.getLine(row);
+ var startLevel = line.search(re);
+ if (startLevel == -1 || line[startLevel] != "##")
+ return;
+
+ var startColumn = line.length;
+ var maxRow = session.getLength();
+ var startRow = row;
+ var endRow = row;
+
+ while (++row < maxRow) {
+ line = session.getLine(row);
+ var level = line.search(re);
+
+ if (level == -1)
+ continue;
+
+ if (line[level] != "##")
+ break;
+
+ endRow = row;
+ }
+
+ if (endRow > startRow) {
+ var endColumn = session.getLine(endRow).length;
+ return new Range(startRow, startColumn, endRow, endColumn);
+ }
+ };
+
+ // must return "" if there's no fold, to enable caching
+ this.getFoldWidget = function(session, foldStyle, row) {
+ var line = session.getLine(row);
+ var indent = line.search(/\S/);
+ var next = session.getLine(row + 1);
+ var prev = session.getLine(row - 1);
+ var prevIndent = prev.search(/\S/);
+ var nextIndent = next.search(/\S/);
+
+ if (indent == -1) {
+ session.foldWidgets[row - 1] = prevIndent!= -1 && prevIndent < nextIndent ? "start" : "";
+ return "";
+ }
+
+ // documentation comments
+ if (prevIndent == -1) {
+ if (indent == nextIndent && line[indent] == "##" && next[indent] == "##") {
+ session.foldWidgets[row - 1] = "";
+ session.foldWidgets[row + 1] = "";
+ return "start";
+ }
+ } else if (prevIndent == indent && line[indent] == "##" && prev[indent] == "##") {
+ if (session.getLine(row - 2).search(/\S/) == -1) {
+ session.foldWidgets[row - 1] = "start";
+ session.foldWidgets[row + 1] = "";
+ return "";
+ }
+ }
+
+ if (prevIndent!= -1 && prevIndent < indent)
+ session.foldWidgets[row - 1] = "start";
+ else
+ session.foldWidgets[row - 1] = "";
+
+ if (indent < nextIndent)
+ return "start";
+ else
+ return "";
+ };
+
+}).call(FoldMode.prototype);
+
+});
diff --git a/lib/ace/mode/velocity.js b/lib/ace/mode/velocity.js
new file mode 100644
index 00000000..feb53742
--- /dev/null
+++ b/lib/ace/mode/velocity.js
@@ -0,0 +1,62 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2012, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Contributor(s):
+ *
+ *
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ THIS FILE WAS AUTOGENERATED BY mode.tmpl.js
+*/
+
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var TextMode = require("./text").Mode;
+var Tokenizer = require("../tokenizer").Tokenizer;
+var VelocityHighlightRules = require("./velocity_highlight_rules").VelocityHighlightRules;
+var FoldMode = require("./folding/velocity").FoldMode;
+
+var Mode = function() {
+ var highlighter = new VelocityHighlightRules();
+ this.foldingRules = new FoldMode();
+ this.$tokenizer = new Tokenizer(highlighter.getRules());
+};
+oop.inherits(Mode, TextMode);
+
+(function() {
+ this.lineCommentStart = "##";
+ this.blockComment = {start: "#*", end: "*#"};
+}).call(Mode.prototype);
+
+exports.Mode = Mode;
+});
\ No newline at end of file
diff --git a/lib/ace/mode/velocity_highlight_rules.js b/lib/ace/mode/velocity_highlight_rules.js
new file mode 100644
index 00000000..8d2510e4
--- /dev/null
+++ b/lib/ace/mode/velocity_highlight_rules.js
@@ -0,0 +1,232 @@
+define(function(require, exports, module) {
+"use strict";
+
+var oop = require("../lib/oop");
+var lang = require("../lib/lang");
+var xmlUtil = require("./xml_util");
+var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
+var CssHighlightRules = require("./css_highlight_rules").CssHighlightRules;
+var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScriptHighlightRules;
+
+var VelocityHighlightRules = function() {
+ var builtinConstants = lang.arrayToMap(
+ ('true|false|null').split('|')
+ );
+
+ var builtinFunctions = lang.arrayToMap(
+ ("_DateTool|_DisplayTool|_EscapeTool|_FieldTool|_MathTool|_NumberTool|_SerializerTool|_SortTool|_StringTool|_XPathTool").split('|')
+ );
+
+ var builtinVariables = lang.arrayToMap(
+ ('$contentRoot|$foreach').split('|')
+ );
+
+ var keywords = lang.arrayToMap(
+ ("#set|#macro|#include|#parse|" +
+ "#if|#elseif|#else|#foreach|" +
+ "#break|#end|#stop"
+ ).split('|')
+ );
+
+ // regexp must not have capturing parentheses. Use (?:) instead.
+ // regexps are ordered -> the first match is used
+
+ this.$rules = {
+ "start" : [
+ {
+ token : "meta.tag",
+ regex : "<\\!\\[CDATA\\[",
+ next : "cdata"
+ }, {
+ token : "xml-pe",
+ regex : "<\\?.*?\\?>"
+ }, {
+ token : "comment",
+ regex : "<\\!--",
+ next : "comment"
+ }, {
+ token : "meta.tag",
+ regex : "<(?=\\s*script\\b)",
+ next : "script"
+ }, {
+ token : "meta.tag",
+ regex : "<(?=\\s*style\\b)",
+ next : "style"
+ }, {
+ token : "meta.tag", // opening tag
+ regex : "<\\/?",
+ next : "tag"
+ }, {
+ token : "comment",
+ regex : "##.*$"
+ },{
+ token : "comment.block", // multi line comment
+ regex : "#\\*",
+ next : "comment"
+ }, {
+ token : "string.regexp",
+ regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"
+ }, {
+ token : "string", // single line
+ regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
+ }, {
+ token : "string", // single line
+ regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
+ }, {
+ token : "constant.numeric", // hex
+ regex : "0[xX][0-9a-fA-F]+\\b"
+ }, {
+ token : "constant.numeric", // float
+ regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
+ }, {
+ token : "constant.language.boolean",
+ regex : "(?:true|false)\\b"
+ }, {
+ token : function(value) {
+ if (keywords.hasOwnProperty(value))
+ return "keyword";
+ else if (builtinConstants.hasOwnProperty(value))
+ return "constant.language";
+ else if (builtinVariables.hasOwnProperty(value))
+ return "variable.language";
+ else if (builtinFunctions.hasOwnProperty(value) || builtinFunctions.hasOwnProperty(value.substring(1)))
+ return "support.function";
+ else if (value == "debugger")
+ return "invalid.deprecated";
+ else
+ if(value.match(/^(\$[a-zA-Z_][a-zA-Z0-9_]*)$/))
+ return "variable";
+ return "identifier";
+ },
+ // TODO: Unicode escape sequences
+ // TODO: Unicode identifiers
+ regex : "[a-zA-Z$#][a-zA-Z0-9_]*\\b"
+ }, {
+ token : "keyword.operator",
+ regex : "!|&|\\*|\\-|\\+|=|!=|<=|>=<|>|&&|\\|\\|"
+ }, {
+ token : "lparen",
+ regex : "[[({]"
+ }, {
+ token : "rparen",
+ regex : "[\\])}]"
+ }, {
+ token : "text",
+ regex : "\\s+"
+ }
+ ],
+ "cdata" : [
+ {
+ token : "text",
+ regex : "\\]\\]>",
+ next : "start"
+ }, {
+ token : "text",
+ regex : "\\s+"
+ }, {
+ token : "text",
+ regex : ".+"
+ }
+ ],
+ "comment" : [
+ {
+ token : "comment.block", // closing comment
+ regex : ".*?\\*#",
+ next : "start"
+ }, {
+ token : "comment", // comment spanning whole line
+ regex : ".+"
+ }
+ ],
+ "vm_start" : [
+ {
+ token: "variable",
+ regex: "}",
+ next: "start"
+ }, {
+ token : "string.regexp",
+ regex : "[/](?:(?:\\[(?:\\\\]|[^\\]])+\\])|(?:\\\\/|[^\\]/]))*[/]\\w*\\s*(?=[).,;]|$)"
+ }, {
+ token : "string", // single line
+ regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
+ }, {
+ token : "string", // single line
+ regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
+ }, {
+ token : "constant.numeric", // hex
+ regex : "0[xX][0-9a-fA-F]+\\b"
+ }, {
+ token : "constant.numeric", // float
+ regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"
+ }, {
+ token : "constant.language.boolean",
+ regex : "(?:true|false)\\b"
+ }, {
+ token : function(value) {
+ if (keywords.hasOwnProperty(value))
+ return "keyword";
+ else if (builtinConstants.hasOwnProperty(value))
+ return "constant.language";
+ else if (builtinVariables.hasOwnProperty(value))
+ return "variable.language";
+ else if (builtinFunctions.hasOwnProperty(value) || builtinFunctions.hasOwnProperty(value.substring(1)))
+ return "support.function";
+ else if (value == "debugger")
+ return "invalid.deprecated";
+ else
+ if(value.match(/^(\$[a-zA-Z_$][a-zA-Z0-9_]*)$/))
+ return "variable";
+ return "identifier";
+ },
+ // TODO: Unicode escape sequences
+ // TODO: Unicode identifiers
+ regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
+ }, {
+ token : "keyword.operator",
+ regex : "!|&|\\*|\\-|\\+|=|!=|<=|>=<|>|&&|\\|\\|"
+ }, {
+ token : "lparen",
+ regex : "[[({]"
+ }, {
+ token : "rparen",
+ regex : "[\\])}]"
+ }, {
+ token : "text",
+ regex : "\\s+"
+ }
+ ]
+ };
+
+ xmlUtil.tag(this.$rules, "tag", "start");
+ xmlUtil.tag(this.$rules, "style", "css-start");
+ xmlUtil.tag(this.$rules, "script", "js-start");
+
+ this.embedRules(JavaScriptHighlightRules, "js-", [{
+ token: "comment",
+ regex: "\\/\\/.*(?=<\\/script>)",
+ next: "tag"
+ }, {
+ token: "meta.tag",
+ regex: "<\\/(?=script)",
+ next: "tag"
+ }]);
+
+ this.embedRules(CssHighlightRules, "css-", [{
+ token: "meta.tag",
+ regex: "<\\/(?=style)",
+ next: "tag"
+ }]);
+
+ for (var i in this.$rules) {
+ this.$rules[i].unshift({
+ token: "variable",
+ regex: "\\${",
+ next: "vm_start"
+ });
+ }
+};
+
+oop.inherits(VelocityHighlightRules, TextHighlightRules);
+
+exports.VelocityHighlightRules = VelocityHighlightRules;
+});
\ No newline at end of file