diff --git a/demo/kitchen-sink/docs/jexl.jexl b/demo/kitchen-sink/docs/jexl.jexl new file mode 100644 index 00000000000..6e1c33e1b97 --- /dev/null +++ b/demo/kitchen-sink/docs/jexl.jexl @@ -0,0 +1,20 @@ +#pragma execution.option 42 + +## +## This is a test script +## + +/* This is +a multi-line +comment +*/ +if (out != null and result == null) out.println("The result is " + result); +x = ~/\w+\/test/; +y = 2; +result = x * y + 5; +if (out != null) out.println("The result is " + result); + +@lenient @silent x.someMethod(); + +`multi-line string +with ${var interpolation = "Hey!"} and \escap\u00E9 chars` diff --git a/src/ext/modelist.js b/src/ext/modelist.js index c95bb54937b..9a91896114a 100644 --- a/src/ext/modelist.js +++ b/src/ext/modelist.js @@ -111,6 +111,7 @@ var supportedModes = { Jade: ["jade|pug"], Java: ["java"], JavaScript: ["js|jsm|jsx|cjs|mjs"], + JEXL: ["jexl"], JSON: ["json"], JSON5: ["json5"], JSONiq: ["jq"], diff --git a/src/mode/_test/tokens_jexl.json b/src/mode/_test/tokens_jexl.json new file mode 100644 index 00000000000..9abf21d9bff --- /dev/null +++ b/src/mode/_test/tokens_jexl.json @@ -0,0 +1,156 @@ +[[ + "start", + ["comment","#pragma"], + ["text"," execution.option 42"] +],[ + "start" +],[ + "start", + ["comment","##"] +],[ + "start", + ["comment","## This is a test script"] +],[ + "start", + ["comment","##"] +],[ + "start" +],[ + "comment", + ["comment","/* This is"] +],[ + "comment", + ["comment","a multi-line"] +],[ + "comment", + ["comment","comment"] +],[ + "start", + ["comment","*/"] +],[ + "start", + ["keyword","if"], + ["text"," "], + ["lparen","("], + ["identifier","out"], + ["text"," "], + ["keyword.operator","!="], + ["text"," "], + ["constant.language","null"], + ["text"," "], + ["keyword","and"], + ["text"," "], + ["identifier","result"], + ["text"," "], + ["keyword.operator","=="], + ["text"," "], + ["constant.language","null"], + ["rparen",")"], + ["text"," "], + ["identifier","out"], + ["punctuation","."], + ["identifier","println"], + ["lparen","("], + ["string","\"The result is \""], + ["text"," "], + ["keyword.operator","+"], + ["text"," "], + ["identifier","result"], + ["rparen",")"], + ["text",";"] +],[ + "start", + ["identifier","x"], + ["text"," "], + ["keyword.operator","="], + ["text"," "], + ["string.regexp","~/\\w+"], + ["constant.language.escape","\\/"], + ["string.regexp","test/"], + ["text",";"] +],[ + "start", + ["identifier","y"], + ["text"," "], + ["keyword.operator","="], + ["text"," "], + ["constant.numeric","2"], + ["text",";"] +],[ + "start", + ["identifier","result"], + ["text"," "], + ["keyword.operator","="], + ["text"," "], + ["identifier","x"], + ["text"," "], + ["keyword.operator","*"], + ["text"," "], + ["identifier","y"], + ["text"," "], + ["keyword.operator","+"], + ["text"," "], + ["constant.numeric","5"], + ["text",";"] +],[ + "start", + ["keyword","if"], + ["text"," "], + ["lparen","("], + ["identifier","out"], + ["text"," "], + ["keyword.operator","!="], + ["text"," "], + ["constant.language","null"], + ["rparen",")"], + ["text"," "], + ["identifier","out"], + ["punctuation","."], + ["identifier","println"], + ["lparen","("], + ["string","\"The result is \""], + ["text"," "], + ["keyword.operator","+"], + ["text"," "], + ["identifier","result"], + ["rparen",")"], + ["text",";"] +],[ + "start" +],[ + "start", + ["storage.type.annotation","@lenient"], + ["text"," "], + ["storage.type.annotation","@silent"], + ["text"," "], + ["identifier","x"], + ["punctuation","."], + ["identifier","someMethod"], + ["lparen","("], + ["rparen",")"], + ["text",";"] +],[ + "start" +],[ + "string", + ["string","`multi-line string"] +],[ + "start", + ["string","with "], + ["lparen","${"], + ["keyword","var"], + ["text"," "], + ["identifier","interpolation"], + ["text"," "], + ["keyword.operator","="], + ["text"," "], + ["string","\"Hey!\""], + ["rparen","}"], + ["string"," and "], + ["constant.language.escape","\\"], + ["string","escap"], + ["constant.language.escape","\\u00E9"], + ["string"," chars`"] +],[ + "start" +]] \ No newline at end of file diff --git a/src/mode/jexl.js b/src/mode/jexl.js new file mode 100644 index 00000000000..1e3e234c808 --- /dev/null +++ b/src/mode/jexl.js @@ -0,0 +1,23 @@ +"use strict"; + +var oop = require("../lib/oop"); +var JexlHighlightRules = require("./jexl_highlight_rules").JexlHighlightRules; +var TextMode = require("./text").Mode; +var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; +var CStyleFoldMode = require("./folding/cstyle").FoldMode; + +var Mode = function () { + this.HighlightRules = JexlHighlightRules; + this.$behaviour = new CstyleBehaviour(); + this.foldingRules = new CStyleFoldMode(); +}; +oop.inherits(Mode, TextMode); + +(function () { + this.lineCommentStart = ["//", "##"]; + this.blockComment = {start: "/*", end: "*/"}; + + this.$id = "ace/mode/jexl"; +}).call(Mode.prototype); + +exports.Mode = Mode; diff --git a/src/mode/jexl_highlight_rules.js b/src/mode/jexl_highlight_rules.js new file mode 100644 index 00000000000..03f6c4fbf59 --- /dev/null +++ b/src/mode/jexl_highlight_rules.js @@ -0,0 +1,138 @@ +"use strict"; + +var oop = require("../lib/oop"); +var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + +var JexlHighlightRules = function () { + + var keywords = "return|var|function|and|or|not|if|for|while|do|continue|break"; + var buildinConstants = "null"; + var supportFunc = "empty|size|new"; + + var keywordMapper = this.createKeywordMapper({ + "keyword": keywords, + "constant.language": buildinConstants, + "support.function": supportFunc + }, "identifier"); + + var escapedRe = "\\\\(?:x[0-9a-fA-F]{2}|" + // hex + "u[0-9a-fA-F]{4}|" + // unicode + "u{[0-9a-fA-F]{1,6}}|" + // es6 unicode + "|.)"; + + // regexp must not have capturing parentheses. Use (?:) instead. + // regexps are ordered -> the first match is used + + this.$rules = { + "start": [ + { + token: "comment", + regex: "\\/\\/.*$" + }, { + token: "comment", + regex: "##.*$" + }, { + token: "comment", // multi line comment + regex: "\\/\\*", + next: "comment" + }, { + token: ["comment", "text"], + regex: "(#pragma)(\\s.*$)" + }, { + token: "string", // single line + regex: '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' + }, { + token: "string", // single line + regex: "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']" + }, { + token: "string", // multi line string + regex: "`", + push: [ + { + token: "constant.language.escape", + regex: escapedRe + }, { + token: "string", + regex: "`", + next: "pop" + }, { + token: "lparen", //interpolation + regex: "\\${", + push: [ + { + token: "rparen", + regex: "}", + next: "pop" + }, { + include: "start" + } + ] + }, { + defaultToken: "string" + } + ] + }, { + token: "constant.numeric", // hex + regex: /0(?:[xX][0-9a-fA-F][0-9a-fA-F_]*|[bB][01][01_]*)[LlSsDdFfYy]?\b/ + }, { + token: "constant.numeric", // float + regex: /[+-]?\d[\d_]*(?:(?:\.[\d_]*)?(?:[eE][+-]?[\d_]+)?)?[LlSsDdFfYy]?\b/ + }, { + token: "constant.language.boolean", + regex: "(?:true|false)\\b" + }, { + token: "string.regexp", + regex: "~/", + push: [ + { + token: "constant.language.escape", + regex: "\\\\/" + }, { + token: "string.regexp", + regex: "$|/", + next: "pop" + }, { + defaultToken: "string.regexp" + } + ] + }, { + token: keywordMapper, + regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + }, { + token: "keyword.operator", + regex: "&&|\\|\\||!|&|\\||\\^|~|\\?|:|\\?\\?|==|!=|<|<=|>|>=|=~|!~|=\\^|=\\$|!\\$|\\+|\\-|\\*|%|\\/|=" + }, { + token: "lparen", + regex: "[[({]" + }, { + token: "rparen", + regex: "[\\])}]" + }, { + token: "text", + regex: "\\s+" + }, { + token: "punctuation", + regex: "[,.]" + }, { + token: "storage.type.annotation", + regex: "@[a-zA-Z_$][a-zA-Z0-9_$]*\\b" + } + ], + "comment": [ + { + token: "comment", + regex: "\\*\\/", + next: "start" + }, { + defaultToken: "comment" + } + ] + }; + + + this.normalizeRules(); +}; + +oop.inherits(JexlHighlightRules, TextHighlightRules); + +exports.JexlHighlightRules = JexlHighlightRules;