diff --git a/src/cssom/om.cpp b/src/cssom/om.cpp index dddf824..a9139cc 100644 --- a/src/cssom/om.cpp +++ b/src/cssom/om.cpp @@ -283,6 +283,65 @@ std::string to_string( } } // namespace +namespace { +// Produces deterministic list of CSS rules. +// The list is sorted by property groups and within a group is sorted by rule "name". +std::vector>> to_string_styles( + utki::span styles, // + const std::function& property_id_to_name, + const std::function& property_value_to_string +) +{ + std::map> props_map; + + std::vector>> str_styles; + + for (const auto& s : styles) { + auto props_ptr = s.properties.get(); + ASSERT(props_ptr) + auto props_iter = props_map.find(props_ptr); + if (props_iter == props_map.end()) { + auto res = props_map.insert( + std::make_pair( + props_ptr, + std::make_shared(to_string( + *s.properties, // + property_id_to_name, + property_value_to_string + )) + ) + ); + ASSERT(res.second) + props_iter = res.first; + } + ASSERT(props_iter != props_map.end()) + + str_styles.emplace_back( + get_name(s), // + props_iter->second + ); + } + + std::sort( + str_styles.begin(), // + str_styles.end(), + [](const auto& a, const auto& b) { + // sort by property_list to make consequent gropus using same property list by name within group + if (*a.second < *b.second) { + return true; + } else if (*a.second > *b.second) { + return false; + } + + // sort by name within the group + return a.first < b.first; + } + ); + + return str_styles; +} +} // namespace + void sheet::write( papki::file& fi, const std::function& property_id_to_name, @@ -290,24 +349,11 @@ void sheet::write( std::string_view indent ) const { - auto styles_to_save = this->styles; // copy - - // TODO: make this sorting deterministic - // std::sort( - // styles_to_save.begin(), // - // styles_to_save.end(), - // [](const auto& a, const auto& b) -> bool { - // // sort by property_list to make consequent gropus using same property list by name within group - // if (a.properties.get() < b.properties.get()) { - // return true; - // } else if (a.properties.get() > b.properties.get()) { - // return false; - // } - - // // sort by name within the group - // return a.get_name() < b.get_name(); - // } - // ); + auto styles_to_save = to_string_styles( + this->styles, // + property_id_to_name, + property_value_to_string + ); papki::file::guard file_guard(fi, papki::file::mode::create); @@ -317,7 +363,7 @@ void sheet::write( // such selector chains will go in a row. auto selector_group_start_iter = i; for (auto j = selector_group_start_iter; j != styles_to_save.end(); ++j) { - if (j->properties.get() != selector_group_start_iter->properties.get()) { + if (j->second.get() != selector_group_start_iter->second.get()) { ASSERT(j > i) i = --j; break; @@ -331,7 +377,7 @@ void sheet::write( fi.write(utki::make_span(indent)); } - fi.write(get_name(*j)); + fi.write(j->first); } // write properties @@ -339,15 +385,9 @@ void sheet::write( fi.write(utki::make_span(indent)); fi.write(tab_char); - ASSERT(selector_group_start_iter->properties) - - auto props_str = to_string( - *selector_group_start_iter->properties, // - property_id_to_name, - property_value_to_string - ); + ASSERT(selector_group_start_iter->second) - fi.write(props_str); + fi.write(*selector_group_start_iter->second); fi.write(new_line_char); fi.write(utki::make_span(indent)); diff --git a/tests/unit/samples_data/sample3.css.cmp b/tests/unit/samples_data/sample3.css.cmp index e341bee..0cca88b 100644 --- a/tests/unit/samples_data/sample3.css.cmp +++ b/tests/unit/samples_data/sample3.css.cmp @@ -1,12 +1,12 @@ -.cls-1, .cls-3 { - fill: none; +.cls-4 { + fill-rule: evenodd; } .cls-2, .cls-4 { fill: #47506a; } +.cls-1, .cls-3 { + fill: none; +} .cls-3 { stroke: #3866f0; stroke-width: 4px; } -.cls-4 { - fill-rule: evenodd; -} diff --git a/tests/unit/samples_data/sample4.css.cmp b/tests/unit/samples_data/sample4.css.cmp index 813826b..d84c7c2 100644 --- a/tests/unit/samples_data/sample4.css.cmp +++ b/tests/unit/samples_data/sample4.css.cmp @@ -1,6 +1,6 @@ -tag-1, tag-2 { - fill: #445566; -} tag-3, tag-4 { fill: #334455; } +tag-1, tag-2 { + fill: #445566; +} diff --git a/tests/unit/samples_data/sample5.css.cmp b/tests/unit/samples_data/sample5.css.cmp index 68e505e..830bd93 100644 --- a/tests/unit/samples_data/sample5.css.cmp +++ b/tests/unit/samples_data/sample5.css.cmp @@ -1,6 +1,6 @@ -#id-1, #id-2 { - fill: #445566; -} #id-3, #id-4 { fill: #334455; } +#id-1, #id-2 { + fill: #445566; +} diff --git a/tests/unit/samples_data/sample6.css.cmp b/tests/unit/samples_data/sample6.css.cmp index 4541327..cb84c5f 100644 --- a/tests/unit/samples_data/sample6.css.cmp +++ b/tests/unit/samples_data/sample6.css.cmp @@ -1,6 +1,6 @@ -.cls-1, .cls-2 { - fill: #445566; -} .cls-3, .cls-4 { fill: #334455; } +.cls-1, .cls-2 { + fill: #445566; +} diff --git a/tests/unit/samples_data/sample7.css.cmp b/tests/unit/samples_data/sample7.css.cmp index aedc856..9eed867 100644 --- a/tests/unit/samples_data/sample7.css.cmp +++ b/tests/unit/samples_data/sample7.css.cmp @@ -1,9 +1,6 @@ -#id-1 { +#id-1, .cls-3.cls-4, tag-1 { fill: #334455; } .cls-1.cls-2 { fill: #445566; } -.cls-3.cls-4, tag-1 { - fill: #334455; -} diff --git a/tests/unit/samples_data/sample8.css.cmp b/tests/unit/samples_data/sample8.css.cmp index 36422ea..84c6bfd 100644 --- a/tests/unit/samples_data/sample8.css.cmp +++ b/tests/unit/samples_data/sample8.css.cmp @@ -1,72 +1,72 @@ -.fence { - fill: none; stroke: #c4e3c3; stroke-width: 1; +.task { + fill: #000; } -.shadow { - filter: drop-shadow(0 0 2px #000); +.trees { + fill: #144043; } -.stairs { - fill: #FFD700; +.building { + fill: #1a2632; } -.task { - fill: #000; +.land { + fill: #1f5054; } -.danger { - fill: red; stroke: red; stroke-width: 2; stroke-dasharray: 4,2; fill-opacity: .4; +.locked { + fill: #37414c; } -.powerline { - fill: none; stroke: #ffce00; stroke-width: 2; stroke-dasharray: 6,6; stroke-miterlimit: 10; +.water { + fill: #4a6b96; } -.railroad { - fill: none; stroke: #914833; stroke-width: 3; stroke-dasharray: 6; +.wood { + fill: #593700; } -.road_large { - stroke-width: 12; +.floor { + fill: #70777f; } -.road_medium { - stroke-width: 8; +.tarmac { + fill: #768089; } -.road_small { - stroke-width: 5; +.gravel { + fill: #946d3e; } -.road_gravel { - fill: none; stroke: #946d3e; +.stairs { + fill: #FFD700; } -.road_tarmac { - fill: none; stroke: #888; +.cement { + fill: #c6c2c2; } -.trees { - fill: #144043; +.rock { + fill: #dcd5b6; } .map_border { fill: none; stroke: #000; stroke-width: 2; } -.locked { - fill: #37414c; +.road_tarmac { + fill: none; stroke: #888; } -.floor { - fill: #70777f; +.railroad { + fill: none; stroke: #914833; stroke-width: 3; stroke-dasharray: 6; } -.building { - fill: #1a2632; +.road_gravel { + fill: none; stroke: #946d3e; } -.gravel { - fill: #946d3e; +.fence { + fill: none; stroke: #c4e3c3; stroke-width: 1; } -.tarmac { - fill: #768089; +.powerline { + fill: none; stroke: #ffce00; stroke-width: 2; stroke-dasharray: 6,6; stroke-miterlimit: 10; } -.wood { - fill: #593700; +.danger { + fill: red; stroke: red; stroke-width: 2; stroke-dasharray: 4,2; fill-opacity: .4; } -.water { - fill: #4a6b96; +.shadow { + filter: drop-shadow(0 0 2px #000); } -.rock { - fill: #dcd5b6; +.road_large { + stroke-width: 12; } -.land { - fill: #1f5054; +.road_small { + stroke-width: 5; } -.cement { - fill: #c6c2c2; +.road_medium { + stroke-width: 8; } diff --git a/tests/unit/samples_data/simple0.css.cmp b/tests/unit/samples_data/simple0.css.cmp index 0ea056a..c1ef706 100644 --- a/tests/unit/samples_data/simple0.css.cmp +++ b/tests/unit/samples_data/simple0.css.cmp @@ -1,6 +1,6 @@ -body h1 { +body { background-color: green; } -body { +body h1 { background-color: green; } diff --git a/tests/unit/samples_data/simple1.css.cmp b/tests/unit/samples_data/simple1.css.cmp index 3f7bb41..02a95ef 100644 --- a/tests/unit/samples_data/simple1.css.cmp +++ b/tests/unit/samples_data/simple1.css.cmp @@ -1,12 +1,12 @@ -body.foo h1.bar { +.foo { background-color: green; } -body.foo.bar { +.foo h1.bar { background-color: green; } -.foo h1.bar { +body.foo h1.bar { background-color: green; } -.foo { +body.foo.bar { background-color: green; }