sassc 2.2.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +13 -0
  4. data/Rakefile +1 -3
  5. data/ext/extconf.rb +13 -5
  6. data/ext/libsass/VERSION +1 -1
  7. data/ext/libsass/include/sass/base.h +2 -1
  8. data/ext/libsass/include/sass/context.h +1 -0
  9. data/ext/libsass/src/ast.cpp +49 -59
  10. data/ext/libsass/src/ast.hpp +263 -102
  11. data/ext/libsass/src/ast_def_macros.hpp +8 -0
  12. data/ext/libsass/src/ast_fwd_decl.cpp +2 -1
  13. data/ext/libsass/src/ast_fwd_decl.hpp +40 -116
  14. data/ext/libsass/src/ast_helpers.hpp +292 -0
  15. data/ext/libsass/src/ast_sel_cmp.cpp +209 -722
  16. data/ext/libsass/src/ast_sel_super.cpp +539 -0
  17. data/ext/libsass/src/ast_sel_unify.cpp +207 -212
  18. data/ext/libsass/src/ast_sel_weave.cpp +616 -0
  19. data/ext/libsass/src/ast_selectors.cpp +559 -1001
  20. data/ext/libsass/src/ast_selectors.hpp +311 -367
  21. data/ext/libsass/src/ast_supports.cpp +1 -17
  22. data/ext/libsass/src/ast_values.cpp +216 -29
  23. data/ext/libsass/src/ast_values.hpp +42 -33
  24. data/ext/libsass/src/bind.cpp +1 -1
  25. data/ext/libsass/src/cencode.c +4 -6
  26. data/ext/libsass/src/check_nesting.cpp +5 -6
  27. data/ext/libsass/src/check_nesting.hpp +4 -0
  28. data/ext/libsass/src/color_maps.cpp +11 -10
  29. data/ext/libsass/src/color_maps.hpp +0 -8
  30. data/ext/libsass/src/constants.cpp +5 -0
  31. data/ext/libsass/src/constants.hpp +6 -0
  32. data/ext/libsass/src/context.cpp +30 -60
  33. data/ext/libsass/src/context.hpp +8 -20
  34. data/ext/libsass/src/cssize.cpp +36 -120
  35. data/ext/libsass/src/cssize.hpp +4 -10
  36. data/ext/libsass/src/dart_helpers.hpp +199 -0
  37. data/ext/libsass/src/debugger.hpp +364 -207
  38. data/ext/libsass/src/emitter.cpp +3 -4
  39. data/ext/libsass/src/emitter.hpp +0 -2
  40. data/ext/libsass/src/environment.hpp +5 -0
  41. data/ext/libsass/src/error_handling.cpp +21 -0
  42. data/ext/libsass/src/error_handling.hpp +25 -3
  43. data/ext/libsass/src/eval.cpp +33 -153
  44. data/ext/libsass/src/eval.hpp +11 -13
  45. data/ext/libsass/src/eval_selectors.cpp +75 -0
  46. data/ext/libsass/src/expand.cpp +214 -167
  47. data/ext/libsass/src/expand.hpp +26 -6
  48. data/ext/libsass/src/extender.cpp +1186 -0
  49. data/ext/libsass/src/extender.hpp +399 -0
  50. data/ext/libsass/src/extension.cpp +43 -0
  51. data/ext/libsass/src/extension.hpp +89 -0
  52. data/ext/libsass/src/file.cpp +15 -14
  53. data/ext/libsass/src/file.hpp +5 -12
  54. data/ext/libsass/src/fn_colors.cpp +12 -10
  55. data/ext/libsass/src/fn_lists.cpp +12 -11
  56. data/ext/libsass/src/fn_miscs.cpp +22 -34
  57. data/ext/libsass/src/fn_numbers.cpp +13 -6
  58. data/ext/libsass/src/fn_selectors.cpp +94 -124
  59. data/ext/libsass/src/fn_strings.cpp +16 -14
  60. data/ext/libsass/src/fn_utils.cpp +5 -6
  61. data/ext/libsass/src/fn_utils.hpp +9 -3
  62. data/ext/libsass/src/inspect.cpp +154 -117
  63. data/ext/libsass/src/inspect.hpp +10 -8
  64. data/ext/libsass/src/lexer.cpp +17 -81
  65. data/ext/libsass/src/lexer.hpp +5 -16
  66. data/ext/libsass/src/listize.cpp +22 -36
  67. data/ext/libsass/src/listize.hpp +8 -9
  68. data/ext/libsass/src/memory/SharedPtr.hpp +39 -5
  69. data/ext/libsass/src/operation.hpp +27 -17
  70. data/ext/libsass/src/operators.cpp +1 -0
  71. data/ext/libsass/src/ordered_map.hpp +112 -0
  72. data/ext/libsass/src/output.cpp +30 -49
  73. data/ext/libsass/src/output.hpp +1 -1
  74. data/ext/libsass/src/parser.cpp +211 -381
  75. data/ext/libsass/src/parser.hpp +17 -15
  76. data/ext/libsass/src/parser_selectors.cpp +189 -0
  77. data/ext/libsass/src/permutate.hpp +140 -0
  78. data/ext/libsass/src/position.hpp +1 -1
  79. data/ext/libsass/src/prelexer.cpp +6 -6
  80. data/ext/libsass/src/remove_placeholders.cpp +55 -56
  81. data/ext/libsass/src/remove_placeholders.hpp +21 -18
  82. data/ext/libsass/src/sass.hpp +1 -0
  83. data/ext/libsass/src/sass2scss.cpp +4 -4
  84. data/ext/libsass/src/sass_context.cpp +42 -91
  85. data/ext/libsass/src/sass_context.hpp +2 -2
  86. data/ext/libsass/src/sass_functions.cpp +1 -1
  87. data/ext/libsass/src/sass_values.cpp +0 -1
  88. data/ext/libsass/src/stylesheet.cpp +22 -0
  89. data/ext/libsass/src/stylesheet.hpp +57 -0
  90. data/ext/libsass/src/to_value.cpp +2 -2
  91. data/ext/libsass/src/to_value.hpp +1 -1
  92. data/ext/libsass/src/units.cpp +5 -3
  93. data/ext/libsass/src/util.cpp +10 -12
  94. data/ext/libsass/src/util.hpp +2 -3
  95. data/ext/libsass/src/util_string.cpp +111 -61
  96. data/ext/libsass/src/util_string.hpp +61 -8
  97. data/lib/sassc/engine.rb +5 -3
  98. data/lib/sassc/functions_handler.rb +8 -8
  99. data/lib/sassc/native.rb +1 -1
  100. data/lib/sassc/script.rb +4 -4
  101. data/lib/sassc/version.rb +1 -1
  102. data/test/functions_test.rb +18 -1
  103. data/test/native_test.rb +1 -1
  104. metadata +17 -12
  105. data/ext/libsass/src/extend.cpp +0 -2132
  106. data/ext/libsass/src/extend.hpp +0 -86
  107. data/ext/libsass/src/node.cpp +0 -322
  108. data/ext/libsass/src/node.hpp +0 -118
  109. data/ext/libsass/src/paths.hpp +0 -71
  110. data/ext/libsass/src/sass_util.cpp +0 -152
  111. data/ext/libsass/src/sass_util.hpp +0 -256
  112. data/ext/libsass/src/subset_map.cpp +0 -58
  113. data/ext/libsass/src/subset_map.hpp +0 -76
@@ -4,16 +4,69 @@
4
4
  #include <string>
5
5
 
6
6
  namespace Sass {
7
- namespace Util {
7
+ namespace Util {
8
8
 
9
- std::string rtrim(const std::string& str);
9
+ // ##########################################################################
10
+ // Special case insensitive string matcher. We can optimize
11
+ // the more general compare case quite a bit by requiring
12
+ // consumers to obey some rules (lowercase and no space).
13
+ // - `literal` must only contain lower case ascii characters
14
+ // there is one edge case where this could give false positives
15
+ // test could contain a (non-ascii) char exactly 32 below literal
16
+ // ##########################################################################
17
+ bool equalsLiteral(const char* lit, const std::string& test);
10
18
 
11
- std::string normalize_newlines(const std::string& str);
12
- std::string normalize_underscores(const std::string& str);
13
- std::string normalize_decimals(const std::string& str);
14
- char opening_bracket_for(char closing_bracket);
15
- char closing_bracket_for(char opening_bracket);
19
+ // ###########################################################################
20
+ // Returns [name] without a vendor prefix.
21
+ // If [name] has no vendor prefix, it's returned as-is.
22
+ // ###########################################################################
23
+ std::string unvendor(const std::string& name);
16
24
 
17
- } // namespace Sass
25
+ std::string rtrim(std::string str);
26
+ std::string normalize_newlines(const std::string& str);
27
+ std::string normalize_underscores(const std::string& str);
28
+ std::string normalize_decimals(const std::string& str);
29
+ char opening_bracket_for(char closing_bracket);
30
+ char closing_bracket_for(char opening_bracket);
31
+
32
+ // Locale-independent ASCII character routines.
33
+
34
+ inline bool ascii_isalpha(unsigned char c) {
35
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
36
+ }
37
+
38
+ inline bool ascii_isdigit(unsigned char c) {
39
+ return (c >= '0' && c <= '9');
40
+ }
41
+
42
+ inline bool ascii_isalnum(unsigned char c) {
43
+ return ascii_isalpha(c) || ascii_isdigit(c);
44
+ }
45
+
46
+ inline bool ascii_isascii(unsigned char c) { return c < 128; }
47
+
48
+ inline bool ascii_isxdigit(unsigned char c) {
49
+ return ascii_isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
50
+ }
51
+
52
+ inline bool ascii_isspace(unsigned char c) {
53
+ return c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n';
54
+ }
55
+
56
+ inline char ascii_tolower(unsigned char c) {
57
+ if (c >= 'A' && c <= 'Z') return c + 32;
58
+ return c;
59
+ }
60
+
61
+ void ascii_str_tolower(std::string* s);
62
+
63
+ inline char ascii_toupper(unsigned char c) {
64
+ if (c >= 'a' && c <= 'z') return c - 32;
65
+ return c;
66
+ }
67
+
68
+ void ascii_str_toupper(std::string* s);
69
+
70
+ } // namespace Sass
18
71
  } // namespace Util
19
72
  #endif // SASS_UTIL_STRING_H
@@ -16,6 +16,7 @@ module SassC
16
16
  def initialize(template, options = {})
17
17
  @template = template
18
18
  @options = options
19
+ @functions = options.fetch(:functions, Script::Functions)
19
20
  end
20
21
 
21
22
  def render
@@ -37,7 +38,7 @@ module SassC
37
38
  Native.option_set_omit_source_map_url(native_options, true) if omit_source_map_url?
38
39
 
39
40
  import_handler.setup(native_options)
40
- functions_handler.setup(native_options)
41
+ functions_handler.setup(native_options, functions: @functions)
41
42
 
42
43
  status = Native.compile_data_context(data_context)
43
44
 
@@ -54,11 +55,12 @@ module SassC
54
55
  @dependencies = Native.context_get_included_files(context)
55
56
  @source_map = Native.context_get_source_map_string(context)
56
57
 
57
- Native.delete_data_context(data_context)
58
-
59
58
  css.force_encoding(@template.encoding)
59
+ @source_map.force_encoding(@template.encoding) if @source_map.is_a?(String)
60
60
 
61
61
  return css unless quiet?
62
+ ensure
63
+ Native.delete_data_context(data_context) if data_context
62
64
  end
63
65
 
64
66
  def dependencies
@@ -6,24 +6,24 @@ module SassC
6
6
  @options = options
