Skip to content

Handle assoc value omission with mixed delimiters #318

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

Merged
merged 1 commit into from
Feb 26, 2023
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 26 additions & 11 deletions lib/syntax_tree/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1780,29 +1780,44 @@ 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
# allowed. Ruby source says that they have to match keyword
# 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

Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@
{
# comment
}
% # >= 3.1.0
{ foo:, "bar" => "baz" }