diff --git a/CHANGELOG.md b/CHANGELOG.md index 3548fa6e..27b1813f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ### Changed - The `nesting` field on the results of the indexing operation is no longer a single flat array. Instead it is an array of arrays, where each array is a single nesting level. This more accurately reflects the nesting of the nodes in the tree. For example, `class Foo::Bar::Baz; end` would result in `[Foo, Bar, Baz]`, but that incorrectly implies that you can see constants at each of those levels. Now this would result in `[[Foo, Bar, Baz]]` to indicate that it can see either the top level or constants within the scope of `Foo::Bar::Baz` only. +- When formatting hashes that have omitted values and mixed hash rockets with labels, the formatting now maintains whichever delimiter was used in the source. This is because forcing the use of hash rockets with omitted values results in a syntax error. ## [6.0.0] - 2023-02-10 diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index 567ec0c8..dd4eb3ab 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -1780,13 +1780,25 @@ def format_key(q, key) end def self.for(container) - labels = - container.assocs.all? do |assoc| - next true if assoc.is_a?(AssocSplat) - + container.assocs.each do |assoc| + if assoc.is_a?(AssocSplat) + # Splat nodes do not impact the formatting choice. + elsif assoc.value.nil? + # If the value is nil, then it has been omitted. In this case we have + # to match the existing formatting because standardizing would + # potentially break the code. For example: + # + # { first:, "second" => "value" } + # + return Identity.new + else + # Otherwise, we need to check the type of the key. If it's a label or + # dynamic symbol, we can use labels. If it's a symbol literal then it + # needs to match a certain pattern to be used as a label. If it's + # anything else, then we need to use hash rockets. case assoc.key - when Label - true + when Label, DynaSymbol + # Here labels can be used. when SymbolLiteral # When attempting to convert a hash rocket into a hash label, # you need to take care because only certain patterns are @@ -1794,15 +1806,18 @@ def self.for(container) # arguments to methods, but don't specify what that is. After # some experimentation, it looks like it's: value = assoc.key.value.value - value.match?(/^[_A-Za-z]/) && !value.end_with?("=") - when DynaSymbol - true + + if !value.match?(/^[_A-Za-z]/) || value.end_with?("=") + return Rockets.new + end else - false + # If the value is anything else, we have to use hash rockets. + return Rockets.new end end + end - (labels ? Labels : Rockets).new + Labels.new end end diff --git a/test/fixtures/hash.rb b/test/fixtures/hash.rb index 9c43a4fe..70e89f69 100644 --- a/test/fixtures/hash.rb +++ b/test/fixtures/hash.rb @@ -29,3 +29,5 @@ { # comment } +% # >= 3.1.0 +{ foo:, "bar" => "baz" }