7
7
  end
8
8
 
9
- def setup(native_options)
9
+ def setup(native_options, functions: Script::Functions)
10
10
  @callbacks = {}
11
11
  @function_names = {}
12
12
 
13
- list = Native.make_function_list(Script.custom_functions.count)
13
+ list = Native.make_function_list(Script.custom_functions(functions: functions).count)
14
14
 
15
15
  # use an anonymous class wrapper to avoid mutations in a threaded environment
16
- functions = Class.new do
16
+ functions_wrapper = Class.new do
17
17
  attr_accessor :options
18
- include Script::Functions
18
+ include functions
19
19
  end.new
20
- functions.options = @options
20
+ functions_wrapper.options = @options
21
21
 
22
- Script.custom_functions.each_with_index do |custom_function, i|
22
+ Script.custom_functions(functions: functions).each_with_index do |custom_function, i|
23
23
  @callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |native_argument_list, cookie|
24
24
  begin
25
25
  function_arguments = arguments_from_native_list(native_argument_list)
26
- result = functions.send(custom_function, *function_arguments)
26
+ result = functions_wrapper.send(custom_function, *function_arguments)
27
27
  to_native_value(result)
28
28
  rescue StandardError => exception
29
29
  # This rescues any exceptions that occur either in value conversion
@@ -32,7 +32,7 @@ module SassC
32
32
  end
33
33
  end
34
34
 
35
- @function_names[custom_function] = Script.formatted_function_name(custom_function)
35
+ @function_names[custom_function] = Script.formatted_function_name(custom_function, functions: functions)
36
36
 
