sass 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING +1 -1
  3. data/MIT-LICENSE +2 -2
  4. data/README.md +29 -17
  5. data/Rakefile +43 -9
  6. data/VERSION +1 -1
  7. data/VERSION_DATE +1 -0
  8. data/VERSION_NAME +1 -1
  9. data/bin/sass +6 -1
  10. data/bin/sass-convert +6 -1
  11. data/bin/scss +6 -1
  12. data/ext/mkrf_conf.rb +27 -0
  13. data/lib/sass/cache_stores/base.rb +7 -3
  14. data/lib/sass/cache_stores/chain.rb +3 -2
  15. data/lib/sass/cache_stores/filesystem.rb +5 -7
  16. data/lib/sass/cache_stores/memory.rb +1 -1
  17. data/lib/sass/cache_stores/null.rb +2 -2
  18. data/lib/sass/callbacks.rb +2 -1
  19. data/lib/sass/css.rb +168 -53
  20. data/lib/sass/engine.rb +502 -174
  21. data/lib/sass/environment.rb +151 -111
  22. data/lib/sass/error.rb +7 -7
  23. data/lib/sass/exec.rb +176 -60
  24. data/lib/sass/features.rb +40 -0
  25. data/lib/sass/importers/base.rb +46 -7
  26. data/lib/sass/importers/deprecated_path.rb +51 -0
  27. data/lib/sass/importers/filesystem.rb +113 -30
  28. data/lib/sass/importers.rb +1 -0
  29. data/lib/sass/logger/base.rb +30 -0
  30. data/lib/sass/logger/log_level.rb +45 -0
  31. data/lib/sass/logger.rb +12 -0
  32. data/lib/sass/media.rb +213 -0
  33. data/lib/sass/plugin/compiler.rb +194 -104
  34. data/lib/sass/plugin/configuration.rb +18 -25
  35. data/lib/sass/plugin/merb.rb +1 -1
  36. data/lib/sass/plugin/staleness_checker.rb +37 -11
  37. data/lib/sass/plugin.rb +10 -13
  38. data/lib/sass/railtie.rb +2 -1
  39. data/lib/sass/repl.rb +5 -6
  40. data/lib/sass/script/css_lexer.rb +8 -4
  41. data/lib/sass/script/css_parser.rb +5 -2
  42. data/lib/sass/script/functions.rb +1547 -618
  43. data/lib/sass/script/lexer.rb +122 -72
  44. data/lib/sass/script/parser.rb +304 -135
  45. data/lib/sass/script/tree/funcall.rb +306 -0
  46. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +43 -13
  47. data/lib/sass/script/tree/list_literal.rb +77 -0
  48. data/lib/sass/script/tree/literal.rb +45 -0
  49. data/lib/sass/script/tree/map_literal.rb +64 -0
  50. data/lib/sass/script/{node.rb → tree/node.rb} +30 -12
  51. data/lib/sass/script/{operation.rb → tree/operation.rb} +33 -21
  52. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +14 -4
  53. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +21 -9
  54. data/lib/sass/script/tree/variable.rb +57 -0
  55. data/lib/sass/script/tree.rb +15 -0
  56. data/lib/sass/script/value/arg_list.rb +36 -0
  57. data/lib/sass/script/value/base.rb +238 -0
  58. data/lib/sass/script/value/bool.rb +40 -0
  59. data/lib/sass/script/{color.rb → value/color.rb} +256 -74
  60. data/lib/sass/script/value/deprecated_false.rb +55 -0
  61. data/lib/sass/script/value/helpers.rb +155 -0
  62. data/lib/sass/script/value/list.rb +128 -0
  63. data/lib/sass/script/value/map.rb +70 -0
  64. data/lib/sass/script/value/null.rb +49 -0
  65. data/lib/sass/script/{number.rb → value/number.rb} +115 -62
  66. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  67. data/lib/sass/script/value.rb +12 -0
  68. data/lib/sass/script.rb +35 -9
  69. data/lib/sass/scss/css_parser.rb +2 -12
  70. data/lib/sass/scss/parser.rb +657 -230
  71. data/lib/sass/scss/rx.rb +17 -12
  72. data/lib/sass/scss/static_parser.rb +37 -6
  73. data/lib/sass/scss.rb +0 -1
  74. data/lib/sass/selector/abstract_sequence.rb +35 -3
  75. data/lib/sass/selector/comma_sequence.rb +29 -14
  76. data/lib/sass/selector/sequence.rb +371 -74
  77. data/lib/sass/selector/simple.rb +28 -13
  78. data/lib/sass/selector/simple_sequence.rb +163 -36
  79. data/lib/sass/selector.rb +138 -36
  80. data/lib/sass/shared.rb +3 -5
  81. data/lib/sass/source/map.rb +196 -0
  82. data/lib/sass/source/position.rb +39 -0
  83. data/lib/sass/source/range.rb +41 -0
  84. data/lib/sass/stack.rb +126 -0
  85. data/lib/sass/supports.rb +228 -0
  86. data/lib/sass/tree/at_root_node.rb +82 -0
  87. data/lib/sass/tree/comment_node.rb +34 -29
  88. data/lib/sass/tree/content_node.rb +9 -0
  89. data/lib/sass/tree/css_import_node.rb +60 -0
  90. data/lib/sass/tree/debug_node.rb +3 -3
  91. data/lib/sass/tree/directive_node.rb +33 -3
  92. data/lib/sass/tree/each_node.rb +9 -9
  93. data/lib/sass/tree/extend_node.rb +20 -6
  94. data/lib/sass/tree/for_node.rb +6 -6
  95. data/lib/sass/tree/function_node.rb +12 -4
  96. data/lib/sass/tree/if_node.rb +2 -15
  97. data/lib/sass/tree/import_node.rb +11 -5
  98. data/lib/sass/tree/media_node.rb +27 -11
  99. data/lib/sass/tree/mixin_def_node.rb +15 -4
  100. data/lib/sass/tree/mixin_node.rb +27 -7
  101. data/lib/sass/tree/node.rb +69 -35
  102. data/lib/sass/tree/prop_node.rb +47 -31
  103. data/lib/sass/tree/return_node.rb +4 -3
  104. data/lib/sass/tree/root_node.rb +20 -4
  105. data/lib/sass/tree/rule_node.rb +37 -26
  106. data/lib/sass/tree/supports_node.rb +38 -0
  107. data/lib/sass/tree/trace_node.rb +33 -0
  108. data/lib/sass/tree/variable_node.rb +10 -4
  109. data/lib/sass/tree/visitors/base.rb +5 -8
  110. data/lib/sass/tree/visitors/check_nesting.rb +67 -52
  111. data/lib/sass/tree/visitors/convert.rb +134 -53
  112. data/lib/sass/tree/visitors/cssize.rb +245 -51
  113. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  114. data/lib/sass/tree/visitors/extend.rb +68 -0
  115. data/lib/sass/tree/visitors/perform.rb +331 -105
  116. data/lib/sass/tree/visitors/set_options.rb +125 -0
  117. data/lib/sass/tree/visitors/to_css.rb +259 -95
  118. data/lib/sass/tree/warn_node.rb +3 -3
  119. data/lib/sass/tree/while_node.rb +3 -3
  120. data/lib/sass/util/cross_platform_random.rb +19 -0
  121. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  122. data/lib/sass/util/normalized_map.rb +130 -0
  123. data/lib/sass/util/ordered_hash.rb +192 -0
  124. data/lib/sass/util/subset_map.rb +11 -2
  125. data/lib/sass/util/test.rb +9 -0
  126. data/lib/sass/util.rb +565 -39
  127. data/lib/sass/version.rb +27 -15
  128. data/lib/sass.rb +39 -4
  129. data/test/sass/cache_test.rb +15 -0
  130. data/test/sass/compiler_test.rb +223 -0
  131. data/test/sass/conversion_test.rb +901 -107
  132. data/test/sass/css2sass_test.rb +94 -0
  133. data/test/sass/engine_test.rb +1059 -164
  134. data/test/sass/exec_test.rb +86 -0
  135. data/test/sass/extend_test.rb +933 -837
  136. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  137. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  138. data/test/sass/functions_test.rb +995 -136
  139. data/test/sass/importer_test.rb +338 -18
  140. data/test/sass/logger_test.rb +58 -0
  141. data/test/sass/more_results/more_import.css +2 -2
  142. data/test/sass/plugin_test.rb +114 -30
  143. data/test/sass/results/cached_import_option.css +3 -0
  144. data/test/sass/results/filename_fn.css +3 -0
  145. data/test/sass/results/import.css +2 -2
  146. data/test/sass/results/import_charset.css +1 -0
  147. data/test/sass/results/import_charset_1_8.css +1 -0
  148. data/test/sass/results/import_charset_ibm866.css +1 -0
  149. data/test/sass/results/import_content.css +1 -0
  150. data/test/sass/results/script.css +1 -1
  151. data/test/sass/results/scss_import.css +2 -2
  152. data/test/sass/results/units.css +2 -2
  153. data/test/sass/script_conversion_test.rb +43 -1
  154. data/test/sass/script_test.rb +380 -36
  155. data/test/sass/scss/css_test.rb +257 -75
  156. data/test/sass/scss/scss_test.rb +2322 -110
  157. data/test/sass/source_map_test.rb +887 -0
  158. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  159. data/test/sass/templates/_double_import_loop2.sass +1 -0
  160. data/test/sass/templates/_filename_fn_import.scss +11 -0
  161. data/test/sass/templates/_imported_content.sass +3 -0
  162. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  163. data/test/sass/templates/bork5.sass +3 -0
  164. data/test/sass/templates/cached_import_option.scss +3 -0
  165. data/test/sass/templates/double_import_loop1.sass +1 -0
  166. data/test/sass/templates/filename_fn.scss +18 -0
  167. data/test/sass/templates/import_charset.sass +2 -0
  168. data/test/sass/templates/import_charset_1_8.sass +2 -0
  169. data/test/sass/templates/import_charset_ibm866.sass +2 -0
  170. data/test/sass/templates/import_content.sass +4 -0
  171. data/test/sass/templates/same_name_different_ext.sass +2 -0
  172. data/test/sass/templates/same_name_different_ext.scss +1 -0
  173. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  174. data/test/sass/templates/single_import_loop.sass +1 -0
  175. data/test/sass/templates/subdir/import_up1.scss +1 -0
  176. data/test/sass/templates/subdir/import_up2.scss +1 -0
  177. data/test/sass/test_helper.rb +1 -1
  178. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  179. data/test/sass/util/normalized_map_test.rb +51 -0
  180. data/test/sass/util_test.rb +183 -0
  181. data/test/sass/value_helpers_test.rb +181 -0
  182. data/test/test_helper.rb +45 -5
  183. data/vendor/listen/CHANGELOG.md +228 -0
  184. data/vendor/listen/CONTRIBUTING.md +38 -0
  185. data/vendor/listen/Gemfile +30 -0
  186. data/vendor/listen/Guardfile +8 -0
  187. data/vendor/{fssm → listen}/LICENSE +1 -1
  188. data/vendor/listen/README.md +315 -0
  189. data/vendor/listen/Rakefile +47 -0
  190. data/vendor/listen/Vagrantfile +96 -0
  191. data/vendor/listen/lib/listen/adapter.rb +214 -0
  192. data/vendor/listen/lib/listen/adapters/bsd.rb +112 -0
  193. data/vendor/listen/lib/listen/adapters/darwin.rb +85 -0
  194. data/vendor/listen/lib/listen/adapters/linux.rb +113 -0
  195. data/vendor/listen/lib/listen/adapters/polling.rb +67 -0
  196. data/vendor/listen/lib/listen/adapters/windows.rb +87 -0
  197. data/vendor/listen/lib/listen/dependency_manager.rb +126 -0
  198. data/vendor/listen/lib/listen/directory_record.rb +371 -0
  199. data/vendor/listen/lib/listen/listener.rb +225 -0
  200. data/vendor/listen/lib/listen/multi_listener.rb +143 -0
  201. data/vendor/listen/lib/listen/turnstile.rb +28 -0
  202. data/vendor/listen/lib/listen/version.rb +3 -0
  203. data/vendor/listen/lib/listen.rb +40 -0
  204. data/vendor/listen/listen.gemspec +22 -0
  205. data/vendor/listen/spec/listen/adapter_spec.rb +183 -0
  206. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +36 -0
  207. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +37 -0
  208. data/vendor/listen/spec/listen/adapters/linux_spec.rb +47 -0
  209. data/vendor/listen/spec/listen/adapters/polling_spec.rb +68 -0
  210. data/vendor/listen/spec/listen/adapters/windows_spec.rb +30 -0
  211. data/vendor/listen/spec/listen/dependency_manager_spec.rb +107 -0
  212. data/vendor/listen/spec/listen/directory_record_spec.rb +1225 -0
  213. data/vendor/listen/spec/listen/listener_spec.rb +169 -0
  214. data/vendor/listen/spec/listen/multi_listener_spec.rb +174 -0
  215. data/vendor/listen/spec/listen/turnstile_spec.rb +56 -0
  216. data/vendor/listen/spec/listen_spec.rb +73 -0
  217. data/vendor/listen/spec/spec_helper.rb +21 -0
  218. data/vendor/listen/spec/support/adapter_helper.rb +629 -0
  219. data/vendor/listen/spec/support/directory_record_helper.rb +55 -0
  220. data/vendor/listen/spec/support/fixtures_helper.rb +29 -0
  221. data/vendor/listen/spec/support/listeners_helper.rb +156 -0
  222. data/vendor/listen/spec/support/platform_helper.rb +15 -0
  223. metadata +344 -271
  224. data/lib/sass/less.rb +0 -382
  225. data/lib/sass/script/bool.rb +0 -18
  226. data/lib/sass/script/funcall.rb +0 -162
  227. data/lib/sass/script/list.rb +0 -76
  228. data/lib/sass/script/literal.rb +0 -245
  229. data/lib/sass/script/variable.rb +0 -54
  230. data/lib/sass/scss/sass_parser.rb +0 -11
  231. data/test/sass/less_conversion_test.rb +0 -653
  232. data/vendor/fssm/README.markdown +0 -55
  233. data/vendor/fssm/Rakefile +0 -59
  234. data/vendor/fssm/VERSION.yml +0 -5
  235. data/vendor/fssm/example.rb +0 -9
  236. data/vendor/fssm/fssm.gemspec +0 -77
  237. data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
  238. data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
  239. data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
  240. data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
  241. data/vendor/fssm/lib/fssm/monitor.rb +0 -26
  242. data/vendor/fssm/lib/fssm/path.rb +0 -91
  243. data/vendor/fssm/lib/fssm/pathname.rb +0 -502
  244. data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
  245. data/vendor/fssm/lib/fssm/state/file.rb +0 -24
  246. data/vendor/fssm/lib/fssm/support.rb +0 -63
  247. data/vendor/fssm/lib/fssm/tree.rb +0 -176
  248. data/vendor/fssm/lib/fssm.rb +0 -33
  249. data/vendor/fssm/profile/prof-cache.rb +0 -40
  250. data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
  251. data/vendor/fssm/profile/prof-pathname.rb +0 -68
  252. data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
  253. data/vendor/fssm/profile/prof.html +0 -2379
  254. data/vendor/fssm/spec/path_spec.rb +0 -75
  255. data/vendor/fssm/spec/root/duck/quack.txt +0 -0
  256. data/vendor/fssm/spec/root/file.css +0 -0
  257. data/vendor/fssm/spec/root/file.rb +0 -0
  258. data/vendor/fssm/spec/root/file.yml +0 -0
  259. data/vendor/fssm/spec/root/moo/cow.txt +0 -0
  260. data/vendor/fssm/spec/spec_helper.rb +0 -14
