From 0ae8dbb0fe017cfb8321307e5bfe5959eb121754 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=CE=9D=CE=99=CE=9A=CE=9F=CE=9B=CE=91=CE=A3?=
Date: Tue, 28 Mar 2023 11:51:10 +0200
Subject: [PATCH] fix: Improves Liquid Support (#5098)
* Improve liquid grammar support
Brings support for Shopify Liquid Variation embedded code blocks which include:
- {% schema %}
- {% javascript %}
- {% style %}
- {% stylesheet %}
In addition, line comment highlighting is now provided.
* Include the additional modes
* add additional context and syntax samples
* purge the mode test file (not really relevant anymore)
* align behaviour for delimiter matches
* fix lookbehind expressions
* restore test for liquid mode
---------
Co-authored-by: nightwing
---
demo/kitchen-sink/docs/liquid.liquid | 57 ++-
src/mode/_test/highlight_rules_test.js | 2 +-
src/mode/_test/tokens_liquid.json | 570 +++++++++++++++++--------
src/mode/behaviour/liquid.js | 24 +-
src/mode/liquid.js | 39 +-
src/mode/liquid_highlight_rules.js | 347 +++++++++++----
6 files changed, 755 insertions(+), 284 deletions(-)
diff --git a/demo/kitchen-sink/docs/liquid.liquid b/demo/kitchen-sink/docs/liquid.liquid
index 29c0b016551..a06a7a7f4e2 100644
--- a/demo/kitchen-sink/docs/liquid.liquid
+++ b/demo/kitchen-sink/docs/liquid.liquid
@@ -1,4 +1,8 @@
-The following examples can be found in full at http://liquidmarkup.org/
+There are a couple of different Liquid variations in circulation. This grammars supports
+both the Standard and Shopify Liquid variations. The following examples can be found in full at:
+
+Standard Variation: https://shopify.github.io/liquid
+Shopify Variation: https://shopify.dev/docs/api/liquid
Liquid is an extraction from the e-commerce system Shopify.
Shopify powers many thousands of e-commerce stores which all call for unique designs.
@@ -37,11 +41,18 @@ Some more features include:
If
- {% if user.name == 'tobi' or user.name == 'marc' %}
+ {% if user.name == 'tobi' or user.name == 'marc' %}
hi marc or tobi
{% endif %}
+Comments
+
+{% # Line Comment %}
+
+{% comment %}
+ Block Comment
+{% endcomment %}
Case
@@ -58,7 +69,7 @@ Some more features include:
For Loops
- {% for item in array %}
+ {% for item in array %}
{{ item }}
{% endfor %}
@@ -74,3 +85,43 @@ Some more features include:
{% endif %}
{% endtablerow %}
+
+Embedded Code Blocks
+
+This support Shopify Liquid variation JSON schema code blocks
+
+{% schema %}
+ {
+ "string": "bar",
+ "boolean": true,
+ "number": 100,
+ "object": {
+ "array": [100, false, "string", {}, [] ]
+ }
+ }
+{% endschema %}
+
+This support Shopify Liquid variation Stylesheet and Style code blocks
+
+{% style %}
+ .class {
+ font-size: 10px; /* comment */
+ }
+{% endstyle %}
+
+{% stylesheet %}
+ .class {
+ font-size: 10px; /* comment */
+ }
+{% endstylesheet %}
+
+This support Shopify Liquid variation JavaScript code blocks
+
+{% javascript %}
+
+ function foo (param) {
+
+ return 'something' // comment
+ }
+
+{% endjavascript %}
diff --git a/src/mode/_test/highlight_rules_test.js b/src/mode/_test/highlight_rules_test.js
index e8940f99189..ccb4faf6839 100644
--- a/src/mode/_test/highlight_rules_test.js
+++ b/src/mode/_test/highlight_rules_test.js
@@ -13,7 +13,7 @@ if (!fs.existsSync)
require("amd-loader");
var cwd = __dirname + "/";
-var root = path.normalize(cwd + Array(5).join("../"));
+var root = path.normalize(cwd + Array(4).join("../"));
function jsFileList(path, filter) {
if (!filter) filter = /_test/;
diff --git a/src/mode/_test/tokens_liquid.json b/src/mode/_test/tokens_liquid.json
index f745a156aa4..cb92f661549 100644
--- a/src/mode/_test/tokens_liquid.json
+++ b/src/mode/_test/tokens_liquid.json
@@ -1,6 +1,17 @@
[[
"start",
- ["text.xml","The following examples can be found in full at http://liquidmarkup.org/"]
+ ["text.xml","There are a couple of different Liquid variations in circulation. This grammars supports"]
+],[
+ "start",
+ ["text.xml","both the Standard and Shopify Liquid variations. The following examples can be found in full at:"]
+],[
+ "start"
+],[
+ "start",
+ ["text.xml","Standard Variation: https://shopify.github.io/liquid"]
+],[
+ "start",
+ ["text.xml","Shopify Variation: https://shopify.dev/docs/api/liquid"]
],[
"start"
],[
@@ -51,17 +62,12 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword.block","for"],
- ["text"," "],
- ["identifier","product"],
- ["text"," "],
- ["keyword","in"],
- ["text"," "],
- ["identifier","products"],
- ["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," for"],
+ ["text"," product"],
+ ["keyword.operator"," in "],
+ ["text","products "],
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
@@ -74,28 +80,29 @@
["meta.tag.punctuation.tag-open.xml","<"],
["meta.tag.tag-name.xml","h2"],
["meta.tag.punctuation.tag-close.xml",">"],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
- ["identifier","product"],
- ["text","."],
- ["identifier","title"],
+ ["support.class","product"],
+ ["keyword.operator","."],
+ ["support.object","title"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","h2"],
["meta.tag.punctuation.tag-close.xml",">"]
],[
"start",
["text.xml"," Only "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
+ ["text"," "],
+ ["support.class","product"],
+ ["keyword.operator","."],
+ ["support.object","price"],
["text"," "],
- ["identifier","product"],
- ["text","."],
- ["identifier","price"],
- ["text"," | "],
- ["identifier","format_as_money"],
+ ["keyword.operator","| "],
+ ["support.function","format_as_money"],
["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start"
],[
@@ -104,19 +111,21 @@
["meta.tag.punctuation.tag-open.xml","<"],
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"],
- ["variable","{{"],
- ["text"," "],
- ["identifier","product"],
- ["text","."],
- ["identifier","description"],
- ["text"," | "],
- ["identifier","prettyprint"],
- ["text"," | "],
+ ["meta.tag.punctuation.ouput-open","{{"],
+ ["text"," "],
+ ["support.class","product"],
+ ["keyword.operator","."],
+ ["support.object","description"],
+ ["text"," "],
+ ["keyword.operator","| "],
+ ["support.function","prettyprint"],
+ ["text"," "],
+ ["keyword.operator","| "],
["support.function","truncate"],
["text",": "],
["constant.numeric","200"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"]
@@ -131,11 +140,10 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","endfor"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endfor"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
@@ -166,13 +174,14 @@
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"],
["text.xml"," The word \"tobi\" in uppercase: "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
["string","'tobi'"],
- ["text"," | "],
+ ["text"," "],
+ ["keyword.operator","| "],
["support.function","upcase"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["text.xml"," "],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","p"],
@@ -183,13 +192,14 @@
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"],
["text.xml","The word \"tobi\" has "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
["string","'tobi'"],
- ["text"," | "],
+ ["text"," "],
+ ["keyword.operator","| "],
["support.function","size"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["text.xml"," letters! "],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","p"],
@@ -200,17 +210,18 @@
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"],
["text.xml","Change \"Hello world\" to \"Hi world\": "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
["string","'Hello world'"],
- ["text"," | "],
+ ["text"," "],
+ ["keyword.operator","| "],
["support.function","replace"],
["text",": "],
["string","'Hello'"],
["text",", "],
["string","'Hi'"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["text.xml"," "],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","p"],
@@ -221,15 +232,16 @@
["meta.tag.tag-name.xml","p"],
["meta.tag.punctuation.tag-close.xml",">"],
["text.xml","The date today is "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
["string","'now'"],
- ["text"," | "],
+ ["text"," "],
+ ["keyword.operator","| "],
["support.function","date"],
["text",": "],
["string","\"%Y %b %d\""],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["text.xml"," "],
["meta.tag.punctuation.end-tag-open.xml",""],
["meta.tag.tag-name.xml","p"],
@@ -255,41 +267,36 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword.block","if"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," if"],
["text"," "],
- ["identifier","user"],
- ["text","."],
- ["identifier","name"],
+ ["support.class","user"],
+ ["keyword.operator","."],
+ ["support.object","name"],
["text"," "],
["keyword.operator","=="],
["text"," "],
["string","'tobi'"],
- ["text"," "],
- ["identifier","or"],
- ["text"," "],
- ["identifier","user"],
- ["text","."],
- ["identifier","name"],
+ ["text"," or "],
+ ["support.class","user"],
+ ["keyword.operator","."],
+ ["support.object","name"],
["text"," "],
["keyword.operator","=="],
["text"," "],
["string","'marc'"],
["text"," "],
- ["variable","%}"],
- ["text.xml"," "]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," hi marc or tobi"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","endif"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endif"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["meta.tag.punctuation.end-tag-open.xml",""],
@@ -297,6 +304,33 @@
["meta.tag.punctuation.tag-close.xml",">"]
],[
"start"
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open.xml","<"],
+ ["meta.tag.tag-name.xml","h2"],
+ ["meta.tag.punctuation.tag-close.xml",">"],
+ ["text.xml","Comments"],
+ ["meta.tag.punctuation.end-tag-open.xml",""],
+ ["meta.tag.tag-name.xml","h2"],
+ ["meta.tag.punctuation.tag-close.xml",">"]
+],[
+ "start"
+],[
+ "start",
+ ["comment.line","{% #"],
+ ["comment"," Line Comment "],
+ ["comment.line","%}"]
+],[
+ "start"
+],[
+ "comment.block",
+ ["comment.block","{% comment %}"]
+],[
+ "comment.block",
+ ["comment"," Block Comment"]
+],[
+ "start",
+ ["comment.block","{% endcomment %}"]
],[
"start"
],[
@@ -316,80 +350,72 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword.block","case"],
- ["text"," "],
- ["identifier","template"],
- ["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," case"],
+ ["text"," template "],
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","when"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," when"],
["text"," "],
["string","'index'"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," Welcome"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","when"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," when"],
["text"," "],
["string","'product'"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
+ ["text"," "],
+ ["support.class","product"],
+ ["keyword.operator","."],
+ ["support.object","vendor"],
["text"," "],
- ["identifier","product"],
- ["text","."],
- ["identifier","vendor"],
- ["text"," | "],
- ["identifier","link_to_vendor"],
+ ["keyword.operator","| "],
+ ["support.function","link_to_vendor"],
["text"," "],
- ["variable","}}"],
+ ["meta.tag.punctuation.ouput-close","}}"],
["text.xml"," / "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
- ["identifier","product"],
- ["text","."],
- ["identifier","title"],
+ ["support.class","product"],
+ ["keyword.operator","."],
+ ["support.object","title"],
["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","else"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," else"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{{"],
- ["text"," "],
- ["identifier","page_title"],
- ["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-open","{{"],
+ ["text"," page_title "],
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","endcase"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endcase"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["meta.tag.punctuation.end-tag-open.xml",""],
@@ -416,34 +442,25 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword.block","for"],
- ["text"," "],
- ["identifier","item"],
- ["text"," "],
- ["keyword","in"],
- ["text"," "],
- ["identifier","array"],
- ["text"," "],
- ["variable","%}"],
- ["text.xml"," "]
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," for"],
+ ["text"," item"],
+ ["keyword.operator"," in "],
+ ["text","array "],
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{{"],
- ["text"," "],
- ["identifier","item"],
- ["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-open","{{"],
+ ["text"," item "],
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","endfor"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endfor"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["meta.tag.punctuation.end-tag-open.xml",""],
@@ -470,77 +487,68 @@
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword.block","tablerow"],
- ["text"," "],
- ["identifier","item"],
- ["text"," "],
- ["keyword","in"],
- ["text"," "],
- ["identifier","items"],
- ["text"," "],
- ["identifier","cols"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," tablerow"],
+ ["text"," item"],
+ ["keyword.operator"," in "],
+ ["text","items"],
+ ["support.function"," cols"],
["text",": "],
["constant.numeric","3"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," if"],
["text"," "],
- ["keyword.block","if"],
+ ["support.class","tablerowloop"],
+ ["keyword.operator","."],
+ ["support.object","col_first"],
["text"," "],
- ["variable.language","tablerowloop"],
- ["text","."],
- ["identifier","col_first"],
- ["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," First column: "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
- ["identifier","item"],
- ["text","."],
- ["identifier","variable"],
+ ["support.class","item"],
+ ["keyword.operator","."],
+ ["support.object","variable"],
["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","else"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," else"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," Different column: "],
- ["variable","{{"],
+ ["meta.tag.punctuation.ouput-open","{{"],
["text"," "],
- ["identifier","item"],
- ["text","."],
- ["identifier","variable"],
+ ["support.class","item"],
+ ["keyword.operator","."],
+ ["support.object","variable"],
["text"," "],
- ["variable","}}"]
+ ["meta.tag.punctuation.ouput-close","}}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
- ["text"," "],
- ["keyword","endif"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endif"],
["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["text.xml"," "],
- ["variable","{%"],
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["keyword.block"," endtablerow"],
["text"," "],
- ["keyword","endtablerow"],
- ["text"," "],
- ["variable","%}"]
+ ["meta.tag.punctuation.tag-close","%}"]
],[
"start",
["meta.tag.punctuation.end-tag-open.xml",""],
@@ -548,4 +556,228 @@
["meta.tag.punctuation.tag-close.xml",">"]
],[
"start"
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open.xml","<"],
+ ["meta.tag.tag-name.xml","h2"],
+ ["meta.tag.punctuation.tag-close.xml",">"],
+ ["text.xml","Embedded Code Blocks"],
+ ["meta.tag.punctuation.end-tag-open.xml",""],
+ ["meta.tag.tag-name.xml","h2"],
+ ["meta.tag.punctuation.tag-close.xml",">"]
+],[
+ "start"
+],[
+ "start",
+ ["text.xml","This support Shopify Liquid variation JSON schema code blocks"]
+],[
+ "start"
+],[
+ "schema-start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagschema.tag-name","schema"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "schema-start",
+ ["text"," "],
+ ["paren.lparen","{"]
+],[
+ "schema-start",
+ ["text"," "],
+ ["variable","\"string\""],
+ ["text",": "],
+ ["string","\"bar\""],
+ ["punctuation.operator",","]
+],[
+ "schema-start",
+ ["text"," "],
+ ["variable","\"boolean\""],
+ ["text",": "],
+ ["constant.language.boolean","true"],
+ ["punctuation.operator",","]
+],[
+ "schema-start",
+ ["text"," "],
+ ["variable","\"number\""],
+ ["text",": "],
+ ["constant.numeric","100"],
+ ["punctuation.operator",","]
+],[
+ "schema-start",
+ ["text"," "],
+ ["variable","\"object\""],
+ ["text",": "],
+ ["paren.lparen","{"]
+],[
+ "schema-start",
+ ["text"," "],
+ ["variable","\"array\""],
+ ["text",": "],
+ ["paren.lparen","["],
+ ["constant.numeric","100"],
+ ["punctuation.operator",","],
+ ["text"," "],
+ ["constant.language.boolean","false"],
+ ["punctuation.operator",","],
+ ["text"," "],
+ ["string","\"string\""],
+ ["punctuation.operator",","],
+ ["text"," "],
+ ["paren.lparen","{"],
+ ["paren.rparen","}"],
+ ["punctuation.operator",","],
+ ["text"," "],
+ ["paren.lparen","["],
+ ["paren.rparen","]"],
+ ["text"," "],
+ ["paren.rparen","]"]
+],[
+ "schema-start",
+ ["text"," "],
+ ["paren.rparen","}"]
+],[
+ "schema-start",
+ ["text"," "],
+ ["paren.rparen","}"]
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagendschema.tag-name","endschema"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "start"
+],[
+ "start",
+ ["text.xml","This support Shopify Liquid variation Stylesheet and Style code blocks"]
+],[
+ "start"
+],[
+ "style-start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagstyle.tag-name","style"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "style-ruleset",
+ ["text"," "],
+ ["variable",".class"],
+ ["text"," "],
+ ["paren.lparen","{"]
+],[
+ "style-ruleset",
+ ["text"," "],
+ ["support.type","font-size"],
+ ["punctuation.operator",":"],
+ ["text"," "],
+ ["constant.numeric","10"],
+ ["keyword","px"],
+ ["punctuation.operator",";"],
+ ["text"," "],
+ ["comment","/* comment */"]
+],[
+ "style-start",
+ ["text"," "],
+ ["paren.rparen","}"]
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagendstyle.tag-name","endstyle"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "start"
+],[
+ "stylesheet-start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagstylesheet.tag-name","stylesheet"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "stylesheet-ruleset",
+ ["text"," "],
+ ["variable",".class"],
+ ["text"," "],
+ ["paren.lparen","{"]
+],[
+ "stylesheet-ruleset",
+ ["text"," "],
+ ["support.type","font-size"],
+ ["punctuation.operator",":"],
+ ["text"," "],
+ ["constant.numeric","10"],
+ ["keyword","px"],
+ ["punctuation.operator",";"],
+ ["text"," "],
+ ["comment","/* comment */"]
+],[
+ "stylesheet-start",
+ ["text"," "],
+ ["paren.rparen","}"]
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagendstylesheet.tag-name","endstylesheet"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "start"
+],[
+ "start",
+ ["text.xml","This support Shopify Liquid variation JavaScript code blocks"]
+],[
+ "start"
+],[
+ "javascript-start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagjavascript.tag-name","javascript"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "javascript-start"
+],[
+ "javascript-start",
+ ["text"," "],
+ ["storage.type","function"],
+ ["text"," "],
+ ["entity.name.function","foo"],
+ ["text"," "],
+ ["paren.lparen","("],
+ ["variable.parameter","param"],
+ ["paren.rparen",")"],
+ ["text"," "],
+ ["paren.lparen","{"]
+],[
+ "javascript-start"
+],[
+ "javascript-no_regex",
+ ["text"," "],
+ ["keyword","return"],
+ ["text"," "],
+ ["string","'something'"],
+ ["text"," "],
+ ["comment","// comment"]
+],[
+ "javascript-no_regex",
+ ["text"," "],
+ ["paren.rparen","}"]
+],[
+ "javascript-no_regex"
+],[
+ "start",
+ ["meta.tag.punctuation.tag-open","{%"],
+ ["text"," "],
+ ["keyword.tagendjavascript.tag-name","endjavascript"],
+ ["text"," "],
+ ["meta.tag.punctuation.tag-close","%}"]
+],[
+ "start"
]]
\ No newline at end of file
diff --git a/src/mode/behaviour/liquid.js b/src/mode/behaviour/liquid.js
index 60b59d2153b..750278235d0 100644
--- a/src/mode/behaviour/liquid.js
+++ b/src/mode/behaviour/liquid.js
@@ -1,15 +1,15 @@
"use strict";
-
+
var oop = require("../../lib/oop");
var Behaviour = require("../behaviour").Behaviour;
var XmlBehaviour = require("./xml").XmlBehaviour;
var TokenIterator = require("../../token_iterator").TokenIterator;
var lang = require("../../lib/lang");
-
+
function is(token, type) {
return token && token.type.lastIndexOf(type + ".xml") > -1;
}
-
+
var LiquidBehaviour = function () {
XmlBehaviour.call(this);
this.add("autoBraceTagClosing","insertion", function (state, action, editor, session, text) {
@@ -21,7 +21,7 @@
// exit if we're not in a tag
if (!token || !( token.value.trim() === '%' || is(token, "tag-name") || is(token, "tag-whitespace") || is(token, "attribute-name") || is(token, "attribute-equals") || is(token, "attribute-value")))
return;
-
+
// exit if we're inside of a quoted attribute value
if (is(token, "reference.attribute-value"))
return;
@@ -38,26 +38,26 @@
iterator.stepBackward();
}
}
- // exit if the tag is empty
+ // exit if the tag is empty
if (/{%\s*%/.test(session.getLine(position.row))) return;
if (/^\s*}/.test(session.getLine(position.row).slice(position.column)))
return;
// find tag name
- while (!token.type != 'keyword.block') {
+ while (!token.type != 'meta.tag.punctuation.tag-open') {
token = iterator.stepBackward();
if (token.value == '{%') {
while(true) {
token = iterator.stepForward();
- if (token.type === 'keyword.block') {
+ if (token.type === 'meta.tag.punctuation.tag-open') {
break;
} else if (token.value.trim() == '%') {
token = null;
break;
}
}
- break;
+ break;
}
}
if (!token ) return ;
@@ -67,11 +67,11 @@
// exit if the tag is ending
if (is(iterator.stepBackward(), "end-tag-open"))
return;
-
+
var element = token.value;
if (tokenRow == position.row)
element = element.substring(0, position.column - tokenColumn);
-
+
if (this.voidElements.hasOwnProperty(element.toLowerCase()))
return;
return {
@@ -80,9 +80,9 @@
};
}
});
-
+
};
oop.inherits(LiquidBehaviour, Behaviour);
-
+
exports.LiquidBehaviour = LiquidBehaviour;
diff --git a/src/mode/liquid.js b/src/mode/liquid.js
index fb512710844..c1fe5f69b9c 100644
--- a/src/mode/liquid.js
+++ b/src/mode/liquid.js
@@ -1,24 +1,40 @@
var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var HtmlMode = require("./html").Mode;
-var HtmlCompletions = require("./html_completions").HtmlCompletions;
-var LiquidBehaviour = require("./behaviour/liquid").LiquidBehaviour;
+var JavascriptMode = require("./javascript").Mode;
+var JsonMode = require("./json").Mode;
+var CssMode = require("./css").Mode;
var LiquidHighlightRules = require("./liquid_highlight_rules").LiquidHighlightRules;
var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
-var Mode = function() {
- this.HighlightRules = LiquidHighlightRules;
- this.$outdent = new MatchingBraceOutdent();
- this.$behaviour = new LiquidBehaviour();
- this.$completer = new HtmlCompletions();
+/* -------------------------------------------- */
+/* FOLDS */
+/* -------------------------------------------- */
+
+var FoldMode = require("./folding/cstyle").FoldMode;
+
+/* -------------------------------------------- */
+/* MODE */
+/* -------------------------------------------- */
+
+var Mode = function () {
+
+ JsonMode.call(this);
+ HtmlMode.call(this);
+ CssMode.call(this);
+ JavascriptMode.call(this);
+ this.HighlightRules = LiquidHighlightRules;
+ this.foldingRules = new FoldMode();
+
};
+
oop.inherits(Mode, TextMode);
-(function() {
- // this.blockComment = {start: "{% comment %}", end: "{% endcomment %}"};
+(function () {
+
this.blockComment = {start: ""};
this.voidElements = new HtmlMode().voidElements;
-
+
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
@@ -50,6 +66,7 @@ oop.inherits(Mode, TextMode);
this.$id = "ace/mode/liquid";
this.snippetFileId = "ace/snippets/liquid";
-}).call(Mode.prototype);
+
+}.call(Mode.prototype));
exports.Mode = Mode;
diff --git a/src/mode/liquid_highlight_rules.js b/src/mode/liquid_highlight_rules.js
index 4db550a62f7..c2fd4070539 100644
--- a/src/mode/liquid_highlight_rules.js
+++ b/src/mode/liquid_highlight_rules.js
@@ -1,103 +1,274 @@
"use strict";
var oop = require("../lib/oop");
+
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
+var CssHighlightRules = require("./css_highlight_rules").CssHighlightRules;
var HtmlHighlightRules = require("./html_highlight_rules").HtmlHighlightRules;
+var JsonHighlightRules = require("./json_highlight_rules").JsonHighlightRules;
+var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScriptHighlightRules;
-var LiquidHighlightRules = function() {
- HtmlHighlightRules.call(this);
+var LiquidHighlightRules = function () {
- // see: https://developer.mozilla.org/en/Liquid/Reference/Global_Objects
- var functions = (
- // Standard Filters
- "date|capitalize|downcase|upcase|first|last|join|sort|map|size|escape|" +
- "escape_once|strip_html|strip_newlines|newline_to_br|replace|replace_first|" +
- "truncate|truncatewords|prepend|append|minus|plus|times|divided_by|split"
- );
+ HtmlHighlightRules.call(this);
- var keywords = (
- // Standard Tags
- "capture|endcapture|case|endcase|when|comment|endcomment|" +
- "cycle|for|endfor|in|reversed|if|endif|else|elsif|include|endinclude|unless|endunless|" +
- // Commonly used tags
- "style|text|image|widget|plugin|marker|endmarker|tablerow|endtablerow"
- );
-
- // common standard block tags that require to be closed with an end[block] token
- var blocks = 'for|if|case|capture|unless|tablerow|marker|comment';
-
- var builtinVariables = 'forloop|tablerowloop';
- // "forloop\\.(length|index|index0|rindex|rindex0|first|last)|limit|offset|range" +
- // "tablerowloop\\.(length|index|index0|rindex|rindex0|first|last|col|col0|"+
- // "col_first|col_last)"
-
- var definitions = ("assign");
-
- var keywordMapper = this.createKeywordMapper({
- "variable.language": builtinVariables,
- "keyword": keywords,
- "keyword.block": blocks,
- "support.function": functions,
- "keyword.definition": definitions
- }, "identifier");
-
- // add liquid start tags to the HTML start tags
- for (var rule in this.$rules) {
- this.$rules[rule].unshift({
- token : "variable",
- regex : "{%",
- push : "liquid-start"
- }, {
- token : "variable",
- regex : "{{",
- push : "liquid-start"
- });
- }
+ /**
+ * Embedded Matches
+ *
+ * Handles `onMatch` tokens and correct parses the
+ * inner contents of the tag.
+ */
+ function onMatchEmbedded(name) {
+
+ const length = name.length;
+
+ return function (value) {
+
+ const idx = value.indexOf(name);
+
+ const x = [
+ {
+ type: "meta.tag.punctuation.tag-open",
+ value: "{%"
+ },
+ {
+ type: "text",
+ value: value.slice(2, idx)
+ },
+ {
+ type: "keyword.tag" + name + ".tag-name",
+ value: value.slice(idx, idx + length)
+ },
+ {
+ type: "text",
+ value: value.slice(idx + length, value.indexOf("%}"))
+ },
+ {
+ type: "meta.tag.punctuation.tag-close",
+ value: "%}"
+ }
+ ];
- this.addRules({
- "liquid-start" : [{
- token: "variable",
- regex: "}}",
+ return x;
+ };
+ }
+
+
+ for (var rule in this.$rules) {
+
+ this.$rules[rule].unshift(
+ {
+ token: "comment.block",
+ regex: /{%-?\s*comment\s*-?%}/,
+ next: [
+ {
+ token: "comment.block",
+ regex: /{%-?\s*endcomment\s*-?%}/,
+ next: "pop"
+ },
+ {
+ defaultToken: "comment",
+ caseInsensitive: false
+ }
+ ]
+ },
+ {
+ token: "comment.line",
+ regex: /{%-?\s*#/,
+ next: [
+ {
+ token: "comment.line",
+ regex: /-?%}/,
next: "pop"
- }, {
- token: "variable",
- regex: "%}",
+ },
+ {
+ defaultToken: "comment",
+ caseInsensitive: false
+ }
+ ]
+ },
+ {
+ token: 'style.embedded.start',
+ regex: /({%-?\s*\bstyle\b\s*-?%})/,
+ next: "style-start",
+ onMatch: onMatchEmbedded("style")
+ },
+ {
+ regex: /({%-?\s*\bstylesheet\b\s*-?%})/,
+ next: "stylesheet-start",
+ onMatch: onMatchEmbedded("stylesheet")
+ },
+ {
+ regex: /({%-?\s*\bschema\b\s*-?%})/,
+ next: "schema-start",
+ onMatch: onMatchEmbedded("schema")
+ },
+ {
+ regex: /({%-?\s*\bjavascript\b\s*-?%})/,
+ next: "javascript-start",
+ onMatch: onMatchEmbedded("javascript")
+ },
+ {
+ token: "meta.tag.punctuation.tag-open",
+ regex: /({%)/,
+ next: [
+ {
+ token: "keyword.block",
+ regex: /-?\s*[a-zA-Z_$][a-zA-Z0-9_$]+\b/,
+ next: 'liquid-start'
+ },
+ {
+ token: "meta.tag.punctuation.tag-close",
+ regex: /(-?)(%})/,
next: "pop"
- }, {
- 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 : keywordMapper,
- regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
- }, {
- token : "keyword.operator",
- regex : "/|\\*|\\-|\\+|=|!=|\\?\\:"
- }, {
- token : "paren.lparen",
- regex : /[\[\({]/
- }, {
- token : "paren.rparen",
- regex : /[\])}]/
- }, {
- token : "text",
- regex : "\\s+"
- }]
- });
-
- this.normalizeRules();
+ }
+ ]
+ },
+ {
+ token: "meta.tag.punctuation.ouput-open",
+ regex: /({{)/,
+ push: "liquid-start"
+ }
+ );
+ }
+
+ /* -------------------------------------------- */
+ /* EMBEDDED REGIONS */
+ /* -------------------------------------------- */
+
+ this.embedRules(JsonHighlightRules, "schema-", [
+ {
+ token: "schema-start",
+ next: "pop",
+ regex: /({%-?\s*\bendschema\b\s*-?%})/,
+ onMatch: onMatchEmbedded("endschema")
+ }
+ ]);
+
+ this.embedRules(JavaScriptHighlightRules, "javascript-", [
+ {
+ token: "javascript-start",
+ next: "pop",
+ regex: /({%-?\s*\bendjavascript\b\s*-?%})/,
+ onMatch: onMatchEmbedded("endjavascript")
+ }
+ ]);
+
+
+
+ this.embedRules(CssHighlightRules, "style-", [
+ {
+ token: "style-start",
+ next: "pop",
+ regex: /({%-?\s*\bendstyle\b\s*-?%})/,
+ onMatch: onMatchEmbedded("endstyle")
+ }
+ ]);
+
+ this.embedRules(CssHighlightRules, "stylesheet-", [
+ {
+ token: "stylesheet-start",
+ next: "pop",
+ regex: /({%-?\s*\bendstylesheet\b\s*-?%})/,
+ onMatch: onMatchEmbedded("endstylesheet")
+ }
+ ]);
+
+ /* -------------------------------------------- */
+ /* LIQUID GRAMMARS */
+ /* -------------------------------------------- */
+
+ this.addRules({
+ "liquid-start": [
+ {
+ token: "meta.tag.punctuation.ouput-close",
+ regex: /}}/,
+ next: "pop"
+ },
+ {
+ token: "meta.tag.punctuation.tag-close",
+ regex: /%}/,
+ next: "pop"
+ },
+ {
+ token: "string",
+ regex: /['](?:(?:\\.)|(?:[^'\\]))*?[']/
+ },
+ {
+ token: "string",
+ regex: /["](?:(?:\\.)|(?:[^'\\]))*?["]/
+ },
+ {
+ token: "constant.numeric",
+ regex: /0[xX][0-9a-fA-F]+\b/
+ },
+ {
+ token: "constant.numeric",
+ regex: /[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
+ },
+ {
+ token: "keyword.operator",
+ regex: /\*|\-|\+|=|!=|\?\|\:/
+ },
+ {
+ token: "constant.language.boolean",
+ regex: /(?:true|false|nil|empty)\b/
+ },
+ {
+ token: "keyword.operator",
+ regex: /\s+(?:and|contains|in|with)\b\s+/
+ },
+ {
+ token: ["keyword.operator", "support.function"],
+ regex: /(\|\s*)([a-zA-Z_]+)/
+
+ },
+ {
+ token: "support.function",
+ regex: /\s*([a-zA-Z_]+\b)(?=:)/
+ },
+ {
+ token: "keyword.operator",
+ regex:
+ /(:)\s*(?=[a-zA-Z_])/
+ },
+ {
+ token: [
+ "support.class",
+ "keyword.operator",
+ "support.object",
+ "keyword.operator",
+ "variable.parameter"
+ ],
+ regex: /(\w+)(\.)(\w+)(\.)?(\w+)?/
+ },
+ {
+ token: "variable.parameter",
+ regex: /\.([a-zA-Z_$][a-zA-Z0-9_$]*\b)$/
+ },
+ {
+ token: "support.class",
+ regex: /(?:additional_checkout_buttons|content_for_additional_checkout_buttons)\b/
+ },
+ {
+ token: "paren.lparen",
+ regex: /[\[\({]/
+ },
+ {
+ token: "paren.rparen",
+ regex: /[\])}]/
+ },
+ {
+ token: "text",
+ regex: /\s+/
+ }
+ ]
+ });
+
+ this.normalizeRules();
+
};
+
oop.inherits(LiquidHighlightRules, TextHighlightRules);
exports.LiquidHighlightRules = LiquidHighlightRules;