37
37
  callback = Native.make_function(
38
38
  @function_names[custom_function],
@@ -6,7 +6,7 @@ module SassC
6
6
  module Native
7
7
  extend FFI::Library
8
8
 
9
- dl_ext = (RbConfig::CONFIG['host_os'] =~ /darwin/ ? 'bundle' : 'so')
9
+ dl_ext = RbConfig::MAKEFILE_CONFIG['DLEXT']
10
10
  begin
11
11
  ffi_lib File.expand_path("libsass.#{dl_ext}", __dir__)
12
12
  rescue LoadError # Some non-rvm environments don't copy a shared object over to lib/sassc
@@ -3,12 +3,12 @@
3
3
  module SassC
4
4
  module Script
5
5
 
6
- def self.custom_functions
7
- Functions.public_instance_methods
6
+ def self.custom_functions(functions: Functions)
7
+ functions.public_instance_methods
8
8
  end
9
9
 
10
- def self.formatted_function_name(function_name)
11
- params = Functions.instance_method(function_name).parameters
10
+ def self.formatted_function_name(function_name, functions: Functions)
11
+ params = functions.instance_method(function_name).parameters
12
12
  params = params.map { |param_type, name| "$#{name}#{': null' if param_type == :opt}" }.join(", ")
13
13
  return "#{function_name}(#{params})"
14
14
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SassC
4
- VERSION = "2.2.1"
4
+ VERSION = "2.3.0"
5
5
  end
@@ -140,7 +140,7 @@ module SassC
140
140
 
141
141
  assert_match /Error: error in C function function_that_raises_errors/, exception.message
142
142
  assert_match /Intentional wrong thing happened somewhere inside the custom function/, exception.message
143
- assert_equal "[SassC::FunctionsHandler] Intentional wrong thing happened somewhere inside the custom function", stderr_output
143
+ assert_match /\[SassC::FunctionsHandler\] Intentional wrong thing happened somewhere inside the custom function/, stderr_output
144
144
  end
145
145
 
146
146
  def test_function_that_returns_a_sass_value
@@ -201,6 +201,17 @@ module SassC
201
201
  end
202
202
  end
203
203
 
204
+ def test_pass_custom_functions_as_a_parameter
205
+ out = Engine.new("div { url: test-function(); }", {functions: ExternalFunctions}).render
206
+ assert_match /custom_function/, out
207
+ end
208
+
209
+ def test_pass_incompatible_type_to_custom_functions
210
+ assert_raises(TypeError) do
211
+ Engine.new("div { url: test-function(); }", {functions: Class.new}).render
212
+ end
213
+ end
214
+
204
215
  private
205
216
 
206
217
  def assert_sass(sass, expected_css)
@@ -319,5 +330,11 @@ module SassC
319
330
 
320
331
  end
321
332
 
333
+ module ExternalFunctions
334
+ def test_function
335
+ SassC::Script::Value::String.new("custom_function", :string)
336
+ end
337
+ end
338
+
322
339
  end
323
340
  end
@@ -11,7 +11,7 @@ module SassC
11
11
 
12
12
  class General < MiniTest::Test
13
13
  def test_it_reports_the_libsass_version
14
- assert_equal "3.6.1", Native.version
14
+ assert_equal "3.6.3", Native.version
15
15
  end
16
16
  end
17
17
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sassc
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Boland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2020-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -171,8 +171,11 @@ files:
171
171
  - ext/libsass/src/ast_def_macros.hpp
172
172
  - ext/libsass/src/ast_fwd_decl.cpp
173
173
  - ext/libsass/src/ast_fwd_decl.hpp
174
+ - ext/libsass/src/ast_helpers.hpp
174
175
  - ext/libsass/src/ast_sel_cmp.cpp
176
+ - ext/libsass/src/ast_sel_super.cpp
175
177
  - ext/libsass/src/ast_sel_unify.cpp
178
+ - ext/libsass/src/ast_sel_weave.cpp
176
179
  - ext/libsass/src/ast_selectors.cpp
177
180
  - ext/libsass/src/ast_selectors.hpp
178
181
  - ext/libsass/src/ast_supports.cpp
@@ -201,6 +204,7 @@ files:
201
204
  - ext/libsass/src/context.hpp
202
205
  - ext/libsass/src/cssize.cpp
203
206
  - ext/libsass/src/cssize.hpp
207
+ - ext/libsass/src/dart_helpers.hpp
204
208
  - ext/libsass/src/debug.hpp
205
209
  - ext/libsass/src/debugger.hpp
206
210
  - ext/libsass/src/emitter.cpp
@@ -211,10 +215,13 @@ files:
211
215
  - ext/libsass/src/error_handling.hpp
212
216
  - ext/libsass/src/eval.cpp
213
217
  - ext/libsass/src/eval.hpp
218
+ - ext/libsass/src/eval_selectors.cpp
214
219
  - ext/libsass/src/expand.cpp
215
220
  - ext/libsass/src/expand.hpp
216
- - ext/libsass/src/extend.cpp
217
- - ext/libsass/src/extend.hpp
221
+ - ext/libsass/src/extender.cpp
222
+ - ext/libsass/src/extender.hpp
223
+ - ext/libsass/src/extension.cpp
224
+ - ext/libsass/src/extension.hpp
218
225
  - ext/libsass/src/file.cpp
219
226
  - ext/libsass/src/file.hpp
220
227
  - ext/libsass/src/fn_colors.cpp
@@ -245,16 +252,16 @@ files:
245
252
  - ext/libsass/src/mapping.hpp
246
253
  - ext/libsass/src/memory/SharedPtr.cpp
247
254
  - ext/libsass/src/memory/SharedPtr.hpp
248
- - ext/libsass/src/node.cpp
249
- - ext/libsass/src/node.hpp
250
255
  - ext/libsass/src/operation.hpp
251
256
  - ext/libsass/src/operators.cpp
252
257
  - ext/libsass/src/operators.hpp
258
+ - ext/libsass/src/ordered_map.hpp
253
259
  - ext/libsass/src/output.cpp
254
260
  - ext/libsass/src/output.hpp
255
261
  - ext/libsass/src/parser.cpp
256
262
  - ext/libsass/src/parser.hpp
257
- - ext/libsass/src/paths.hpp
263
+ - ext/libsass/src/parser_selectors.cpp
264
+ - ext/libsass/src/permutate.hpp
258
265
  - ext/libsass/src/plugins.cpp
259
266
  - ext/libsass/src/plugins.hpp
260
267
  - ext/libsass/src/position.cpp
@@ -270,14 +277,12 @@ files:
270
277
  - ext/libsass/src/sass_context.hpp
271
278
  - ext/libsass/src/sass_functions.cpp
272
279
  - ext/libsass/src/sass_functions.hpp
273
- - ext/libsass/src/sass_util.cpp
274
- - ext/libsass/src/sass_util.hpp
275
280
  - ext/libsass/src/sass_values.cpp
276
281
  - ext/libsass/src/sass_values.hpp
277
282
  - ext/libsass/src/source_map.cpp
278
283
  - ext/libsass/src/source_map.hpp
279
- - ext/libsass/src/subset_map.cpp
280
- - ext/libsass/src/subset_map.hpp
284
+ - ext/libsass/src/stylesheet.cpp
285
+ - ext/libsass/src/stylesheet.hpp
281
286
  - ext/libsass/src/to_value.cpp
282
287
  - ext/libsass/src/to_value.hpp
283
288
  - ext/libsass/src/units.cpp
@@ -360,7 +365,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
360
365
  - !ruby/object:Gem::Version
361
366
  version: '0'
362
367
  requirements: []
363
- rubygems_version: 3.0.3
368
+ rubygems_version: 3.1.2
364
369
  signing_key:
365
370
  specification_version: 4
366
371
  summary: Use libsass with Ruby!
@@ -1,2132 +0,0 @@
1
- // sass.hpp must go before all system headers to get the
2
- // __EXTENSIONS__ fix on Solaris.
3
- #include "sass.hpp"
4
-
5
- #include "extend.hpp"
6
- #include "context.hpp"
7
- #include "backtrace.hpp"
8
- #include "paths.hpp"
9
- #include "parser.hpp"
10
- #include "expand.hpp"
11
- #include "node.hpp"
12
- #include "sass_util.hpp"
13
- #include "remove_placeholders.hpp"
14
- #include "debug.hpp"
15
- #include <iostream>
16
- #include <deque>
17
- #include <set>
18
-
19
- /*
20
- NOTES:
21
-
22
- - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which
23
- is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what
24
- ruby already outputs (to make comparisons easier).
25
-
26
- - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing.
27
- Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass.
28
- The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually
29
- used for the porting.
30
-
31
- - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby
32
- output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to
33
- the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see
34
- something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment.
35
-
36
- - Coding conventions in this file (these may need to be changed before merging back into master)
37
- - Very basic hungarian notation:
38
- p prefix for pointers (pSelector)
39
- no prefix for value types and references (selector)
40
- - Use STL iterators where possible
41
- - prefer verbose naming over terse naming
42
- - use typedefs for STL container types for make maintenance easier
43
-
44
- - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators
45
- in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby
46
- sass causes this to be necessary.
47
-
48
-
49
- GLOBAL TODOS:
50
-
51
- - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode.
52
-
53
- - consider making the extend* functions member functions to avoid passing around ctx and subset_map map around. This has the
54
- drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and
55
- can cause additional compile time dependencies.
56
-
57
- - mark the helper methods in this file static to given them compilation unit linkage.
58
-
59
- - implement parent directive matching
60
-
61
- - fix compilation warnings for unused Extend members if we really don't need those references anymore.
62
- */
63
-
64
-
65
- namespace Sass {
66
-
67
-
68
-
69
- #ifdef DEBUG
70
-
71
- // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp
72
- std::ostream& operator<<(std::ostream& os, const Complex_Selector::Combinator combinator) {
73
- switch (combinator) {
74
- case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;
75
- case Complex_Selector::PARENT_OF: os << "\">\""; break;
76
- case Complex_Selector::PRECEDES: os << "\"~\""; break;
77
- case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;
78
- case Complex_Selector::REFERENCE: os << "\"/\""; break;
79
- }
80
-
81
- return os;
82
- }
83
-
84
-
85
- std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {
86
- for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) {
87
- if (i > 0) os << ", ";
88
- os << compoundSelector[i]->to_string();
89
- }
90
- return os;
91
- }
92
-
93
- std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {
94
- os << simpleSelector.to_string();
95
- return os;
96
- }
97
-
98
- // Print a string representation of a Compound_Selector
99
- static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) {
100
-
101
- if (message) {
102
- std::cerr << message;
103
- }
104
-
105
- if (pSimpleSelector) {
106
- std::cerr << "[" << *pSimpleSelector << "]";
107
- } else {
108
- std::cerr << "NULL";
109
- }
110
-
111
- if (newline) {
112
- std::cerr << std::endl;
113
- }
114
- }
115
-
116
- // Print a string representation of a Compound_Selector
117
- static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {
118
-
119
- if (message) {
120
- std::cerr << message;
121
- }
122
-
123
- if (pCompoundSelector) {
124
- std::cerr << "[" << *pCompoundSelector << "]";
125
- } else {
126
- std::cerr << "NULL";
127
- }
128
-
129
- if (newline) {
130
- std::cerr << std::endl;
131
- }
132
- }
133
-
134
-
135
- std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) {
136
-
137
- os << "[";
138
- Complex_Selector* pIter = &complexSelector;
139
- bool first = true;
140
- while (pIter) {
141
- if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {
142
- if (!first) {
143
- os << ", ";
144
- }
145
- first = false;
146
- os << pIter->combinator();
147
- }
148
-
149
- if (!first) {
150
- os << ", ";
151
- }
152
- first = false;
153
-
154
- if (pIter->head()) {
155
- os << pIter->head()->to_string();
156
- } else {
157
- os << "NULL_HEAD";
158
- }
159
-
160
- pIter = pIter->tail();
161
- }
162
- os << "]";
163
-
164
- return os;
165
- }
166
-
167
-
168
- // Print a string representation of a Complex_Selector
169
- static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) {
170
-
171
- if (message) {
172
- std::cerr << message;
173
- }
174
-
175
- if (pComplexSelector) {
176
- std::cerr << *pComplexSelector;
177
- } else {
178
- std::cerr << "NULL";
179
- }
180
-
181
- if (newline) {
182
- std::cerr << std::endl;
183
- }
184
- }
185
-
186
- static void printSelsNewSeqPairCollection(SubSetMapLookups& collection, const char* message=NULL, bool newline=true) {
187
-
188
- if (message) {
189
- std::cerr << message;
190
- }
191
- bool first = true;
192
- std::cerr << "[";
193
- for(SubSetMapLookup& pair : collection) {
194
- if (first) {
195
- first = false;
196
- } else {
197
- std::cerr << ", ";
198
- }
199
- std::cerr << "[";
200
- Compound_Selector* pSels = pair.first;
201
- Complex_Selector* pNewSelector = pair.second;
202
- std::cerr << "[" << *pSels << "], ";
203
- printComplexSelector(pNewSelector, NULL, false);
204
- }
205
- std::cerr << "]";
206
-
207
- if (newline) {
208
- std::cerr << std::endl;
209
- }
210
- }
211
-
212
- // Print a string representation of a ComplexSelectorSet
213
- static void printSourcesSet(ComplexSelectorSet& sources, const char* message=NULL, bool newline=true) {
214
-
215
- if (message) {
216
- std::cerr << message;
217
- }
218
-
219
- // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on
220
- // the differences we see when debug printing.
221
- typedef std::deque<std::string> SourceStrings;
222
- SourceStrings sourceStrings;
223
- for (ComplexSelectorSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {
224
- Complex_Selector* pSource = *iterator;
225
- std::stringstream sstream;
226
- sstream << complexSelectorToNode(pSource);
227
- sourceStrings.push_back(sstream.str());
228
- }
229
-
230
- // Sort to get consistent output
231
- std::sort(sourceStrings.begin(), sourceStrings.end());
232
-
233
- std::cerr << "ComplexSelectorSet[";
234
- for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {
235
- std::string source = *iterator;
236
- if (iterator != sourceStrings.begin()) {
237
- std::cerr << ", ";
238
- }
239
- std::cerr << source;
240
- }
241
- std::cerr << "]";
242
-
243
- if (newline) {
244
- std::cerr << std::endl;
245
- }
246
- }
247
-
248
-
249
- std::ostream& operator<<(std::ostream& os, SubSetMapPairs& entries) {
250
- os << "SUBSET_MAP_ENTRIES[";
251
-
252
- for (SubSetMapPairs::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {
253
- Complex_Selector_Obj pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)
254
- Compound_Selector_Obj pExtCompoundSelector = iterator->second; // The stuff after the @extend
255
-
256
- if (iterator != entries.begin()) {
257
- os << ", ";
258
- }
259
-
260
- os << "(";
261
-
262
- if (pExtComplexSelector) {
263
- std::cerr << *pExtComplexSelector;
264
- } else {
265
- std::cerr << "NULL";
266
- }
267
-
268
- os << " -> ";
269
-
270
- if (pExtCompoundSelector) {
271
- std::cerr << *pExtCompoundSelector;
272
- } else {
273
- std::cerr << "NULL";
274
- }
275
-
276
- os << ")";
277
-
278
- }
279
-
280
- os << "]";
281
-
282
- return os;
283
- }
284
- #endif
285
-
286
- static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo) {
287
- // TODO: figure out a better way to create a Complex_Selector from scratch
288
- // TODO: There's got to be a better way. This got ugly quick...
289
- Type_Selector_Obj fakeParent = SASS_MEMORY_NEW(Type_Selector, ParserState("[FAKE]"), "temp");
290
- Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);
291
- fakeHead->elements().push_back(fakeParent);
292
- Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, {} /*tail*/);
293
-
294
- pOne->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);
295
- pTwo->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);
296
-
297
- bool isSuperselector = pOne->is_superselector_of(pTwo);
298
-
299
- pOne->clear_innermost();
300
- pTwo->clear_innermost();
301
-
302
- return isSuperselector;
303
- }
304
-
305
- void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out) {
306
- for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {
307
- Node& child = *iter;
308
- out.push_back(nodeToComplexSelector(child));
309
- }
310
- }
311
-
312
- Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque) {
313
- Node result = Node::createCollection();
314
-
315
- for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {
316
- Complex_Selector_Obj pChild = *iter;
317
- result.collection()->push_back(complexSelectorToNode(pChild));
318
- }
319
-
320
- return result;
321
- }
322
-
323
- class LcsCollectionComparator {
324
- public:
325
- LcsCollectionComparator() {}
326
-
327
- bool operator()(Complex_Selector_Obj pOne, Complex_Selector_Obj pTwo, Complex_Selector_Obj& pOut) const {
328
- /*
329
- This code is based on the following block from ruby sass' subweave
330
- do |s1, s2|
331
- next s1 if s1 == s2
332
- next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
333
- next s2 if parent_superselector?(s1, s2)
334
- next s1 if parent_superselector?(s2, s1)
335
- end
336
- */
337
-
338
- if (*pOne == *pTwo) {
339
- pOut = pOne;
340
- return true;
341
- }
342
-
343
- if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) {
344
- return false;
345
- }
346
-
347
- if (parentSuperselector(pOne, pTwo)) {
348
- pOut = pTwo;
349
- return true;
350
- }
351
-
352
- if (parentSuperselector(pTwo, pOne)) {
353
- pOut = pOne;
354
- return true;
355
- }
356
-
357
- return false;
358
- }
359
- };
360
-
361
-
362
- /*
363
- This is the equivalent of ruby's Sass::Util.lcs_backtrace.
364
-
365
- # Computes a single longest common subsequence for arrays x and y.
366
- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS
367
- */
368
- void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {
369
- //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)
370
- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
371
-
372
- if (i == 0 || j == 0) {
373
- DEBUG_PRINTLN(LCS, "RETURNING EMPTY")
374
- return;
375
- }
376
-
377
-
378
- Complex_Selector_Obj pCompareOut;
379
- if (comparator(x[i], y[j], pCompareOut)) {
380
- DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")
381
- lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);
382
- out.push_back(pCompareOut);
383
- return;
384
- }
385
-
386
- if (c[i][j - 1] > c[i - 1][j]) {
387
- DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")
388
- lcs_backtrace(c, x, y, i, j - 1, comparator, out);
389
- return;
390
- }
391
-
392
- DEBUG_PRINTLN(LCS, "FINAL RETURN")
393
- lcs_backtrace(c, x, y, i - 1, j, comparator, out);
394
- return;
395
- }
396
-
397
- /*
398
- This is the equivalent of ruby's Sass::Util.lcs_table.
399
-
400
- # Calculates the memoization table for the Least Common Subsequence algorithm.
401
- # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS
402
- */
403
- void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) {
404
- //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)
405
- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
406
-
407
- LCSTable c(x.size(), std::vector<int>(y.size()));
408
-
409
- // These shouldn't be necessary since the vector will be initialized to 0 already.
410
- // x.size.times {|i| c[i][0] = 0}
411
- // y.size.times {|j| c[0][j] = 0}
412
-
413
- for (size_t i = 1; i < x.size(); i++) {
414
- for (size_t j = 1; j < y.size(); j++) {
415
- Complex_Selector_Obj pCompareOut;
416
-
417
- if (comparator(x[i], y[j], pCompareOut)) {
418
- c[i][j] = c[i - 1][j - 1] + 1;
419
- } else {
420
- c[i][j] = std::max(c[i][j - 1], c[i - 1][j]);
421
- }
422
- }
423
- }
424
-
425
- out = c;
426
- }
427
-
428
- /*
429
- This is the equivalent of ruby's Sass::Util.lcs.
430
-
431
- # Computes a single longest common subsequence for `x` and `y`.
432
- # If there are more than one longest common subsequences,
433
- # the one returned is that which starts first in `x`.
434
-
435
- # @param x [NodeCollection]
436
- # @param y [NodeCollection]
437
- # @comparator An equality check between elements of `x` and `y`.
438
- # @return [NodeCollection] The LCS
439
-
440
- http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
441
- */
442
- void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {
443
- //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)
444
- // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output
445
-
446
- x.push_front({});
447
- y.push_front({});
448
-
449
- LCSTable table;
450
- lcs_table(x, y, comparator, table);
451
-
452
- return lcs_backtrace(table, x, y, static_cast<int>(x.size()) - 1, static_cast<int>(y.size()) - 1, comparator, out);
453
- }
454
-
455
-
456
- /*
457
- This is the equivalent of ruby's Sequence.trim.
458
-
459
- The following is the modified version of the ruby code that was more portable to C++. You
460
- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.
461
-
462
- # Avoid truly horrific quadratic behavior. TODO: I think there
463
- # may be a way to get perfect trimming without going quadratic.
464
- return seqses if seqses.size > 100
465
-
466
- # Keep the results in a separate array so we can be sure we aren't
467
- # comparing against an already-trimmed selector. This ensures that two
468
- # identical selectors don't mutually trim one another.
469
- result = seqses.dup
470
-
471
- # This is n^2 on the sequences, but only comparing between
472
- # separate sequences should limit the quadratic behavior.
473
- seqses.each_with_index do |seqs1, i|
474
- tempResult = []
475
-
476
- for seq1 in seqs1 do
477
- max_spec = 0
478
- for seq in _sources(seq1) do
479
- max_spec = [max_spec, seq.specificity].max
480
- end
481
-
482
-
483
- isMoreSpecificOuter = false
484
- for seqs2 in result do
485
- if seqs1.equal?(seqs2) then
486
- next
487
- end
488
-
489
- # Second Law of Extend: the specificity of a generated selector
490
- # should never be less than the specificity of the extending
491
- # selector.
492
- #
493
- # See https://github.com/nex3/sass/issues/324.
494
- isMoreSpecificInner = false
495
- for seq2 in seqs2 do
496
- isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)
497
- if isMoreSpecificInner then
498
- break
499
- end
500
- end
501
-
502
- if isMoreSpecificInner then
503
- isMoreSpecificOuter = true
504
- break
505
- end
506
- end
507
-
508
- if !isMoreSpecificOuter then
509
- tempResult.push(seq1)
510
- end
511
- end
512
-
513
- result[i] = tempResult
514
-
515
- end
516
-
517
- result
518
- */
519
- /*
520
- - IMPROVEMENT: We could probably work directly in the output trimmed deque.
521
- */
522
- Node Extend::trim(Node& seqses, bool isReplace) {
523
- // See the comments in the above ruby code before embarking on understanding this function.
524
-
525
- // Avoid poor performance in extreme cases.
526
- if (seqses.collection()->size() > 100) {
527
- return seqses;
528
- }
529
-
530
-
531
- DEBUG_PRINTLN(TRIM, "TRIM: " << seqses)
532
-
533
-
534
- Node result = Node::createCollection();
535
- result.plus(seqses);
536
-
537
- DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result)
538
-
539
- // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're
540
- // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track
541
- // of the index manually.
542
- int toTrimIndex = 0;
543
-
544
- for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) {
545
- Node& seqs1 = *seqsesIter;
546
-
547
- DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex)
548
-
549
- Node tempResult = Node::createCollection();
550
- tempResult.got_line_feed = seqs1.got_line_feed;
551
-
552
- for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {
553
- Node& seq1 = *seqs1Iter;
554
-
555
- Complex_Selector_Obj pSeq1 = nodeToComplexSelector(seq1);
556
-
557
- // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code
558
- // for a good description of sources.
559
- //
560
- // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test.
561
- // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We
562
- // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My
563
- // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely
564
- // a guess though.
565
- unsigned long maxSpecificity = isReplace ? pSeq1->specificity() : 0;
566
- ComplexSelectorSet sources = pSeq1->sources();
567
-
568
- DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1)
569
- DEBUG_EXEC(TRIM, printSourcesSet(sources, "TRIM SOURCES: "))
570
-
571
- for (ComplexSelectorSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {
572
- const Complex_Selector_Obj& pCurrentSelector = *sourcesSetIterator;
573
- maxSpecificity = std::max(maxSpecificity, pCurrentSelector->specificity());
574
- }
575
-
576
- DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity)
577
-
578
- bool isMoreSpecificOuter = false;
579
-
580
- int resultIndex = 0;
581
-
582
- for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) {
583
- Node& seqs2 = *resultIter;
584
-
585
- DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1)
586
- DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2)
587
-
588
- // Do not compare the same sequence to itself. The ruby call we're trying to
589
- // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision.
590
- // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is
591
- // derived from seqses and seqs2 is derived from result.
592
- if (seqs1.collection() == seqs2.collection()) {
593
- DEBUG_PRINTLN(TRIM, "CONTINUE")
594
- continue;
595
- }
596
-
597
- bool isMoreSpecificInner = false;
598
-
599
- for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {
600
- Node& seq2 = *seqs2Iter;
601
-
602
- Complex_Selector_Obj pSeq2 = nodeToComplexSelector(seq2);
603
-
604
- DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())
605
- DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))
606
- DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false"))
607
-
608
- isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1);
609
-
610
- if (isMoreSpecificInner) {
611
- DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC")
612
- break;
613
- }
614
- }
615
-
616
- // If we found something more specific, we're done. Let the outer loop know and stop iterating.
617
- if (isMoreSpecificInner) {
618
- isMoreSpecificOuter = true;
619
- break;
620
- }
621
-
622
- resultIndex++;
623
- }
624
-
625
- if (!isMoreSpecificOuter) {
626
- DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1)
627
- tempResult.collection()->push_back(seq1);
628
- }
629
-
630
- }
631
-
632
- DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result)
633
- DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult)
634
- (*result.collection())[toTrimIndex] = tempResult;
635
-
636
- toTrimIndex++;
637
-
638
- DEBUG_PRINTLN(TRIM, "RESULT: " << result)
639
- }
640
-
641
- return result;
642
- }
643
-
644
-
645
-
646
- static bool parentSuperselector(const Node& one, const Node& two) {
647
- // TODO: figure out a better way to create a Complex_Selector from scratch
648
- // TODO: There's got to be a better way. This got ugly quick...
649
- Type_Selector_Obj fakeParent = SASS_MEMORY_NEW(Type_Selector, ParserState("[FAKE]"), "temp");
650
- Compound_Selector_Obj fakeHead = SASS_MEMORY_NEW(Compound_Selector, ParserState("[FAKE]"), 1 /*size*/);
651
- fakeHead->elements().push_back(fakeParent);
652
- Complex_Selector_Obj fakeParentContainer = SASS_MEMORY_NEW(Complex_Selector, ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, fakeHead /*head*/, {} /*tail*/);
653
-
654
- Complex_Selector_Obj pOneWithFakeParent = nodeToComplexSelector(one);
655
- pOneWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);
656
- Complex_Selector_Obj pTwoWithFakeParent = nodeToComplexSelector(two);
657
- pTwoWithFakeParent->set_innermost(fakeParentContainer, Complex_Selector::ANCESTOR_OF);
658
-
659
- return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);
660
- }
661
-
662
-
663
- class ParentSuperselectorChunker {
664
- public:
665
- ParentSuperselectorChunker(Node& lcs) : mLcs(lcs) {}
666
- Node& mLcs;
667
-
668
- bool operator()(const Node& seq) const {
669
- // {|s| parent_superselector?(s.first, lcs.first)}
670
- if (seq.collection()->size() == 0) return false;
671
- return parentSuperselector(seq.collection()->front(), mLcs.collection()->front());
672
- }
673
- };
674
-
675
- class SubweaveEmptyChunker {
676
- public:
677
- bool operator()(const Node& seq) const {
678
- // {|s| s.empty?}
679
-
680
- return seq.collection()->empty();
681
- }
682
- };
683
-
684
- /*
685
- # Takes initial subsequences of `seq1` and `seq2` and returns all
686
- # orderings of those subsequences. The initial subsequences are determined
687
- # by a block.
688
- #
689
- # Destructively removes the initial subsequences of `seq1` and `seq2`.
690
- #
691
- # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
692
- # denoting the boundary of the initial subsequence), this would return
693
- # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
694
- # `(3 4 5)`.
695
- #
696
- # @param seq1 [Array]
697
- # @param seq2 [Array]
698
- # @yield [a] Used to determine when to cut off the initial subsequences.
699
- # Called repeatedly for each sequence until it returns true.
700
- # @yieldparam a [Array] A final subsequence of one input sequence after
701
- # cutting off some initial subsequence.
702
- # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
703
- # here.
704
- # @return [Array<Array>] All possible orderings of the initial subsequences.
705
- def chunks(seq1, seq2)
706
- chunk1 = []
707
- chunk1 << seq1.shift until yield seq1
708
- chunk2 = []
709
- chunk2 << seq2.shift until yield seq2
710
- return [] if chunk1.empty? && chunk2.empty?
711
- return [chunk2] if chunk1.empty?
712
- return [chunk1] if chunk2.empty?
713
- [chunk1 + chunk2, chunk2 + chunk1]
714
- end
715
- */
716
- template<typename ChunkerType>
717
- static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) {
718
- Node chunk1 = Node::createCollection();
719
- while (seq1.collection()->size() && !chunker(seq1)) {
720
- chunk1.collection()->push_back(seq1.collection()->front());
721
- seq1.collection()->pop_front();
722
- }
723
-
724
- Node chunk2 = Node::createCollection();
725
- while (!seq2.collection()->empty() && !chunker(seq2)) {
726
- chunk2.collection()->push_back(seq2.collection()->front());
727
- seq2.collection()->pop_front();
728
- }
729
-
730
- if (chunk1.collection()->empty() && chunk2.collection()->empty()) {
731
- DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY")
732
- return Node::createCollection();
733
- }
734
-
735
- if (chunk1.collection()->empty()) {
736
- Node chunk2Wrapper = Node::createCollection();
737
- chunk2Wrapper.collection()->push_back(chunk2);
738
- DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY")
739
- return chunk2Wrapper;
740
- }
741
-
742
- if (chunk2.collection()->empty()) {
743
- Node chunk1Wrapper = Node::createCollection();
744
- chunk1Wrapper.collection()->push_back(chunk1);
745
- DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY")
746
- return chunk1Wrapper;
747
- }
748
-
749
- Node perms = Node::createCollection();
750
-
751
- Node firstPermutation = Node::createCollection();
752
- firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());
753
- firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());
754
- perms.collection()->push_back(firstPermutation);
755
-
756
- Node secondPermutation = Node::createCollection();
757
- secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());
758
- secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());
759
- perms.collection()->push_back(secondPermutation);
760
-
761
- DEBUG_PRINTLN(CHUNKS, "RETURNING PERM")
762
-
763
- return perms;
764
- }
765
-
766
-
767
- static Node groupSelectors(Node& seq) {
768
- Node newSeq = Node::createCollection();
769
-
770
- Node tail = Node::createCollection();
771
- tail.plus(seq);
772
-
773
- while (!tail.collection()->empty()) {
774
- Node head = Node::createCollection();
775
-
776
- do {
777
- head.collection()->push_back(tail.collection()->front());
778
- tail.collection()->pop_front();
779
- } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator()));
780
-
781
- newSeq.collection()->push_back(head);
782
- }
783
-
784
- return newSeq;
785
- }
786
-
787
-
788
- static void getAndRemoveInitialOps(Node& seq, Node& ops) {
789
- NodeDeque& seqCollection = *(seq.collection());
790
- NodeDeque& opsCollection = *(ops.collection());
791
-
792
- while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) {
793
- opsCollection.push_back(seqCollection.front());
794
- seqCollection.pop_front();
795
- }
796
- }
797
-
798
-
799
- static void getAndRemoveFinalOps(Node& seq, Node& ops) {
800
- NodeDeque& seqCollection = *(seq.collection());
801
- NodeDeque& opsCollection = *(ops.collection());
802
-
803
- while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) {
804
- opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code
805
- seqCollection.pop_back();
806
- }
807
- }
808
-
809
-
810
- /*
811
- def merge_initial_ops(seq1, seq2)
812
- ops1, ops2 = [], []
813
- ops1 << seq1.shift while seq1.first.is_a?(String)
814
- ops2 << seq2.shift while seq2.first.is_a?(String)
815
-
816
- newline = false
817
- newline ||= !!ops1.shift if ops1.first == "\n"
818
- newline ||= !!ops2.shift if ops2.first == "\n"
819
-
820
- # If neither sequence is a subsequence of the other, they cannot be
821
- # merged successfully
822
- lcs = Sass::Util.lcs(ops1, ops2)
823
- return unless lcs == ops1 || lcs == ops2
824
- return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
825
- end
826
- */
827
- static Node mergeInitialOps(Node& seq1, Node& seq2) {
828
- Node ops1 = Node::createCollection();
829
- Node ops2 = Node::createCollection();
830
-
831
- getAndRemoveInitialOps(seq1, ops1);
832
- getAndRemoveInitialOps(seq2, ops2);
833
-
834
- // TODO: Do we have this information available to us?
835
- // newline = false
836
- // newline ||= !!ops1.shift if ops1.first == "\n"
837
- // newline ||= !!ops2.shift if ops2.first == "\n"
838
-
839
- // If neither sequence is a subsequence of the other, they cannot be merged successfully
840
- DefaultLcsComparator lcsDefaultComparator;
841
- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);
842
-
843
- if (!(opsLcs == ops1 || opsLcs == ops2)) {
844
- return Node::createNil();
845
- }
846
-
847
- // TODO: more newline logic
848
- // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
849
-
850
- return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2);
851
- }
852
-
853
-
854
- /*
855
- def merge_final_ops(seq1, seq2, res = [])
856
-
857
-
858
- # This code looks complicated, but it's actually just a bunch of special
859
- # cases for interactions between different combinators.
860
- op1, op2 = ops1.first, ops2.first
861
- if op1 && op2
862
- sel1 = seq1.pop
863
- sel2 = seq2.pop
864
- if op1 == '~' && op2 == '~'
865
- if sel1.superselector?(sel2)
866
- res.unshift sel2, '~'
867
- elsif sel2.superselector?(sel1)
868
- res.unshift sel1, '~'
869
- else
870
- merged = sel1.unify(sel2.members, sel2.subject?)
871
- res.unshift [
872
- [sel1, '~', sel2, '~'],
873
- [sel2, '~', sel1, '~'],
874
- ([merged, '~'] if merged)
875
- ].compact
876
- end
877
- elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')
878
- if op1 == '~'
879
- tilde_sel, plus_sel = sel1, sel2
880
- else
881
- tilde_sel, plus_sel = sel2, sel1
882
- end
883
-
884
- if tilde_sel.superselector?(plus_sel)
885
- res.unshift plus_sel, '+'
886
- else
887
- merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
888
- res.unshift [
889
- [tilde_sel, '~', plus_sel, '+'],
890
- ([merged, '+'] if merged)
891
- ].compact
892
- end
893
- elsif op1 == '>' && %w[~ +].include?(op2)
894
- res.unshift sel2, op2
895
- seq1.push sel1, op1
896
- elsif op2 == '>' && %w[~ +].include?(op1)
897
- res.unshift sel1, op1
898
- seq2.push sel2, op2
899
- elsif op1 == op2
900
- return unless merged = sel1.unify(sel2.members, sel2.subject?)
901
- res.unshift merged, op1
902
- else
903
- # Unknown selector combinators can't be unified
904
- return
905
- end
906
- return merge_final_ops(seq1, seq2, res)
907
- elsif op1
908
- seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)
909
- res.unshift seq1.pop, op1
910
- return merge_final_ops(seq1, seq2, res)
911
- else # op2
912
- seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)
913
- res.unshift seq2.pop, op2
914
- return merge_final_ops(seq1, seq2, res)
915
- end
916
- end
917
- */
918
- static Node mergeFinalOps(Node& seq1, Node& seq2, Node& res) {
919
-
920
- Node ops1 = Node::createCollection();
921
- Node ops2 = Node::createCollection();
922
-
923
- getAndRemoveFinalOps(seq1, ops1);
924
- getAndRemoveFinalOps(seq2, ops2);
925
-
926
- // TODO: do we have newlines to remove?
927
- // ops1.reject! {|o| o == "\n"}
928
- // ops2.reject! {|o| o == "\n"}
929
-
930
- if (ops1.collection()->empty() && ops2.collection()->empty()) {
931
- return res;
932
- }
933
-
934
- if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) {
935
- DefaultLcsComparator lcsDefaultComparator;
936
- Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator);
937
-
938
- // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up.
939
-
940
- if (!(opsLcs == ops1 || opsLcs == ops2)) {
941
- return Node::createNil();
942
- }
943
-
944
- if (ops1.collection()->size() > ops2.collection()->size()) {
945
- res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend());
946
- } else {
947
- res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend());
948
- }
949
-
950
- return res;
951
- }
952
-
953
- if (!ops1.collection()->empty() && !ops2.collection()->empty()) {
954
-
955
- Node op1 = ops1.collection()->front();
956
- Node op2 = ops2.collection()->front();
957
-
958
- Node sel1 = seq1.collection()->back();
959
- seq1.collection()->pop_back();
960
-
961
- Node sel2 = seq2.collection()->back();
962
- seq2.collection()->pop_back();
963
-
964
- if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) {
965
-
966
- if (sel1.selector()->is_superselector_of(sel2.selector())) {
967
-
968
- res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);
969
- res.collection()->push_front(sel2);
970
-
971
- } else if (sel2.selector()->is_superselector_of(sel1.selector())) {
972
-
973
- res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);
974
- res.collection()->push_front(sel1);
975
-
976
- } else {
977
-
978
- DEBUG_PRINTLN(ALL, "sel1: " << sel1)
979
- DEBUG_PRINTLN(ALL, "sel2: " << sel2)
980
-
981
- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
982
- // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)
983
- Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());
984
- pMergedWrapper->head(pMerged);
985
-
986
- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
987
-
988
- Node newRes = Node::createCollection();
989
-
990
- Node firstPerm = Node::createCollection();
991
- firstPerm.collection()->push_back(sel1);
992
- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
993
- firstPerm.collection()->push_back(sel2);
994
- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
995
- newRes.collection()->push_back(firstPerm);
996
-
997
- Node secondPerm = Node::createCollection();
998
- secondPerm.collection()->push_back(sel2);
999
- secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
1000
- secondPerm.collection()->push_back(sel1);
1001
- secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
1002
- newRes.collection()->push_back(secondPerm);
1003
-
1004
- if (pMerged) {
1005
- Node mergedPerm = Node::createCollection();
1006
- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));
1007
- mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
1008
- newRes.collection()->push_back(mergedPerm);
1009
- }
1010
-
1011
- res.collection()->push_front(newRes);
1012
-
1013
- DEBUG_PRINTLN(ALL, "RESULT: " << res)
1014
-
1015
- }
1016
-
1017
- } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) {
1018
-
1019
- Node tildeSel = sel1;
1020
- Node plusSel = sel2;
1021
- Node plusOp = op2;
1022
- if (op1.combinator() != Complex_Selector::PRECEDES) {
1023
- tildeSel = sel2;
1024
- plusSel = sel1;
1025
- plusOp = op1;
1026
- }
1027
-
1028
- if (tildeSel.selector()->is_superselector_of(plusSel.selector())) {
1029
-
1030
- res.collection()->push_front(plusOp);
1031
- res.collection()->push_front(plusSel);
1032
-
1033
- } else {
1034
-
1035
- DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)
1036
- DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel)
1037
-
1038
- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(plusSel.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
1039
- // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
1040
- Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head());
1041
- pMergedWrapper->head(pMerged);
1042
-
1043
- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
1044
-
1045
- Node newRes = Node::createCollection();
1046
-
1047
- Node firstPerm = Node::createCollection();
1048
- firstPerm.collection()->push_back(tildeSel);
1049
- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));
1050
- firstPerm.collection()->push_back(plusSel);
1051
- firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));
1052
- newRes.collection()->push_back(firstPerm);
1053
-
1054
- if (pMerged) {
1055
- Node mergedPerm = Node::createCollection();
1056
- mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper));
1057
- mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));
1058
- newRes.collection()->push_back(mergedPerm);
1059
- }
1060
-
1061
- res.collection()->push_front(newRes);
1062
-
1063
- DEBUG_PRINTLN(ALL, "RESULT: " << res)
1064
-
1065
- }
1066
- } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) {
1067
-
1068
- res.collection()->push_front(op2);
1069
- res.collection()->push_front(sel2);
1070
-
1071
- seq1.collection()->push_back(sel1);
1072
- seq1.collection()->push_back(op1);
1073
-
1074
- } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) {
1075
-
1076
- res.collection()->push_front(op1);
1077
- res.collection()->push_front(sel1);
1078
-
1079
- seq2.collection()->push_back(sel2);
1080
- seq2.collection()->push_back(op2);
1081
-
1082
- } else if (op1.combinator() == op2.combinator()) {
1083
-
1084
- DEBUG_PRINTLN(ALL, "sel1: " << sel1)
1085
- DEBUG_PRINTLN(ALL, "sel2: " << sel2)
1086
-
1087
- Complex_Selector_Obj pMergedWrapper = SASS_MEMORY_CLONE(sel1.selector()); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result
1088
- // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)
1089
- Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head());
1090
- pMergedWrapper->head(pMerged);
1091
-
1092
- DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))
1093
-
1094
- if (!pMerged) {
1095
- return Node::createNil();
1096
- }
1097
-
1098
- res.collection()->push_front(op1);
1099
- res.collection()->push_front(Node::createSelector(pMergedWrapper));
1100
-
1101
- DEBUG_PRINTLN(ALL, "RESULT: " << res)
1102
-
1103
- } else {
1104
- return Node::createNil();
1105
- }
1106
-
1107
- return mergeFinalOps(seq1, seq2, res);
1108
-
1109
- } else if (!ops1.collection()->empty()) {
1110
-
1111
- Node op1 = ops1.collection()->front();
1112
-
1113
- if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) {
1114
- seq2.collection()->pop_back();
1115
- }
1116
-
1117
- // TODO: consider unshift(NodeCollection, Node)
1118
- res.collection()->push_front(op1);
1119
- res.collection()->push_front(seq1.collection()->back());
1120
- seq1.collection()->pop_back();
1121
-
1122
- return mergeFinalOps(seq1, seq2, res);
1123
-
1124
- } else { // !ops2.collection()->empty()
1125
-
1126
- Node op2 = ops2.collection()->front();
1127
-
1128
- if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) {
1129
- seq1.collection()->pop_back();
1130
- }
1131
-
1132
- res.collection()->push_front(op2);
1133
- res.collection()->push_front(seq2.collection()->back());
1134
- seq2.collection()->pop_back();
1135
-
1136
- return mergeFinalOps(seq1, seq2, res);
1137
-
1138
- }
1139
-
1140
- }
1141
-
1142
-
1143
- /*
1144
- This is the equivalent of ruby's Sequence.subweave.
1145
-
1146
- Here is the original subweave code for reference during porting.
1147
-
1148
- def subweave(seq1, seq2)
1149
- return [seq2] if seq1.empty?
1150
- return [seq1] if seq2.empty?
1151
-
1152
- seq1, seq2 = seq1.dup, seq2.dup
1153
- return unless init = merge_initial_ops(seq1, seq2)
1154
- return unless fin = merge_final_ops(seq1, seq2)
1155
- seq1 = group_selectors(seq1)
1156
- seq2 = group_selectors(seq2)
1157
- lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
1158
- next s1 if s1 == s2
1159
- next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
1160
- next s2 if parent_superselector?(s1, s2)
1161
- next s1 if parent_superselector?(s2, s1)
1162
- end
1163
-
1164
- diff = [[init]]
1165
- until lcs.empty?
1166
- diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]
1167
- seq1.shift
1168
- seq2.shift
1169
- end
1170
- diff << chunks(seq1, seq2) {|s| s.empty?}
1171
- diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
1172
- diff.reject! {|c| c.empty?}
1173
-
1174
- result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}
1175
-
1176
- result
1177
- end
1178
- */
1179
- Node subweave(Node& one, Node& two) {
1180
- // Check for the simple cases
1181
- if (one.collection()->size() == 0) {
1182
- Node out = Node::createCollection();
1183
- out.collection()->push_back(two);
1184
- return out;
1185
- }
1186
- if (two.collection()->size() == 0) {
1187
- Node out = Node::createCollection();
1188
- out.collection()->push_back(one);
1189
- return out;
1190
- }
1191
-
1192
- Node seq1 = Node::createCollection();
1193
- seq1.plus(one);
1194
- Node seq2 = Node::createCollection();
1195
- seq2.plus(two);
1196
-
1197
- DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1)
1198
- DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2)
1199
-
1200
- Node init = mergeInitialOps(seq1, seq2);
1201
- if (init.isNil()) {
1202
- return Node::createNil();
1203
- }
1204
-
1205
- DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init)
1206
-
1207
- Node res = Node::createCollection();
1208
- Node fin = mergeFinalOps(seq1, seq2, res);
1209
- if (fin.isNil()) {
1210
- return Node::createNil();
1211
- }
1212
-
1213
- DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin)
1214
-
1215
-
1216
- // Moving this line up since fin isn't modified between now and when it happened before
1217
- // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
1218
-
1219
- for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end();
1220
- finIter != finEndIter; ++finIter) {
1221
-
1222
- Node& childNode = *finIter;
1223
-
1224
- if (!childNode.isCollection()) {
1225
- Node wrapper = Node::createCollection();
1226
- wrapper.collection()->push_back(childNode);
1227
- childNode = wrapper;
1228
- }
1229
-
1230
- }
1231
-
1232
- DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin)
1233
-
1234
-
1235
-
1236
- Node groupSeq1 = groupSelectors(seq1);
1237
- DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1)
1238
-
1239
- Node groupSeq2 = groupSelectors(seq2);
1240
- DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2)
1241
-
1242
-
1243
- ComplexSelectorDeque groupSeq1Converted;
1244
- nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted);
1245
-
1246
- ComplexSelectorDeque groupSeq2Converted;
1247
- nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted);
1248
-
1249
- ComplexSelectorDeque out;
1250
- LcsCollectionComparator collectionComparator;
1251
- lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, out);
1252
- Node seqLcs = complexSelectorDequeToNode(out);
1253
-
1254
- DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs)
1255
-
1256
-
1257
- Node initWrapper = Node::createCollection();
1258
- initWrapper.collection()->push_back(init);
1259
- Node diff = Node::createCollection();
1260
- diff.collection()->push_back(initWrapper);
1261
-
1262
- DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff)
1263
-
1264
-
1265
- while (!seqLcs.collection()->empty()) {
1266
- ParentSuperselectorChunker superselectorChunker(seqLcs);
1267
- Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker);
1268
- diff.collection()->push_back(chunksResult);
1269
-
1270
- Node lcsWrapper = Node::createCollection();
1271
- lcsWrapper.collection()->push_back(seqLcs.collection()->front());
1272
- seqLcs.collection()->pop_front();
1273
- diff.collection()->push_back(lcsWrapper);
1274
-
1275
- if (groupSeq1.collection()->size()) groupSeq1.collection()->pop_front();
1276
- if (groupSeq2.collection()->size()) groupSeq2.collection()->pop_front();
1277
- }
1278
-
1279
- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff)
1280
-
1281
-
1282
- DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2)
1283
-
1284
-
1285
- SubweaveEmptyChunker emptyChunker;
1286
- Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker);
1287
- diff.collection()->push_back(chunksResult);
1288
-
1289
-
1290
- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff)
1291
-
1292
-
1293
- diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end());
1294
-
1295
- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff)
1296
-
1297
- // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection)
1298
- Node diffFiltered = Node::createCollection();
1299
- for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end();
1300
- diffIter != diffEndIter; ++diffIter) {
1301
- Node& node = *diffIter;
1302
- if (node.collection() && !node.collection()->empty()) {
1303
- diffFiltered.collection()->push_back(node);
1304
- }
1305
- }
1306
- diff = diffFiltered;
1307
-
1308
- DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff)
1309
-
1310
-
1311
- Node pathsResult = paths(diff);
1312
-
1313
- DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult)
1314
-
1315
-
1316
- // We're flattening in place
1317
- for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end();
1318
- pathsIter != pathsEndIter; ++pathsIter) {
1319
-
1320
- Node& child = *pathsIter;
1321
- child = flatten(child);
1322
- }
1323
-
1324
- DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)
1325
-
1326
-
1327
- /*
1328
- TODO: implement
1329
- rejected = mapped.reject {|p| path_has_two_subjects?(p)}
1330
- $stderr.puts "REJECTED: #{rejected}"
1331
- */
1332
-
1333
-
1334
- return pathsResult;
1335
-
1336
- }
1337
- /*
1338
- // disabled to avoid clang warning [-Wunused-function]
1339
- static Node subweaveNaive(const Node& one, const Node& two) {
1340
- Node out = Node::createCollection();
1341
-
1342
- // Check for the simple cases
1343
- if (one.isNil()) {
1344
- out.collection()->push_back(two.klone());
1345
- } else if (two.isNil()) {
1346
- out.collection()->push_back(one.klone());
1347
- } else {
1348
- // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B
1349
- // See https://gist.github.com/nex3/7609394 for details.
1350
-
1351
- Node firstPerm = one.klone();
1352
- Node twoCloned = two.klone();
1353
- firstPerm.plus(twoCloned);
1354
- out.collection()->push_back(firstPerm);
1355
-
1356
- Node secondPerm = two.klone();
1357
- Node oneCloned = one.klone();
1358
- secondPerm.plus(oneCloned );
1359
- out.collection()->push_back(secondPerm);
1360
- }
1361
-
1362
- return out;
1363
- }
1364
- */
1365
-
1366
-
1367
- /*
1368
- This is the equivalent of ruby's Sequence.weave.
1369
-
1370
- The following is the modified version of the ruby code that was more portable to C++. You
1371
- should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.
1372
-
1373
- def weave(path)
1374
- # This function works by moving through the selector path left-to-right,
1375
- # building all possible prefixes simultaneously. These prefixes are
1376
- # `befores`, while the remaining parenthesized suffixes is `afters`.
1377
- befores = [[]]
1378
- afters = path.dup
1379
-
1380
- until afters.empty?
1381
- current = afters.shift.dup
1382
- last_current = [current.pop]
1383
-
1384
- tempResult = []
1385
-
1386
- for before in befores do
1387
- sub = subweave(before, current)
1388
- if sub.nil?
1389
- next
1390
- end
1391
-
1392
- for seqs in sub do
1393
- tempResult.push(seqs + last_current)
1394
- end
1395
- end
1396
-
1397
- befores = tempResult
1398
-
1399
- end
1400
-
1401
- return befores
1402
- end
1403
- */
1404
- /*
1405
- def weave(path)
1406
- befores = [[]]
1407
- afters = path.dup
1408
-
1409
- until afters.empty?
1410
- current = afters.shift.dup
1411
-
1412
- last_current = [current.pop]
1413
-
1414
-
1415
- tempResult = []
1416
-
1417
- for before in befores do
1418
- sub = subweave(before, current)
1419
-
1420
- if sub.nil?
1421
- next []
1422
- end
1423
-
1424
-
1425
- for seqs in sub do
1426
- toPush = seqs + last_current
1427
-
1428
- tempResult.push(seqs + last_current)
1429
- end
1430
-
1431
- end
1432
-
1433
- befores = tempResult
1434
-
1435
- end
1436
-
1437
- return befores
1438
- end
1439
- */
1440
- Node Extend::weave(Node& path) {
1441
-
1442
- DEBUG_PRINTLN(WEAVE, "WEAVE: " << path)
1443
-
1444
- Node befores = Node::createCollection();
1445
- befores.collection()->push_back(Node::createCollection());
1446
-
1447
- Node afters = Node::createCollection();
1448
- afters.plus(path);
1449
-
1450
- while (!afters.collection()->empty()) {
1451
- Node current = afters.collection()->front().klone();
1452
- afters.collection()->pop_front();
1453
- DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)
1454
- if (current.collection()->size() == 0) continue;
1455
-
1456
- Node last_current = Node::createCollection();
1457
- last_current.collection()->push_back(current.collection()->back());
1458
- current.collection()->pop_back();
1459
- DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current)
1460
- DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current)
1461
-
1462
- Node tempResult = Node::createCollection();
1463
-
1464
- for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {
1465
- Node& before = *beforesIter;
1466
-
1467
- Node sub = subweave(before, current);
1468
-
1469
- DEBUG_PRINTLN(WEAVE, "SUB: " << sub)
1470
-
1471
- if (sub.isNil()) {
1472
- return Node::createCollection();
1473
- }
1474
-
1475
- for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) {
1476
- Node& seqs = *subIter;
1477
-
1478
- Node toPush = Node::createCollection();
1479
- toPush.plus(seqs);
1480
- toPush.plus(last_current);
1481
-
1482
- // move line feed from inner to outer selector (very hacky indeed)
1483
- if (last_current.collection() && last_current.collection()->front().selector()) {
1484
- toPush.got_line_feed = last_current.collection()->front().got_line_feed;
1485
- last_current.collection()->front().selector()->has_line_feed(false);
1486
- last_current.collection()->front().got_line_feed = false;
1487
- }
1488
-
1489
- tempResult.collection()->push_back(toPush);
1490
-
1491
- }
1492
- }
1493
-
1494
- befores = tempResult;
1495
-
1496
- }
1497
-
1498
- return befores;
1499
- }
1500
-
1501
-
1502
-
1503
- /*
1504
- This is the equivalent of ruby's SimpleSequence.do_extend.
1505
-
1506
- // TODO: I think I have some modified ruby code to put here. Check.
1507
- */
1508
- /*
1509
- ISSUES:
1510
- - Previous TODO: Do we need to group the results by extender?
1511
- - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?)
1512
- - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything...
1513
- - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing...
1514
- */
1515
- template<typename KeyType>
1516
- class GroupByToAFunctor {
1517
- public:
1518
- KeyType operator()(SubSetMapPair& extPair) const {
1519
- Complex_Selector_Obj pSelector = extPair.first;
1520
- return pSelector;
1521
- }
1522
- };
1523
- Node Extend::extendCompoundSelector(Compound_Selector* pSelector, CompoundSelectorSet& seen, bool isReplace) {
1524
-
1525
- /* this turned out to be too much overhead
1526
- probably due to holding a "Node" object
1527
- // check if we already extended this selector
1528
- // we can do this since subset_map is "static"
1529
- auto memoized = memoizeCompound.find(pSelector);
1530
- if (memoized != memoizeCompound.end()) {
1531
- return memoized->second.klone();
1532
- }
1533
- */
1534
-
1535
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))
1536
- // TODO: Ruby has another loop here to skip certain members?
1537
-
1538
- // let RESULTS be an empty list of complex selectors
1539
- Node results = Node::createCollection();
1540
- // extendedSelectors.got_line_feed = true;
1541
-
1542
- SubSetMapPairs entries = subset_map.get_v(pSelector);
1543
-
1544
- GroupByToAFunctor<Complex_Selector_Obj> extPairKeyFunctor;
1545
- SubSetMapResults arr;
1546
- group_by_to_a(entries, extPairKeyFunctor, arr);
1547
-
1548
- SubSetMapLookups holder;
1549
-
1550
- // for each (EXTENDER, TARGET) in MAP.get(COMPOUND):
1551
- for (SubSetMapResult& groupedPair : arr) {
1552
-
1553
- Complex_Selector_Obj seq = groupedPair.first;
1554
- SubSetMapPairs& group = groupedPair.second;
1555
-
1556
- DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(seq, "SEQ: "))
1557
-
1558
- Compound_Selector_Obj pSels = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());
1559
- for (SubSetMapPair& pair : group) {
1560
- pair.second->extended(true);
1561
- pSels->concat(pair.second);
1562
- }
1563
-
1564
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))
1565
-
1566
- // The selector up to where the @extend is (ie, the thing to merge)
1567
- Complex_Selector* pExtComplexSelector = seq;
1568
-
1569
- // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL?
1570
- // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)
1571
- Compound_Selector_Obj pSelectorWithoutExtendSelectors = pSelector->minus(pSels);
1572
-
1573
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))
1574
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))
1575
-
1576
- Compound_Selector_Obj pInnermostCompoundSelector = pExtComplexSelector->last()->head();
1577
-
1578
- if (!pInnermostCompoundSelector) {
1579
- pInnermostCompoundSelector = SASS_MEMORY_NEW(Compound_Selector, pSelector->pstate());
1580
- }
1581
- Compound_Selector_Obj pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors);
1582
-
1583
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))
1584
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))
1585
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: "))
1586
-
1587
- // RUBY: next unless unified
1588
- if (!pUnifiedSelector || pUnifiedSelector->length() == 0) {
1589
- continue;
1590
- }
1591
-
1592
- // TODO: implement the parent directive match (if necessary based on test failures)
1593
- // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
1594
-
1595
- // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just
1596
- // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more
1597
- // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered
1598
- // out and aren't operated on.
1599
- Complex_Selector_Obj pNewSelector = SASS_MEMORY_CLONE(pExtComplexSelector); // ->first();
1600
-
1601
- Complex_Selector_Obj pNewInnerMost = SASS_MEMORY_NEW(Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, {});
1602
-
1603
- Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();
1604
- pNewSelector->set_innermost(pNewInnerMost, combinator);
1605
-
1606
- #ifdef DEBUG
1607
- ComplexSelectorSet debugSet;
1608
- debugSet = pNewSelector->sources();
1609
- if (debugSet.size() > 0) {
1610
- throw std::runtime_error("The new selector should start with no sources. Something needs to be cloned to fix this.");
1611
- }
1612
- debugSet = pExtComplexSelector->sources();
1613
- if (debugSet.size() > 0) {
1614
- throw std::runtime_error("The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.");
1615
- }
1616
- #endif
1617
-
1618
-
1619
- // if (pSelector && pSelector->has_line_feed()) pNewInnerMost->has_line_feed(true);
1620
- // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.
1621
- DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector))
1622
-
1623
- DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, "SOURCES NEW SEQ BEGIN: "))
1624
-
1625
- // I actually want to create a copy here (performance!)
1626
- ComplexSelectorSet newSourcesSet = pSelector->sources(); // XXX
1627
- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES THIS EXTEND: "))
1628
-
1629
- newSourcesSet.insert(pExtComplexSelector);
1630
- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, "SOURCES WITH NEW SOURCE: "))
1631
-
1632
- // RUBY: new_seq.add_sources!(sources + [seq])
1633
- pNewSelector->addSources(newSourcesSet);
1634
-
1635
- DEBUG_EXEC(EXTEND_COMPOUND, ComplexSelectorSet newSet = pNewSelector->sources(); printSourcesSet(newSet, "SOURCES ON NEW SELECTOR AFTER ADD: "))
1636
- DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))
1637
-
1638
-
1639
- if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);
1640
-
1641
- holder.push_back(std::make_pair(pSels, pNewSelector));
1642
- }
1643
-
1644
-
1645
- for (SubSetMapLookup& pair : holder) {
1646
-
1647
- Compound_Selector_Obj pSels = pair.first;
1648
- Complex_Selector_Obj pNewSelector = pair.second;
1649
-
1650
-
1651
- // RUBY??: next [] if seen.include?(sels)
1652
- if (seen.find(pSels) != seen.end()) {
1653
- continue;
1654
- }
1655
-
1656
-
1657
- CompoundSelectorSet recurseSeen(seen);
1658
- recurseSeen.insert(pSels);
1659
-
1660
-
1661
- DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector))
1662
- Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, recurseSeen, isReplace, false); // !:isOriginal
1663
-
1664
- DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors)
1665
-
1666
- for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end();
1667
- iterator != endIterator; ++iterator) {
1668
- Node newSelector = *iterator;
1669
-
1670
- // DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << results)
1671
- // DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << results.contains(newSelector, false /*simpleSelectorOrderDependent*/));
1672
-
1673
- if (!results.contains(newSelector)) {
1674
- // DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")
1675
- results.collection()->push_back(newSelector);
1676
- }
1677
- }
1678
- }
1679
-
1680
- DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: "))
1681
-
1682
- // this turned out to be too much overhead
1683
- // memory results in a map table - since extending is very expensive
1684
- // memoizeCompound.insert(std::pair<Compound_Selector_Obj, Node>(pSelector, results));
1685
-
1686
- return results;
1687
- }
1688
-
1689
-
1690
- // check if selector has something to be extended by subset_map
1691
- bool Extend::complexSelectorHasExtension(Complex_Selector* selector, CompoundSelectorSet& seen) {
1692
-
1693
- bool hasExtension = false;
1694
-
1695
- Complex_Selector_Obj pIter = selector;
1696
-
1697
- while (!hasExtension && pIter) {
1698
- Compound_Selector_Obj pHead = pIter->head();
1699
-
1700
- if (pHead) {
1701
- SubSetMapPairs entries = subset_map.get_v(pHead);
1702
- for (SubSetMapPair ext : entries) {
1703
- // check if both selectors have the same media block parent
1704
- // if (ext.first->media_block() == pComplexSelector->media_block()) continue;
1705
- if (ext.second->media_block() == 0) continue;
1706
- if (pHead->media_block() &&
1707
- ext.second->media_block()->media_queries() &&
1708
- pHead->media_block()->media_queries()
1709
- ) {
1710
- std::string query_left(ext.second->media_block()->media_queries()->to_string());
1711
- std::string query_right(pHead->media_block()->media_queries()->to_string());
1712
- if (query_left == query_right) continue;
1713
- }
1714
-
1715
- // fail if one goes across media block boundaries
1716
- std::stringstream err;
1717
- std::string cwd(Sass::File::get_cwd());
1718
- ParserState pstate(ext.second->pstate());
1719
- std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));
1720
- err << "You may not @extend an outer selector from within @media.\n";
1721
- err << "You may only @extend selectors within the same directive.\n";
1722
- err << "From \"@extend " << ext.second->to_string() << "\"";
1723
- err << " on line " << pstate.line+1 << " of " << rel_path << "\n";
1724
- error(err.str(), selector->pstate(), eval->exp.traces);
1725
- }
1726
- if (entries.size() > 0) hasExtension = true;
1727
- }
1728
-
1729
- pIter = pIter->tail();
1730
- }
1731
-
1732
- return hasExtension;
1733
- }
1734
-
1735
-
1736
- /*
1737
- This is the equivalent of ruby's Sequence.do_extend.
1738
-
1739
- // TODO: I think I have some modified ruby code to put here. Check.
1740
- */
1741
- /*
1742
- ISSUES:
1743
- - check to automatically include combinators doesn't transfer over to libsass' data model where
1744
- the combinator and compound selector are one unit
1745
- next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
1746
- */
1747
- Node Extend::extendComplexSelector(Complex_Selector* selector, CompoundSelectorSet& seen, bool isReplace, bool isOriginal) {
1748
-
1749
- // check if we already extended this selector
1750
- // we can do this since subset_map is "static"
1751
- auto memoized = memoizeComplex.find(selector);
1752
- if (memoized != memoizeComplex.end()) {
1753
- return memoized->second;
1754
- }
1755
-
1756
- // convert the input selector to extend node format
1757
- Node complexSelector = complexSelectorToNode(selector);
1758
- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)
1759
-
1760
- // let CHOICES be an empty list of selector-lists
1761
- // create new collection to hold the results
1762
- Node choices = Node::createCollection();
1763
-
1764
- // for each compound selector COMPOUND in COMPLEX:
1765
- for (Node& sseqOrOp : *complexSelector.collection()) {
1766
-
1767
- DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp)
1768
-
1769
- // If it's not a selector (meaning it's a combinator), just include it automatically
1770
- // RUBY: next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
1771
- if (!sseqOrOp.isSelector()) {
1772
- // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node
1773
- // with one collection child. The collection child represents a Complex_Selector that is only a combinator.
1774
- Node outer = Node::createCollection();
1775
- Node inner = Node::createCollection();
1776
- outer.collection()->push_back(inner);
1777
- inner.collection()->push_back(sseqOrOp);
1778
- choices.collection()->push_back(outer);
1779
- continue;
1780
- }
1781
-
1782
- // verified now that node is a valid selector
1783
- Complex_Selector_Obj sseqSel = sseqOrOp.selector();
1784
- Compound_Selector_Obj sseqHead = sseqSel->head();
1785
-
1786
- // let EXTENDED be extend_compound(COMPOUND, SEEN)
1787
- // extend the compound selector against the given subset_map
1788
- // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)
1789
- Node extended = extendCompoundSelector(sseqHead, seen, isReplace); // slow(17%)!
1790
- if (sseqOrOp.got_line_feed) extended.got_line_feed = true;
1791
- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended)
1792
-
1793
- // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with a ruby
1794
- // Array instead of a Sequence due to the member mapping: choices = extended.map {|seq| seq.members}
1795
- // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?
1796
- if (isOriginal && !selector->has_placeholder()) {
1797
- ComplexSelectorSet srcset;
1798
- srcset.insert(selector);
1799
- sseqSel->addSources(srcset);
1800
- // DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector)
1801
- }
1802
-
1803
- bool isSuperselector = false;
1804
- // if no complex selector in EXTENDED is a superselector of COMPOUND:
1805
- for (Node& childNode : *extended.collection()) {
1806
- Complex_Selector_Obj pExtensionSelector = nodeToComplexSelector(childNode);
1807
- if (pExtensionSelector->is_superselector_of(sseqSel)) {
1808
- isSuperselector = true;
1809
- break;
1810
- }
1811
- }
1812
-
1813
- if (!isSuperselector) {
1814
- // add a complex selector composed only of COMPOUND to EXTENDED
1815
- if (sseqOrOp.got_line_feed) sseqSel->has_line_feed(sseqOrOp.got_line_feed);
1816
- extended.collection()->push_front(complexSelectorToNode(sseqSel));
1817
- }
1818
-
1819
- DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)
1820
-
1821
- // add EXTENDED to CHOICES
1822
- // Aggregate our current extensions
1823
- choices.collection()->push_back(extended);
1824
- }
1825
-
1826
-
1827
- DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << choices)
1828
-
1829
-
1830
-
1831
- // Ruby Equivalent: paths
1832
- Node paths = Sass::paths(choices);
1833
-
1834
- DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths)
1835
-
1836
- // let WEAVES be an empty list of selector lists
1837
- Node weaves = Node::createCollection();
1838
-
1839
- // for each list of complex selectors PATH in paths(CHOICES):
1840
- for (Node& path : *paths.collection()) {
1841
- // add weave(PATH) to WEAVES
1842
- Node weaved = weave(path); // slow(12%)!
1843
- weaved.got_line_feed = path.got_line_feed;
1844
- weaves.collection()->push_back(weaved);
1845
- }
1846
-
1847
- DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)
1848
-
1849
- // Ruby Equivalent: trim
1850
- Node trimmed(trim(weaves, isReplace)); // slow(19%)!
1851
-
1852
- DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed)
1853
-
1854
- // Ruby Equivalent: flatten
1855
- Node flattened(flatten(trimmed, 1));
1856
-
1857
- DEBUG_PRINTLN(EXTEND_COMPLEX, "FLATTENED: " << flattened)
1858
-
1859
- // memory results in a map table - since extending is very expensive
1860
- memoizeComplex.insert(std::pair<Complex_Selector_Obj, Node>(selector, flattened));
1861
-
1862
- // return trim(WEAVES)
1863
- return flattened;
1864
- }
1865
-
1866
-
1867
-
1868
- /*
1869
- This is the equivalent of ruby's CommaSequence.do_extend.
1870
- */
1871
- // We get a selector list with has something to extend and a subset_map with
1872
- // all extenders. Pick the ones that match our selectors in the list.
1873
- Selector_List* Extend::extendSelectorList(Selector_List_Obj pSelectorList, bool isReplace, bool& extendedSomething, CompoundSelectorSet& seen) {
1874
-
1875
- Selector_List_Obj pNewSelectors = SASS_MEMORY_NEW(Selector_List, pSelectorList->pstate(), pSelectorList->length());
1876
-
1877
- // check if we already extended this selector
1878
- // we can do this since subset_map is "static"
1879
- auto memoized = memoizeList.find(pSelectorList);
1880
- if (memoized != memoizeList.end()) {
1881
- extendedSomething = true;
1882
- return memoized->second;
1883
- }
1884
-
1885
- extendedSomething = false;
1886
- // process each comlplex selector in the selector list.
1887
- // Find the ones that can be extended by given subset_map.
1888
- for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {
1889
- Complex_Selector_Obj pSelector = (*pSelectorList)[index];
1890
-
1891
- // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that.
1892
- // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to
1893
- // run through the extend code (which does a data model transformation), check if there is anything to extend before doing
1894
- // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps
1895
- // when debugging).
1896
- if (!complexSelectorHasExtension(pSelector, seen)) {
1897
- pNewSelectors->append(pSelector);
1898
- continue;
1899
- }
1900
-
1901
- // complexSelectorHasExtension was true!
1902
- extendedSomething = true;
1903
-
1904
- // now do the actual extension of the complex selector
1905
- Node extendedSelectors = extendComplexSelector(pSelector, seen, isReplace, true);
1906
-
1907
- if (!pSelector->has_placeholder()) {
1908
- Node nSelector(complexSelectorToNode(pSelector));
1909
- if (!extendedSelectors.contains(nSelector)) {
1910
- pNewSelectors->append(pSelector);
1911
- continue;
1912
- }
1913
- }
1914
-
1915
- bool doReplace = isReplace;
1916
- for (Node& childNode : *extendedSelectors.collection()) {
1917
- // When it is a replace, skip the first one, unless there is only one
1918
- if(doReplace && extendedSelectors.collection()->size() > 1 ) {
1919
- doReplace = false;
1920
- continue;
1921
- }
1922
- pNewSelectors->append(nodeToComplexSelector(childNode));
1923
- }
1924
- }
1925
-
1926
- Remove_Placeholders remove_placeholders;
1927
- // it seems that we have to remove the place holders early here
1928
- // normally we do this as the very last step (compare to ruby sass)
1929
- pNewSelectors = remove_placeholders.remove_placeholders(pNewSelectors);
1930
-
1931
- // unwrap all wrapped selectors with inner lists
1932
- for (Complex_Selector_Obj cur : pNewSelectors->elements()) {
1933
- // process tails
1934
- while (cur) {
1935
- // process header
1936
- if (cur->head() && seen.find(cur->head()) == seen.end()) {
1937
- CompoundSelectorSet recseen(seen);
1938
- recseen.insert(cur->head());
1939
- // create a copy since we add multiple items if stuff get unwrapped
1940
- Compound_Selector_Obj cpy_head = SASS_MEMORY_NEW(Compound_Selector, cur->pstate());
1941
- for (Simple_Selector_Obj hs : *cur->head()) {
1942
- if (Wrapped_Selector_Obj ws = Cast<Wrapped_Selector>(hs)) {
1943
- ws->selector(SASS_MEMORY_CLONE(ws->selector()));
1944
- if (Selector_List_Obj sl = Cast<Selector_List>(ws->selector())) {
1945
- // special case for ruby ass
1946
- if (sl->empty()) {
1947
- // this seems inconsistent but it is how ruby sass seems to remove parentheses
1948
- cpy_head->append(SASS_MEMORY_NEW(Type_Selector, hs->pstate(), ws->name()));
1949
- }
1950
- // has wrapped not selectors
1951
- else if (ws->name() == ":not") {
1952
- // extend the inner list of wrapped selector
1953
- bool extended = false;
1954
- Selector_List_Obj ext_sl = extendSelectorList(sl, false, extended, recseen);
1955
- for (size_t i = 0; i < ext_sl->length(); i += 1) {
1956
- if (Complex_Selector_Obj ext_cs = ext_sl->at(i)) {
1957
- // create clones for wrapped selector and the inner list
1958
- Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);
1959
- Selector_List_Obj cpy_ws_sl = SASS_MEMORY_NEW(Selector_List, sl->pstate());
1960
- // remove parent selectors from inner selector
1961
- Compound_Selector_Obj ext_head;
1962
- if (ext_cs->first()) ext_head = ext_cs->first()->head();
1963
- if (ext_head && ext_head && ext_head->length() > 0) {
1964
- cpy_ws_sl->append(ext_cs->mutable_first());
1965
- }
1966
- // assign list to clone
1967
- cpy_ws->selector(cpy_ws_sl);
1968
- // append the clone
1969
- cpy_head->append(cpy_ws);
1970
- }
1971
- }
1972
- if (eval && extended) {
1973
- eval->exp.selector_stack.push_back(pNewSelectors);
1974
- cpy_head->perform(eval);
1975
- eval->exp.selector_stack.pop_back();
1976
- }
1977
- }
1978
- // has wrapped selectors
1979
- else {
1980
- Wrapped_Selector_Obj cpy_ws = SASS_MEMORY_COPY(ws);
1981
- Selector_List_Obj ext_sl = extendSelectorList(sl, recseen);
1982
- cpy_ws->selector(ext_sl);
1983
- cpy_head->append(cpy_ws);
1984
- }
1985
- } else {
1986
- cpy_head->append(hs);
1987
- }
1988
- } else {
1989
- cpy_head->append(hs);
1990
- }
1991
- }
1992
- // replace header
1993
- cur->head(cpy_head);
1994
- }
1995
- // process tail
1996
- cur = cur->tail();
1997
- }
1998
- }
1999
-
2000
- // memory results in a map table - since extending is very expensive
2001
- memoizeList.insert(std::pair<Selector_List_Obj, Selector_List_Obj>(pSelectorList, pNewSelectors));
2002
-
2003
- return pNewSelectors.detach();
2004
-
2005
- }
2006
-
2007
-
2008
- bool shouldExtendBlock(Block_Obj b) {
2009
-
2010
- // If a block is empty, there's no reason to extend it since any rules placed on this block
2011
- // won't have any output. The main benefit of this is for structures like:
2012
- //
2013
- // .a {
2014
- // .b {
2015
- // x: y;
2016
- // }
2017
- // }
2018
- //
2019
- // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b).
2020
- // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since
2021
- // there are no child statements. However .a .b should have extensions applied.
2022
-
2023
- for (size_t i = 0, L = b->length(); i < L; ++i) {
2024
- Statement_Obj stm = b->at(i);
2025
-
2026
- if (Cast<Ruleset>(stm)) {
2027
- // Do nothing. This doesn't count as a statement that causes extension since we'll
2028
- // iterate over this rule set in a future visit and try to extend it.
2029
- }
2030
- else {
2031
- return true;
2032
- }
2033
- }
2034
-
2035
- return false;
2036
-
2037
- }
2038
-
2039
-
2040
- // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.
2041
- // Every Ruleset in the whole tree is calling this function. We decide if there
2042
- // was is @extend that matches our selector. If we find one, we will go further
2043
- // and call the extend magic for our selector. The subset_map contains all blocks
2044
- // where @extend was found. Pick the ones that match our selector!
2045
- void Extend::extendObjectWithSelectorAndBlock(Ruleset* pObject) {
2046
-
2047
- DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << Cast<Selector_List>(pObject->selector())->to_string())
2048
-
2049
- // Ruby sass seems to filter nodes that don't have any content well before we get here.
2050
- // I'm not sure the repercussions of doing so, so for now, let's just not extend things
2051
- // that won't be output later. Profiling shows this may us 0.2% or so.
2052
- if (!shouldExtendBlock(pObject->block())) {
2053
- DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT")
2054
- return;
2055
- }
2056
-
2057
- bool extendedSomething = false;
2058
-
2059
- CompoundSelectorSet seen;
2060
- Selector_List_Obj pNewSelectorList = extendSelectorList(pObject->selector(), false, extendedSomething, seen);
2061
-
2062
- if (extendedSomething && pNewSelectorList) {
2063
- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << pObject->selector()->to_string())
2064
- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string())
2065
- pNewSelectorList->remove_parent_selectors();
2066
- pObject->selector(pNewSelectorList);
2067
- } else {
2068
- DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")
2069
- }
2070
- }
2071
-
2072
- Extend::Extend(Subset_Map& ssm)
2073
- : subset_map(ssm), eval(NULL)
2074
- { }
2075
-
2076
- void Extend::setEval(Eval& e) {
2077
- eval = &e;
2078
- }
2079
-
2080
- void Extend::operator()(Block* b)
2081
- {
2082
- for (size_t i = 0, L = b->length(); i < L; ++i) {
2083
- Statement_Obj stm = b->at(i);
2084
- stm->perform(this);
2085
- }
2086
- // do final check if everything was extended
2087
- // we set `extended` flag on extended selectors
2088
- if (b->is_root()) {
2089
- // debug_subset_map(subset_map);
2090
- for(auto const &it : subset_map.values()) {
2091
- const Complex_Selector* sel = nullptr;
2092
- const Compound_Selector* ext = nullptr;
2093
- if (it.first) sel = it.first->first();
2094
- if (it.second) ext = it.second;
2095
- if (ext && (ext->extended() || ext->is_optional())) continue;
2096
- std::string str_sel(sel ? sel->to_string({ NESTED, 5 }) : "NULL");
2097
- std::string str_ext(ext ? ext->to_string({ NESTED, 5 }) : "NULL");
2098
- // debug_ast(sel, "sel: ");
2099
- // debug_ast(ext, "ext: ");
2100
- error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"
2101
- "The selector \"" + str_ext + "\" was not found.\n"
2102
- "Use \"@extend " + str_ext + " !optional\" if the"
2103
- " extend should be able to fail.", (ext ? ext->pstate() : NULL), eval->exp.traces);
2104
- }
2105
- }
2106
-
2107
- }
2108
-
2109
- void Extend::operator()(Ruleset* pRuleset)
2110
- {
2111
- extendObjectWithSelectorAndBlock( pRuleset );
2112
- pRuleset->block()->perform(this);
2113
- }
2114
-
2115
- void Extend::operator()(Supports_Block* pFeatureBlock)
2116
- {
2117
- pFeatureBlock->block()->perform(this);
2118
- }
2119
-
2120
- void Extend::operator()(Media_Block* pMediaBlock)
2121
- {
2122
- pMediaBlock->block()->perform(this);
2123
- }
2124
-
2125
- void Extend::operator()(Directive* a)
2126
- {
2127
- // Selector_List* ls = Cast<Selector_List>(a->selector());
2128
- // selector_stack.push_back(ls);
2129
- if (a->block()) a->block()->perform(this);
2130
- // exp.selector_stack.pop_back();
2131
- }
2132
- }