diff --git a/grammar.js b/grammar.js index c88bdde..b5a14d1 100644 --- a/grammar.js +++ b/grammar.js @@ -1,54 +1,72 @@ module.exports = grammar({ - name: 'pug', - externals: $ => [ - $._newline, - $._indent, - $._dedent - ], + name: "pug", + externals: ($) => [$._newline, $._indent, $._dedent], rules: { - source_file: $ => repeat(choice($.comment, $.tag)), - tag: $ => seq( - choice($.tag_name, $.id, $.class), - optional(repeat(choice($.id, $.class))), - optional($.attributes), - optional(seq(' ', $.content)), - $._newline, - optional($.children), - ), - attributes: $ => seq( - '(', - repeat(seq( - $.attribute, - choice(',', ' ') - )), - optional($.attribute), - ')', - ), - attribute: $ => seq( - $.attribute_name, - optional(seq( - '=', - $.quoted_attribute_value - )) - ), - children: $ => choice( - seq($._indent, repeat($.tag), $._dedent), - ), - comment: $ => seq( - '//', - optional($._comment_content), - $._newline, - optional(seq($._indent, repeat(seq($._comment_content, $._newline)), $._dedent)) - ), - tag_name: $ => /\w(?:[-:\w]*\w)?/, - class: $ => /\.[_a-z0-9\-]*[_a-z][_a-z0-9\-]*/i, - id: $ => /#[\w-]+/, - attribute_name: $ => /[\w@\-:]+/, - quoted_attribute_value: $ => choice( - seq("'", optional(alias(/[^']+/, $.attribute_value)), "'"), - seq('"', optional(alias(/[^"]+/, $.attribute_value)), '"') - ), - content: $ => /[^\n]+/, - _comment_content: $ => /[^ ][^\n]*/ - } -}) + source_file: ($) => repeat(choice($.comment, $.tag)), + pipe_content: ($) => + seq( + "|", + optional($._content_or_javascript), + $._newline, + ), + tag: ($) => + seq( + choice($.tag_name, $.id, $.class), + optional(repeat1(choice($.id, $.class))), + optional($.attributes), + choice( + seq(":", $.tag), + seq( + optional(seq(" ", $._content_or_javascript)), + $._newline, + optional($.children) + ) + ) + ), + attributes: ($) => + seq( + "(", + repeat(seq($.attribute, choice(",", " "))), + optional($.attribute), + ")" + ), + attribute: ($) => + seq( + $.attribute_name, + optional(repeat1(seq( + ".", + alias(/[\w@\-:]+/, $.attribute_modifier) + ))), + optional(seq("=", $.quoted_attribute_value)) + ), + children: $ => seq($._indent, repeat1($._children_choice), $._dedent), + _children_choice: ($) => + choice( + $.pipe_content, + $.tag, + ), + comment: ($) => + seq( + "//", + optional($._comment_content), + $._newline, + optional( + seq($._indent, repeat(seq($._comment_content, $._newline)), $._dedent) + ) + ), + tag_name: ($) => /\w(?:[-:\w]*\w)?/, + class: ($) => /\.[_a-z0-9\-]*[_a-z][_a-z0-9\-]*/i, + id: ($) => /#[\w-]+/, + attribute_name: ($) => /[\w@\-:]+/, + quoted_attribute_value: ($) => + choice( + seq("'", optional(alias(/[^']+/, $.attribute_value)), "'"), + seq('"', optional(alias(/[^"]+/, $.attribute_value)), '"') + ), + javascript: ($) => /[^\n}]+/, + content: ($) => /[^\n\{]+/, + _content_or_javascript: ($) => + repeat1(choice(seq("{{", $.javascript, "}}"), $.content)), + _comment_content: ($) => /[^ ][^\n]*/, + }, +}); diff --git a/src/node-types.json b/src/node-types.json index 1debdf6..77b6af0 100644 --- a/src/node-types.json +++ b/src/node-types.json @@ -7,6 +7,10 @@ "multiple": true, "required": true, "types": [ + { + "type": "attribute_modifier", + "named": true + }, { "type": "attribute_name", "named": true @@ -18,6 +22,11 @@ ] } }, + { + "type": "attribute_name", + "named": true, + "fields": {} + }, { "type": "attributes", "named": true, @@ -39,8 +48,12 @@ "fields": {}, "children": { "multiple": true, - "required": false, + "required": true, "types": [ + { + "type": "pipe_content", + "named": true + }, { "type": "tag", "named": true @@ -53,6 +66,25 @@ "named": true, "fields": {} }, + { + "type": "pipe_content", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "content", + "named": true + }, + { + "type": "javascript", + "named": true + } + ] + } + }, { "type": "quoted_attribute_value", "named": true, @@ -115,6 +147,14 @@ "type": "id", "named": true }, + { + "type": "javascript", + "named": true + }, + { + "type": "tag", + "named": true + }, { "type": "tag_name", "named": true @@ -146,16 +186,24 @@ "type": ",", "named": false }, + { + "type": ".", + "named": false + }, { "type": "//", "named": false }, + { + "type": ":", + "named": false + }, { "type": "=", "named": false }, { - "type": "attribute_name", + "type": "attribute_modifier", "named": true }, { @@ -174,8 +222,24 @@ "type": "id", "named": true }, + { + "type": "javascript", + "named": true + }, { "type": "tag_name", "named": true + }, + { + "type": "{{", + "named": false + }, + { + "type": "|", + "named": false + }, + { + "type": "}}", + "named": false } ] \ No newline at end of file diff --git a/test/corpus/comment.txt b/test/corpus/comment.txt new file mode 100644 index 0000000..cfd6efe --- /dev/null +++ b/test/corpus/comment.txt @@ -0,0 +1,26 @@ +======================== +Comment +======================== + +// Some comment that is + +--- + +(source_file + (comment)) + + +======================== +Multiline comment +======================== + +// Some comment + with nested line that + are still commented + +--- + +(source_file + (comment)) + + diff --git a/test/corpus/complex.txt b/test/corpus/complex.txt new file mode 100644 index 0000000..0f3e31f --- /dev/null +++ b/test/corpus/complex.txt @@ -0,0 +1,34 @@ +======================== +Mixed example +======================== + +// some comment +some-tag.class-one(attr) the content + sub-tag.some-slass + sub-sub-tag content + other-subtag#with-id.and-class(and-attr) + +--- + +(source_file + (comment) + (tag + (tag_name) + (class) + (attributes + (attribute (attribute_name))) + (content) + (children + (tag + (tag_name) + (class) + (children + (tag + (tag_name) + (content)))) + (tag + (tag_name) + (id) + (class) + (attributes + (attribute (attribute_name))))))) diff --git a/test/corpus/pipe.txt b/test/corpus/pipe.txt new file mode 100644 index 0000000..a6a18f4 --- /dev/null +++ b/test/corpus/pipe.txt @@ -0,0 +1,74 @@ +======================== +Tag with piped content +========================= + +div + |Content here + +--- + +(source_file + (tag + (tag_name) + (children + (pipe_content (content))))) + +======================== +Tag with blank piped content + tag +========================= + +div + | + | Content here + | + div + +--- + +(source_file + (tag + (tag_name) + (children + (pipe_content) + (pipe_content (content)) + (pipe_content) + (tag (tag_name)) + ) +)) + + +======================== +Tag with multi-line piped content +========================= + +div + |Content here + |more + | content here, + +--- + +(source_file + (tag + (tag_name) + (children + (pipe_content (content)) + (pipe_content (content)) + (pipe_content (content)) + ))) + +======================== +Content Javascript +========================= + +div + |Content {{ some JS code here }} Content + +--- + +(source_file + (tag + (tag_name) + (children + (pipe_content (content) (javascript) (content))))) + diff --git a/test/corpus/tag.txt b/test/corpus/tag.txt index 72b162c..7337615 100644 --- a/test/corpus/tag.txt +++ b/test/corpus/tag.txt @@ -54,10 +54,10 @@ some-tag(attr, other-attr="test") (attribute (attribute_name) (quoted_attribute_value (attribute_value)))))) ======================== -Tag with content -========================= +vue-attribute modifier +======================== -some-tag(attr) content +some-tag(@click.prevent.stop="click") --- @@ -65,33 +65,43 @@ some-tag(attr) content (tag (tag_name) (attributes - (attribute (attribute_name))) - (content))) + (attribute (attribute_name) (attribute_modifier) (attribute_modifier) (quoted_attribute_value (attribute_value)))))) ======================== -Comment -======================== +Tag with content +========================= -// Some comment that is +some-tag(attr) content --- (source_file - (comment)) - + (tag + (tag_name) + (attributes + (attribute (attribute_name))) + (content))) ======================== -Multiline comment -======================== +Wrapped Tag with content +========================= -// Some comment - with nested line that - are still commented +div.card: div.card-body content --- (source_file - (comment)) + (tag + (tag_name) + (class) + (tag + (tag_name) + (class) + (content) + ) + ) +) + ======================== Tag with children @@ -116,37 +126,3 @@ some-tag (attribute (attribute_name))))))) -======================== -Mixed example -======================== - -// some comment -some-tag.class-one(attr) the content - sub-tag.some-slass - sub-sub-tag content - other-subtag#with-id.and-class(and-attr) - ---- - -(source_file - (comment) - (tag - (tag_name) - (class) - (attributes - (attribute (attribute_name))) - (content) - (children - (tag - (tag_name) - (class) - (children - (tag - (tag_name) - (content)))) - (tag - (tag_name) - (id) - (class) - (attributes - (attribute (attribute_name))))))) diff --git a/test/example.pug b/test/example.pug index 45537f3..e3117ad 100644 --- a/test/example.pug +++ b/test/example.pug @@ -1,6 +1,10 @@ // some comment multiline though -some-tag.class-one(attr) the content +some-tag.class-one(attr="1" v-if='jsCode' :toggle='true') the content sub-tag.some-slass sub-sub-tag content other-subtag#with-id.and-class(and-attr) + | Text +div + | Text {{ variable() == "javascript" }} with inline JS +.foo(style="width: 2px")