Compare commits

...
5 Commits
5 changed files with 241 additions and 1 deletions
+2
View File
@@ -2,12 +2,14 @@
errors:
syntax:
tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: %{tag}"
block_tag_unexpected_args: "Syntax Error in '%{tag}' - Valid syntax: {% %{tag} %}{% end%{tag} %}"
assign: "Syntax Error in 'assign' - Valid syntax: assign [var] = [source]"
capture: "Syntax Error in 'capture' - Valid syntax: capture [var]"
case: "Syntax Error in 'case' - Valid syntax: case [condition]"
case_invalid_when: "Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %}"
case_invalid_else: "Syntax Error in tag 'case' - Valid else condition: {% else %} (no parameters) "
cycle: "Syntax Error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]"
doc_invalid_nested: "Syntax Error in 'doc' - Nested doc tags are not allowed"
for: "Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]"
for_invalid_in: "For loops require an 'in' clause"
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
+2
View File
@@ -19,6 +19,7 @@ require_relative "tags/comment"
require_relative "tags/raw"
require_relative "tags/render"
require_relative "tags/cycle"
require_relative "tags/doc"
module Liquid
module Tags
@@ -42,6 +43,7 @@ module Liquid
'if' => If,
'echo' => Echo,
'tablerow' => TableRow,
'doc' => Doc,
}.freeze
end
end
+70
View File
@@ -0,0 +1,70 @@
# frozen_string_literal: true
module Liquid
# @liquid_public_docs
# @liquid_type tag
# @liquid_category syntax
# @liquid_name doc
# @liquid_summary
# Documents template elements with annotations.
# @liquid_description
# The `doc` tag allows developers to include documentation within Liquid
# templates. Any content inside `doc` tags is not rendered or outputted.
# Liquid code inside will be parsed but not executed. This facilitates
# tooling support for features like code completion, linting, and inline
# documentation.
# @liquid_syntax
# {% doc %}
# Renders a message.
#
# @param {string} foo - A foo value.
# @param {string} [bar] - An optional bar value.
#
# @example
# {% render 'message', foo: 'Hello', bar: 'World' %}
# {% enddoc %}
# {{ foo }}, {{ bar }}!
class Doc < Block
NO_UNEXPECTED_ARGS = /\A\s*\z/
def initialize(tag_name, markup, parse_context)
super
ensure_valid_markup(tag_name, markup, parse_context)
end
def parse(tokens)
while (token = tokens.shift)
tag_name = token =~ BlockBody::FullTokenPossiblyInvalid && Regexp.last_match(2)
raise_nested_doc_error if tag_name == @tag_name
if tag_name == block_delimiter
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
return
end
end
raise_tag_never_closed(block_name)
end
def render_to_output_buffer(_context, output)
output
end
def blank?
true
end
private
def ensure_valid_markup(tag_name, markup, parse_context)
unless NO_UNEXPECTED_ARGS.match?(markup)
raise SyntaxError, parse_context.locale.t("errors.syntax.block_tag_unexpected_args", tag: tag_name)
end
end
def raise_nested_doc_error
raise SyntaxError, parse_context.locale.t("errors.syntax.doc_invalid_nested")
end
end
end
+7 -1
View File
@@ -47,12 +47,18 @@ class BlockUnitTest < Minitest::Test
)
end
def test_with_block
def test_comment_tag_with_block
template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
assert_equal([String, Comment, String], block_types(template.root.nodelist))
assert_equal(3, template.root.nodelist.size)
end
def test_doc_tag_with_block
template = Liquid::Template.parse(" {% doc %} {% enddoc %} ")
assert_equal([String, Doc, String], block_types(template.root.nodelist))
assert_equal(3, template.root.nodelist.size)
end
private
def block_types(nodelist)
+160
View File
@@ -0,0 +1,160 @@
# frozen_string_literal: true
require 'test_helper'
class DocTagUnitTest < Minitest::Test
def test_doc_tag
template = <<~LIQUID.chomp
{% doc %}
Renders loading-spinner.
@param {string} foo - some foo
@param {string} [bar] - optional bar
@example
{% render 'loading-spinner', foo: 'foo' %}
{% render 'loading-spinner', foo: 'foo', bar: 'bar' %}
{% enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_does_not_support_extra_arguments
error = assert_raises(Liquid::SyntaxError) do
template = <<~LIQUID.chomp
{% doc extra %}
{% enddoc %}
LIQUID
Liquid::Template.parse(template)
end
exp_error = "Liquid syntax error: Syntax Error in 'doc' - Valid syntax: {% doc %}{% enddoc %}"
act_error = error.message
assert_equal(exp_error, act_error)
end
def test_doc_tag_must_support_valid_tags
assert_match_syntax_error("Liquid syntax error (line 1): 'doc' tag was never closed", '{% doc %} foo')
assert_match_syntax_error("Liquid syntax error (line 1): Syntax Error in 'doc' - Valid syntax: {% doc %}{% enddoc %}", '{% doc } foo {% enddoc %}')
assert_match_syntax_error("Liquid syntax error (line 1): Syntax Error in 'doc' - Valid syntax: {% doc %}{% enddoc %}", '{% doc } foo %}{% enddoc %}')
end
def test_doc_tag_ignores_liquid_nodes
template = <<~LIQUID.chomp
{% doc %}
{% if true %}
{% if ... %}
{%- for ? -%}
{% while true %}
{%
unless if
%}
{% endcase %}
{% enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_ignores_unclosed_liquid_tags
template = <<~LIQUID.chomp
{% doc %}
{% if true %}
{% enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_does_not_allow_nested_docs
error = assert_raises(Liquid::SyntaxError) do
template = <<~LIQUID.chomp
{% doc %}
{% doc %}
{% doc %}
{% enddoc %}
LIQUID
Liquid::Template.parse(template)
end
exp_error = "Liquid syntax error: Syntax Error in 'doc' - Nested doc tags are not allowed"
act_error = error.message
assert_equal(exp_error, act_error)
end
def test_doc_tag_ignores_nested_raw_tags
template = <<~LIQUID.chomp
{% doc %}
{% raw %}
{% enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_ignores_unclosed_assign
template = <<~LIQUID.chomp
{% doc %}
{% assign foo = "1"
{% enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_ignores_malformed_syntax
template = <<~LIQUID.chomp
{% doc %}
{% {{ {%- enddoc %}
LIQUID
assert_template_result('', template)
end
def test_doc_tag_preserves_error_line_numbers
template = Liquid::Template.parse(<<~LIQUID.chomp, line_numbers: true)
{% doc %}
{% if true %}
{% enddoc %}
{{ errors.standard_error }}
LIQUID
expected = <<~TEXT.chomp
Liquid error (line 4): standard error
TEXT
assert_equal(expected, template.render('errors' => ErrorDrop.new))
end
def test_doc_tag_whitespace_control
# Basic whitespace control
assert_template_result("Hello!", " {%- doc -%}123{%- enddoc -%}Hello!")
assert_template_result("Hello!", "{%- doc -%}123{%- enddoc -%} Hello!")
assert_template_result("Hello!", " {%- doc -%}123{%- enddoc -%} Hello!")
assert_template_result("Hello!", <<~LIQUID.chomp)
{%- doc %}Whitespace control!{% enddoc -%}
Hello!
LIQUID
end
def test_doc_tag_delimiter_handling
assert_template_result('', <<~LIQUID.chomp)
{% if true %}
{% doc %}
{% docEXTRA %}wut{% enddocEXTRA %}xyz
{% enddoc %}
{% endif %}
LIQUID
assert_template_result('', "{% doc %}123{% enddoc xyz %}")
assert_template_result('', "{% doc %}123{% enddoc\txyz %}")
assert_template_result('', "{% doc %}123{% enddoc\nxyz %}")
assert_template_result('', "{% doc %}123{% enddoc\n xyz enddoc %}")
end
end