Merge branch 'main' into fix/custom_tag_rendering

* main:
  implement whitespace control to comment tag
  fix parsing comment tag delimiter
  fix parsing nested comment tag with extra strings
  fix parsing comment tag delimiter with extra strings
  fix parsing comment block body inside a liquid tag
  allow incomplete tags inside a comment tag
  refactor comment tag unit test to use test helper
  add a quirky comment tag unit test
  clean up comment tag body parsing
  don't parse nodes inside a comment tag
This commit is contained in:
Ian Ker-Seymer
2023-12-10 10:33:59 -05:00
4 changed files with 241 additions and 2 deletions
+1
View File
@@ -6,6 +6,7 @@ module Liquid
class BlockBody
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
WhitespaceOrNothing = /\A\s*\z/
TAGSTART = "{%"
+70
View File
@@ -15,6 +15,8 @@ module Liquid
# {% endcomment %}
# @liquid_syntax_keyword content The content of the comment.
class Comment < Block
TAG_DELIMITER = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(endcomment)\s*(\s.*)?#{WhitespaceControl}?#{TagEnd}\z/om
def render_to_output_buffer(_context, output)
output
end
@@ -25,6 +27,74 @@ module Liquid
def blank?
true
end
private
def parse_body(body, tokenizer)
if parse_context.depth >= MAX_DEPTH
raise StackLevelError, "Nesting too deep"
end
parse_context.depth += 1
comment_tag_depth = 1
begin
# Consume tokens without creating child nodes.
# The children tag doesn't require to be a valid Liquid except the comment and raw tag.
# The child comment and raw tag must be closed.
while (token = tokenizer.send(:shift))
tag_name = if tokenizer.for_liquid_tag
next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)
tag_name_match = BlockBody::LiquidTagToken.match(token)
next if tag_name_match.nil?
tag_name_match[1]
elsif TAG_DELIMITER.match?(token)
# aggressively match comment delimiter
"endcomment"
elsif token =~ BlockBody::FullToken && Regexp.last_match(2) == "comment"
# aggressively match comment tag
"comment"
else
tag_name_match = BlockBody::FullTokenPossiblyInvalid.match(token)
next if tag_name_match.nil?
tag_name_match[2]
end
case tag_name
when "raw"
parse_raw_tag_body(tokenizer)
when "comment"
comment_tag_depth += 1
when "endcomment"
comment_tag_depth -= 1
if comment_tag_depth.zero?
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
return false
end
end
end
raise_tag_never_closed(block_name)
ensure
parse_context.depth -= 1
end
false
end
def parse_raw_tag_body(tokenizer)
while (token = tokenizer.send(:shift))
return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
end
raise_tag_never_closed("raw")
end
end
Template.register_tag('comment', Comment)
+1 -2
View File
@@ -14,7 +14,6 @@ module Liquid
# @liquid_syntax_keyword expression The expression to be output without being rendered.
class Raw < Block
Syntax = /\A\s*\z/
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
def initialize(tag_name, markup, parse_context)
super
@@ -25,7 +24,7 @@ module Liquid
def parse(tokens)
@body = +''
while (token = tokens.shift)
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
if token =~ BlockBody::FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
return