1 #ifndef SASS_EXTENDER_H 2 #define SASS_EXTENDER_H 3 4 #include <set> 5 #include <map> 6 #include <string> 7 8 #include "ast_helpers.hpp" 9 #include "ast_fwd_decl.hpp" 10 #include "operation.hpp" 11 #include "extension.hpp" 12 #include "backtrace.hpp" 13 #include "ordered_map.hpp" 14 15 namespace Sass { 16 17 // ########################################################################## 18 // Different hash map types used by extender 19 // ########################################################################## 20 21 // This is special (ptrs!) 22 typedef std::unordered_set< 23 ComplexSelectorObj, 24 ObjPtrHash, 25 ObjPtrEquality 26 > ExtCplxSelSet; 27 28 typedef std::unordered_set< 29 SimpleSelectorObj, 30 ObjHash, 31 ObjEquality 32 > ExtSmplSelSet; 33 34 typedef std::unordered_set< 35 SelectorListObj, 36 ObjPtrHash, 37 ObjPtrEquality 38 > ExtListSelSet; 39 40 typedef std::unordered_map< 41 SimpleSelectorObj, 42 ExtListSelSet, 43 ObjHash, 44 ObjEquality 45 > ExtSelMap; 46 47 typedef ordered_map< 48 ComplexSelectorObj, 49 Extension, 50 ObjHash, 51 ObjEquality 52 > ExtSelExtMapEntry; 53 54 typedef std::unordered_map< 55 SimpleSelectorObj, 56 ExtSelExtMapEntry, 57 ObjHash, 58 ObjEquality 59 > ExtSelExtMap; 60 61 typedef std::unordered_map < 62 SimpleSelectorObj, 63 sass::vector< 64 Extension 65 >, 66 ObjHash, 67 ObjEquality 68 > ExtByExtMap; 69 70 class Extender : public Operation_CRTP<void, Extender> { 71 72 public: 73 74 enum ExtendMode { TARGETS, REPLACE, NORMAL, }; 75 76 private: 77 78 // ########################################################################## 79 // The mode that controls this extender's behavior. 80 // ########################################################################## 81 ExtendMode mode; 82 83 // ########################################################################## 84 // Shared backtraces with context and expander. Needed the throw 85 // errors when e.g. extending across media query boundaries. 86 // ########################################################################## 87 Backtraces& traces; 88 89 // ########################################################################## 90 // A map from all simple selectors in the stylesheet to the rules that 91 // contain them.This is used to find which rules an `@extend` applies to. 92 // ########################################################################## 93 ExtSelMap selectors; 94 95 // ########################################################################## 96 // A map from all extended simple selectors 97 // to the sources of those extensions. 98 // ########################################################################## 99 ExtSelExtMap extensions; 100 101 // ########################################################################## 102 // A map from all simple selectors in extenders to 103 // the extensions that those extenders define. 104 // ########################################################################## 105 ExtByExtMap extensionsByExtender; 106 107 // ########################################################################## 108 // A map from CSS rules to the media query contexts they're defined in. 109 // This tracks the contexts in which each style rule is defined. 110 // If a rule is defined at the top level, it doesn't have an entry. 111 // ########################################################################## 112 ordered_map< 113 SelectorListObj, 114 CssMediaRuleObj, 115 ObjPtrHash, 116 ObjPtrEquality 117 > mediaContexts; 118 119 // ########################################################################## 120 // A map from [SimpleSelector]s to the specificity of their source selectors. 121 // This tracks the maximum specificity of the [ComplexSelector] that originally 122 // contained each [SimpleSelector]. This allows us to ensure we don't trim any 123 // selectors that need to exist to satisfy the [second law that of extend][]. 124 // [second law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184 125 // ########################################################################## 126 std::unordered_map< 127 SimpleSelectorObj, 128 size_t, 129 ObjPtrHash, 130 ObjPtrEquality 131 > sourceSpecificity; 132 133 // ########################################################################## 134 // A set of [ComplexSelector]s that were originally part of their 135 // component [SelectorList]s, as opposed to being added by `@extend`. 136 // This allows us to ensure that we don't trim any selectors 137 // that need to exist to satisfy the [first law of extend][]. 138 // ########################################################################## 139 ExtCplxSelSet originals; 140 141 public: 142 143 // Constructor without default [mode]. 144 // [traces] are needed to throw errors. 145 Extender(Backtraces& traces); 146 147 // ########################################################################## 148 // Constructor with specific [mode]. 149 // [traces] are needed to throw errors. 150 // ########################################################################## 151 Extender(ExtendMode mode, Backtraces& traces); 152 153 // ########################################################################## 154 // Empty desctructor 155 // ########################################################################## ~Extender()156 ~Extender() {}; 157 158 // ########################################################################## 159 // Extends [selector] with [source] extender and [targets] extendees. 160 // This works as though `source {@extend target}` were written in the 161 // stylesheet, with the exception that [target] can contain compound 162 // selectors which must be extended as a unit. 163 // ########################################################################## 164 static SelectorListObj extend( 165 SelectorListObj& selector, 166 const SelectorListObj& source, 167 const SelectorListObj& target, 168 Backtraces& traces); 169 170 // ########################################################################## 171 // Returns a copy of [selector] with [targets] replaced by [source]. 172 // ########################################################################## 173 static SelectorListObj replace( 174 SelectorListObj& selector, 175 const SelectorListObj& source, 176 const SelectorListObj& target, 177 Backtraces& traces); 178 179 // ########################################################################## 180 // Adds [selector] to this extender, with [selectorSpan] as the span covering 181 // the selector and [ruleSpan] as the span covering the entire style rule. 182 // Extends [selector] using any registered extensions, then returns an empty 183 // [ModifiableCssStyleRule] with the resulting selector. If any more relevant 184 // extensions are added, the returned rule is automatically updated. 185 // The [mediaContext] is the media query context in which the selector was 186 // defined, or `null` if it was defined at the top level of the document. 187 // ########################################################################## 188 void addSelector( 189 const SelectorListObj& selector, 190 const CssMediaRuleObj& mediaContext); 191 192 // ########################################################################## 193 // Registers the [SimpleSelector]s in [list] 194 // to point to [rule] in [selectors]. 195 // ########################################################################## 196 void registerSelector( 197 const SelectorListObj& list, 198 const SelectorListObj& rule); 199 200 // ########################################################################## 201 // Adds an extension to this extender. The [extender] is the selector for the 202 // style rule in which the extension is defined, and [target] is the selector 203 // passed to `@extend`. The [extend] provides the extend span and indicates 204 // whether the extension is optional. The [mediaContext] defines the media query 205 // context in which the extension is defined. It can only extend selectors 206 // within the same context. A `null` context indicates no media queries. 207 // ########################################################################## 208 void addExtension( 209 const SelectorListObj& extender, 210 const SimpleSelectorObj& target, 211 const CssMediaRuleObj& mediaQueryContext, 212 bool is_optional = false); 213 214 // ########################################################################## 215 // The set of all simple selectors in style rules handled 216 // by this extender. This includes simple selectors that 217 // were added because of downstream extensions. 218 // ########################################################################## 219 ExtSmplSelSet getSimpleSelectors() const; 220 221 // ########################################################################## 222 // Check for extends that have not been satisfied. 223 // Returns true if any non-optional extension did not 224 // extend any selector. Updates the passed reference 225 // to point to that Extension for further analysis. 226 // ########################################################################## 227 bool checkForUnsatisfiedExtends( 228 Extension& unsatisfied) const; 229 230 private: 231 232 // ########################################################################## 233 // A helper function for [extend] and [replace]. 234 // ########################################################################## 235 static SelectorListObj extendOrReplace( 236 SelectorListObj& selector, 237 const SelectorListObj& source, 238 const SelectorListObj& target, 239 const ExtendMode mode, 240 Backtraces& traces); 241 242 // ########################################################################## 243 // Returns an extension that combines [left] and [right]. Throws 244 // a [SassException] if [left] and [right] have incompatible 245 // media contexts. Throws an [ArgumentError] if [left] 246 // and [right] don't have the same extender and target. 247 // ########################################################################## 248 static Extension mergeExtension( 249 const Extension& lhs, 250 const Extension& rhs); 251 252 // ########################################################################## 253 // Extend [extensions] using [newExtensions]. 254 // ########################################################################## 255 // Note: dart-sass throws an error in here 256 // ########################################################################## 257 void extendExistingStyleRules( 258 const ExtListSelSet& rules, 259 const ExtSelExtMap& newExtensions); 260 261 // ########################################################################## 262 // Extend [extensions] using [newExtensions]. Note that this does duplicate 263 // some work done by [_extendExistingStyleRules], but it's necessary to 264 // expand each extension's extender separately without reference to the full 265 // selector list, so that relevant results don't get trimmed too early. 266 // Returns `null` (Note: empty map) if there are no extensions to add. 267 // ########################################################################## 268 ExtSelExtMap extendExistingExtensions( 269 // Taking in a reference here makes MSVC debug stuck!? 270 const sass::vector<Extension>& extensions, 271 const ExtSelExtMap& newExtensions); 272 273 // ########################################################################## 274 // Extends [list] using [extensions]. 275 // ########################################################################## 276 SelectorListObj extendList( 277 const SelectorListObj& list, 278 const ExtSelExtMap& extensions, 279 const CssMediaRuleObj& mediaContext); 280 281 // ########################################################################## 282 // Extends [complex] using [extensions], and 283 // returns the contents of a [SelectorList]. 284 // ########################################################################## 285 sass::vector<ComplexSelectorObj> extendComplex( 286 // Taking in a reference here makes MSVC debug stuck!? 287 const ComplexSelectorObj& list, 288 const ExtSelExtMap& extensions, 289 const CssMediaRuleObj& mediaQueryContext); 290 291 // ########################################################################## 292 // Returns a one-off [Extension] whose 293 // extender is composed solely of [simple]. 294 // ########################################################################## 295 Extension extensionForSimple( 296 const SimpleSelectorObj& simple) const; 297 298 // ########################################################################## 299 // Returns a one-off [Extension] whose extender is composed 300 // solely of a compound selector containing [simples]. 301 // ########################################################################## 302 Extension extensionForCompound( 303 // Taking in a reference here makes MSVC debug stuck!? 304 const sass::vector<SimpleSelectorObj>& simples) const; 305 306 // ########################################################################## 307 // Extends [compound] using [extensions], and returns the 308 // contents of a [SelectorList]. The [inOriginal] parameter 309 // indicates whether this is in an original complex selector, 310 // meaning that [compound] should not be trimmed out. 311 // ########################################################################## 312 sass::vector<ComplexSelectorObj> extendCompound( 313 const CompoundSelectorObj& compound, 314 const ExtSelExtMap& extensions, 315 const CssMediaRuleObj& mediaQueryContext, 316 bool inOriginal = false); 317 318 // ########################################################################## 319 // Extends [simple] without extending the 320 // contents of any selector pseudos it contains. 321 // ########################################################################## 322 sass::vector<Extension> extendWithoutPseudo( 323 const SimpleSelectorObj& simple, 324 const ExtSelExtMap& extensions, 325 ExtSmplSelSet* targetsUsed) const; 326 327 // ########################################################################## 328 // Extends [simple] and also extending the 329 // contents of any selector pseudos it contains. 330 // ########################################################################## 331 sass::vector<sass::vector<Extension>> extendSimple( 332 const SimpleSelectorObj& simple, 333 const ExtSelExtMap& extensions, 334 const CssMediaRuleObj& mediaQueryContext, 335 ExtSmplSelSet* targetsUsed); 336 337 // ########################################################################## 338 // Inner loop helper for [extendPseudo] function 339 // ########################################################################## 340 static sass::vector<ComplexSelectorObj> extendPseudoComplex( 341 const ComplexSelectorObj& complex, 342 const PseudoSelectorObj& pseudo, 343 const CssMediaRuleObj& mediaQueryContext); 344 345 // ########################################################################## 346 // Extends [pseudo] using [extensions], and returns 347 // a list of resulting pseudo selectors. 348 // ########################################################################## 349 sass::vector<PseudoSelectorObj> extendPseudo( 350 const PseudoSelectorObj& pseudo, 351 const ExtSelExtMap& extensions, 352 const CssMediaRuleObj& mediaQueryContext); 353 354 // ########################################################################## 355 // Rotates the element in list from [start] (inclusive) to [end] (exclusive) 356 // one index higher, looping the final element back to [start]. 357 // ########################################################################## 358 static void rotateSlice( 359 sass::vector<ComplexSelectorObj>& list, 360 size_t start, size_t end); 361 362 // ########################################################################## 363 // Removes elements from [selectors] if they're subselectors of other 364 // elements. The [isOriginal] callback indicates which selectors are 365 // original to the document, and thus should never be trimmed. 366 // ########################################################################## 367 sass::vector<ComplexSelectorObj> trim( 368 const sass::vector<ComplexSelectorObj>& selectors, 369 const ExtCplxSelSet& set) const; 370 371 // ########################################################################## 372 // Returns the maximum specificity of the given [simple] source selector. 373 // ########################################################################## 374 size_t maxSourceSpecificity(const SimpleSelectorObj& simple) const; 375 376 // ########################################################################## 377 // Returns the maximum specificity for sources that went into producing [compound]. 378 // ########################################################################## 379 size_t maxSourceSpecificity(const CompoundSelectorObj& compound) const; 380 381 // ########################################################################## 382 // Helper function used as callbacks on lists 383 // ########################################################################## 384 static bool dontTrimComplex( 385 const ComplexSelector* complex2, 386 const ComplexSelector* complex1, 387 const size_t maxSpecificity); 388 389 // ########################################################################## 390 // Helper function used as callbacks on lists 391 // ########################################################################## 392 static bool hasExactlyOne(const ComplexSelectorObj& vec); 393 static bool hasMoreThanOne(const ComplexSelectorObj& vec); 394 395 }; 396 397 } 398 399 #endif 400