diff --git a/lib/erb/formatter.rb b/lib/erb/formatter.rb index 461e801..ed91fa6 100644 --- a/lib/erb/formatter.rb +++ b/lib/erb/formatter.rb @@ -39,7 +39,7 @@ class Error < StandardError; end ATTR = Regexp.union(SINGLE_QUOTE_ATTR, DOUBLE_QUOTE_ATTR, UNQUOTED_ATTR, UNQUOTED_VALUE) MULTILINE_ATTR_NAMES = %w[class data-action] - ERB_TAG = %r{(<%(?:==|=|-|))\s*(.*?)\s*(-?%>)}m + ERB_TAG = %r{(<%(?:=|-|#)*)(?:(?!\n)\s)*(.*?)\s*(-?%>)}m ERB_PLACEHOLDER = %r{erb[a-z0-9]+tag} TAG_NAME = /[a-z0-9_:-]+/u @@ -273,7 +273,11 @@ def format_ruby(code, autoclose: false) SyntaxTree::Command.prepend SyntaxTreeCommandPatch code = begin - SyntaxTree.format(code, @line_width) + # TODO: For single-lines, 7 should be subtracted instead of 2: 3 for opening, 2 for closing and 2 surrounding spaces + # Subtract 2 for multiline indentation or for the surrounding tags + # Then subtract twice the tag_stack size to respect indentation + width = @line_width - 2 - tag_stack.size * 2 + SyntaxTree.format(code, width) rescue SyntaxTree::Parser::ParseError => error p RUBY_PARSE_ERROR: error if @debug code @@ -281,7 +285,7 @@ def format_ruby(code, autoclose: false) lines = code.strip.lines lines = lines[0...-1] if autoclose - code = lines.map { |l| indented(l.chomp("\n"), strip: false) }.join.strip + code = lines.map { |l| indented(l.chomp("\n"), strip: false) }.join p RUBY_OUT: code if @debug code end @@ -307,31 +311,44 @@ def format_erb_tags(string) format_text(erb_pre_match) erb_open, ruby_code, erb_close = ERB_TAG.match(erb_code).captures - erb_open << ' ' unless ruby_code.start_with?('#') - case ruby_code - when RUBY_STANDALONE_BLOCK + block_type = + if erb_open.include?('#') + :comment + else + case ruby_code + when RUBY_STANDALONE_BLOCK then :standalone + when RUBY_CLOSE_BLOCK then :close + when RUBY_REOPEN_BLOCK then :reopen + when RUBY_OPEN_BLOCK then :open + else :other + end + end + + # Format Ruby code, and indent if it's multiline + if %i[standalone other].include? block_type ruby_code = format_ruby(ruby_code, autoclose: false) - full_erb_tag = "#{erb_open}#{ruby_code} #{erb_close}" - html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) - when RUBY_CLOSE_BLOCK - full_erb_tag = "#{erb_open}#{ruby_code} #{erb_close}" - tag_stack_pop('%erb%', ruby_code) - html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) - when RUBY_REOPEN_BLOCK - full_erb_tag = "#{erb_open}#{ruby_code} #{erb_close}" - tag_stack_pop('%erb%', ruby_code) - html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) - tag_stack_push('%erb%', ruby_code) - when RUBY_OPEN_BLOCK - full_erb_tag = "#{erb_open}#{ruby_code} #{erb_close}" - html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) - tag_stack_push('%erb%', ruby_code) + ruby_code.gsub!(/^/, ' ') if ruby_code.strip.include?("\n") + end + + # Remove the first line if it only has whitespace + ruby_code.sub!(/\A((?!\n)\s)*\n/, '') + + # Reset "common" indentation of multi-line comments + if block_type == :comment && ruby_code.strip.include?("\n") + # Leave comments intact, but if they're multiline, replace common indentation + ruby_code.gsub!(/^#{ruby_code.scan(/^ */).min_by(&:length)}/, ' ') + end + + if ruby_code.strip.include?("\n") + full_erb_tag = "#{erb_open}\n#{ruby_code}#{indented(erb_close)}" else - ruby_code = format_ruby(ruby_code, autoclose: false) - full_erb_tag = "#{erb_open}#{ruby_code} #{erb_close}" - html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) + full_erb_tag = "#{erb_open} #{ruby_code.strip} #{erb_close}" end + + tag_stack_pop('%erb%', ruby_code) if %i[close reopen].include? block_type + html << (erb_pre_match.match?(/\s+\z/) ? indented(full_erb_tag) : full_erb_tag) + tag_stack_push('%erb%', ruby_code) if %i[reopen open].include? block_type else p ERB_REST: erb_scanner.rest if @debug rest = erb_scanner.rest.to_s diff --git a/test/erb/test_formatter.rb b/test/erb/test_formatter.rb index bf41eea..bef5603 100644 --- a/test/erb/test_formatter.rb +++ b/test/erb/test_formatter.rb @@ -146,12 +146,16 @@ def test_tagnames_with_dashes def test_format_ruby assert_equal( - "
\n" \ - " <%= render MyComponent.new(\n" \ - " foo: barbarbarbarbarbarbarbar,\n" \ - " bar: bazbazbazbazbazbazbazbaz,\n" \ - " ) %>\n" \ - "
\n", + <<~ERB, +
+ <%= + render MyComponent.new( + foo: barbarbarbarbarbarbarbar, + bar: bazbazbazbazbazbazbazbaz, + ) + %> +
+ ERB ERB::Formatter.format("
<%=render MyComponent.new(foo:barbarbarbarbarbarbarbar,bar:bazbazbazbazbazbazbazbaz)%>
"), ) end diff --git a/test/fixtures/comments-2.html.erb b/test/fixtures/comments-2.html.erb index 54c6d14..bf133c8 100644 --- a/test/fixtures/comments-2.html.erb +++ b/test/fixtures/comments-2.html.erb @@ -4,5 +4,5 @@ <%-# link_to 'Approve', some_path, class: 'something', disabled: disabled %> <%# if smth %> -<%#else %> +<%# else %> <%# end %> \ No newline at end of file diff --git a/test/fixtures/comments-2.html.expected.erb b/test/fixtures/comments-2.html.expected.erb index 441c788..84f0ce5 100644 --- a/test/fixtures/comments-2.html.expected.erb +++ b/test/fixtures/comments-2.html.expected.erb @@ -4,5 +4,5 @@ <%-# link_to 'Approve', some_path, class: 'something', disabled: disabled %> <%# if smth %> -<%#else %> +<%# else %> <%# end %> diff --git a/test/fixtures/comments.html.erb b/test/fixtures/comments.html.erb index 3036cd6..4173190 100644 --- a/test/fixtures/comments.html.erb +++ b/test/fixtures/comments.html.erb @@ -72,4 +72,4 @@ This fails <%# hey -%> \ No newline at end of file +%> diff --git a/test/fixtures/comments.html.expected.erb b/test/fixtures/comments.html.expected.erb index a4b7c62..a0b914d 100644 --- a/test/fixtures/comments.html.expected.erb +++ b/test/fixtures/comments.html.expected.erb @@ -1,64 +1,74 @@ <%# -This fails -hey -hey -hey -hey %> + This fails + hey + hey + hey + hey +%> <%# -This fails -hey -hey -hey -hey %> + This fails + hey + hey + hey + hey +%> <%# -This fails -hey -hey -hey -hey %> + This fails + hey + hey + hey + hey +%> -<%# This fails -This fails -hey -hey -hey -hey %> - -<%# This fails -This fails -hey -hey -hey -hey %> - -<%# This fails -This fails -hey -hey -hey -hey %> - -<%#This fails -This fails -hey -hey -hey -hey %> +<%# + This fails + This fails + hey + hey + hey + hey +%> -<%# This fails -This fails -hey -hey -hey -hey %> +<%# + This fails + This fails + hey + hey + hey + hey +%> <%# -hey %> + This fails + This fails + hey + hey + hey + hey +%> <%# -hey %> + This fails + This fails + hey + hey + hey + hey +%> <%# -hey %> + This fails + This fails + hey + hey + hey + hey +%> + +<%# hey %> + +<%# hey %> + +<%# hey %> diff --git a/test/fixtures/complex_case_when.html.expected.erb b/test/fixtures/complex_case_when.html.expected.erb index 5a0e743..70f9a7e 100644 --- a/test/fixtures/complex_case_when.html.expected.erb +++ b/test/fixtures/complex_case_when.html.expected.erb @@ -14,11 +14,13 @@ <% when 'Foo::PaymentMethod' %> <%= t(".payment.stripe_invoice") %> <% else %> - <% Rails.logger.error.report( - StandardError.new( - "No human readable name found for payment method #{payment_method.class}", - ), - ) %> + <% + Rails.logger.error.report( + StandardError.new( + "No human readable name found for payment method #{payment_method.class}", + ), + ) + %> <% end %> <% else %> <%= t(".payment.no_payment_method_found") %> diff --git a/test/fixtures/formatted-2.html.expected.erb b/test/fixtures/formatted-2.html.expected.erb index 169ba7f..56b7f30 100644 --- a/test/fixtures/formatted-2.html.expected.erb +++ b/test/fixtures/formatted-2.html.expected.erb @@ -5,12 +5,14 @@ <%= react_component("HelloWorld", { greeting: "Hello from react-rails." }) %> <%= react_component("HelloWorld", { greeting: "Hello from react-rails." }) %> - <%= react_component( - "HelloWorld", - { greeting: "Hello from react-rails." }, - { greeting: "Hello from react-rails." }, - { greeting: "Hello from react-rails." }, - { greeting: "Hello from react-rails." }, - ) %> + <%= + react_component( + "HelloWorld", + { greeting: "Hello from react-rails." }, + { greeting: "Hello from react-rails." }, + { greeting: "Hello from react-rails." }, + { greeting: "Hello from react-rails." }, + ) + %> diff --git a/test/fixtures/formatted.html.expected.erb b/test/fixtures/formatted.html.expected.erb index 2adfd9c..ced7d10 100644 --- a/test/fixtures/formatted.html.expected.erb +++ b/test/fixtures/formatted.html.expected.erb @@ -1,6 +1,8 @@ -<% link_to "Very long string here and there", -very_very_very_long_long_long_pathhhhhh_here, -opt: "212", -options: "222sdasdasd", -class: " 322 ", -dis: diss %> +<% + link_to "Very long string here and there", + very_very_very_long_long_long_pathhhhhh_here, + opt: "212", + options: "222sdasdasd", + class: " 322 ", + dis: diss +%> diff --git a/test/fixtures/if_then_else.html.expected.erb b/test/fixtures/if_then_else.html.expected.erb index f26f63f..5596316 100644 --- a/test/fixtures/if_then_else.html.expected.erb +++ b/test/fixtures/if_then_else.html.expected.erb @@ -1,17 +1,21 @@ <% eeee ? "b" : c %> <% eeee ? a : c %> -<% if longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong - a -else - c -end %> +<% + if longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong + a + else + c + end +%> -<% if longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong - "a" -else - "c" -end %> +<% + if longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong + "a" + else + "c" + end +%>
>
>
diff --git a/test/fixtures/long_deep_nested-2.html.expected.erb b/test/fixtures/long_deep_nested-2.html.expected.erb index 216894f..e548c13 100644 --- a/test/fixtures/long_deep_nested-2.html.expected.erb +++ b/test/fixtures/long_deep_nested-2.html.expected.erb @@ -16,19 +16,21 @@
- <%= react_component( - "HelloWorld", - { - greeting: - "Hello from react-rails react-rails react-rails react-rails react-rails react-rails react-rails.", - }, - { - greeting: - "Hello from react-rails react-rails react-rails react-rails react-rails react-rails react-rails.", - }, - { greeting: "Hello from react-rails." }, - { greeting: "Hello from react-rails." }, - ) %> + <%= + react_component( + "HelloWorld", + { + greeting: + "Hello from react-rails react-rails react-rails react-rails react-rails react-rails react-rails.", + }, + { + greeting: + "Hello from react-rails react-rails react-rails react-rails react-rails react-rails react-rails.", + }, + { greeting: "Hello from react-rails." }, + { greeting: "Hello from react-rails." }, + ) + %>
diff --git a/test/fixtures/long_deep_nested.html.expected.erb b/test/fixtures/long_deep_nested.html.expected.erb index 17d219d..8d66b2f 100644 --- a/test/fixtures/long_deep_nested.html.expected.erb +++ b/test/fixtures/long_deep_nested.html.expected.erb @@ -16,20 +16,31 @@
- <% link_to "Very long long long long long long long long string here and there", - very_very_very_long_long_long_pathhhhhh_here, - opt: "212", - options: "222sdasdasd", - class: " 322 ", - dis: diss %> + <% + link_to "Very long long long long long long long long string here and there", + very_very_very_long_long_long_pathhhhhh_here, + opt: "212", + options: "222sdasdasd", + class: " 322 ", + dis: diss + %> - <% link_to "string", path, opt: "212", options: "222sdasdasd" %> + <% + link_to "string", + path, + opt: "212", + options: "222sdasdasd" + %>
- <%= react_component({ greeting: "react-rails." }) %> + <%= + react_component( + { greeting: "react-rails." }, + ) + %>
diff --git a/test/fixtures/trailing_comma.html.expected.erb b/test/fixtures/trailing_comma.html.expected.erb index 206c678..96eb5ac 100644 --- a/test/fixtures/trailing_comma.html.expected.erb +++ b/test/fixtures/trailing_comma.html.expected.erb @@ -1,9 +1,11 @@ -<%= render component("ui/table").new( - id: "option-types-list", - data: { - rows: @option_types, - url: ->(option_type) { edit_admin_option_type_path(option_type) }, - columns: columns, - batch_actions: batch_actions, - }, -) %> +<%= + render component("ui/table").new( + id: "option-types-list", + data: { + rows: @option_types, + url: ->(option_type) { edit_admin_option_type_path(option_type) }, + columns: columns, + batch_actions: batch_actions, + }, + ) +%> diff --git a/test/fixtures/utf8.html.expected.erb b/test/fixtures/utf8.html.expected.erb index 922ec10..a44539b 100644 --- a/test/fixtures/utf8.html.expected.erb +++ b/test/fixtures/utf8.html.expected.erb @@ -27,17 +27,19 @@

Dein PEND Team

<%= form_with model: model, url: path, method: :patch do |f| %> - <%= f.select :type, - [ - ["не выбрано", nil], - %w[Тип1 type1], - %w[Тип2 type2], - %w[Тип3 type3], - %w[Тип4 type4], - %w[Тип5 type5], - ], - {}, - { class: "select" } %> + <%= + f.select :type, + [ + ["не выбрано", nil], + %w[Тип1 type1], + %w[Тип2 type2], + %w[Тип3 type3], + %w[Тип4 type4], + %w[Тип5 type5], + ], + {}, + { class: "select" } + %> <%= f.submit "Сохранить" %> The dash: more—effortlessly The copyright: © The quote: Figma and Tailwind ” are used