Skip to content
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

Preserve rules priority while expanding shorthands #119

Merged
merged 1 commit into from
Jan 21, 2021

Conversation

ojab
Copy link
Contributor

@ojab ojab commented Jan 19, 2021

We can piggyback on Hash preserving it's order in all supported rubies & drop order. Looks quite ugly, but does the trick.
I assume that it will be faster if we'll properly track and propagate order in all places, but I suspect it will be also harder to work with.

And we're little faster at parsing because of order is removed:
Before:

Executing: bundle exec rake benchmark
Warming up --------------------------------------
 import1.css loading   376.000  i/100ms
 complex.css loading    14.000  i/100ms
Calculating -------------------------------------
 import1.css loading      3.670k (± 3.6%) i/s -     18.424k in   5.027200s
 complex.css loading    142.421  (± 4.2%) i/s -    714.000  in   5.021913s

Loading `import1.css` allocated 372 objects, 26 KiB
Loading `complex.css` allocated 10437 objects, 752 KiB

After:

Executing: bundle exec rake benchmark
Warming up --------------------------------------
 import1.css loading   394.000  i/100ms
 complex.css loading    14.000  i/100ms
Calculating -------------------------------------
 import1.css loading      3.809k (± 3.7%) i/s -     19.306k in   5.075438s
 complex.css loading    147.464  (± 3.4%) i/s -    742.000  in   5.037139s

Loading `import1.css` allocated 357 objects, 25 KiB
Loading `complex.css` allocated 9536 objects, 680 KiB

And little slower at expanding shorthands (tested on github's 500KB css with parser.each_rule_set { |rule_set, _media_type| rule_set.expand_shorthand! }, not sure how relevant this file is though)
Before:

Executing: bundle exec ruby b.rb
Warming up --------------------------------------
github-bf18d76d7dfd1ad6ed77f31817d6c749.css loading
                         1.000  i/100ms
Calculating -------------------------------------
github-bf18d76d7dfd1ad6ed77f31817d6c749.css loading
                          2.798  (± 0.0%) i/s -     84.000  in  30.089636s
Loading `github.css` and expanding shothands allocated 439154 objects, 29732 KiB

After:

Executing: bundle exec ruby b.rb
Warming up --------------------------------------
github-bf18d76d7dfd1ad6ed77f31817d6c749.css loading
                         1.000  i/100ms
Calculating -------------------------------------
github-bf18d76d7dfd1ad6ed77f31817d6c749.css loading
                          2.789  (± 0.0%) i/s -     84.000  in  30.161549s
Loading `github.css` and expanding shothands allocated 437074 objects, 30186 KiB
Successfully rebased and updated refs/heads/fixup_expanding_shorthands.

Fixes #111

@ojab ojab marked this pull request as draft January 19, 2021 17:40
@@ -113,8 +113,44 @@ def delete(property)
end
alias remove_declaration! delete

def replace_declaration!(property, replacements)
Copy link
Contributor

@grosser grosser Jan 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting this to be something like:

declarations.fetch(property) # blow up when unknown
declarations.each_with_object({}) do |(k, v), h|
  next if replacements.key?(k)
  if k == property
    h.merge! replacements
  else
    h[k] = v
  end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should obey !important & declarations order, because we shouldn't override subsequent declarations and declarations before with !important if we're not, so I don't see easier way to do that. Will add .key? check for the replaced property though.

Copy link
Contributor

@grosser grosser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea!

@ojab ojab force-pushed the fixup_expanding_shorthands branch 2 times, most recently from bb456c4 to d06c5d3 Compare January 19, 2021 17:55
@ojab ojab marked this pull request as ready for review January 19, 2021 18:02
@ojab
Copy link
Contributor Author

ojab commented Jan 19, 2021

So yeah, it's more or less RFC at this point and I will add Declarations#replace_declaration! tests, just want to make sure that this approach is acceptable.

@ojab ojab force-pushed the fixup_expanding_shorthands branch 3 times, most recently from e134cc1 to 6cfcca1 Compare January 21, 2021 18:30
end

def value=(value)
value = value.to_s.sub(/\s*;\s*\Z/, '').gsub(CssParser::IMPORTANT_IN_PROPERTY_RX, '').strip
value = value.to_s.sub(/\s*;\s*\Z/, '')
self.important = !value.slice!(CssParser::IMPORTANT_IN_PROPERTY_RX).nil?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat!

@ojab ojab requested a review from grosser January 21, 2021 18:35
replacement_values = declarations.values
property_index = replacement_keys.index(property)

replacements = replacement_declarations.each.with_object({}) do |(key, value), result|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comment on what this section does / why it's needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

next
end

next if declarations[key].important && !value.important
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the the if above, maybe assign to a variable ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or do some if + if/else flow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's opposite of the one above, made order the same to make it more clear.

Comment on lines 172 to 180
def each(&block)
declarations.sort_by { |_name, value| value.order }.each(&block)
declarations.each(&block)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can forward this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@grosser grosser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great!
... lmk when you thing this is ready to merge

@ojab ojab force-pushed the fixup_expanding_shorthands branch 3 times, most recently from 0fc2d37 to c2d54df Compare January 21, 2021 18:45
@grosser
Copy link
Contributor

grosser commented Jan 21, 2021

CI failed

Comment on lines 151 to 169
if !declarations.key?(key) || (value.important && !declarations[key].important)
result[key] = value
next unless declarations.key?(key)

replaced_index = replacement_keys.index(key)
replacement_keys.delete_at(replaced_index)
replacement_values.delete_at(replaced_index)
property_index -= 1 if replaced_index < property_index
next
end
Copy link
Contributor

@grosser grosser Jan 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if !declarations.key?(key) || (value.important && !declarations[key].important)
result[key] = value
next unless declarations.key?(key)
replaced_index = replacement_keys.index(key)
replacement_keys.delete_at(replaced_index)
replacement_values.delete_at(replaced_index)
property_index -= 1 if replaced_index < property_index
next
end
if !declarations.key?(key)
result[key] = value
elsif !declarations[key].important && value.important # explain what this means and does
result[key] = value
replaced_index = replacement_keys.index(key)
replacement_keys.delete_at(replaced_index)
replacement_values.delete_at(replaced_index)
property_index -= 1 if replaced_index < property_index
elsif declarations[key].important && value.important # explain what this means
# discard
elsif property_index < replacement_keys.index(key) # explain what this means
# discard
else
result[key] = value
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added more comments to the existing code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great comments!

@ojab
Copy link
Contributor Author

ojab commented Jan 21, 2021

Yeah, trying to test forwardable, but looks like it's not trivial here minitest/minitest#545

@ojab ojab force-pushed the fixup_expanding_shorthands branch from c2d54df to e5421ee Compare January 21, 2021 19:25
@ojab
Copy link
Contributor Author

ojab commented Jan 21, 2021

Comments are more or less addressed, CI is passing, I think it's ready if everything is fine for you.

@grosser grosser merged commit c6321e6 into premailer:master Jan 21, 2021
@ojab
Copy link
Contributor Author

ojab commented Jan 21, 2021

Thanks 🎉! Can I haz release?

@grosser
Copy link
Contributor

grosser commented Jan 21, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

expand_shorthand! overrides subsequent styles
2 participants