Skip to content

Commit da1decb

Browse files
committed
Adds a plugin for empty hash formatting
1 parent 527833f commit da1decb

File tree

5 files changed

+104
-1
lines changed

5 files changed

+104
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ To register plugins, define a file somewhere in your load path named `syntax_tre
711711
* `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes.
712712
* `plugin/trailing_comma` - This will put trailing commas into multiline array literals, hash literals, and method calls that can support trailing commas.
713713
* `plugin/disable_auto_ternary` - This will prevent the automatic conversion of `if ... else` to ternary expressions.
714+
* `plugin/compact_empty_hash` - This will prevent the splitting of empty hashes `{}` over multiple lines.
714715

715716
If you're using Syntax Tree as a library, you can require those files directly or manually pass those options to the formatter initializer through the `SyntaxTree::Formatter::Options` class.
716717

lib/syntax_tree/formatter.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ class Options
2424
attr_reader :quote,
2525
:trailing_comma,
2626
:disable_auto_ternary,
27+
:compact_empty_hash,
2728
:target_ruby_version
2829

2930
def initialize(
3031
quote: :default,
3132
trailing_comma: :default,
3233
disable_auto_ternary: :default,
34+
compact_empty_hash: :default,
3335
target_ruby_version: :default
3436
)
3537
@quote =
@@ -65,6 +67,17 @@ def initialize(
6567
disable_auto_ternary
6668
end
6769

70+
@compact_empty_hash =
71+
if compact_empty_hash == :default
72+
# We ship with a compact empty hash plugin that will define this
73+
# constant. That constant is responsible for determining the default
74+
# compact empty hash value. If it's defined, then we default to true.
75+
# Otherwise we default to false.
76+
defined?(COMPACT_EMPTY_HASH)
77+
else
78+
compact_empty_hash
79+
end
80+
6881
@target_ruby_version =
6982
if target_ruby_version == :default
7083
# The default target Ruby version is the current version of Ruby.
@@ -87,10 +100,12 @@ def initialize(
87100
attr_reader :quote,
88101
:trailing_comma,
89102
:disable_auto_ternary,
103+
:compact_empty_hash,
90104
:target_ruby_version
91105

92106
alias trailing_comma? trailing_comma
93107
alias disable_auto_ternary? disable_auto_ternary
108+
alias compact_empty_hash? compact_empty_hash
94109

95110
def initialize(source, *args, options: Options.new)
96111
super(*args)
@@ -102,6 +117,7 @@ def initialize(source, *args, options: Options.new)
102117
@quote = options.quote
103118
@trailing_comma = options.trailing_comma
104119
@disable_auto_ternary = options.disable_auto_ternary
120+
@compact_empty_hash = options.compact_empty_hash
105121
@target_ruby_version = options.target_ruby_version
106122
end
107123

lib/syntax_tree/node.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5766,7 +5766,7 @@ def format_contents(q)
57665766
q.format(lbrace)
57675767

57685768
if assocs.empty?
5769-
q.breakable_empty
5769+
q.breakable_empty unless q.compact_empty_hash?
57705770
else
57715771
q.indent do
57725772
q.breakable_space
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# frozen_string_literal: true
2+
3+
module SyntaxTree
4+
class Formatter
5+
COMPACT_EMPTY_HASH = true
6+
end
7+
end
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "../test_helper"
4+
5+
module SyntaxTree
6+
class CompactEmptyHashTest < Minitest::Test
7+
def test_empty_hash
8+
assert_format("{}\n", "{}")
9+
end
10+
11+
def test_empty_hash_with_spaces
12+
assert_format("{}\n", "{ }")
13+
end
14+
15+
def test_empty_hash_with_newlines
16+
assert_format("{}\n", "{\n}")
17+
end
18+
19+
def test_empty_hash_in_assignment
20+
assert_format("x = {}\n", "x = {}")
21+
end
22+
23+
def test_empty_hash_in_method_call
24+
assert_format("method({})\n", "method({})")
25+
end
26+
27+
def test_empty_hash_in_array
28+
assert_format("[{}]\n", "[{}]")
29+
end
30+
31+
def test_long_assignment_with_empty_hash
32+
source = "this_is_a_very_long_variable_name_that_might_cause_line_breaks_when_assigned_an_empty_hash = {}"
33+
expected = "this_is_a_very_long_variable_name_that_might_cause_line_breaks_when_assigned_an_empty_hash = {}\n"
34+
assert_format(expected, source)
35+
end
36+
37+
def test_empty_hash_values_in_multiline_hash
38+
source = "{ very_long_key_name_that_might_cause_issues: {}, another_very_long_key_name: {}, yet_another_key: {} }"
39+
expected = <<~RUBY
40+
{
41+
very_long_key_name_that_might_cause_issues: {},
42+
another_very_long_key_name: {},
43+
yet_another_key: {}
44+
}
45+
RUBY
46+
assert_format(expected, source)
47+
end
48+
49+
def test_non_empty_hash_still_works
50+
source = "{ key: value }"
51+
expected = "{ key: value }\n"
52+
assert_format(expected, source)
53+
end
54+
55+
def test_without_plugin_allows_multiline_empty_hash
56+
source = "this_is_a_very_long_variable_name_that_might_cause_line_breaks_when_assigned_an_empty_hash = {}"
57+
58+
# Format without the compact_empty_hash option
59+
options = Formatter::Options.new(compact_empty_hash: false)
60+
formatter = Formatter.new(source, [], options: options)
61+
SyntaxTree.parse(source).format(formatter)
62+
formatter.flush
63+
result = formatter.output.join
64+
65+
# Should allow the hash to break across lines
66+
assert(result.include?("= {\n}"), "Expected empty hash to break across lines when plugin is disabled")
67+
end
68+
69+
private
70+
71+
def assert_format(expected, source = expected.chomp)
72+
options = Formatter::Options.new(compact_empty_hash: true)
73+
formatter = Formatter.new(source, [], options: options)
74+
SyntaxTree.parse(source).format(formatter)
75+
formatter.flush
76+
assert_equal(expected, formatter.output.join)
77+
end
78+
end
79+
end

0 commit comments

Comments
 (0)