sass 3.2.7 → 3.3.0.rc.1

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 (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
data/lib/sass/selector.rb CHANGED
@@ -21,8 +21,6 @@ module Sass
21
21
  # The base used for calculating selector specificity. The spec says this
22
22
  # should be "sufficiently high"; it's extremely unlikely that any single
23
23
  # selector sequence will contain 1,000 simple selectors.
24
- #
25
- # @type [Fixnum]
26
24
  SPECIFICITY_BASE = 1_000
27
25
 
28
26
  # A parent-referencing selector (`&` in Sass).
@@ -47,10 +45,10 @@ module Sass
47
45
  class Class < Simple
48
46
  # The class name.
49
47
  #
50
- # @return [Array<String, Sass::Script::Node>]
48
+ # @return [Array<String, Sass::Script::Tree::Node>]
51
49
  attr_reader :name
52
50
 
53
- # @param name [Array<String, Sass::Script::Node>] The class name
51
+ # @param name [Array<String, Sass::Script::Tree::Node>] The class name
54
52
  def initialize(name)
55
53
  @name = name
56
54
  end
@@ -70,10 +68,10 @@ module Sass
70
68
  class Id < Simple
71
69
  # The id name.
72
70
  #
73
- # @return [Array<String, Sass::Script::Node>]
71
+ # @return [Array<String, Sass::Script::Tree::Node>]
74
72
  attr_reader :name
75
73
 
76
- # @param name [Array<String, Sass::Script::Node>] The id name
74
+ # @param name [Array<String, Sass::Script::Tree::Node>] The id name
77
75
  def initialize(name)
78
76
  @name = name
79
77
  end
@@ -88,7 +86,7 @@ module Sass
88
86
  #
89
87
  # @see Selector#unify
90
88
  def unify(sels)
91
- return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
89
+ return if sels.any? {|sel2| sel2.is_a?(Id) && name != sel2.name}
92
90
  super
93
91
  end
94
92
 
@@ -105,10 +103,10 @@ module Sass
105
103
  class Placeholder < Simple
106
104
  # The placeholder name.
107
105
  #
108
- # @return [Array<String, Sass::Script::Node>]
106
+ # @return [Array<String, Sass::Script::Tree::Node>]
109
107
  attr_reader :name
110
108
 
111
- # @param name [Array<String, Sass::Script::Node>] The placeholder name
109
+ # @param name [Array<String, Sass::Script::Tree::Node>] The placeholder name
112
110
  def initialize(name)
113
111
  @name = name
114
112
  end
@@ -120,7 +118,7 @@ module Sass
120
118
 
121
119
  # @see AbstractSequence#specificity
122
120
  def specificity
123
- 0
121
+ SPECIFICITY_BASE
124
122
  end
125
123
  end
126
124
 
@@ -131,10 +129,10 @@ module Sass
131
129
  # `[""]` means no namespace,
132
130
  # `["*"]` means any namespace.
133
131
  #
134
- # @return [Array<String, Sass::Script::Node>, nil]
132
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
135
133
  attr_reader :namespace
136
134
 
137
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
135
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
138
136
  def initialize(namespace)
139
137
  @namespace = namespace
140
138
  end
@@ -195,7 +193,7 @@ module Sass
195
193
  class Element < Simple
196
194
  # The element name.
197
195
  #
198
- # @return [Array<String, Sass::Script::Node>]
196
+ # @return [Array<String, Sass::Script::Tree::Node>]
199
197
  attr_reader :name
200
198
 
201
199
  # The selector namespace.
@@ -203,11 +201,11 @@ module Sass
203
201
  # `[""]` means no namespace,
204
202
  # `["*"]` means any namespace.
205
203
  #
206
- # @return [Array<String, Sass::Script::Node>, nil]
204
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
207
205
  attr_reader :namespace
208
206
 
209
- # @param name [Array<String, Sass::Script::Node>] The element name
210
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
207
+ # @param name [Array<String, Sass::Script::Tree::Node>] The element name
208
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
211
209
  def initialize(name, namespace)
212
210
  @name = name
213
211
  @namespace = namespace
@@ -262,10 +260,10 @@ module Sass
262
260
  class Interpolation < Simple
263
261
  # The script to run.
264
262
  #
265
- # @return [Sass::Script::Node]
263
+ # @return [Sass::Script::Tree::Node]
266
264
  attr_reader :script
267
265
 
268
- # @param script [Sass::Script::Node] The script to run
266
+ # @param script [Sass::Script::Tree::Node] The script to run
269
267
  def initialize(script)
270
268
  @script = script
271
269
  end
@@ -288,7 +286,7 @@ module Sass
288
286
  class Attribute < Simple
289
287
  # The attribute name.
290
288
  #
291
- # @return [Array<String, Sass::Script::Node>]
289
+ # @return [Array<String, Sass::Script::Tree::Node>]
292
290
  attr_reader :name
293
291
 
294
292
  # The attribute namespace.
@@ -296,7 +294,7 @@ module Sass
296
294
  # `[""]` means no namespace,
297
295
  # `["*"]` means any namespace.
298
296
  #
299
- # @return [Array<String, Sass::Script::Node>, nil]
297
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
300
298
  attr_reader :namespace
301
299
 
302
300
  # The matching operator, e.g. `"="` or `"^="`.
@@ -306,20 +304,23 @@ module Sass
306
304
 
307
305
  # The right-hand side of the operator.
308
306
  #
309
- # @return [Array<String, Sass::Script::Node>]
307
+ # @return [Array<String, Sass::Script::Tree::Node>]
310
308
  attr_reader :value
311
309
 
312
310
  # Flags for the attribute selector (e.g. `i`).
313
311
  #
314
- # @return [Array<String, Sass::Script::Node>]
312
+ # @return [Array<String, Sass::Script::Tree::Node>]
315
313
  attr_reader :flags
316
314
 
317
- # @param name [Array<String, Sass::Script::Node>] The attribute name
318
- # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
315
+ # @param name [Array<String, Sass::Script::Tree::Node>] The attribute name
316
+ # @param namespace [Array<String, Sass::Script::Tree::Node>, nil] See \{#namespace}
319
317
  # @param operator [String] The matching operator, e.g. `"="` or `"^="`
320
- # @param value [Array<String, Sass::Script::Node>] See \{#value}
321
- # @param value [Array<String, Sass::Script::Node>] See \{#flags}
318
+ # @param value [Array<String, Sass::Script::Tree::Node>] See \{#value}
319
+ # @param flags [Array<String, Sass::Script::Tree::Node>] See \{#flags}
320
+ # @comment
321
+ # rubocop:disable ParameterLists
322
322
  def initialize(name, namespace, operator, value, flags)
323
+ # rubocop:enable ParameterLists
323
324
  @name = name
324
325
  @namespace = namespace
325
326
  @operator = operator
@@ -346,23 +347,23 @@ module Sass
346
347
  # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
347
348
  # It can have arguments (e.g. `:nth-child(2n+1)`).
348
349
  class Pseudo < Simple
349
- # The type of the selector.
350
- # `:class` if this is a pseudoclass selector,
351
- # `:element` if it's a pseudoelement.
350
+ # Some psuedo-class-syntax selectors are actually considered
351
+ # pseudo-elements and must be treated differently. This is a list of such
352
+ # selectors
352
353
  #
353
- # @return [Symbol]
354
- attr_reader :type
354
+ # @return [Array<String>]
355
+ ACTUALLY_ELEMENTS = %w[after before first-line first-letter]
355
356
 
356
- # Some psuedo-class-syntax selectors (`:after` and `:before)
357
- # are actually considered pseudo-elements
358
- # and must be at the end of the selector to function properly.
357
+ # Like \{#type}, but returns the type of selector this looks like, rather
358
+ # than the type it is semantically. This only differs from type for
359
+ # selectors in \{ACTUALLY\_ELEMENTS}.
359
360
  #
360
- # @return [Array<String>]
361
- FINAL_SELECTORS = %w[after before]
361
+ # @return [Symbol]
362
+ attr_reader :syntactic_type
362
363
 
363
364
  # The name of the selector.
364
365
  #
365
- # @return [Array<String, Sass::Script::Node>]
366
+ # @return [Array<String, Sass::Script::Tree::Node>]
366
367
  attr_reader :name
367
368
 
368
369
  # The argument to the selector,
@@ -372,26 +373,30 @@ module Sass
372
373
  # Note that this should not include SassScript nodes
373
374
  # after resolution has taken place.
374
375
  #
375
- # @return [Array<String, Sass::Script::Node>, nil]
376
+ # @return [Array<String, Sass::Script::Tree::Node>, nil]
376
377
  attr_reader :arg
377
378
 
378
379
  # @param type [Symbol] See \{#type}
379
- # @param name [Array<String, Sass::Script::Node>] The name of the selector
380
- # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
380
+ # @param name [Array<String, Sass::Script::Tree::Node>] The name of the selector
381
+ # @param arg [nil, Array<String, Sass::Script::Tree::Node>] The argument to the selector,
381
382
  # or nil if no argument was given
382
383
  def initialize(type, name, arg)
383
- @type = type
384
+ @syntactic_type = type
384
385
  @name = name
385
386
  @arg = arg
386
387
  end
387
388
 
388
- def final?
389
- type == :class && FINAL_SELECTORS.include?(name.first)
389
+ # The type of the selector. `:class` if this is a pseudoclass selector,
390
+ # `:element` if it's a pseudoelement.
391
+ #
392
+ # @return [Symbol]
393
+ def type
394
+ ACTUALLY_ELEMENTS.include?(name.first) ? :element : syntactic_type
390
395
  end
391
396
 
392
397
  # @see Selector#to_a
393
398
  def to_a
394
- res = [@type == :class ? ":" : "::"] + @name
399
+ res = [syntactic_type == :class ? ":" : "::"] + @name
395
400
  (res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
396
401
  res
397
402
  end
@@ -403,9 +408,8 @@ module Sass
403
408
  def unify(sels)
404
409
  return if type == :element && sels.any? do |sel|
405
410
  sel.is_a?(Pseudo) && sel.type == :element &&
406
- (sel.name != self.name || sel.arg != self.arg)
411
+ (sel.name != name || sel.arg != arg)
407
412
  end
408
- return sels + [self] if final?
409
413
  super
410
414
  end
411
415
 
@@ -428,8 +432,8 @@ module Sass
428
432
  # @return [Selector::Sequence]
429
433
  attr_reader :selector
430
434
 
431
- # @param [String] The name of the pseudoclass
432
- # @param [Selector::CommaSequence] The selector argument
435
+ # @param name [String] The name of the pseudoclass
436
+ # @param selector [Selector::CommaSequence] The selector argument
433
437
  def initialize(name, selector)
434
438
  @name = name
435
439
  @selector = selector
@@ -0,0 +1,211 @@
1
+ require 'pathname'
2
+
3
+ module Sass::Source
4
+ class Map
5
+ # A mapping from one source range to another. Indicates that `input` was
6
+ # compiled to `output`.
7
+ #
8
+ # @!attribute input
9
+ # @return [Sass::Source::Range] The source range in the input document.
10
+ #
11
+ # @!attribute output
12
+ # @return [Sass::Source::Range] The source range in the output document.
13
+ class Mapping < Struct.new(:input, :output)
14
+ # @return [String] A string representation of the mapping.
15
+ def inspect
16
+ "#{input.inspect} => #{output.inspect}"
17
+ end
18
+ end
19
+
20
+ # The mapping data ordered by the location in the target.
21
+ #
22
+ # @return [Array<Mapping>]
23
+ attr_reader :data
24
+
25
+ def initialize
26
+ @data = []
27
+ end
28
+
29
+ # Adds a new mapping from one source range to another. Multiple invocations
30
+ # of this method should have each `output` range come after all previous ranges.
31
+ #
32
+ # @param input [Sass::Source::Range]
33
+ # The source range in the input document.
34
+ # @param output [Sass::Source::Range]
35
+ # The source range in the output document.
36
+ def add(input, output)
37
+ @data.push(Mapping.new(input, output))
38
+ end
39
+
40
+ # Shifts all output source ranges forward one or more lines.
41
+ #
42
+ # @param delta [Fixnum] The number of lines to shift the ranges forward.
43
+ def shift_output_lines(delta)
44
+ return if delta == 0
45
+ @data.each do |m|
46
+ m.output.start_pos.line += delta
47
+ m.output.end_pos.line += delta
48
+ end
49
+ end
50
+
51
+ # Shifts any output source ranges that lie on the first line forward one or
52
+ # more characters on that line.
53
+ #
54
+ # @param delta [Fixnum] The number of characters to shift the ranges
55
+ # forward.
56
+ def shift_output_offsets(delta)
57
+ return if delta == 0
58
+ @data.each do |m|
59
+ break if m.output.start_pos.line > 1
60
+ m.output.start_pos.offset += delta
61
+ m.output.end_pos.offset += delta if m.output.end_pos.line > 1
62
+ end
63
+ end
64
+
65
+ # Returns the standard JSON representation of the source map.
66
+ #
67
+ # If the `:css_uri` option isn't specified, the `:css_path` and
68
+ # `:sourcemap_path` options must both be specified. Any options may also be
69
+ # specified alongside the `:css_uri` option. If `:css_uri` isn't specified,
70
+ # it will be inferred from `:css_path` and `:sourcemap_path` using the
71
+ # assumption that the local file system has the same layout as the server.
72
+ #
73
+ # If any source stylesheets use the default filesystem importer, sourcemap
74
+ # generation will fail unless the `:sourcemap_path` option is specified.
75
+ # The layout of the local file system is assumed to be the same as the
76
+ # layout of the server for the purposes of linking to source stylesheets
77
+ # that use the filesystem importer.
78
+ #
79
+ # Regardless of which options are passed to this method, source stylesheets
80
+ # that are imported using a non-default importer will only be linked to in
81
+ # the source map if their importers implement
82
+ # \{Sass::Importers::Base#public\_url\}.
83
+ #
84
+ # @option options :css_uri [String]
85
+ # The publicly-visible URI of the CSS output file.
86
+ # @option options :css_path [String]
87
+ # The local path of the CSS output file.
88
+ # @option options :sourcemap_path [String]
89
+ # The (eventual) local path of the sourcemap file.
90
+ # @return [String] The JSON string.
91
+ # @raise [ArgumentError] If neither `:css_uri` nor `:css_path` and
92
+ # `:sourcemap_path` are specified.
93
+ # @comment
94
+ # rubocop:disable MethodLength
95
+ def to_json(options)
96
+ css_uri, css_path, sourcemap_path =
97
+ [:css_uri, :css_path, :sourcemap_path].map {|o| options[o]}
98
+ unless css_uri || (css_path && sourcemap_path)
99
+ raise ArgumentError.new("Sass::Source::Map#to_json requires either " +
100
+ "the :css_uri option or both the :css_path and :soucemap_path options.")
101
+ end
102
+ css_path &&= Pathname.pwd.join(Pathname.new(css_path)).cleanpath
103
+ sourcemap_path &&= Pathname.pwd.join(Pathname.new(sourcemap_path)).cleanpath
104
+ css_uri ||= css_path.relative_path_from(sourcemap_path.dirname).to_s
105
+
106
+ result = "{\n"
107
+ write_json_field(result, "version", "3", true)
108
+
109
+ source_uri_to_id = {}
110
+ id_to_source_uri = {}
111
+ next_source_id = 0
112
+ line_data = []
113
+ segment_data_for_line = []
114
+ no_public_url = Set.new
115
+
116
+ # These track data necessary for the delta coding.
117
+ previous_target_line = nil
118
+ previous_target_offset = 1
119
+ previous_source_line = 1
120
+ previous_source_offset = 1
121
+ previous_source_id = 0
122
+
123
+ @data.each do |m|
124
+ file, importer = m.input.file, m.input.importer
125
+ unless (source_uri = importer && importer.public_url(file))
126
+ if importer.is_a?(Sass::Importers::Filesystem) && sourcemap_path
127
+ file_path = Pathname.new(importer.root).join(file)
128
+ source_uri = file_path.relative_path_from(sourcemap_path.dirname).to_s
129
+ elsif no_public_url.include?(file)
130
+ next
131
+ else
132
+ no_public_url << file
133
+ Sass::Util.sass_warn <<WARNING
134
+ WARNING: Couldn't determine public URL for "#{file}" while generating sourcemap.
135
+ Without a public URL, there's nothing for the source map to link to.
136
+ Custom importers should define the #public_url method.
137
+ WARNING
138
+ next
139
+ end
140
+ end
141
+
142
+ current_source_id = source_uri_to_id[source_uri]
143
+ unless current_source_id
144
+ current_source_id = next_source_id
145
+ next_source_id += 1
146
+
147
+ source_uri_to_id[source_uri] = current_source_id
148
+ id_to_source_uri[current_source_id] = source_uri
149
+ end
150
+
151
+ [
152
+ [m.input.start_pos, m.output.start_pos],
153
+ [m.input.end_pos, m.output.end_pos]
154
+ ].each do |source_pos, target_pos|
155
+ if previous_target_line != target_pos.line
156
+ line_data.push(segment_data_for_line.join(",")) unless segment_data_for_line.empty?
157
+ (target_pos.line - 1 - (previous_target_line || 0)).times {line_data.push("")}
158
+ previous_target_line = target_pos.line
159
+ previous_target_offset = 1
160
+ segment_data_for_line = []
161
+ end
162
+
163
+ # `segment` is a data chunk for a single position mapping.
164
+ segment = ""
165
+
166
+ # Field 1: zero-based starting offset.
167
+ segment << Sass::Util.encode_vlq(target_pos.offset - previous_target_offset)
168
+ previous_target_offset = target_pos.offset
169
+
170
+ # Field 2: zero-based index into the "sources" list.
171
+ segment << Sass::Util.encode_vlq(current_source_id - previous_source_id)
172
+ previous_source_id = current_source_id
173
+
174
+ # Field 3: zero-based starting line in the original source.
175
+ segment << Sass::Util.encode_vlq(source_pos.line - previous_source_line)
176
+ previous_source_line = source_pos.line
177
+
178
+ # Field 4: zero-based starting offset in the original source.
179
+ segment << Sass::Util.encode_vlq(source_pos.offset - previous_source_offset)
180
+ previous_source_offset = source_pos.offset
181
+
182
+ segment_data_for_line.push(segment)
183
+
184
+ previous_target_line = target_pos.line
185
+ end
186
+ end
187
+ line_data.push(segment_data_for_line.join(","))
188
+ write_json_field(result, "mappings", line_data.join(";"))
189
+
190
+ source_names = []
191
+ (0...next_source_id).each {|id| source_names.push(id_to_source_uri[id].to_s)}
192
+ write_json_field(result, "sources", source_names)
193
+ write_json_field(result, "file", css_uri)
194
+
195
+ result << "\n}"
196
+ result
197
+ end
198
+ # @comment
199
+ # rubocop:enable MethodLength
200
+
201
+ private
202
+
203
+ def write_json_field(out, name, value, is_first = false)
204
+ out << (is_first ? "" : ",\n") <<
205
+ "\"" <<
206
+ Sass::Util.json_escape_string(name) <<
207
+ "\": " <<
208
+ Sass::Util.json_value_of(value)
209
+ end
210
+ end
211
+ 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,120 @@
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
+ private
111
+
112
+ def with_frame(filename, line, type, name = nil)
113
+ @frames.pop if @frames.last && @frames.last.type == :base
114
+ @frames.push(Frame.new(filename, line, type, name))
115
+ yield
116
+ ensure
117
+ @frames.pop unless type == :base && @frames.last && @frames.last.type != :base
118
+ end
119
+ end
120
+ end