Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to LiquidDoc with the new {% doc %} tag #1895

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/liquid/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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"
Expand Down
2 changes: 2 additions & 0 deletions lib/liquid/tags.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
require_relative "tags/raw"
require_relative "tags/render"
require_relative "tags/cycle"
require_relative "tags/doc"

module Liquid
module Tags
Expand All @@ -42,6 +43,7 @@ module Tags
'if' => If,
'echo' => Echo,
'tablerow' => TableRow,
'doc' => Doc,
}.freeze
end
end
71 changes: 71 additions & 0 deletions lib/liquid/tags/doc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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
def render_to_output_buffer(_context, output)
output
end

def unknown_tag(_tag, _markup, _tokens)
end

def blank?
true
end

def parse_body(body, tokenizer)
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]
else
token =~ BlockBody::FullToken
Regexp.last_match(2)
end

raise_nested_doc_error if tag_name == "doc"

if tag_name == "enddoc"
parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
return false
end
end

raise_tag_never_closed(block_name)
end

private

def raise_nested_doc_error
raise SyntaxError, parse_context.locale.t("errors.syntax.doc_invalid_nested")
end
end
end
8 changes: 7 additions & 1 deletion test/unit/block_unit_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ def test_variable_many_embedded_fragments
)
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)
Expand Down
194 changes: 194 additions & 0 deletions test/unit/tags/doc_tag_unit_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# 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_inside_liquid_tag
template = <<~LIQUID.chomp
{% liquid
doc
Assigns foo to 1.
enddoc
assign foo = 1
%}
LIQUID

assert_template_result('', template)
end

def test_doc_tag_inside_liquid_tag_with_control_flow_nodes
template = <<~LIQUID.chomp
{% liquid
if 1 != 1
doc
else
echo 123
enddoc
endif
%}
LIQUID

assert_template_result('', template)
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_raises_an_error_for_unclosed_assign
error = assert_raises(Liquid::SyntaxError) do
template = <<~LIQUID.chomp
{% doc %}
{% assign foo = "1"
{% enddoc %}
LIQUID

Liquid::Template.parse(template)
end

exp_error = "Liquid syntax error: 'doc' tag was never closed"
act_error = error.message

assert_equal(exp_error, act_error)
end

def test_doc_tag_raises_an_error_for_malformed_syntax
error = assert_raises(Liquid::SyntaxError) do
template = <<~LIQUID.chomp
{% doc %}
{% {{ {%- enddoc %}
LIQUID

Liquid::Template.parse(template)
end

exp_error = "Liquid syntax error: 'doc' tag was never closed"
act_error = error.message

assert_equal(exp_error, act_error)
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!")

# Whitespace control within liquid tags
assert_template_result("Hello!World!", <<~LIQUID.chomp)
Hello!
{%- liquid
doc
this is inside a liquid tag
enddoc
-%}
World!
LIQUID

# Multiline whitespace control
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 %}")
assert_template_result('', "{%doc}{% assign a = 1 %}{%enddoc}{% endif %}")
end
end
Loading