sassc 2.0.0 → 2.4.0

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