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
15namespace 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 // ##########################################################################
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

source code of gtk/subprojects/libsass/src/extender.hpp