From 2463471b39209c14d6d0e2eeb8b10ef9e1e164f1 Mon Sep 17 00:00:00 2001 From: Ryan Griffith Date: Fri, 5 Apr 2013 08:08:23 -0400 Subject: [PATCH 1/4] Add Velocity highlighter --- lib/ace/mode/folding/velocity.js | 120 ++++++++++++ lib/ace/mode/velocity.js | 61 ++++++ lib/ace/mode/velocity_highlight_rules.js | 232 +++++++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 lib/ace/mode/folding/velocity.js create mode 100644 lib/ace/mode/velocity.js create mode 100644 lib/ace/mode/velocity_highlight_rules.js diff --git a/lib/ace/mode/folding/velocity.js b/lib/ace/mode/folding/velocity.js new file mode 100644 index 00000000..4a529090 --- /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..ec1adfa5 --- /dev/null +++ b/lib/ace/mode/velocity.js @@ -0,0 +1,61 @@ +/* ***** 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() { + // Extra logic goes here. +}).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 From 646b0442b61915ec638c040e548914245e1ef9f6 Mon Sep 17 00:00:00 2001 From: Ryan Griffith Date: Fri, 5 Apr 2013 10:39:17 -0400 Subject: [PATCH 2/4] Fixed matching of single line comments --- lib/ace/mode/folding/velocity.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ace/mode/folding/velocity.js b/lib/ace/mode/folding/velocity.js index 4a529090..ce235b49 100644 --- a/lib/ace/mode/folding/velocity.js +++ b/lib/ace/mode/folding/velocity.js @@ -63,7 +63,7 @@ oop.inherits(FoldMode, BaseFoldMode); if (level == -1) continue; - if (line[level] != "#") + if (line[level] != "##") break; endRow = row; @@ -91,12 +91,12 @@ oop.inherits(FoldMode, BaseFoldMode); // documentation comments if (prevIndent == -1) { - if (indent == nextIndent && line[indent] == "#" && next[indent] == "#") { + 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] == "#") { + } 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] = ""; From 31aede445d1d4a202e6ba2ac5b4bae9d10c95f5c Mon Sep 17 00:00:00 2001 From: Ryan Griffith Date: Fri, 5 Apr 2013 14:45:22 -0400 Subject: [PATCH 3/4] Added velocity mode and sample doc to demo --- demo/kitchen-sink/doclist.js | 1 + demo/kitchen-sink/docs/velocity.vm | 44 ++++++++++++++++++++++++++++++ demo/kitchen-sink/modelist.js | 3 +- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 demo/kitchen-sink/docs/velocity.vm 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"] From a22dda79fc72f1350427d35269d233015ee6b787 Mon Sep 17 00:00:00 2001 From: Ryan Griffith Date: Fri, 5 Apr 2013 14:46:53 -0400 Subject: [PATCH 4/4] Added comment settings to add comment shortcut (ctrl+/) --- lib/ace/mode/velocity.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ace/mode/velocity.js b/lib/ace/mode/velocity.js index ec1adfa5..feb53742 100644 --- a/lib/ace/mode/velocity.js +++ b/lib/ace/mode/velocity.js @@ -54,7 +54,8 @@ var Mode = function() { oop.inherits(Mode, TextMode); (function() { - // Extra logic goes here. + this.lineCommentStart = "##"; + this.blockComment = {start: "#*", end: "*#"}; }).call(Mode.prototype); exports.Mode = Mode;