@@ -0,0 +1,196 @@
1
+ module Sass::Source
2
+ class Map
3
+ # A mapping from one source range to another. Indicates that `input` was
4
+ # compiled to `output`.
5
+ #
6
+ # @!attribute input
7
+ # @return [Sass::Source::Range] The source range in the input document.
8
+ #
9
+ # @!attribute output
10
+ # @return [Sass::Source::Range] The source range in the output document.
11
+ class Mapping < Struct.new(:input, :output)
12
+ # @return [String] A string representation of the mapping.
13
+ def inspect
14
+ "#{input.inspect} => #{output.inspect}"
15
+ end
16
+ end
17
+
18
+ # The mapping data ordered by the location in the target.
19
+ #
20
+ # @return [Array<Mapping>]
21
+ attr_reader :data
22
+
23
+ def initialize
24
+ @data = []
25
+ end
26
+
27
+ # Adds a new mapping from one source range to another. Multiple invocations
28
+ # of this method should have each `output` range come after all previous ranges.
29
+ #
30
+ # @param input [Sass::Source::Range]
31
+ # The source range in the input document.
32
+ # @param output [Sass::Source::Range]
33
+ # The source range in the output document.
34
+ def add(input, output)
35
+ @data.push(Mapping.new(input, output))
36
+ end
37
+
38
+ # Shifts all output source ranges forward one or more lines.
39
+ #
40
+ # @param delta [Fixnum] The number of lines to shift the ranges forward.
41
+ def shift_output_lines(delta)
42
+ return if delta == 0
43
+ @data.each do |m|
44
+ m.output.start_pos.line += delta
45
+ m.output.end_pos.line += delta
46
+ end
47
+ end
48
+
49
+ # Shifts any output source ranges that lie on the first line forward one or
50
+ # more characters on that line.
51
+ #
52
+ # @param delta [Fixnum] The number of characters to shift the ranges
53
+ # forward.
54
+ def shift_output_offsets(delta)
55
+ return if delta == 0
56
+ @data.each do |m|
57
+ break if m.output.start_pos.line > 1
58
+ m.output.start_pos.offset += delta
59
+ m.output.end_pos.offset += delta if m.output.end_pos.line > 1
60
+ end
61
+ end
62
+
63
+ # Returns the standard JSON representation of the source map.
64
+ #
65
+ # If the `:css_uri` option isn't specified, the `:css_path` and
66
+ # `:sourcemap_path` options must both be specified. Any options may also be
67
+ # specified alongside the `:css_uri` option. If `:css_uri` isn't specified,
68
+ # it will be inferred from `:css_path` and `:sourcemap_path` using the
69
+ # assumption that the local file system has the same layout as the server.
70
+ #
71
+ # If any source stylesheets use the default filesystem importer, sourcemap
72
+ # generation will fail unless the `:sourcemap_path` option is specified.
73
+ # The layout of the local file system is assumed to be the same as the
74
+ # layout of the server for the purposes of linking to source stylesheets
75
+ # that use the filesystem importer.
76
+ #
77
+ # Regardless of which options are passed to this method, source stylesheets
78
+ # that are imported using a non-default importer will only be linked to in
79
+ # the source map if their importers implement
80
+ # \{Sass::Importers::Base#public\_url\}.
81
+ #
82
+ # @option options :css_uri [String]
83
+ # The publicly-visible URI of the CSS output file.
84
+ # @option options :css_path [String]
85
+ # The local path of the CSS output file.
86
+ # @option options :sourcemap_path [String]
87
+ # The (eventual) local path of the sourcemap file.
88
+ # @return [String] The JSON string.
89
+ # @raise [ArgumentError] If neither `:css_uri` nor `:css_path` and
90
+ # `:sourcemap_path` are specified.
91
+ # @comment
92
+ # rubocop:disable MethodLength
93
+ def to_json(options)
94
+ css_uri, css_path, sourcemap_path =
95
+ options[:css_uri], options[:css_path], options[:sourcemap_path]
96
+ unless css_uri || (css_path && sourcemap_path)
97
+ raise ArgumentError.new("Sass::Source::Map#to_json requires either " \
98
+ "the :css_uri option or both the :css_path and :soucemap_path options.")
99
+ end
100
+ css_path &&= Pathname.pwd.join(Sass::Util.pathname(css_path)).cleanpath
101
+ sourcemap_path &&= Pathname.pwd.join(Sass::Util.pathname(sourcemap_path)).cleanpath
102
+ css_uri ||= css_path.relative_path_from(sourcemap_path.dirname).to_s
103
+
104
+ result = "{\n"
105
+ write_json_field(result, "version", 3, true)
106
+
107
+ source_uri_to_id = {}
108
+ id_to_source_uri = {}
109
+ next_source_id = 0
110
+ line_data = []
111
+ segment_data_for_line = []
112
+
113
+ # These track data necessary for the delta coding.
114
+ previous_target_line = nil
115
+ previous_target_offset = 1
116
+ previous_source_line = 1
117
+ previous_source_offset = 1
118
+ previous_source_id = 0
119
+
120
+ @data.each do |m|
121
+ file, importer = m.input.file, m.input.importer
122
+ source_uri = importer &&
123
+ importer.public_url(file, sourcemap_path && sourcemap_path.dirname.to_s)
124
+ next unless source_uri
125
+
126
+ current_source_id = source_uri_to_id[source_uri]
127
+ unless current_source_id
128
+ current_source_id = next_source_id
129
+ next_source_id += 1
130
+
131
+ source_uri_to_id[source_uri] = current_source_id
132
+ id_to_source_uri[current_source_id] = source_uri
133
+ end
134
+
135
+ [
136
+ [m.input.start_pos, m.output.start_pos],
137
+ [m.input.end_pos, m.output.end_pos]
138
+ ].each do |source_pos, target_pos|
139
+ if previous_target_line != target_pos.line
140
+ line_data.push(segment_data_for_line.join(",")) unless segment_data_for_line.empty?
141
+ (target_pos.line - 1 - (previous_target_line || 0)).times {line_data.push("")}
142
+ previous_target_line = target_pos.line
143
+ previous_target_offset = 1
144
+ segment_data_for_line = []
145
+ end
146
+
147
+ # `segment` is a data chunk for a single position mapping.
148
+ segment = ""
149
+
150
+ # Field 1: zero-based starting offset.
151
+ segment << Sass::Util.encode_vlq(target_pos.offset - previous_target_offset)
152
+ previous_target_offset = target_pos.offset
153
+
154
+ # Field 2: zero-based index into the "sources" list.
155
+ segment << Sass::Util.encode_vlq(current_source_id - previous_source_id)
156
+ previous_source_id = current_source_id
157
+
158
+ # Field 3: zero-based starting line in the original source.
159
+ segment << Sass::Util.encode_vlq(source_pos.line - previous_source_line)
160
+ previous_source_line = source_pos.line
161
+
162
+ # Field 4: zero-based starting offset in the original source.
163
+ segment << Sass::Util.encode_vlq(source_pos.offset - previous_source_offset)
164
+ previous_source_offset = source_pos.offset
165
+
166
+ segment_data_for_line.push(segment)
167
+
168
+ previous_target_line = target_pos.line
169
+ end
170
+ end
171
+ line_data.push(segment_data_for_line.join(","))
172
+ write_json_field(result, "mappings", line_data.join(";"))
173
+
174
+ source_names = []
175
+ (0...next_source_id).each {|id| source_names.push(id_to_source_uri[id].to_s)}
176
+ write_json_field(result, "sources", source_names)
177
+ write_json_field(result, "names", [])
178
+ write_json_field(result, "file", css_uri)
179
+
180
+ result << "\n}"
181
+ result
182
+ end
183
+ # @comment
184
+ # rubocop:enable MethodLength
185
+
186
+ private
187
+
188
+ def write_json_field(out, name, value, is_first = false)
189
+ out << (is_first ? "" : ",\n") <<
190
+ "\"" <<
191
+ Sass::Util.json_escape_string(name) <<
192
+ "\": " <<
193
+ Sass::Util.json_value_of(value)
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,39 @@
1
+ module Sass::Source
2
+ class Position
3
+ # The one-based line of the document associated with the position.
4
+ #
5
+ # @return [Fixnum]
6
+ attr_accessor :line
7
+
8
+ # The one-based offset in the line of the document associated with the
9
+ # position.
10
+ #
11
+ # @return [Fixnum]
12
+ attr_accessor :offset
13
+
14
+ # @param line [Fixnum] The source line
15
+ # @param offset [Fixnum] The source offset
16
+ def initialize(line, offset)
17
+ @line = line
18
+ @offset = offset
19
+ end
20
+
21
+ # @return [String] A string representation of the source position.
22
+ def inspect
23
+ "#{line.inspect}:#{offset.inspect}"
24
+ end
25
+
26
+ # @param str [String] The string to move through.
27
+ # @return [Position] The source position after proceeding forward through
28
+ # `str`.
29
+ def after(str)
30
+ newlines = str.count("\n")
31
+ Position.new(line + newlines,
32
+ if newlines == 0
33
+ offset + str.length
34
+ else
35
+ str.length - str.rindex("\n") - 1
36
+ end)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ module Sass::Source
2
+ class Range
3
+ # The starting position of the range in the document (inclusive).
4
+ #
5
+ # @return [Sass::Source::Position]
6
+ attr_accessor :start_pos
7
+
8
+ # The ending position of the range in the document (exclusive).
9
+ #
10
+ # @return [Sass::Source::Position]
11
+ attr_accessor :end_pos
12
+
13
+ # The file in which this source range appears. This can be nil if the file
14
+ # is unknown or not yet generated.
15
+ #
16
+ # @return [String]
17
+ attr_accessor :file
18
+
19
+ # The importer that imported the file in which this source range appears.
20
+ # This is nil for target ranges.
21
+ #
22
+ # @return [Sass::Importers::Base]
23
+ attr_accessor :importer
24
+
25
+ # @param start_pos [Sass::Source::Position] See \{#start_pos}
26
+ # @param end_pos [Sass::Source::Position] See \{#end_pos}
27
+ # @param file [String] See \{#file}
28
+ # @param importer [Sass::Importers::Base] See \{#importer}
29
+ def initialize(start_pos, end_pos, file, importer = nil)
30
+ @start_pos = start_pos
31
+ @end_pos = end_pos
32
+ @file = file
33
+ @importer = importer
34
+ end
35
+
36
+ # @return [String] A string representation of the source range.
37
+ def inspect
38
+ "(#{start_pos.inspect} to #{end_pos.inspect}#{" in #{@file}" if @file})"
39
+ end
40
+ end
41
+ end
data/lib/sass/stack.rb ADDED
@@ -0,0 +1,126 @@
1
+ module Sass
2
+ # A class representing the stack when compiling a Sass file.
3
+ class Stack
4
+ # TODO: use this to generate stack information for Sass::SyntaxErrors.
5
+
6
+ # A single stack frame.
7
+ class Frame
8
+ # The filename of the file in which this stack frame was created.
9
+ #
10
+ # @return [String]
11
+ attr_reader :filename
12
+
13
+ # The line number on which this stack frame was created.
14
+ #
15
+ # @return [String]
16
+ attr_reader :line
17
+
18
+ # The type of this stack frame. This can be `:import`, `:mixin`, or
19
+ # `:base`.
20
+ #
21
+ # `:base` indicates that this is the bottom-most frame, meaning that it
22
+ # represents a single line of code rather than a nested context. The stack
23
+ # will only ever have one base frame, and it will always be the most
24
+ # deeply-nested frame.
25
+ #
26
+ # @return [Symbol?]
27
+ attr_reader :type
28
+
29
+ # The name of the stack frame. For mixin frames, this is the mixin name;
30
+ # otherwise, it's `nil`.
31
+ #
32
+ # @return [String?]
33
+ attr_reader :name
34
+
35
+ def initialize(filename, line, type, name = nil)
36
+ @filename = filename
37
+ @line = line
38
+ @type = type
39
+ @name = name
40
+ end
41
+
42
+ # Whether this frame represents an import.
43
+ #
44
+ # @return [Boolean]
45
+ def is_import?
46
+ type == :import
47
+ end
48
+
49
+ # Whether this frame represents a mixin.
50
+ #
51
+ # @return [Boolean]
52
+ def is_mixin?
53
+ type == :mixin
54
+ end
55
+
56
+ # Whether this is the base frame.
57
+ #
58
+ # @return [Boolean]
59
+ def is_base?
60
+ type == :base
61
+ end
62
+ end
63
+
64
+ # The stack frames. The last frame is the most deeply-nested.
65
+ #
66
+ # @return [Array<Frame>]
67
+ attr_reader :frames
68
+
69
+ def initialize
70
+ @frames = []
71
+ end
72
+
73
+ # Pushes a base frame onto the stack.
74
+ #
75
+ # @param filename [String] See \{Frame#filename}.
76
+ # @param line [String] See \{Frame#line}.
77
+ # @yield [] A block in which the new frame is on the stack.
78
+ def with_base(filename, line)
79
+ with_frame(filename, line, :base) {yield}
80
+ end
81
+
82
+ # Pushes an import frame onto the stack.
83
+ #
84
+ # @param filename [String] See \{Frame#filename}.
85
+ # @param line [String] See \{Frame#line}.
86
+ # @yield [] A block in which the new frame is on the stack.
87
+ def with_import(filename, line)
88
+ with_frame(filename, line, :import) {yield}
89
+ end
90
+
91
+ # Pushes a mixin frame onto the stack.
92
+ #
93
+ # @param filename [String] See \{Frame#filename}.
94
+ # @param line [String] See \{Frame#line}.
95
+ # @param name [String] See \{Frame#name}.
96
+ # @yield [] A block in which the new frame is on the stack.
97
+ def with_mixin(filename, line, name)
98
+ with_frame(filename, line, :mixin, name) {yield}
99
+ end
100
+
101
+ def to_s
102
+ Sass::Util.enum_with_index(Sass::Util.enum_cons(frames.reverse + [nil], 2)).
103
+ map do |(frame, caller), i|
104
+ "#{i == 0 ? "on" : "from"} line #{frame.line}" +
105
+ " of #{frame.filename || "an unknown file"}" +
106
+ (caller && caller.name ? ", in `#{caller.name}'" : "")
107
+ end.join("\n")
108
+ end
109
+
110
+ def deep_copy
111
+ stack = Stack.new
112
+ stack.frames.replace frames
113
+ stack
114
+ end
115
+
116
+ private
117
+
118
+ def with_frame(filename, line, type, name = nil)
119
+ @frames.pop if @frames.last && @frames.last.type == :base
120
+ @frames.push(Frame.new(filename, line, type, name))
121
+ yield
122
+ ensure
123
+ @frames.pop unless type == :base && @frames.last && @frames.last.type != :base
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,228 @@
1
+ # A namespace for the `@supports` condition parse tree.
2
+ module Sass::Supports
3
+ # The abstract superclass of all Supports conditions.
4
+ class Condition
5
+ # Runs the SassScript in the supports condition.
6
+ #
7
+ # @param environment [Sass::Environment] The environment in which to run the script.
8
+ def perform(environment); Sass::Util.abstract(self); end
9
+
10
+ # Returns the CSS for this condition.
11
+ #
12
+ # @return [String]
13
+ def to_css; Sass::Util.abstract(self); end
14
+
15
+ # Returns the Sass/CSS code for this condition.
16
+ #
17
+ # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
18
+ # @return [String]
19
+ def to_src(options); Sass::Util.abstract(self); end
20
+
21
+ # Returns a deep copy of this condition and all its children.
22
+ #
23
+ # @return [Condition]
24
+ def deep_copy; Sass::Util.abstract(self); end
25
+
26
+ # Sets the options hash for the script nodes in the supports condition.
27
+ #
28
+ # @param options [{Symbol => Object}] The options has to set.
29
+ def options=(options); Sass::Util.abstract(self); end
30
+ end
31
+
32
+ # An operator condition (e.g. `CONDITION1 and CONDITION2`).
33
+ class Operator < Condition
34
+ # The left-hand condition.
35
+ #
36
+ # @return [Sass::Supports::Condition]
37
+ attr_accessor :left
38
+
39
+ # The right-hand condition.
40
+ #
41
+ # @return [Sass::Supports::Condition]
42
+ attr_accessor :right
43
+
44
+ # The operator ("and" or "or").
45
+ #
46
+ # @return [String]
47
+ attr_accessor :op
48
+
49
+ def initialize(left, right, op)
50
+ @left = left
51
+ @right = right
52
+ @op = op
53
+ end
54
+
55
+ def perform(env)
56
+ @left.perform(env)
57
+ @right.perform(env)
58
+ end
59
+
60
+ def to_css
61
+ "#{left_parens @left.to_css} #{op} #{right_parens @right.to_css}"
62
+ end
63
+
64
+ def to_src(options)
65
+ "#{left_parens @left.to_src(options)} #{op} #{right_parens @right.to_src(options)}"
66
+ end
67
+
68
+ def deep_copy
69
+ copy = dup
70
+ copy.left = @left.deep_copy
71
+ copy.right = @right.deep_copy
72
+ copy
73
+ end
74
+
75
+ def options=(options)
76
+ @left.options = options
77
+ @right.options = options
78
+ end
79
+
80
+ private
81
+
82
+ def left_parens(str)
83
+ return "(#{str})" if @left.is_a?(Negation)
84
+ str
85
+ end
86
+
87
+ def right_parens(str)
88
+ return "(#{str})" if @right.is_a?(Negation) || @right.is_a?(Operator)
89
+ str
90
+ end
91
+ end
92
+
93
+ # A negation condition (`not CONDITION`).
94
+ class Negation < Condition
95
+ # The condition being negated.
96
+ #
97
+ # @return [Sass::Supports::Condition]
98
+ attr_accessor :condition
99
+
100
+ def initialize(condition)
101
+ @condition = condition
102
+ end
103
+
104
+ def perform(env)
105
+ @condition.perform(env)
106
+ end
107
+
108
+ def to_css
109
+ "not #{parens @condition.to_css}"
110
+ end
111
+
112
+ def to_src(options)
113
+ "not #{parens @condition.to_src(options)}"
114
+ end
115
+
116
+ def deep_copy
117
+ copy = dup
118
+ copy.condition = condition.deep_copy
119
+ copy
120
+ end
121
+
122
+ def options=(options)
123
+ condition.options = options
124
+ end
125
+
126
+ private
127
+
128
+ def parens(str)
129
+ return "(#{str})" if @condition.is_a?(Negation) || @condition.is_a?(Operator)
130
+ str
131
+ end
132
+ end
133
+
134
+ # A declaration condition (e.g. `(feature: value)`).
135
+ class Declaration < Condition
136
+ # @return [Sass::Script::Tree::Node] The feature name.
137
+ attr_accessor :name
138
+
139
+ # @!attribute resolved_name
140
+ # The name of the feature after any SassScript has been resolved.
141
+ # Only set once \{Tree::Visitors::Perform} has been run.
142
+ #
143
+ # @return [String]
144
+ attr_accessor :resolved_name
145
+
146
+ # The feature value.
147
+ #
148
+ # @return [Sass::Script::Tree::Node]
149
+ attr_accessor :value
150
+
151
+ # The value of the feature after any SassScript has been resolved.
152
+ # Only set once \{Tree::Visitors::Perform} has been run.
153
+ #
154
+ # @return [String]
155
+ attr_accessor :resolved_value
156
+
157
+ def initialize(name, value)
158
+ @name = name
159
+ @value = value
160
+ end
161
+
162
+ def perform(env)
163
+ @resolved_name = name.perform(env)
164
+ @resolved_value = value.perform(env)
165
+ end
166
+
167
+ def to_css
168
+ "(#{@resolved_name}: #{@resolved_value})"
169
+ end
170
+
171
+ def to_src(options)
172
+ "(#{@name.to_sass(options)}: #{@value.to_sass(options)})"
173
+ end
174
+
175
+ def deep_copy
176
+ copy = dup
177
+ copy.name = @name.deep_copy
178
+ copy.value = @value.deep_copy
179
+ copy
180
+ end
181
+
182
+ def options=(options)
183
+ @name.options = options
184
+ @value.options = options
185
+ end
186
+ end
187
+
188
+ # An interpolation condition (e.g. `#{$var}`).
189
+ class Interpolation < Condition
190
+ # The SassScript expression in the interpolation.
191
+ #
192
+ # @return [Sass::Script::Tree::Node]
193
+ attr_accessor :value
194
+
195
+ # The value of the expression after it's been resolved.
196
+ # Only set once \{Tree::Visitors::Perform} has been run.
197
+ #
198
+ # @return [String]
199
+ attr_accessor :resolved_value
200
+
201
+ def initialize(value)
202
+ @value = value
203
+ end
204
+
205
+ def perform(env)
206
+ val = value.perform(env)
207
+ @resolved_value = val.is_a?(Sass::Script::Value::String) ? val.value : val.to_s
208
+ end
209
+
210
+ def to_css
211
+ @resolved_value
212
+ end
213
+
214
+ def to_src(options)
215
+ "\#{#{@value.to_sass(options)}}"
216
+ end
217
+
218
+ def deep_copy
219
+ copy = dup
220
+ copy.value = @value.deep_copy
221
+ copy
222
+ end
223
+
224
+ def options=(options)
225
+ @value.options = options
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,82 @@
1
+ module Sass
2
+ module Tree
3
+ # A dynamic node representing an `@at-root` directive.
4
+ #
5
+ # An `@at-root` directive with a selector is converted to an \{AtRootNode}
6
+ # containing a \{RuleNode} at parse time.
7
+ #
8
+ # @see Sass::Tree
9
+ class AtRootNode < Node
10
+ # The query for this node (e.g. `(without: media)`),
11
+ # interspersed with {Sass::Script::Tree::Node}s representing
12
+ # `#{}`-interpolation. Any adjacent strings will be merged
13
+ # together.
14
+ #
15
+ # This will be nil if the directive didn't have a query. In this
16
+ # case, {#resolved\_type} will automatically be set to
17
+ # `:without` and {#resolved\_rule} will automatically be set to `["rule"]`.
18
+ #
19
+ # @return [Array<String, Sass::Script::Tree::Node>]
20
+ attr_accessor :query
21
+
22
+ # The resolved type of this directive. `:with` or `:without`.
23
+ #
24
+ # @return [Symbol]
25
+ attr_accessor :resolved_type
26
+
27
+ # The resolved value of this directive -- a list of directives
28
+ # to either include or exclude.
29
+ #
30
+ # @return [Array<String>]
31
+ attr_accessor :resolved_value
32
+
33
+ # The number of additional tabs that the contents of this node
34
+ # should be indented.
35
+ #
36
+ # @return [Number]
37
+ attr_accessor :tabs
38
+
39
+ # Whether the last child of this node should be considered the
40
+ # end of a group.
41
+ #
42
+ # @return [Boolean]
43
+ attr_accessor :group_end
44
+
45
+ def initialize(query = nil)
46
+ super()
47
+ @query = Sass::Util.strip_string_array(Sass::Util.merge_adjacent_strings(query)) if query
48
+ @tabs = 0
49
+ end
50
+
51
+ # Returns whether or not the given directive is excluded by this
52
+ # node. `directive` may be "rule", which indicates whether
53
+ # normal CSS rules should be excluded.
54
+ #
55
+ # @param directive [String]
56
+ # @return [Boolean]
57
+ def exclude?(directive)
58
+ if resolved_type == :with
59
+ return false if resolved_value.include?('all')
60
+ !resolved_value.include?(directive)
61
+ else # resolved_type == :without
62
+ return true if resolved_value.include?('all')
63
+ resolved_value.include?(directive)
64
+ end
65
+ end
66
+
67
+ # Returns whether the given node is excluded by this node.
68
+ #
69
+ # @param node [Sass::Tree::Node]
70
+ # @return [Boolean]
71
+ def exclude_node?(node)
72
+ return exclude?(node.name.gsub(/^@/, '')) if node.is_a?(Sass::Tree::DirectiveNode)
73
+ exclude?('rule') && node.is_a?(Sass::Tree::RuleNode)
74
+ end
75
+
76
+ # @see Node#bubbles?
77
+ def bubbles?
78
+ true
79
+ end
80
+ end
81
+ end
82
+ end