From f8559fe6e583e5e3cd52a48bdc7b2a8857da80ef Mon Sep 17 00:00:00 2001 From: David Wessman Date: Sun, 27 Aug 2023 17:52:47 +0200 Subject: [PATCH] Supports ERB-tags with if-else-end in same tag (#61) - Fixes #60 - Release v0.10.3 --- CHANGELOG.md | 15 ++++++ Gemfile.lock | 2 +- lib/syntax_tree/erb/format.rb | 23 ++------ lib/syntax_tree/erb/nodes.rb | 52 ++++++++++++------- lib/syntax_tree/erb/parser.rb | 2 +- lib/syntax_tree/erb/version.rb | 2 +- test/erb_test.rb | 39 ++++++++++++-- test/fixture/if_statements_formatted.html.erb | 6 ++- 8 files changed, 96 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef38a3c..6093172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [Unreleased] +## [0.10.3] - 2023-08-27 + +## Fixes + +- Allows parsing ERB-tags with if, else and end in the same tag + +```erb +<%= if true + what +end %> +``` + +This opens the possibility for formatting all if-statements with SyntaxTree properly +and removes the fix where any if-statement was force to one line. + ## [0.10.2] - 2023-08-22 ### Fixes diff --git a/Gemfile.lock b/Gemfile.lock index 98756b5..933cbcc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - w_syntax_tree-erb (0.10.2) + w_syntax_tree-erb (0.10.3) prettier_print (~> 1.2, >= 1.2.0) syntax_tree (~> 6.1, >= 6.1.1) diff --git a/lib/syntax_tree/erb/format.rb b/lib/syntax_tree/erb/format.rb index 6e2b02c..bec5168 100644 --- a/lib/syntax_tree/erb/format.rb +++ b/lib/syntax_tree/erb/format.rb @@ -114,7 +114,7 @@ def visit_erb(node) q.text(" ") visit(node.keyword) end - node.content.nil? ? q.text(" ") : visit(node.content) + node.content.blank? ? q.text(" ") : visit(node.content) visit(node.closing_tag) end @@ -163,7 +163,8 @@ def visit_erb_content(node) def format_statement(statement) formatter = - SyntaxTree::Formatter.new("", [], erb_print_width(statement)) + SyntaxTree::Formatter.new("", [], SyntaxTree::ERB::MAX_WIDTH) + formatter.format(statement) formatter.flush rows = formatter.output.join.split("\n") @@ -305,24 +306,6 @@ def node_new_line_count(node) node.respond_to?(:new_line) ? node.new_line&.count || 0 : 0 end - def erb_print_width(node) - # Set the width to maximum if we have an IfNode or IfOp, - # we cannot format them purely with SyntaxTree because the ERB-syntax will be unparseable. - check_for_if_statement(node) ? 999_999 : SyntaxTree::ERB::MAX_WIDTH - end - - def check_for_if_statement(node) - return false if node.nil? - - if node.is_a?(SyntaxTree::IfNode) || node.is_a?(SyntaxTree::IfOp) - return true - end - - node.child_nodes.any? do |child_node| - check_for_if_statement(child_node) - end - end - def handle_child_nodes(child_nodes) group = [] diff --git a/lib/syntax_tree/erb/nodes.rb b/lib/syntax_tree/erb/nodes.rb index 3c65e64..7690622 100644 --- a/lib/syntax_tree/erb/nodes.rb +++ b/lib/syntax_tree/erb/nodes.rb @@ -295,16 +295,7 @@ def initialize( ) end - @content = - if content.is_a?(ErbContent) - content - else - # Set content to nil if it is empty - content ||= [] - content = content.map(&:value).join if content.is_a?(Array) - ErbContent.new(value: content) unless content.strip.empty? - end - + @content = prepare_content(content) @closing_tag = closing_tag end @@ -338,6 +329,24 @@ def deconstruct_keys(keys) closing_tag: closing_tag ) end + + private + + def prepare_content(content) + if content.is_a?(ErbContent) + content + else + # Set content to nil if it is empty + content ||= [] + + ErbContent.new(value: content) + end + rescue SyntaxTree::Parser::ParseError + # Try to add the keyword to see if it parses + result = ErbContent.new(value: [keyword, *content]) + @keyword = nil + result + end end class ErbBlock < Block @@ -413,7 +422,7 @@ def accept(visitor) class ErbElse < ErbIf def accept(visitor) - visitor.visit_erb_if(self) + visitor.visit_erb_else(self) end end @@ -448,16 +457,23 @@ def accept(visitor) end class ErbContent < Node - attr_reader(:value, :unparsed_value) + attr_reader(:value) def initialize(value:) - @unparsed_value = value - begin - @value = SyntaxTree.parse(value.strip) - rescue SyntaxTree::Parser::ParseError - # Removes leading and trailing whitespace - @value = value&.lstrip&.rstrip + if value.is_a?(Array) + value = + value.map { |token| token.is_a?(Token) ? token.value : token }.join end + @value = SyntaxTree.parse(value.strip) + end + + def blank? + value.nil? || + value + .statements + .child_nodes + .reject { |node| node.is_a?(SyntaxTree::VoidStmt) } + .empty? end def accept(visitor) diff --git a/lib/syntax_tree/erb/parser.rb b/lib/syntax_tree/erb/parser.rb index 7052151..e6f22a7 100644 --- a/lib/syntax_tree/erb/parser.rb +++ b/lib/syntax_tree/erb/parser.rb @@ -588,7 +588,7 @@ def parse_erb_tag location: opening_tag.location.to(closing_tag.location) ) - case keyword&.type + case erb_node.keyword&.type when :erb_if, :erb_unless, :erb_elsif parse_erb_if(erb_node) when :erb_case, :erb_when diff --git a/lib/syntax_tree/erb/version.rb b/lib/syntax_tree/erb/version.rb index f530b17..5d22a1e 100644 --- a/lib/syntax_tree/erb/version.rb +++ b/lib/syntax_tree/erb/version.rb @@ -2,6 +2,6 @@ module SyntaxTree module ERB - VERSION = "0.10.2" + VERSION = "0.10.3" end end diff --git a/test/erb_test.rb b/test/erb_test.rb index d0329af..2b93d18 100644 --- a/test/erb_test.rb +++ b/test/erb_test.rb @@ -35,11 +35,44 @@ def test_erb_code_with_non_ascii assert_instance_of(SyntaxTree::ERB::ErbNode, parsed.elements.first) end + def test_if_and_end_in_same_output_tag_short + source = "<%= if true\n what\nend %>" + expected = "<%= what if true %>\n" + + assert_formatting(source, expected) + end + + def test_if_and_end_in_same_tag + source = "<% if true then this elsif false then that else maybe end %>" + expected = + "<% if true\n this\nelsif false\n that\nelse\n maybe\nend %>\n" + + assert_formatting(source, expected) + end + def test_long_if_statement source = "<%=number_to_percentage(@reports&.first&.stability*100,precision: 1) if @reports&.first&.other&.stronger&.longer %>" expected = - "<%= number_to_percentage(@reports&.first&.stability * 100, precision: 1) if @reports&.first&.other&.stronger&.longer %>\n" + "<%= if @reports&.first&.other&.stronger&.longer\n number_to_percentage(@reports&.first&.stability * 100, precision: 1)\nend %>\n" + + assert_formatting(source, expected) + end + + def test_erb_else_if_statement + source = + "<%if this%>\n

A

\n<%elsif that%>\n

B

\n<%else%>\n

C

\n<%end%>" + expected = + "<% if this %>\n

A

\n<% elsif that %>\n

B

\n<% else %>\n

C

\n<% end %>\n" + + assert_formatting(source, expected) + end + + def test_long_ternary + source = + "<%= number_to_percentage(@reports&.first&.stability * 100, precision: @reports&.first&.stability ? 'Stable' : 'Unstable') %>" + expected = + "<%= number_to_percentage(\n @reports&.first&.stability * 100,\n precision: @reports&.first&.stability ? \"Stable\" : \"Unstable\"\n) %>\n" assert_formatting(source, expected) end @@ -73,9 +106,9 @@ def test_erb_only_erb_comment def test_erb_ternary_as_argument_without_parentheses source = - "<%= f.submit f.object.id.present? ? t('buttons.titles.save'):t('buttons.titles.create') %>" + "<%= f.submit( f.object.id.present? ? t('buttons.titles.save'):t('buttons.titles.create')) %>" expected = - "<%= f.submit f.object.id.present? ? t(\"buttons.titles.save\") : t(\"buttons.titles.create\") %>\n" + "<%= f.submit(\n f.object.id.present? ? t(\"buttons.titles.save\") : t(\"buttons.titles.create\")\n) %>\n" assert_formatting(source, expected) end diff --git a/test/fixture/if_statements_formatted.html.erb b/test/fixture/if_statements_formatted.html.erb index ad91327..2e7a9d3 100644 --- a/test/fixture/if_statements_formatted.html.erb +++ b/test/fixture/if_statements_formatted.html.erb @@ -19,4 +19,8 @@ <% end %> -<%= @model ? @model.name : t("views.more_than_80_characters_long_row.categories.shared.version.default") %> +<%= if @model + @model.name +else + t("views.more_than_80_characters_long_row.categories.shared.version.default") +end %>