PageRenderTime 515ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 3ms

/BundleTransformer.SassAndScss/Resources/sass_in_one.rb

http://bundletransformer.codeplex.com
Ruby | 12296 lines | 6778 code | 1263 blank | 4255 comment | 960 complexity | 0f37ab1933d39476db91811877f06a4d MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, BSD-2-Clause, BSD-3-Clause
  1. # This is necessary to set so that the Haml code that tries to load Sass
  2. # knows that Sass is indeed loading,
  3. # even if there's some crazy autoload stuff going on.
  4. SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
  5. # This is necessary for loading Sass when Haml is required in Rails 3.
  6. # Once the split is complete, we can remove it.
  7. #require 'erb'
  8. require 'set'
  9. require 'enumerator'
  10. require 'stringio'
  11. require 'strscan'
  12. require 'rbconfig'
  13. module Sass
  14. # The root directory of the Sass source tree.
  15. # This may be overridden by the package manager
  16. # if the lib directory is separated from the main source tree.
  17. # @api public
  18. ROOT_DIR = File.expand_path(File.join(__FILE__, "../../.."))
  19. end
  20. require 'set'
  21. module Sass
  22. module Util
  23. # A map from sets to values.
  24. # A value is \{#\[]= set} by providing a set (the "set-set") and a value,
  25. # which is then recorded as corresponding to that set.
  26. # Values are \{#\[] accessed} by providing a set (the "get-set")
  27. # and returning all values that correspond to set-sets
  28. # that are subsets of the get-set.
  29. #
  30. # SubsetMap preserves the order of values as they're inserted.
  31. #
  32. # @example
  33. # ssm = SubsetMap.new
  34. # ssm[Set[1, 2]] = "Foo"
  35. # ssm[Set[2, 3]] = "Bar"
  36. # ssm[Set[1, 2, 3]] = "Baz"
  37. #
  38. # ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
  39. class SubsetMap
  40. # Creates a new, empty SubsetMap.
  41. def initialize
  42. @hash = {}
  43. @vals = []
  44. end
  45. # Whether or not this SubsetMap has any key-value pairs.
  46. #
  47. # @return [Boolean]
  48. def empty?
  49. @hash.empty?
  50. end
  51. # Associates a value with a set.
  52. # When `set` or any of its supersets is accessed,
  53. # `value` will be among the values returned.
  54. #
  55. # Note that if the same `set` is passed to this method multiple times,
  56. # all given `value`s will be associated with that `set`.
  57. #
  58. # This runs in `O(n)` time, where `n` is the size of `set`.
  59. #
  60. # @param set [#to_set] The set to use as the map key. May not be empty.
  61. # @param value [Object] The value to associate with `set`.
  62. # @raise [ArgumentError] If `set` is empty.
  63. def []=(set, value)
  64. raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
  65. index = @vals.size
  66. @vals << value
  67. set.each do |k|
  68. @hash[k] ||= []
  69. @hash[k] << [set, set.to_set, index]
  70. end
  71. end
  72. # Returns all values associated with subsets of `set`.
  73. #
  74. # In the worst case, this runs in `O(m*max(n, log m))` time,
  75. # where `n` is the size of `set`
  76. # and `m` is the number of assocations in the map.
  77. # However, unless many keys in the map overlap with `set`,
  78. # `m` will typically be much smaller.
  79. #
  80. # @param set [Set] The set to use as the map key.
  81. # @return [Array<(Object, #to_set)>] An array of pairs,
  82. # where the first value is the value associated with a subset of `set`,
  83. # and the second value is that subset of `set`
  84. # (or whatever `#to_set` object was used to set the value)
  85. # This array is in insertion order.
  86. # @see #[]
  87. def get(set)
  88. res = set.map do |k|
  89. next unless subsets = @hash[k]
  90. subsets.map do |subenum, subset, index|
  91. next unless subset.subset?(set)
  92. [index, subenum]
  93. end
  94. end
  95. res = Sass::Util.flatten(res, 1)
  96. res.compact!
  97. res.uniq!
  98. res.sort!
  99. res.map! {|i, s| [@vals[i], s]}
  100. return res
  101. end
  102. # Same as \{#get}, but doesn't return the subsets of the argument
  103. # for which values were found.
  104. #
  105. # @param set [Set] The set to use as the map key.
  106. # @return [Array] The array of all values
  107. # associated with subsets of `set`, in insertion order.
  108. # @see #get
  109. def [](set)
  110. get(set).map {|v, _| v}
  111. end
  112. end
  113. end
  114. end
  115. module Sass
  116. # A module containing various useful functions.
  117. module Util
  118. extend self
  119. # An array of ints representing the Ruby version number.
  120. # @api public
  121. RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
  122. # The Ruby engine we're running under. Defaults to `"ruby"`
  123. # if the top-level constant is undefined.
  124. # @api public
  125. RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
  126. # Returns the path of a file relative to the Sass root directory.
  127. #
  128. # @param file [String] The filename relative to the Sass root
  129. # @return [String] The filename relative to the the working directory
  130. def scope(file)
  131. File.join(Sass::ROOT_DIR, file)
  132. end
  133. # Converts an array of `[key, value]` pairs to a hash.
  134. #
  135. # @example
  136. # to_hash([[:foo, "bar"], [:baz, "bang"]])
  137. # #=> {:foo => "bar", :baz => "bang"}
  138. # @param arr [Array<(Object, Object)>] An array of pairs
  139. # @return [Hash] A hash
  140. def to_hash(arr)
  141. Hash[arr.compact]
  142. end
  143. # Maps the keys in a hash according to a block.
  144. #
  145. # @example
  146. # map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
  147. # #=> {"foo" => "bar", "baz" => "bang"}
  148. # @param hash [Hash] The hash to map
  149. # @yield [key] A block in which the keys are transformed
  150. # @yieldparam key [Object] The key that should be mapped
  151. # @yieldreturn [Object] The new value for the key
  152. # @return [Hash] The mapped hash
  153. # @see #map_vals
  154. # @see #map_hash
  155. def map_keys(hash)
  156. to_hash(hash.map {|k, v| [yield(k), v]})
  157. end
  158. # Maps the values in a hash according to a block.
  159. #
  160. # @example
  161. # map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
  162. # #=> {:foo => :bar, :baz => :bang}
  163. # @param hash [Hash] The hash to map
  164. # @yield [value] A block in which the values are transformed
  165. # @yieldparam value [Object] The value that should be mapped
  166. # @yieldreturn [Object] The new value for the value
  167. # @return [Hash] The mapped hash
  168. # @see #map_keys
  169. # @see #map_hash
  170. def map_vals(hash)
  171. to_hash(hash.map {|k, v| [k, yield(v)]})
  172. end
  173. # Maps the key-value pairs of a hash according to a block.
  174. #
  175. # @example
  176. # map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
  177. # #=> {"foo" => :bar, "baz" => :bang}
  178. # @param hash [Hash] The hash to map
  179. # @yield [key, value] A block in which the key-value pairs are transformed
  180. # @yieldparam [key] The hash key
  181. # @yieldparam [value] The hash value
  182. # @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
  183. # @return [Hash] The mapped hash
  184. # @see #map_keys
  185. # @see #map_vals
  186. def map_hash(hash, &block)
  187. to_hash(hash.map(&block))
  188. end
  189. # Computes the powerset of the given array.
  190. # This is the set of all subsets of the array.
  191. #
  192. # @example
  193. # powerset([1, 2, 3]) #=>
  194. # Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
  195. # @param arr [Enumerable]
  196. # @return [Set<Set>] The subsets of `arr`
  197. def powerset(arr)
  198. arr.inject([Set.new].to_set) do |powerset, el|
  199. new_powerset = Set.new
  200. powerset.each do |subset|
  201. new_powerset << subset
  202. new_powerset << subset + [el]
  203. end
  204. new_powerset
  205. end
  206. end
  207. # Restricts a number to falling within a given range.
  208. # Returns the number if it falls within the range,
  209. # or the closest value in the range if it doesn't.
  210. #
  211. # @param value [Numeric]
  212. # @param range [Range<Numeric>]
  213. # @return [Numeric]
  214. def restrict(value, range)
  215. [[value, range.first].max, range.last].min
  216. end
  217. # Concatenates all strings that are adjacent in an array,
  218. # while leaving other elements as they are.
  219. #
  220. # @example
  221. # merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
  222. # #=> [1, "foobar", 2, "baz"]
  223. # @param arr [Array]
  224. # @return [Array] The enumerable with strings merged
  225. def merge_adjacent_strings(arr)
  226. # Optimize for the common case of one element
  227. return arr if arr.size < 2
  228. arr.inject([]) do |a, e|
  229. if e.is_a?(String)
  230. if a.last.is_a?(String)
  231. a.last << e
  232. else
  233. a << e.dup
  234. end
  235. else
  236. a << e
  237. end
  238. a
  239. end
  240. end
  241. # Intersperses a value in an enumerable, as would be done with `Array#join`
  242. # but without concatenating the array together afterwards.
  243. #
  244. # @param enum [Enumerable]
  245. # @param val
  246. # @return [Array]
  247. def intersperse(enum, val)
  248. enum.inject([]) {|a, e| a << e << val}[0...-1]
  249. end
  250. # Substitutes a sub-array of one array with another sub-array.
  251. #
  252. # @param ary [Array] The array in which to make the substitution
  253. # @param from [Array] The sequence of elements to replace with `to`
  254. # @param to [Array] The sequence of elements to replace `from` with
  255. def substitute(ary, from, to)
  256. res = ary.dup
  257. i = 0
  258. while i < res.size
  259. if res[i...i+from.size] == from
  260. res[i...i+from.size] = to
  261. end
  262. i += 1
  263. end
  264. res
  265. end
  266. # Destructively strips whitespace from the beginning and end
  267. # of the first and last elements, respectively,
  268. # in the array (if those elements are strings).
  269. #
  270. # @param arr [Array]
  271. # @return [Array] `arr`
  272. def strip_string_array(arr)
  273. arr.first.lstrip! if arr.first.is_a?(String)
  274. arr.last.rstrip! if arr.last.is_a?(String)
  275. arr
  276. end
  277. # Return an array of all possible paths through the given arrays.
  278. #
  279. # @param arrs [Array<Array>]
  280. # @return [Array<Arrays>]
  281. #
  282. # @example
  283. # paths([[1, 2], [3, 4], [5]]) #=>
  284. # # [[1, 3, 5],
  285. # # [2, 3, 5],
  286. # # [1, 4, 5],
  287. # # [2, 4, 5]]
  288. def paths(arrs)
  289. arrs.inject([[]]) do |paths, arr|
  290. flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
  291. end
  292. end
  293. # Computes a single longest common subsequence for `x` and `y`.
  294. # If there are more than one longest common subsequences,
  295. # the one returned is that which starts first in `x`.
  296. #
  297. # @param x [Array]
  298. # @param y [Array]
  299. # @yield [a, b] An optional block to use in place of a check for equality
  300. # between elements of `x` and `y`.
  301. # @yieldreturn [Object, nil] If the two values register as equal,
  302. # this will return the value to use in the LCS array.
  303. # @return [Array] The LCS
  304. def lcs(x, y, &block)
  305. x = [nil, *x]
  306. y = [nil, *y]
  307. block ||= proc {|a, b| a == b && a}
  308. lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
  309. end
  310. # Returns information about the caller of the previous method.
  311. #
  312. # @param entry [String] An entry in the `#caller` list, or a similarly formatted string
  313. # @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
  314. # The method name may be nil
  315. def caller_info(entry = caller[1])
  316. info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
  317. info[1] = info[1].to_i
  318. # This is added by Rubinius to designate a block, but we don't care about it.
  319. info[2].sub!(/ \{\}\Z/, '') if info[2]
  320. info
  321. end
  322. # Returns whether one version string represents a more recent version than another.
  323. #
  324. # @param v1 [String] A version string.
  325. # @param v2 [String] Another version string.
  326. # @return [Boolean]
  327. def version_gt(v1, v2)
  328. # Construct an array to make sure the shorter version is padded with nil
  329. Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
  330. p1 ||= "0"
  331. p2 ||= "0"
  332. release1 = p1 =~ /^[0-9]+$/
  333. release2 = p2 =~ /^[0-9]+$/
  334. if release1 && release2
  335. # Integer comparison if both are full releases
  336. p1, p2 = p1.to_i, p2.to_i
  337. next if p1 == p2
  338. return p1 > p2
  339. elsif !release1 && !release2
  340. # String comparison if both are prereleases
  341. next if p1 == p2
  342. return p1 > p2
  343. else
  344. # If only one is a release, that one is newer
  345. return release1
  346. end
  347. end
  348. end
  349. # Returns whether one version string represents the same or a more
  350. # recent version than another.
  351. #
  352. # @param v1 [String] A version string.
  353. # @param v2 [String] Another version string.
  354. # @return [Boolean]
  355. def version_geq(v1, v2)
  356. version_gt(v1, v2) || !version_gt(v2, v1)
  357. end
  358. # Throws a NotImplementedError for an abstract method.
  359. #
  360. # @param obj [Object] `self`
  361. # @raise [NotImplementedError]
  362. def abstract(obj)
  363. raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
  364. end
  365. # Silence all output to STDERR within a block.
  366. #
  367. # @yield A block in which no output will be printed to STDERR
  368. def silence_warnings
  369. the_real_stderr, $stderr = $stderr, StringIO.new
  370. yield
  371. ensure
  372. $stderr = the_real_stderr
  373. end
  374. @@silence_warnings = false
  375. # Silences all Sass warnings within a block.
  376. #
  377. # @yield A block in which no Sass warnings will be printed
  378. def silence_sass_warnings
  379. old_silence_warnings = @@silence_warnings
  380. @@silence_warnings = true
  381. yield
  382. ensure
  383. @@silence_warnings = old_silence_warnings
  384. end
  385. # The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
  386. #
  387. # @param msg [String]
  388. def sass_warn(msg)
  389. return if @@silence_warnings
  390. warn(msg)
  391. end
  392. ## Cross Rails Version Compatibility
  393. # Returns the root of the Rails application,
  394. # if this is running in a Rails context.
  395. # Returns `nil` if no such root is defined.
  396. #
  397. # @return [String, nil]
  398. def rails_root
  399. if defined?(::Rails.root)
  400. return ::Rails.root.to_s if ::Rails.root
  401. raise "ERROR: Rails.root is nil!"
  402. end
  403. return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
  404. return nil
  405. end
  406. # Returns the environment of the Rails application,
  407. # if this is running in a Rails context.
  408. # Returns `nil` if no such environment is defined.
  409. #
  410. # @return [String, nil]
  411. def rails_env
  412. return ::Rails.env.to_s if defined?(::Rails.env)
  413. return RAILS_ENV.to_s if defined?(RAILS_ENV)
  414. return nil
  415. end
  416. # Returns whether this environment is using ActionPack
  417. # version 3.0.0 or greater.
  418. #
  419. # @return [Boolean]
  420. def ap_geq_3?
  421. ap_geq?("3.0.0.beta1")
  422. end
  423. # Returns whether this environment is using ActionPack
  424. # of a version greater than or equal to that specified.
  425. #
  426. # @param version [String] The string version number to check against.
  427. # Should be greater than or equal to Rails 3,
  428. # because otherwise ActionPack::VERSION isn't autoloaded
  429. # @return [Boolean]
  430. def ap_geq?(version)
  431. # The ActionPack module is always loaded automatically in Rails >= 3
  432. return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
  433. defined?(ActionPack::VERSION::STRING)
  434. version_geq(ActionPack::VERSION::STRING, version)
  435. end
  436. # Returns an ActionView::Template* class.
  437. # In pre-3.0 versions of Rails, most of these classes
  438. # were of the form `ActionView::TemplateFoo`,
  439. # while afterwards they were of the form `ActionView;:Template::Foo`.
  440. #
  441. # @param name [#to_s] The name of the class to get.
  442. # For example, `:Error` will return `ActionView::TemplateError`
  443. # or `ActionView::Template::Error`.
  444. def av_template_class(name)
  445. return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
  446. return ActionView::Template.const_get(name.to_s)
  447. end
  448. ## Cross-OS Compatibility
  449. # Whether or not this is running on Windows.
  450. #
  451. # @return [Boolean]
  452. def windows?
  453. RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
  454. end
  455. # Whether or not this is running on IronRuby.
  456. #
  457. # @return [Boolean]
  458. def ironruby?
  459. RUBY_ENGINE == "ironruby"
  460. end
  461. ## Cross-Ruby-Version Compatibility
  462. # Whether or not this is running under Ruby 1.8 or lower.
  463. #
  464. # Note that IronRuby counts as Ruby 1.8,
  465. # because it doesn't support the Ruby 1.9 encoding API.
  466. #
  467. # @return [Boolean]
  468. def ruby1_8?
  469. # IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
  470. # We have to fall back to 1.8 behavior.
  471. ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
  472. end
  473. # Whether or not this is running under Ruby 1.8.6 or lower.
  474. # Note that lower versions are not officially supported.
  475. #
  476. # @return [Boolean]
  477. def ruby1_8_6?
  478. ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
  479. end
  480. # Checks that the encoding of a string is valid in Ruby 1.9
  481. # and cleans up potential encoding gotchas like the UTF-8 BOM.
  482. # If it's not, yields an error string describing the invalid character
  483. # and the line on which it occurrs.
  484. #
  485. # @param str [String] The string of which to check the encoding
  486. # @yield [msg] A block in which an encoding error can be raised.
  487. # Only yields if there is an encoding error
  488. # @yieldparam msg [String] The error message to be raised
  489. # @return [String] `str`, potentially with encoding gotchas like BOMs removed
  490. def check_encoding(str)
  491. if ruby1_8?
  492. return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
  493. elsif str.valid_encoding?
  494. # Get rid of the Unicode BOM if possible
  495. if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
  496. return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
  497. else
  498. return str
  499. end
  500. end
  501. encoding = str.encoding
  502. newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
  503. str.force_encoding("binary").split(newlines).each_with_index do |line, i|
  504. begin
  505. line.encode(encoding)
  506. rescue Encoding::UndefinedConversionError => e
  507. yield <<MSG.rstrip, i + 1
  508. Invalid #{encoding.name} character #{e.error_char.dump}
  509. MSG
  510. end
  511. end
  512. return str
  513. end
  514. # Like {\#check\_encoding}, but also checks for a `@charset` declaration
  515. # at the beginning of the file and uses that encoding if it exists.
  516. #
  517. # The Sass encoding rules are simple.
  518. # If a `@charset` declaration exists,
  519. # we assume that that's the original encoding of the document.
  520. # Otherwise, we use whatever encoding Ruby has.
  521. # Then we convert that to UTF-8 to process internally.
  522. # The UTF-8 end result is what's returned by this method.
  523. #
  524. # @param str [String] The string of which to check the encoding
  525. # @yield [msg] A block in which an encoding error can be raised.
  526. # Only yields if there is an encoding error
  527. # @yieldparam msg [String] The error message to be raised
  528. # @return [(String, Encoding)] The original string encoded as UTF-8,
  529. # and the source encoding of the string (or `nil` under Ruby 1.8)
  530. # @raise [Encoding::UndefinedConversionError] if the source encoding
  531. # cannot be converted to UTF-8
  532. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  533. def check_sass_encoding(str, &block)
  534. return check_encoding(str, &block), nil if ruby1_8?
  535. # We allow any printable ASCII characters but double quotes in the charset decl
  536. bin = str.dup.force_encoding("BINARY")
  537. encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
  538. bin =~ Sass::Util::CHARSET_REGEXPS[enc]
  539. end
  540. charset, bom = $1, $2
  541. if charset
  542. charset = charset.force_encoding(encoding).encode("UTF-8")
  543. if endianness = encoding[/[BL]E$/]
  544. begin
  545. Encoding.find(charset + endianness)
  546. charset << endianness
  547. rescue ArgumentError # Encoding charset + endianness doesn't exist
  548. end
  549. end
  550. str.force_encoding(charset)
  551. elsif bom
  552. str.force_encoding(encoding)
  553. end
  554. str = check_encoding(str, &block)
  555. return str.encode("UTF-8"), str.encoding
  556. end
  557. unless ruby1_8?
  558. # @private
  559. def _enc(string, encoding)
  560. string.encode(encoding).force_encoding("BINARY")
  561. end
  562. # We could automatically add in any non-ASCII-compatible encodings here,
  563. # but there's not really a good way to do that
  564. # without manually checking that each encoding
  565. # encodes all ASCII characters properly,
  566. # which takes long enough to affect the startup time of the CLI.
  567. ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
  568. CHARSET_REGEXPS = Hash.new do |h, e|
  569. h[e] =
  570. begin
  571. # /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
  572. Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
  573. _enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
  574. _enc("\uFEFF", e)})/)
  575. rescue
  576. # /\A@charset "(.*?)"/
  577. Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
  578. end
  579. end
  580. end
  581. # Checks to see if a class has a given method.
  582. # For example:
  583. #
  584. # Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
  585. #
  586. # Method collections like `Class#instance_methods`
  587. # return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
  588. # so this handles checking for them in a compatible way.
  589. #
  590. # @param attr [#to_s] The (singular) name of the method-collection method
  591. # (e.g. `:instance_methods`, `:private_methods`)
  592. # @param klass [Module] The class to check the methods of which to check
  593. # @param method [String, Symbol] The name of the method do check for
  594. # @return [Boolean] Whether or not the given collection has the given method
  595. def has?(attr, klass, method)
  596. klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
  597. end
  598. # A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
  599. #
  600. # @param enum [Enumerable] The enumerable to get the enumerator for
  601. # @return [Enumerator] The with-index enumerator
  602. def enum_with_index(enum)
  603. ruby1_8? ? enum.enum_with_index : enum.each_with_index
  604. end
  605. # A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
  606. #
  607. # @param enum [Enumerable] The enumerable to get the enumerator for
  608. # @param n [Fixnum] The size of each cons
  609. # @return [Enumerator] The consed enumerator
  610. def enum_cons(enum, n)
  611. ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
  612. end
  613. # A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
  614. #
  615. # @param enum [Enumerable] The enumerable to get the enumerator for
  616. # @param n [Fixnum] The size of each slice
  617. # @return [Enumerator] The consed enumerator
  618. def enum_slice(enum, n)
  619. ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
  620. end
  621. # Returns the ASCII code of the given character.
  622. #
  623. # @param c [String] All characters but the first are ignored.
  624. # @return [Fixnum] The ASCII code of `c`.
  625. def ord(c)
  626. ruby1_8? ? c[0] : c.ord
  627. end
  628. # Flattens the first `n` nested arrays in a cross-version manner.
  629. #
  630. # @param arr [Array] The array to flatten
  631. # @param n [Fixnum] The number of levels to flatten
  632. # @return [Array] The flattened array
  633. def flatten(arr, n)
  634. return arr.flatten(n) unless ruby1_8_6?
  635. return arr if n == 0
  636. arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
  637. end
  638. # Returns the hash code for a set in a cross-version manner.
  639. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  640. #
  641. # @param set [Set]
  642. # @return [Fixnum] The order-independent hashcode of `set`
  643. def set_hash(set)
  644. return set.hash unless ruby1_8_6?
  645. set.map {|e| e.hash}.uniq.sort.hash
  646. end
  647. # Tests the hash-equality of two sets in a cross-version manner.
  648. # Aggravatingly, this is order-dependent in Ruby 1.8.6.
  649. #
  650. # @param set1 [Set]
  651. # @param set2 [Set]
  652. # @return [Boolean] Whether or not the sets are hashcode equal
  653. def set_eql?(set1, set2)
  654. return set1.eql?(set2) unless ruby1_8_6?
  655. set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
  656. end
  657. # Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
  658. # This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
  659. # before being evaluated.
  660. #
  661. # @param obj {Object}
  662. # @return {String}
  663. def inspect_obj(obj)
  664. return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
  665. return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
  666. return obj.inspect unless obj.is_a?(String)
  667. '"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
  668. end
  669. ## Static Method Stuff
  670. # The context in which the ERB for \{#def\_static\_method} will be run.
  671. class StaticConditionalContext
  672. # @param set [#include?] The set of variables that are defined for this context.
  673. def initialize(set)
  674. @set = set
  675. end
  676. # Checks whether or not a variable is defined for this context.
  677. #
  678. # @param name [Symbol] The name of the variable
  679. # @return [Boolean]
  680. def method_missing(name, *args, &block)
  681. super unless args.empty? && block.nil?
  682. @set.include?(name)
  683. end
  684. end
  685. private
  686. # Calculates the memoization table for the Least Common Subsequence algorithm.
  687. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
  688. def lcs_table(x, y)
  689. c = Array.new(x.size) {[]}
  690. x.size.times {|i| c[i][0] = 0}
  691. y.size.times {|j| c[0][j] = 0}
  692. (1...x.size).each do |i|
  693. (1...y.size).each do |j|
  694. c[i][j] =
  695. if yield x[i], y[j]
  696. c[i-1][j-1] + 1
  697. else
  698. [c[i][j-1], c[i-1][j]].max
  699. end
  700. end
  701. end
  702. return c
  703. end
  704. # Computes a single longest common subsequence for arrays x and y.
  705. # Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
  706. def lcs_backtrace(c, x, y, i, j, &block)
  707. return [] if i == 0 || j == 0
  708. if v = yield(x[i], y[j])
  709. return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
  710. end
  711. return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
  712. return lcs_backtrace(c, x, y, i-1, j, &block)
  713. end
  714. end
  715. end
  716. module Sass
  717. # Handles Sass version-reporting.
  718. # Sass not only reports the standard three version numbers,
  719. # but its Git revision hash as well,
  720. # if it was installed from Git.
  721. module Version
  722. include Sass::Util
  723. # Returns a hash representing the version of Sass.
  724. # The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Fixnums.
  725. # The `:name` key has the name of the version.
  726. # The `:string` key contains a human-readable string representation of the version.
  727. # The `:number` key is the major, minor, and teeny keys separated by periods.
  728. # If Sass is checked out from Git, the `:rev` key will have the revision hash.
  729. # For example:
  730. #
  731. # {
  732. # :string => "2.1.0.9616393",
  733. # :rev => "9616393b8924ef36639c7e82aa88a51a24d16949",
  734. # :number => "2.1.0",
  735. # :major => 2, :minor => 1, :teeny => 0
  736. # }
  737. #
  738. # If a prerelease version of Sass is being used,
  739. # the `:string` and `:number` fields will reflect the full version
  740. # (e.g. `"2.2.beta.1"`), and the `:teeny` field will be `-1`.
  741. # A `:prerelease` key will contain the name of the prerelease (e.g. `"beta"`),
  742. # and a `:prerelease_number` key will contain the rerelease number.
  743. # For example:
  744. #
  745. # {
  746. # :string => "3.0.beta.1",
  747. # :number => "3.0.beta.1",
  748. # :major => 3, :minor => 0, :teeny => -1,
  749. # :prerelease => "beta",
  750. # :prerelease_number => 1
  751. # }
  752. #
  753. # @return [{Symbol => String/Fixnum}] The version hash
  754. def version
  755. return @@version if defined?(@@version)
  756. #numbers = File.read(scope('VERSION')).strip.split('.').
  757. numbers = "3.2.0.alpha.0".strip.split('.').
  758. map {|n| n =~ /^[0-9]+$/ ? n.to_i : n}
  759. name = "Bleeding Edge"
  760. @@version = {
  761. :major => numbers[0],
  762. :minor => numbers[1],
  763. :teeny => numbers[2],
  764. :name => name
  765. }
  766. if numbers[3].is_a?(String)
  767. @@version[:teeny] = -1
  768. @@version[:prerelease] = numbers[3]
  769. @@version[:prerelease_number] = numbers[4]
  770. end
  771. @@version[:number] = numbers.join('.')
  772. @@version[:string] = @@version[:number].dup
  773. if rev = revision_number
  774. @@version[:rev] = rev
  775. unless rev[0] == ?(
  776. @@version[:string] << "." << rev[0...7]
  777. end
  778. end
  779. @@version[:string] << " (#{name})"
  780. @@version
  781. end
  782. private
  783. def revision_number
  784. if File.exists?(scope('REVISION'))
  785. rev = File.read(scope('REVISION')).strip
  786. return rev unless rev =~ /^([a-f0-9]+|\(.*\))$/ || rev == '(unknown)'
  787. end
  788. return unless File.exists?(scope('.git/HEAD'))
  789. rev = File.read(scope('.git/HEAD')).strip
  790. return rev unless rev =~ /^ref: (.*)$/
  791. ref_name = $1
  792. ref_file = scope(".git/#{ref_name}")
  793. info_file = scope(".git/info/refs")
  794. return File.read(ref_file).strip if File.exists?(ref_file)
  795. return unless File.exists?(info_file)
  796. File.open(info_file) do |f|
  797. f.each do |l|
  798. sha, ref = l.strip.split("\t", 2)
  799. next unless ref == ref_name
  800. return sha
  801. end
  802. end
  803. return nil
  804. end
  805. end
  806. extend Sass::Version
  807. # A string representing the version of Sass.
  808. # A more fine-grained representation is available from Sass.version.
  809. # @api public
  810. VERSION = version[:string] unless defined?(Sass::VERSION)
  811. end
  812. # The module that contains everything Sass-related:
  813. #
  814. # * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
  815. # * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
  816. # * {Sass::SyntaxError} is raised when Sass encounters an error.
  817. # * {Sass::CSS} handles conversion of CSS to Sass.
  818. #
  819. # Also see the {file:SASS_REFERENCE.md full Sass reference}.
  820. module Sass
  821. # Compile a Sass or SCSS string to CSS.
  822. # Defaults to SCSS.
  823. #
  824. # @param contents [String] The contents of the Sass file.
  825. # @param options [{Symbol => Object}] An options hash;
  826. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  827. # @raise [Sass::SyntaxError] if there's an error in the document
  828. # @raise [Encoding::UndefinedConversionError] if the source encoding
  829. # cannot be converted to UTF-8
  830. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  831. def self.compile(contents, options = {})
  832. options[:syntax] ||= :scss
  833. Engine.new(contents, options).to_css
  834. end
  835. # Compile a file on disk to CSS.
  836. #
  837. # @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
  838. # @param options [{Symbol => Object}] An options hash;
  839. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  840. # @raise [Sass::SyntaxError] if there's an error in the document
  841. # @raise [Encoding::UndefinedConversionError] if the source encoding
  842. # cannot be converted to UTF-8
  843. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  844. #
  845. # @overload compile_file(filename, options = {})
  846. # Return the compiled CSS rather than writing it to a file.
  847. #
  848. # @return [String] The compiled CSS.
  849. #
  850. # @overload compile_file(filename, css_filename, options = {})
  851. # Write the compiled CSS to a file.
  852. #
  853. # @param css_filename [String] The location to which to write the compiled CSS.
  854. def self.compile_file(filename, *args)
  855. options = args.last.is_a?(Hash) ? args.pop : {}
  856. css_filename = args.shift
  857. result = Sass::Engine.for_file(filename, options).render
  858. if css_filename
  859. options[:css_filename] ||= css_filename
  860. open(css_filename,"w") {|css_file| css_file.write(result)}
  861. nil
  862. else
  863. result
  864. end
  865. end
  866. end
  867. require 'strscan'
  868. require 'set'
  869. require 'digest/sha1'
  870. require 'stringio'
  871. module Sass
  872. # Sass cache stores are in charge of storing cached information,
  873. # especially parse trees for Sass documents.
  874. #
  875. # User-created importers must inherit from {CacheStores::Base}.
  876. module CacheStores
  877. end
  878. end
  879. module Sass
  880. module CacheStores
  881. # An abstract base class for backends for the Sass cache.
  882. # Any key-value store can act as such a backend;
  883. # it just needs to implement the
  884. # \{#_store} and \{#_retrieve} methods.
  885. #
  886. # To use a cache store with Sass,
  887. # use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
  888. #
  889. # @abstract
  890. class Base
  891. # Store cached contents for later retrieval
  892. # Must be implemented by all CacheStore subclasses
  893. #
  894. # Note: cache contents contain binary data.
  895. #
  896. # @param key [String] The key to store the contents under
  897. # @param version [String] The current sass version.
  898. # Cached contents must not be retrieved across different versions of sass.
  899. # @param sha [String] The sha of the sass source.
  900. # Cached contents must not be retrieved if the sha has changed.
  901. # @param contents [String] The contents to store.
  902. def _store(key, version, sha, contents)
  903. raise "#{self.class} must implement #_store."
  904. end
  905. # Retrieved cached contents.
  906. # Must be implemented by all subclasses.
  907. #
  908. # Note: if the key exists but the sha or version have changed,
  909. # then the key may be deleted by the cache store, if it wants to do so.
  910. #
  911. # @param key [String] The key to retrieve
  912. # @param version [String] The current sass version.
  913. # Cached contents must not be retrieved across different versions of sass.
  914. # @param sha [String] The sha of the sass source.
  915. # Cached contents must not be retrieved if the sha has changed.
  916. # @return [String] The contents that were previously stored.
  917. # @return [NilClass] when the cache key is not found or the version or sha have changed.
  918. def _retrieve(key, version, sha)
  919. raise "#{self.class} must implement #_retrieve."
  920. end
  921. # Store a {Sass::Tree::RootNode}.
  922. #
  923. # @param key [String] The key to store it under.
  924. # @param sha [String] The checksum for the contents that are being stored.
  925. # @param obj [Object] The object to cache.
  926. def store(key, sha, root)
  927. _store(key, Sass::VERSION, sha, Marshal.dump(root))
  928. rescue TypeError, LoadError => e
  929. Sass::Util.sass_warn "Warning. Error encountered while saving cache #{path_to(key)}: #{e}"
  930. end
  931. # Retrieve a {Sass::Tree::RootNode}.
  932. #
  933. # @param key [String] The key the root element was stored under.
  934. # @param sha [String] The checksum of the root element's content.
  935. # @return [Object] The cached object.
  936. def retrieve(key, sha)
  937. contents = _retrieve(key, Sass::VERSION, sha)
  938. Marshal.load(contents) if contents
  939. rescue EOFError, TypeError, ArgumentError, LoadError => e
  940. Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
  941. end
  942. # Return the key for the sass file.
  943. #
  944. # The `(sass_dirname, sass_basename)` pair
  945. # should uniquely identify the Sass document,
  946. # but otherwise there are no restrictions on their content.
  947. #
  948. # @param sass_dirname [String]
  949. # The fully-expanded location of the Sass file.
  950. # This corresponds to the directory name on a filesystem.
  951. # @param sass_basename [String] The name of the Sass file that is being referenced.
  952. # This corresponds to the basename on a filesystem.
  953. def key(sass_dirname, sass_basename)
  954. dir = Digest::SHA1.hexdigest(sass_dirname)
  955. filename = "#{sass_basename}c"
  956. "#{dir}/#{filename}"
  957. end
  958. end
  959. end
  960. end
  961. module Sass
  962. module CacheStores
  963. # A backend for the Sass cache using the filesystem.
  964. class Filesystem < Base
  965. # The directory where the cached files will be stored.
  966. #
  967. # @return [String]
  968. attr_accessor :cache_location
  969. # @param cache_location [String] see \{#cache\_location}
  970. def initialize(cache_location)
  971. @cache_location = cache_location
  972. end
  973. # @see Base#\_retrieve
  974. def _retrieve(key, version, sha)
  975. return unless File.readable?(path_to(key))
  976. contents = nil
  977. File.open(path_to(key), "rb") do |f|
  978. if f.readline("\n").strip == version && f.readline("\n").strip == sha
  979. return f.read
  980. end
  981. end
  982. File.unlink path_to(key)
  983. nil
  984. rescue EOFError, TypeError, ArgumentError => e
  985. Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
  986. end
  987. # @see Base#\_store
  988. def _store(key, version, sha, contents)
  989. # return unless File.writable?(File.dirname(@cache_location))
  990. # return if File.exists?(@cache_location) && !File.writable?(@cache_location)
  991. compiled_filename = path_to(key)
  992. # return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
  993. # return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
  994. FileUtils.mkdir_p(File.dirname(compiled_filename))
  995. File.open(compiled_filename, "wb") do |f|
  996. f.puts(version)
  997. f.puts(sha)
  998. f.write(contents)
  999. end
  1000. rescue Errno::EACCES
  1001. #pass
  1002. end
  1003. private
  1004. # Returns the path to a file for the given key.
  1005. #
  1006. # @param key [String]
  1007. # @return [String] The path to the cache file.
  1008. def path_to(key)
  1009. File.join(cache_location, key)
  1010. end
  1011. end
  1012. end
  1013. end
  1014. module Sass
  1015. module CacheStores
  1016. # A backend for the Sass cache using in-process memory.
  1017. class Memory < Base
  1018. # Since the {Memory} store is stored in the Sass tree's options hash,
  1019. # when the options get serialized as part of serializing the tree,
  1020. # you get crazy exponential growth in the size of the cached objects
  1021. # unless you don't dump the cache.
  1022. #
  1023. # @private
  1024. def _dump(depth)
  1025. ""
  1026. end
  1027. # If we deserialize this class, just make a new empty one.
  1028. #
  1029. # @private
  1030. def self._load(repr)
  1031. Memory.new
  1032. end
  1033. # Create a new, empty cache store.
  1034. def initialize
  1035. @contents = {}
  1036. end
  1037. # @see Base#retrieve
  1038. def retrieve(key, sha)
  1039. if @contents.has_key?(key)
  1040. return unless @contents[key][:sha] == sha
  1041. obj = @contents[key][:obj]
  1042. obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
  1043. end
  1044. end
  1045. # @see Base#store
  1046. def store(key, sha, obj)
  1047. @contents[key] = {:sha => sha, :obj => obj}
  1048. end
  1049. # Destructively clear the cache.
  1050. def reset!
  1051. @contents = {}
  1052. end
  1053. end
  1054. end
  1055. end
  1056. module Sass
  1057. module CacheStores
  1058. # A meta-cache that chains multiple caches together.
  1059. # Specifically:
  1060. #
  1061. # * All `#store`s are passed to all caches.
  1062. # * `#retrieve`s are passed to each cache until one has a hit.
  1063. # * When one cache has a hit, the value is `#store`d in all earlier caches.
  1064. class Chain < Base
  1065. # Create a new cache chaining the given caches.
  1066. #
  1067. # @param caches [Array<Sass::CacheStores::Base>] The caches to chain.
  1068. def initialize(*caches)
  1069. @caches = caches
  1070. end
  1071. # @see Base#store
  1072. def store(key, sha, obj)
  1073. @caches.each {|c| c.store(key, sha, obj)}
  1074. end
  1075. # @see Base#retrieve
  1076. def retrieve(key, sha)
  1077. @caches.each_with_index do |c, i|
  1078. next unless obj = c.retrieve(key, sha)
  1079. @caches[0...i].each {|c| c.store(key, sha, obj)}
  1080. return obj
  1081. end
  1082. nil
  1083. end
  1084. end
  1085. end
  1086. end
  1087. module Sass
  1088. # A namespace for nodes in the Sass parse tree.
  1089. #
  1090. # The Sass parse tree has three states: dynamic, static Sass, and static CSS.
  1091. #
  1092. # When it's first parsed, a Sass document is in the dynamic state.
  1093. # It has nodes for mixin definitions and `@for` loops and so forth,
  1094. # in addition to nodes for CSS rules and properties.
  1095. # Nodes that only appear in this state are called **dynamic nodes**.
  1096. #
  1097. # {Tree::Visitors::Perform} creates a static Sass tree, which is different.
  1098. # It still has nodes for CSS rules and properties
  1099. # but it doesn't have any dynamic-generation-related nodes.
  1100. # The nodes in this state are in the same structure as the Sass document:
  1101. # rules and properties are nested beneath one another.
  1102. # Nodes that can be in this state or in the dynamic state
  1103. # are called **static nodes**.
  1104. #
  1105. # {Tree::Visitors::Cssize} is then used to create a static CSS tree.
  1106. # This is like a static Sass tree,
  1107. # but the structure exactly mirrors that of the generated CSS.
  1108. # Rules and properties can't be nested beneath one another in this state.
  1109. #
  1110. # Finally, {Tree::Visitors::ToCss} can be called on a static CSS tree
  1111. # to get the actual CSS code as a string.
  1112. module Tree
  1113. # The abstract superclass of all parse-tree nodes.
  1114. class Node
  1115. include Enumerable
  1116. # The child nodes of this node.
  1117. #
  1118. # @return [Array<Tree::Node>]
  1119. attr_accessor :children
  1120. # Whether or not this node has child nodes.
  1121. # This may be true even when \{#children} is empty,
  1122. # in which case this node has an empty block (e.g. `{}`).
  1123. #
  1124. # @return [Boolean]
  1125. attr_accessor :has_children
  1126. # The line of the document on which this node appeared.
  1127. #
  1128. # @return [Fixnum]
  1129. attr_accessor :line
  1130. # The name of the document on which this node appeared.
  1131. #
  1132. # @return [String]
  1133. attr_writer :filename
  1134. # The options hash for the node.
  1135. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  1136. #
  1137. # @return [{Symbol => Object}]
  1138. attr_reader :options
  1139. def initialize
  1140. @children = []
  1141. end
  1142. # Sets the options hash for the node and all its children.
  1143. #
  1144. # @param options [{Symbol => Object}] The options
  1145. # @see #options
  1146. def options=(options)
  1147. children.each {|c| c.options = options}
  1148. @options = options
  1149. end
  1150. # @private
  1151. def children=(children)
  1152. self.has_children ||= !children.empty?
  1153. @children = children
  1154. end
  1155. # The name of the document on which this node appeared.
  1156. #
  1157. # @return [String]
  1158. def filename
  1159. @filename || (@options && @options[:filename])
  1160. end
  1161. # Appends a child to the node.
  1162. #
  1163. # @param child [Tree::Node, Array<Tree::Node>] The child node or nodes
  1164. # @raise [Sass::SyntaxError] if `child` is invalid
  1165. def <<(child)
  1166. return if child.nil?
  1167. if child.is_a?(Array)
  1168. child.each {|c| self << c}
  1169. else
  1170. self.has_children = true
  1171. @children << child
  1172. end
  1173. end
  1174. # Compares this node and another object (only other {Tree::Node}s will be equal).
  1175. # This does a structural comparison;
  1176. # if the contents of the nodes and all the child nodes are equivalent,
  1177. # then the nodes are as well.
  1178. #
  1179. # Only static nodes need to override this.
  1180. #
  1181. # @param other [Object] The object to compare with
  1182. # @return [Boolean] Whether or not this node and the other object
  1183. # are the same
  1184. # @see Sass::Tree
  1185. def ==(other)
  1186. self.class == other.class && other.children == children
  1187. end
  1188. # True if \{#to\_s} will return `nil`;
  1189. # that is, if the node shouldn't be rendered.
  1190. # Should only be called in a static tree.
  1191. #
  1192. # @return [Boolean]
  1193. def invisible?; false; end
  1194. # The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  1195. #
  1196. # @return [Symbol]
  1197. def style
  1198. @options[:style]
  1199. end
  1200. # Computes the CSS corresponding to this static CSS tree.
  1201. #
  1202. # @return [String, nil] The resulting CSS
  1203. # @see Sass::Tree
  1204. def to_s
  1205. Sass::Tree::Visitors::ToCss.visit(self)
  1206. end
  1207. # Converts a static CSS tree (e.g. the output of \{Tree::Visitors::Cssize})
  1208. # into another static CSS tree,
  1209. # with the given extensions applied to all relevant {RuleNode}s.
  1210. #
  1211. # @todo Link this to the reference documentation on `@extend`
  1212. # when such a thing exists.
  1213. #
  1214. # @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  1215. # The extensions to perform on this tree
  1216. # @return [Tree::Node] The resulting tree of static CSS nodes.
  1217. # @raise [Sass::SyntaxError] Only if there's a programmer error
  1218. # and this is not a static CSS tree
  1219. def do_extend(extends)
  1220. node = dup
  1221. node.children = children.map {|c| c.do_extend(extends)}
  1222. node
  1223. rescue Sass::SyntaxError => e
  1224. e.modify_backtrace(:filename => filename, :line => line)
  1225. raise e
  1226. end
  1227. # Iterates through each node in the tree rooted at this node
  1228. # in a pre-order walk.
  1229. #
  1230. # @yield node
  1231. # @yieldparam node [Node] a node in the tree
  1232. def each(&block)
  1233. yield self
  1234. children.each {|c| c.each(&block)}
  1235. end
  1236. # Converts a node to Sass code that will generate it.
  1237. #
  1238. # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  1239. # @return [String] The Sass code corresponding to the node
  1240. def to_sass(options = {})
  1241. Sass::Tree::Visitors::Convert.visit(self, options, :sass)
  1242. end
  1243. # Converts a node to SCSS code that will generate it.
  1244. #
  1245. # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
  1246. # @return [String] The Sass code corresponding to the node
  1247. def to_scss(options = {})
  1248. Sass::Tree::Visitors::Convert.visit(self, options, :scss)
  1249. end
  1250. # Return a deep clone of this node.
  1251. # The child nodes are cloned, but options are not.
  1252. #
  1253. # @return [Node]
  1254. def deep_copy
  1255. node = dup
  1256. node.children = children.map {|c| c.deep_copy}
  1257. node
  1258. end
  1259. protected
  1260. # @see Sass::Shared.balance
  1261. # @raise [Sass::SyntaxError] if the brackets aren't balanced
  1262. def balance(*args)
  1263. res = Sass::Shared.balance(*args)
  1264. return res if res
  1265. raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
  1266. end
  1267. end
  1268. end
  1269. end
  1270. module Sass
  1271. module Tree
  1272. # A static node that is the root node of the Sass document.
  1273. class RootNode < Node
  1274. # The Sass template from which this node was created
  1275. #
  1276. # @param template [String]
  1277. attr_reader :template
  1278. # @param template [String] The Sass template from which this node was created
  1279. def initialize(template)
  1280. super()
  1281. @template = template
  1282. end
  1283. # Runs the dynamic Sass code *and* computes the CSS for the tree.
  1284. # @see #to_s
  1285. def render
  1286. Visitors::CheckNesting.visit(self)
  1287. result = Visitors::Perform.visit(self)
  1288. Visitors::CheckNesting.visit(result) # Check again to validate mixins
  1289. result, extends = Visitors::Cssize.visit(result)
  1290. result = result.do_extend(extends) unless extends.empty?
  1291. result.to_s
  1292. end
  1293. end
  1294. end
  1295. end
  1296. require 'pathname'
  1297. require 'uri'
  1298. module Sass::Tree
  1299. # A static node reprenting a CSS rule.
  1300. #
  1301. # @see Sass::Tree
  1302. class RuleNode < Node
  1303. # The character used to include the parent selector
  1304. PARENT = '&'
  1305. # The CSS selector for this rule,
  1306. # interspersed with {Sass::Script::Node}s
  1307. # representing `#{}`-interpolation.
  1308. # Any adjacent strings will be merged together.
  1309. #
  1310. # @return [Array<String, Sass::Script::Node>]
  1311. attr_accessor :rule
  1312. # The CSS selector for this rule,
  1313. # without any unresolved interpolation
  1314. # but with parent references still intact.
  1315. # It's only set once {Tree::Node#perform} has been called.
  1316. #
  1317. # @return [Selector::CommaSequence]
  1318. attr_accessor :parsed_rules
  1319. # The CSS selector for this rule,
  1320. # without any unresolved interpolation or parent references.
  1321. # It's only set once {Tree::Visitors::Cssize} has been run.
  1322. #
  1323. # @return [Selector::CommaSequence]
  1324. attr_accessor :resolved_rules
  1325. # How deep this rule is indented
  1326. # relative to a base-level rule.
  1327. # This is only greater than 0 in the case that:
  1328. #
  1329. # * This node is in a CSS tree
  1330. # * The style is :nested
  1331. # * This is a child rule of another rule
  1332. # * The parent rule has properties, and thus will be rendered
  1333. #
  1334. # @return [Fixnum]
  1335. attr_accessor :tabs
  1336. # Whether or not this rule is the last rule in a nested group.
  1337. # This is only set in a CSS tree.
  1338. #
  1339. # @return [Boolean]
  1340. attr_accessor :group_end
  1341. # @param rule [Array<String, Sass::Script::Node>]
  1342. # The CSS rule. See \{#rule}
  1343. def initialize(rule)
  1344. merged = Sass::Util.merge_adjacent_strings(rule)
  1345. @rule = Sass::Util.strip_string_array(merged)
  1346. @tabs = 0
  1347. try_to_parse_non_interpolated_rules
  1348. super()
  1349. end
  1350. # If we've precached the parsed selector, set the line on it, too.
  1351. def line=(line)
  1352. @parsed_rules.line = line if @parsed_rules
  1353. super
  1354. end
  1355. # If we've precached the parsed selector, set the filename on it, too.
  1356. def filename=(filename)
  1357. @parsed_rules.filename = filename if @parsed_rules
  1358. super
  1359. end
  1360. # Compares the contents of two rules.
  1361. #
  1362. # @param other [Object] The object to compare with
  1363. # @return [Boolean] Whether or not this node and the other object
  1364. # are the same
  1365. def ==(other)
  1366. self.class == other.class && rule == other.rule && super
  1367. end
  1368. # Adds another {RuleNode}'s rules to this one's.
  1369. #
  1370. # @param node [RuleNode] The other node
  1371. def add_rules(node)
  1372. @rule = Sass::Util.strip_string_array(
  1373. Sass::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
  1374. try_to_parse_non_interpolated_rules
  1375. end
  1376. # @return [Boolean] Whether or not this rule is continued on the next line
  1377. def continued?
  1378. last = @rule.last
  1379. last.is_a?(String) && last[-1] == ?,
  1380. end
  1381. # Extends this Rule's selector with the given `extends`.
  1382. #
  1383. # @see Node#do_extend
  1384. def do_extend(extends)
  1385. node = dup
  1386. node.resolved_rules = resolved_rules.do_extend(extends)
  1387. node
  1388. end
  1389. # A hash that will be associated with this rule in the CSS document
  1390. # if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.
  1391. # This data is used by e.g. [the FireSass Firebug extension](https://addons.mozilla.org/en-US/firefox/addon/103988).
  1392. #
  1393. # @return [{#to_s => #to_s}]
  1394. def debug_info
  1395. {:filename => filename && ("file://" + URI.escape(File.expand_path(filename))),
  1396. :line => self.line}
  1397. end
  1398. private
  1399. def try_to_parse_non_interpolated_rules
  1400. if @rule.all? {|t| t.kind_of?(String)}
  1401. # We don't use real filename/line info because we don't have it yet.
  1402. # When we get it, we'll set it on the parsed rules if possible.
  1403. parser = Sass::SCSS::StaticParser.new(@rule.join.strip, 1)
  1404. @parsed_rules = parser.parse_selector('') rescue nil
  1405. end
  1406. end
  1407. end
  1408. end
  1409. module Sass::Tree
  1410. # A static node representing a Sass comment (silent or loud).
  1411. #
  1412. # @see Sass::Tree
  1413. class CommentNode < Node
  1414. # The text of the comment, not including `/*` and `*/`.
  1415. #
  1416. # @return [String]
  1417. attr_accessor :value
  1418. # Whether the comment is loud.
  1419. #
  1420. # Loud comments start with ! and force the comment to be generated
  1421. # irrespective of compilation settings or the comment syntax used.
  1422. #
  1423. # @return [Boolean]
  1424. attr_accessor :loud
  1425. # Whether or not the comment is silent (that is, doesn't output to CSS).
  1426. #
  1427. # @return [Boolean]
  1428. attr_accessor :silent
  1429. # @param value [String] See \{#value}
  1430. # @param silent [Boolean] See \{#silent}
  1431. def initialize(value, silent)
  1432. @lines = []
  1433. @silent = silent
  1434. @value = normalize_indentation value
  1435. @loud = @value =~ %r{^(/[\/\*])?!}
  1436. @value.sub!("#{$1}!", $1.to_s) if @loud
  1437. super()
  1438. end
  1439. # Compares the contents of two comments.
  1440. #
  1441. # @param other [Object] The object to compare with
  1442. # @return [Boolean] Whether or not this node and the other object
  1443. # are the same
  1444. def ==(other)
  1445. self.class == other.class && value == other.value && silent == other.silent
  1446. end
  1447. # Returns `true` if this is a silent comment
  1448. # or the current style doesn't render comments.
  1449. #
  1450. # Comments starting with ! are never invisible (and the ! is removed from the output.)
  1451. #
  1452. # @return [Boolean]
  1453. def invisible?
  1454. if @loud
  1455. return false
  1456. else
  1457. @silent || (style == :compressed)
  1458. end
  1459. end
  1460. # Returns whether this comment should be interpolated for dynamic comment generation.
  1461. def evaluated?
  1462. @loud
  1463. end
  1464. private
  1465. def normalize_indentation(str)
  1466. pre = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
  1467. line[/^[ \t]*/].split("").zip(pre).inject([]) do |arr, (a, b)|
  1468. break arr if a != b
  1469. arr + [a]
  1470. end
  1471. end.join
  1472. str.gsub(/^#{pre}/, '')
  1473. end
  1474. end
  1475. end
  1476. module Sass::Tree
  1477. # A static node reprenting a CSS property.
  1478. #
  1479. # @see Sass::Tree
  1480. class PropNode < Node
  1481. # The name of the property,
  1482. # interspersed with {Sass::Script::Node}s
  1483. # representing `#{}`-interpolation.
  1484. # Any adjacent strings will be merged together.
  1485. #
  1486. # @return [Array<String, Sass::Script::Node>]
  1487. attr_accessor :name
  1488. # The name of the property
  1489. # after any interpolated SassScript has been resolved.
  1490. # Only set once \{Tree::Visitors::Perform} has been run.
  1491. #
  1492. # @return [String]
  1493. attr_accessor :resolved_name
  1494. # The value of the property.
  1495. #
  1496. # @return [Sass::Script::Node]
  1497. attr_accessor :value
  1498. # The value of the property
  1499. # after any interpolated SassScript has been resolved.
  1500. # Only set once \{Tree::Visitors::Perform} has been run.
  1501. #
  1502. # @return [String]
  1503. attr_accessor :resolved_value
  1504. # How deep this property is indented
  1505. # relative to a normal property.
  1506. # This is only greater than 0 in the case that:
  1507. #
  1508. # * This node is in a CSS tree
  1509. # * The style is :nested
  1510. # * This is a child property of another property
  1511. # * The parent property has a value, and thus will be rendered
  1512. #
  1513. # @return [Fixnum]
  1514. attr_accessor :tabs
  1515. # @param name [Array<String, Sass::Script::Node>] See \{#name}
  1516. # @param value [Sass::Script::Node] See \{#value}
  1517. # @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
  1518. # `:old` if it uses `:a b`-style syntax
  1519. def initialize(name, value, prop_syntax)
  1520. @name = Sass::Util.strip_string_array(
  1521. Sass::Util.merge_adjacent_strings(name))
  1522. @value = value
  1523. @tabs = 0
  1524. @prop_syntax = prop_syntax
  1525. super()
  1526. end
  1527. # Compares the names and values of two properties.
  1528. #
  1529. # @param other [Object] The object to compare with
  1530. # @return [Boolean] Whether or not this node and the other object
  1531. # are the same
  1532. def ==(other)
  1533. self.class == other.class && name == other.name && value == other.value && super
  1534. end
  1535. # Returns a appropriate message indicating how to escape pseudo-class selectors.
  1536. # This only applies for old-style properties with no value,
  1537. # so returns the empty string if this is new-style.
  1538. #
  1539. # @return [String] The message
  1540. def pseudo_class_selector_message
  1541. return "" if @prop_syntax == :new || !value.is_a?(Sass::Script::String) || !value.value.empty?
  1542. "\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
  1543. end
  1544. # Computes the Sass or SCSS code for the variable declaration.
  1545. # This is like \{#to\_scss} or \{#to\_sass},
  1546. # except it doesn't print any child properties or a trailing semicolon.
  1547. #
  1548. # @param opts [{Symbol => Object}] The options hash for the tree.
  1549. # @param fmt [Symbol] `:scss` or `:sass`.
  1550. def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
  1551. name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
  1552. if name[0] == ?:
  1553. raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\" hack is not allowed in the Sass indented syntax")
  1554. end
  1555. old = opts[:old] && fmt == :sass
  1556. initial = old ? ':' : ''
  1557. mid = old ? '' : ':'
  1558. "#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
  1559. end
  1560. private
  1561. def check!
  1562. if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
  1563. raise Sass::SyntaxError.new(
  1564. "Illegal property syntax: can't use #{@prop_syntax} syntax when :property_syntax => #{@options[:property_syntax].inspect} is set.")
  1565. elsif resolved_value.empty?
  1566. raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value)." +
  1567. pseudo_class_selector_message)
  1568. end
  1569. end
  1570. class << self
  1571. # @private
  1572. def val_to_sass(value, opts)
  1573. val_to_sass_comma(value, opts).to_sass(opts)
  1574. end
  1575. private
  1576. def val_to_sass_comma(node, opts)
  1577. return node unless node.is_a?(Sass::Script::Operation)
  1578. return val_to_sass_concat(node, opts) unless node.operator == :comma
  1579. Sass::Script::Operation.new(
  1580. val_to_sass_concat(node.operand1, opts),
  1581. val_to_sass_comma(node.operand2, opts),
  1582. node.operator)
  1583. end
  1584. def val_to_sass_concat(node, opts)
  1585. return node unless node.is_a?(Sass::Script::Operation)
  1586. return val_to_sass_div(node, opts) unless node.operator == :space
  1587. Sass::Script::Operation.new(
  1588. val_to_sass_div(node.operand1, opts),
  1589. val_to_sass_concat(node.operand2, opts),
  1590. node.operator)
  1591. end
  1592. def val_to_sass_div(node, opts)
  1593. unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
  1594. node.operand1.is_a?(Sass::Script::Number) &&
  1595. node.operand2.is_a?(Sass::Script::Number) &&
  1596. (!node.operand1.original || !node.operand2.original)
  1597. return node
  1598. end
  1599. Sass::Script::String.new("(#{node.to_sass(opts)})")
  1600. end
  1601. end
  1602. end
  1603. end
  1604. module Sass::Tree
  1605. # A static node representing an unproccessed Sass `@`-directive.
  1606. # Directives known to Sass, like `@for` and `@debug`,
  1607. # are handled by their own nodes;
  1608. # only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
  1609. #
  1610. # `@import` and `@charset` are special cases;
  1611. # they become {ImportNode}s and {CharsetNode}s, respectively.
  1612. #
  1613. # @see Sass::Tree
  1614. class DirectiveNode < Node
  1615. # The text of the directive, `@` and all.
  1616. #
  1617. # @return [String]
  1618. attr_accessor :value
  1619. # @param value [String] See \{#value}
  1620. def initialize(value)
  1621. @value = value
  1622. super()
  1623. end
  1624. end
  1625. end
  1626. module Sass::Tree
  1627. # A static node representing a `@media` rule.
  1628. # `@media` rules behave differently from other directives
  1629. # in that when they're nested within rules,
  1630. # they bubble up to top-level.
  1631. #
  1632. # @see Sass::Tree
  1633. class MediaNode < DirectiveNode
  1634. # The media query (e.g. `print` or `screen`).
  1635. #
  1636. # @return [String]
  1637. attr_accessor :query
  1638. # @see RuleNode#tabs
  1639. attr_accessor :tabs
  1640. # @see RuleNode#group_end
  1641. attr_accessor :group_end
  1642. # @param query [String] See \{#query}
  1643. def initialize(query)
  1644. @query = query
  1645. @tabs = 0
  1646. super('')
  1647. end
  1648. # @see DirectiveNode#value
  1649. def value
  1650. "@media #{query}"
  1651. end
  1652. end
  1653. end
  1654. module Sass
  1655. module Tree
  1656. # A dynamic node representing a variable definition.
  1657. #
  1658. # @see Sass::Tree
  1659. class VariableNode < Node
  1660. # The name of the variable.
  1661. # @return [String]
  1662. attr_reader :name
  1663. # The parse tree for the variable value.
  1664. # @return [Script::Node]
  1665. attr_reader :expr
  1666. # Whether this is a guarded variable assignment (`!default`).
  1667. # @return [Boolean]
  1668. attr_reader :guarded
  1669. # @param name [String] The name of the variable
  1670. # @param expr [Script::Node] See \{#expr}
  1671. # @param guarded [Boolean] See \{#guarded}
  1672. def initialize(name, expr, guarded)
  1673. @name = name
  1674. @expr = expr
  1675. @guarded = guarded
  1676. super()
  1677. end
  1678. end
  1679. end
  1680. end
  1681. module Sass
  1682. module Tree
  1683. # A dynamic node representing a mixin definition.
  1684. #
  1685. # @see Sass::Tree
  1686. class MixinDefNode < Node
  1687. # The mixin name.
  1688. # @return [String]
  1689. attr_reader :name
  1690. # The arguments for the mixin.
  1691. # Each element is a tuple containing the variable for argument
  1692. # and the parse tree for the default value of the argument.
  1693. #
  1694. # @return [Array<(Script::Node, Script::Node)>]
  1695. attr_reader :args
  1696. # @param name [String] The mixin name
  1697. # @param args [Array<(Script::Node, Script::Node)>] See \{#args}
  1698. def initialize(name, args)
  1699. @name = name
  1700. @args = args
  1701. super()
  1702. end
  1703. end
  1704. end
  1705. end
  1706. module Sass::Tree
  1707. # A static node representing a mixin include.
  1708. # When in a static tree, the sole purpose is to wrap exceptions
  1709. # to add the mixin to the backtrace.
  1710. #
  1711. # @see Sass::Tree
  1712. class MixinNode < Node
  1713. # The name of the mixin.
  1714. # @return [String]
  1715. attr_reader :name
  1716. # The arguments to the mixin.
  1717. # @return [Array<Script::Node>]
  1718. attr_reader :args
  1719. # A hash from keyword argument names to values.
  1720. # @return [{String => Script::Node}]
  1721. attr_reader :keywords
  1722. # @param name [String] The name of the mixin
  1723. # @param args [Array<Script::Node>] See \{#args}
  1724. # @param keywords [{String => Script::Node}] See \{#keywords}
  1725. def initialize(name, args, keywords)
  1726. @name = name
  1727. @args = args
  1728. @keywords = keywords
  1729. super()
  1730. end
  1731. end
  1732. end
  1733. module Sass
  1734. module Tree
  1735. # A dynamic node representing a function definition.
  1736. #
  1737. # @see Sass::Tree
  1738. class FunctionNode < Node
  1739. # The name of the function.
  1740. # @return [String]
  1741. attr_reader :name
  1742. # The arguments to the function. Each element is a tuple
  1743. # containing the variable for argument and the parse tree for
  1744. # the default value of the argument
  1745. #
  1746. # @return [Array<Script::Node>]
  1747. attr_reader :args
  1748. # @param name [String] The function name
  1749. # @param args [Array<(Script::Node, Script::Node)>] The arguments for the function.
  1750. def initialize(name, args)
  1751. @name = name
  1752. @args = args
  1753. super()
  1754. end
  1755. end
  1756. end
  1757. end
  1758. module Sass
  1759. module Tree
  1760. # A dynamic node representing returning from a function.
  1761. #
  1762. # @see Sass::Tree
  1763. class ReturnNode < Node
  1764. # The expression to return.
  1765. # @type [Script::Node]
  1766. attr_reader :expr
  1767. # @param expr [Script::Node] The expression to return
  1768. def initialize(expr)
  1769. @expr = expr
  1770. super()
  1771. end
  1772. end
  1773. end
  1774. end
  1775. module Sass::Tree
  1776. # A static node reprenting an `@extend` directive.
  1777. #
  1778. # @see Sass::Tree
  1779. class ExtendNode < Node
  1780. # The parsed selector after interpolation has been resolved.
  1781. # Only set once {Tree::Visitors::Perform} has been run.
  1782. #
  1783. # @return [Selector::CommaSequence]
  1784. attr_accessor :resolved_selector
  1785. # The CSS selector to extend, interspersed with {Sass::Script::Node}s
  1786. # representing `#{}`-interpolation.
  1787. #
  1788. # @return [Array<String, Sass::Script::Node>]
  1789. attr_reader :selector
  1790. # @param selector [Array<String, Sass::Script::Node>]
  1791. # The CSS selector to extend,
  1792. # interspersed with {Sass::Script::Node}s
  1793. # representing `#{}`-interpolation.
  1794. def initialize(selector)
  1795. @selector = selector
  1796. super()
  1797. end
  1798. end
  1799. end
  1800. module Sass::Tree
  1801. # A dynamic node representing a Sass `@if` statement.
  1802. #
  1803. # {IfNode}s are a little odd, in that they also represent `@else` and `@else if`s.
  1804. # This is done as a linked list:
  1805. # each {IfNode} has a link (\{#else}) to the next {IfNode}.
  1806. #
  1807. # @see Sass::Tree
  1808. class IfNode < Node
  1809. # The conditional expression.
  1810. # If this is nil, this is an `@else` node, not an `@else if`.
  1811. #
  1812. # @return [Script::Expr]
  1813. attr_reader :expr
  1814. # The next {IfNode} in the if-else list, or `nil`.
  1815. #
  1816. # @return [IfNode]
  1817. attr_accessor :else
  1818. # @param expr [Script::Expr] See \{#expr}
  1819. def initialize(expr)
  1820. @expr = expr
  1821. @last_else = self
  1822. super()
  1823. end
  1824. # Append an `@else` node to the end of the list.
  1825. #
  1826. # @param node [IfNode] The `@else` node to append
  1827. def add_else(node)
  1828. @last_else.else = node
  1829. @last_else = node
  1830. end
  1831. # @see Node#options=
  1832. def options=(options)
  1833. super
  1834. self.else.options = options if self.else
  1835. end
  1836. def _dump(f)
  1837. Marshal.dump([self.expr, self.else, self.children])
  1838. end
  1839. def self._load(data)
  1840. expr, else_, children = Marshal.load(data)
  1841. node = IfNode.new(expr)
  1842. node.else = else_
  1843. node.children = children
  1844. node.instance_variable_set('@last_else',
  1845. node.else ? node.else.instance_variable_get('@last_else') : node)
  1846. node
  1847. end
  1848. # @see Node#deep_copy
  1849. def deep_copy
  1850. node = super
  1851. node.else = self.else.deep_copy if self.else
  1852. node
  1853. end
  1854. end
  1855. end
  1856. module Sass::Tree
  1857. # A dynamic node representing a Sass `@while` loop.
  1858. #
  1859. # @see Sass::Tree
  1860. class WhileNode < Node
  1861. # The parse tree for the continuation expression.
  1862. # @return [Script::Node]
  1863. attr_reader :expr
  1864. # @param expr [Script::Node] See \{#expr}
  1865. def initialize(expr)
  1866. @expr = expr
  1867. super()
  1868. end
  1869. end
  1870. end
  1871. module Sass::Tree
  1872. # A dynamic node representing a Sass `@for` loop.
  1873. #
  1874. # @see Sass::Tree
  1875. class ForNode < Node
  1876. # The name of the loop variable.
  1877. # @return [String]
  1878. attr_reader :var
  1879. # The parse tree for the initial expression.
  1880. # @return [Script::Node]
  1881. attr_reader :from
  1882. # The parse tree for the final expression.
  1883. # @return [Script::Node]
  1884. attr_reader :to
  1885. # Whether to include `to` in the loop or stop just before.
  1886. # @return [Boolean]
  1887. attr_reader :exclusive
  1888. # @param var [String] See \{#var}
  1889. # @param from [Script::Node] See \{#from}
  1890. # @param to [Script::Node] See \{#to}
  1891. # @param exclusive [Boolean] See \{#exclusive}
  1892. def initialize(var, from, to, exclusive)
  1893. @var = var
  1894. @from = from
  1895. @to = to
  1896. @exclusive = exclusive
  1897. super()
  1898. end
  1899. end
  1900. end
  1901. module Sass::Tree
  1902. # A dynamic node representing a Sass `@each` loop.
  1903. #
  1904. # @see Sass::Tree
  1905. class EachNode < Node
  1906. # The name of the loop variable.
  1907. # @return [String]
  1908. attr_reader :var
  1909. # The parse tree for the list.
  1910. # @param [Script::Node]
  1911. attr_reader :list
  1912. # @param var [String] The name of the loop variable
  1913. # @param list [Script::Node] The parse tree for the list
  1914. def initialize(var, list)
  1915. @var = var
  1916. @list = list
  1917. super()
  1918. end
  1919. end
  1920. end
  1921. module Sass
  1922. module Tree
  1923. # A dynamic node representing a Sass `@debug` statement.
  1924. #
  1925. # @see Sass::Tree
  1926. class DebugNode < Node
  1927. # The expression to print.
  1928. # @return [Script::Node]
  1929. attr_reader :expr
  1930. # @param expr [Script::Node] The expression to print
  1931. def initialize(expr)
  1932. @expr = expr
  1933. super()
  1934. end
  1935. end
  1936. end
  1937. end
  1938. module Sass
  1939. module Tree
  1940. # A dynamic node representing a Sass `@warn` statement.
  1941. #
  1942. # @see Sass::Tree
  1943. class WarnNode < Node
  1944. # The expression to print.
  1945. # @return [Script::Node]
  1946. attr_reader :expr
  1947. # @param expr [Script::Node] The expression to print
  1948. def initialize(expr)
  1949. @expr = expr
  1950. super()
  1951. end
  1952. end
  1953. end
  1954. end
  1955. module Sass
  1956. module Tree
  1957. # A static node that wraps the {Sass::Tree} for an `@import`ed file.
  1958. # It doesn't have a functional purpose other than to add the `@import`ed file
  1959. # to the backtrace if an error occurs.
  1960. class ImportNode < RootNode
  1961. # The name of the imported file as it appears in the Sass document.
  1962. #
  1963. # @return [String]
  1964. attr_reader :imported_filename
  1965. # @param imported_filename [String] The name of the imported file
  1966. def initialize(imported_filename)
  1967. @imported_filename = imported_filename
  1968. super(nil)
  1969. end
  1970. def invisible?; to_s.empty?; end
  1971. # Returns the imported file.
  1972. #
  1973. # @return [Sass::Engine]
  1974. # @raise [Sass::SyntaxError] If no file could be found to import.
  1975. def imported_file
  1976. @imported_file ||= import
  1977. end
  1978. # Returns whether or not this import should emit a CSS @import declaration
  1979. #
  1980. # @return [Boolean] Whether or not this is a simple CSS @import declaration.
  1981. def css_import?
  1982. if @imported_filename =~ /\.css$/
  1983. @imported_filename
  1984. elsif imported_file.is_a?(String) && imported_file =~ /\.css$/
  1985. imported_file
  1986. end
  1987. end
  1988. private
  1989. def import
  1990. paths = @options[:load_paths]
  1991. if @options[:importer]
  1992. f = @options[:importer].find_relative(
  1993. @imported_filename, @options[:filename], @options.dup)
  1994. return f if f
  1995. end
  1996. paths.each do |p|
  1997. if f = p.find(@imported_filename, @options.dup)
  1998. return f
  1999. end
  2000. end
  2001. message = "File to import not found or unreadable: #{@imported_filename}.\n"
  2002. if paths.size == 1
  2003. message << "Load path: #{paths.first}"
  2004. else
  2005. message << "Load paths:\n " << paths.join("\n ")
  2006. end
  2007. raise SyntaxError.new(message)
  2008. rescue SyntaxError => e
  2009. raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
  2010. end
  2011. end
  2012. end
  2013. end
  2014. module Sass::Tree
  2015. # A static node representing an unproccessed Sass `@charset` directive.
  2016. #
  2017. # @see Sass::Tree
  2018. class CharsetNode < Node
  2019. # The name of the charset.
  2020. #
  2021. # @return [String]
  2022. attr_accessor :name
  2023. # @param name [String] see \{#name}
  2024. def initialize(name)
  2025. @name = name
  2026. super()
  2027. end
  2028. # @see Node#invisible?
  2029. def invisible?
  2030. !Sass::Util.ruby1_8?
  2031. end
  2032. end
  2033. end
  2034. # Visitors are used to traverse the Sass parse tree.
  2035. # Visitors should extend {Visitors::Base},
  2036. # which provides a small amount of scaffolding for traversal.
  2037. module Sass::Tree::Visitors
  2038. # The abstract base class for Sass visitors.
  2039. # Visitors should extend this class,
  2040. # then implement `visit_*` methods for each node they care about
  2041. # (e.g. `visit_rule` for {RuleNode} or `visit_for` for {ForNode}).
  2042. # These methods take the node in question as argument.
  2043. # They may `yield` to visit the child nodes of the current node.
  2044. #
  2045. # *Note*: due to the unusual nature of {Sass::Tree::IfNode},
  2046. # special care must be taken to ensure that it is properly handled.
  2047. # In particular, there is no built-in scaffolding
  2048. # for dealing with the return value of `@else` nodes.
  2049. #
  2050. # @abstract
  2051. class Base
  2052. # Runs the visitor on a tree.
  2053. #
  2054. # @param root [Tree::Node] The root node of the Sass tree.
  2055. # @return [Object] The return value of \{#visit} for the root node.
  2056. def self.visit(root)
  2057. new.send(:visit, root)
  2058. end
  2059. protected
  2060. # Runs the visitor on the given node.
  2061. # This can be overridden by subclasses that need to do something for each node.
  2062. #
  2063. # @param node [Tree::Node] The node to visit.
  2064. # @return [Object] The return value of the `visit_*` method for this node.
  2065. def visit(node)
  2066. method = "visit_#{node_name node}"
  2067. if self.respond_to?(method)
  2068. self.send(method, node) {visit_children(node)}
  2069. else
  2070. visit_children(node)
  2071. end
  2072. end
  2073. # Visit the child nodes for a given node.
  2074. # This can be overridden by subclasses that need to do something
  2075. # with the child nodes' return values.
  2076. #
  2077. # This method is run when `visit_*` methods `yield`,
  2078. # and its return value is returned from the `yield`.
  2079. #
  2080. # @param parent [Tree::Node] The parent node of the children to visit.
  2081. # @return [Array<Object>] The return values of the `visit_*` methods for the children.
  2082. def visit_children(parent)
  2083. parent.children.map {|c| visit(c)}
  2084. end
  2085. NODE_NAME_RE = /.*::(.*?)Node$/
  2086. # Returns the name of a node as used in the `visit_*` method.
  2087. #
  2088. # @param [Tree::Node] node The node.
  2089. # @return [String] The name.
  2090. def node_name(node)
  2091. @@node_names ||= {}
  2092. @@node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
  2093. end
  2094. # `yield`s, then runs the visitor on the `@else` clause if the node has one.
  2095. # This exists to ensure that the contents of the `@else` clause get visited.
  2096. def visit_if(node)
  2097. yield
  2098. visit(node.else) if node.else
  2099. node
  2100. end
  2101. end
  2102. end
  2103. # A visitor for converting a dynamic Sass tree into a static Sass tree.
  2104. class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
  2105. # @param root [Tree::Node] The root node of the tree to visit.
  2106. # @param environment [Sass::Environment] The lexical environment.
  2107. # @return [Tree::Node] The resulting tree of static nodes.
  2108. def self.visit(root, environment = Sass::Environment.new)
  2109. new(environment).send(:visit, root)
  2110. end
  2111. protected
  2112. def initialize(env)
  2113. @environment = env
  2114. end
  2115. # If an exception is raised, this add proper metadata to the backtrace.
  2116. def visit(node)
  2117. super(node.dup)
  2118. rescue Sass::SyntaxError => e
  2119. e.modify_backtrace(:filename => node.filename, :line => node.line)
  2120. raise e
  2121. end
  2122. # Keeps track of the current environment.
  2123. def visit_children(parent)
  2124. with_environment Sass::Environment.new(@environment) do
  2125. parent.children = super.flatten
  2126. parent
  2127. end
  2128. end
  2129. # Runs a block of code with the current environment replaced with the given one.
  2130. #
  2131. # @param env [Sass::Environment] The new environment for the duration of the block.
  2132. # @yield A block in which the environment is set to `env`.
  2133. # @return [Object] The return value of the block.
  2134. def with_environment(env)
  2135. old_env, @environment = @environment, env
  2136. yield
  2137. ensure
  2138. @environment = old_env
  2139. end
  2140. # Sets the options on the environment if this is the top-level root.
  2141. def visit_root(node)
  2142. @environment.options = node.options if @environment.options.nil? || @environment.options.empty?
  2143. yield
  2144. rescue Sass::SyntaxError => e
  2145. e.sass_template ||= node.template
  2146. raise e
  2147. end
  2148. # Removes this node from the tree if it's a silent comment.
  2149. def visit_comment(node)
  2150. return [] if node.invisible?
  2151. if node.evaluated?
  2152. node.value.gsub!(/(^|[^\\])\#\{([^}]*)\}/) do |md|
  2153. $1+Sass::Script.parse($2, node.line, 0, node.options).perform(@environment).to_s
  2154. end
  2155. node.value = run_interp([Sass::Script::String.new(node.value)])
  2156. end
  2157. node
  2158. end
  2159. # Prints the expression to STDERR.
  2160. def visit_debug(node)
  2161. res = node.expr.perform(@environment)
  2162. res = res.value if res.is_a?(Sass::Script::String)
  2163. if node.filename
  2164. $stderr.puts "#{node.filename}:#{node.line} DEBUG: #{res}"
  2165. else
  2166. $stderr.puts "Line #{node.line} DEBUG: #{res}"
  2167. end
  2168. []
  2169. end
  2170. # Runs the child nodes once for each value in the list.
  2171. def visit_each(node)
  2172. list = node.list.perform(@environment)
  2173. with_environment Sass::Environment.new(@environment) do
  2174. list.to_a.map do |v|
  2175. @environment.set_local_var(node.var, v)
  2176. node.children.map {|c| visit(c)}
  2177. end.flatten
  2178. end
  2179. end
  2180. # Runs SassScript interpolation in the selector,
  2181. # and then parses the result into a {Sass::Selector::CommaSequence}.
  2182. def visit_extend(node)
  2183. parser = Sass::SCSS::CssParser.new(run_interp(node.selector), node.line)
  2184. node.resolved_selector = parser.parse_selector(node.filename)
  2185. node
  2186. end
  2187. # Runs the child nodes once for each time through the loop, varying the variable each time.
  2188. def visit_for(node)
  2189. from = node.from.perform(@environment)
  2190. to = node.to.perform(@environment)
  2191. from.assert_int!
  2192. to.assert_int!
  2193. to = to.coerce(from.numerator_units, from.denominator_units)
  2194. range = Range.new(from.to_i, to.to_i, node.exclusive)
  2195. with_environment Sass::Environment.new(@environment) do
  2196. range.map do |i|
  2197. @environment.set_local_var(node.var,
  2198. Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
  2199. node.children.map {|c| visit(c)}
  2200. end.flatten
  2201. end
  2202. end
  2203. # Loads the function into the environment.
  2204. def visit_function(node)
  2205. @environment.set_function(node.name,
  2206. Sass::Callable.new(node.name, node.args, @environment, node.children))
  2207. []
  2208. end
  2209. # Runs the child nodes if the conditional expression is true;
  2210. # otherwise, tries the else nodes.
  2211. def visit_if(node)
  2212. if node.expr.nil? || node.expr.perform(@environment).to_bool
  2213. yield
  2214. node.children
  2215. elsif node.else
  2216. visit(node.else)
  2217. else
  2218. []
  2219. end
  2220. end
  2221. # Returns a static DirectiveNode if this is importing a CSS file,
  2222. # or parses and includes the imported Sass file.
  2223. def visit_import(node)
  2224. if path = node.css_import?
  2225. return Sass::Tree::DirectiveNode.new("@import url(#{path})")
  2226. end
  2227. @environment.push_frame(:filename => node.filename, :line => node.line)
  2228. root = node.imported_file.to_tree
  2229. node.children = root.children.map {|c| visit(c)}.flatten
  2230. node
  2231. rescue Sass::SyntaxError => e
  2232. e.modify_backtrace(:filename => node.imported_file.options[:filename])
  2233. e.add_backtrace(:filename => node.filename, :line => node.line)
  2234. raise e
  2235. ensure
  2236. @environment.pop_frame
  2237. end
  2238. # Loads a mixin into the environment.
  2239. def visit_mixindef(node)
  2240. @environment.set_mixin(node.name,
  2241. Sass::Callable.new(node.name, node.args, @environment, node.children))
  2242. []
  2243. end
  2244. # Runs a mixin.
  2245. def visit_mixin(node)
  2246. handle_include_loop!(node) if @environment.mixins_in_use.include?(node.name)
  2247. original_env = @environment
  2248. original_env.push_frame(:filename => node.filename, :line => node.line)
  2249. original_env.prepare_frame(:mixin => node.name)
  2250. raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
  2251. passed_args = node.args.dup
  2252. passed_keywords = node.keywords.dup
  2253. raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < passed_args.size
  2254. Mixin #{node.name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
  2255. but #{node.args.size} #{node.args.size == 1 ? 'was' : 'were'} passed.
  2256. END
  2257. passed_keywords.each do |name, value|
  2258. # TODO: Make this fast
  2259. unless mixin.args.find {|(var, default)| var.underscored_name == name}
  2260. raise Sass::SyntaxError.new("Mixin #{node.name} doesn't have an argument named $#{name}")
  2261. end
  2262. end
  2263. environment = mixin.args.zip(passed_args).
  2264. inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
  2265. env.set_local_var(var.name,
  2266. if value
  2267. value.perform(@environment)
  2268. elsif kv = passed_keywords[var.underscored_name]
  2269. kv.perform(@environment)
  2270. elsif default
  2271. default.perform(env)
  2272. end)
  2273. raise Sass::SyntaxError.new("Mixin #{node.name} is missing parameter #{var.inspect}.") unless env.var(var.name)
  2274. env
  2275. end
  2276. with_environment(environment) {node.children = mixin.tree.map {|c| visit(c)}.flatten}
  2277. node
  2278. rescue Sass::SyntaxError => e
  2279. if original_env # Don't add backtrace info if this is an @include loop
  2280. e.modify_backtrace(:mixin => node.name, :line => node.line)
  2281. e.add_backtrace(:line => node.line)
  2282. end
  2283. raise e
  2284. ensure
  2285. original_env.pop_frame if original_env
  2286. end
  2287. # Runs any SassScript that may be embedded in a property.
  2288. def visit_prop(node)
  2289. node.resolved_name = run_interp(node.name)
  2290. val = node.value.perform(@environment)
  2291. node.resolved_value = val.to_s
  2292. yield
  2293. end
  2294. # Returns the value of the expression.
  2295. def visit_return(node)
  2296. throw :_sass_return, node.expr.perform(@environment)
  2297. end
  2298. # Runs SassScript interpolation in the selector,
  2299. # and then parses the result into a {Sass::Selector::CommaSequence}.
  2300. def visit_rule(node)
  2301. parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.line)
  2302. node.parsed_rules ||= parser.parse_selector(node.filename)
  2303. yield
  2304. end
  2305. # Loads the new variable value into the environment.
  2306. def visit_variable(node)
  2307. return [] if node.guarded && !@environment.var(node.name).nil?
  2308. val = node.expr.perform(@environment)
  2309. @environment.set_var(node.name, val)
  2310. []
  2311. end
  2312. # Prints the expression to STDERR with a stylesheet trace.
  2313. def visit_warn(node)
  2314. @environment.push_frame(:filename => node.filename, :line => node.line)
  2315. res = node.expr.perform(@environment)
  2316. res = res.value if res.is_a?(Sass::Script::String)
  2317. msg = "WARNING: #{res}\n"
  2318. @environment.stack.reverse.each_with_index do |entry, i|
  2319. msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
  2320. " of #{entry[:filename] || "an unknown file"}"
  2321. msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
  2322. msg << "\n"
  2323. end
  2324. Sass::Util.sass_warn msg
  2325. []
  2326. ensure
  2327. @environment.pop_frame
  2328. end
  2329. # Runs the child nodes until the continuation expression becomes false.
  2330. def visit_while(node)
  2331. children = []
  2332. with_environment Sass::Environment.new(@environment) do
  2333. children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
  2334. end
  2335. children.flatten
  2336. end
  2337. def visit_directive(node)
  2338. if node.value['#{']
  2339. node.value = run_interp(Sass::Engine.parse_interp(node.value, node.line, 0, node.options))
  2340. end
  2341. yield
  2342. node
  2343. end
  2344. private
  2345. def run_interp(text)
  2346. text.map do |r|
  2347. next r if r.is_a?(String)
  2348. val = r.perform(@environment)
  2349. # Interpolated strings should never render with quotes
  2350. next val.value if val.is_a?(Sass::Script::String)
  2351. val.to_s
  2352. end.join.strip
  2353. end
  2354. def handle_include_loop!(node)
  2355. msg = "An @include loop has been found:"
  2356. mixins = @environment.stack.map {|s| s[:mixin]}.compact
  2357. if mixins.size == 2 && mixins[0] == mixins[1]
  2358. raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself")
  2359. end
  2360. mixins << node.name
  2361. msg << "\n" << Sass::Util.enum_cons(mixins, 2).map do |m1, m2|
  2362. " #{m1} includes #{m2}"
  2363. end.join("\n")
  2364. raise Sass::SyntaxError.new(msg)
  2365. end
  2366. end
  2367. # A visitor for converting a static Sass tree into a static CSS tree.
  2368. class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
  2369. # @param root [Tree::Node] The root node of the tree to visit.
  2370. # @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
  2371. # *and* the extensions defined for this tree
  2372. def self.visit(root); super; end
  2373. protected
  2374. # Returns the immediate parent of the current node.
  2375. # @return [Tree::Node]
  2376. attr_reader :parent
  2377. def initialize
  2378. @extends = Sass::Util::SubsetMap.new
  2379. end
  2380. # If an exception is raised, this adds proper metadata to the backtrace.
  2381. def visit(node)
  2382. super(node.dup)
  2383. rescue Sass::SyntaxError => e
  2384. e.modify_backtrace(:filename => node.filename, :line => node.line)
  2385. raise e
  2386. end
  2387. # Keeps track of the current parent node.
  2388. def visit_children(parent)
  2389. with_parent parent do
  2390. parent.children = super.flatten
  2391. parent
  2392. end
  2393. end
  2394. # Runs a block of code with the current parent node
  2395. # replaced with the given node.
  2396. #
  2397. # @param parent [Tree::Node] The new parent for the duration of the block.
  2398. # @yield A block in which the parent is set to `parent`.
  2399. # @return [Object] The return value of the block.
  2400. def with_parent(parent)
  2401. old_parent, @parent = @parent, parent
  2402. yield
  2403. ensure
  2404. @parent = old_parent
  2405. end
  2406. # In Ruby 1.8, ensures that there's only one `@charset` directive
  2407. # and that it's at the top of the document.
  2408. #
  2409. # @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
  2410. # *and* the extensions defined for this tree
  2411. def visit_root(node)
  2412. yield
  2413. # In Ruby 1.9 we can make all @charset nodes invisible
  2414. # and infer the final @charset from the encoding of the final string.
  2415. if Sass::Util.ruby1_8? && parent.nil?
  2416. charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
  2417. node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
  2418. node.children.unshift charset if charset
  2419. end
  2420. return node, @extends
  2421. rescue Sass::SyntaxError => e
  2422. e.sass_template ||= node.template
  2423. raise e
  2424. end
  2425. # Registers an extension in the `@extends` subset map.
  2426. def visit_extend(node)
  2427. node.resolved_selector.members.each do |seq|
  2428. if seq.members.size > 1
  2429. raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
  2430. end
  2431. sseq = seq.members.first
  2432. if !sseq.is_a?(Sass::Selector::SimpleSequence)
  2433. raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
  2434. end
  2435. sel = sseq.members
  2436. parent.resolved_rules.members.each do |seq|
  2437. if !seq.members.last.is_a?(Sass::Selector::SimpleSequence)
  2438. raise Sass::SyntaxError.new("#{seq} can't extend: invalid selector")
  2439. end
  2440. @extends[sel] = seq
  2441. end
  2442. end
  2443. []
  2444. end
  2445. # Modifies exception backtraces to include the imported file.
  2446. def visit_import(node)
  2447. # Don't use #visit_children to avoid adding the import node to the list of parents.
  2448. node.children.map {|c| visit(c)}.flatten
  2449. rescue Sass::SyntaxError => e
  2450. e.modify_backtrace(:filename => node.children.first.filename)
  2451. e.add_backtrace(:filename => node.filename, :line => node.line)
  2452. raise e
  2453. end
  2454. # Bubbles the `@media` directive up through RuleNodes
  2455. # and merges it with other `@media` directives.
  2456. def visit_media(node)
  2457. if parent.is_a?(Sass::Tree::RuleNode)
  2458. new_rule = parent.dup
  2459. new_rule.children = node.children
  2460. node.children = with_parent(node) {Array(visit(new_rule))}
  2461. # If the last child is actually the end of the group,
  2462. # the parent's cssize will set it properly
  2463. node.children.last.group_end = false unless node.children.empty?
  2464. else
  2465. yield
  2466. end
  2467. media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
  2468. node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
  2469. media.each {|n| n.query = "#{node.query} and #{n.query}"}
  2470. (node.children.empty? ? [] : [node]) + media
  2471. end
  2472. # Asserts that all the mixin's children are valid in their new location.
  2473. def visit_mixin(node)
  2474. # Don't use #visit_children to avoid adding the mixin node to the list of parents.
  2475. node.children.map {|c| visit(c)}.flatten
  2476. rescue Sass::SyntaxError => e
  2477. e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
  2478. e.add_backtrace(:filename => node.filename, :line => node.line)
  2479. raise e
  2480. end
  2481. # Converts nested properties into flat properties
  2482. # and updates the indentation of the prop node based on the nesting level.
  2483. def visit_prop(node)
  2484. if parent.is_a?(Sass::Tree::PropNode)
  2485. node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
  2486. node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
  2487. end
  2488. yield
  2489. result = node.children.dup
  2490. if !node.resolved_value.empty? || node.children.empty?
  2491. node.send(:check!)
  2492. result.unshift(node)
  2493. end
  2494. result
  2495. end
  2496. # Resolves parent references and nested selectors,
  2497. # and updates the indentation of the rule node based on the nesting level.
  2498. def visit_rule(node)
  2499. parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
  2500. # It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
  2501. node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
  2502. yield
  2503. rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode)}
  2504. props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.is_a?(Sass::Tree::MediaNode) || c.invisible?}
  2505. unless props.empty?
  2506. node.children = props
  2507. rules.each {|r| r.tabs += 1} if node.style == :nested
  2508. rules.unshift(node)
  2509. end
  2510. rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
  2511. rules
  2512. end
  2513. end
  2514. # A visitor for converting a Sass tree into a source string.
  2515. class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
  2516. # Runs the visitor on a tree.
  2517. #
  2518. # @param root [Tree::Node] The root node of the Sass tree.
  2519. # @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
  2520. # @param format [Symbol] `:sass` or `:scss`.
  2521. # @return [String] The Sass or SCSS source for the tree.
  2522. def self.visit(root, options, format)
  2523. new(options, format).send(:visit, root)
  2524. end
  2525. protected
  2526. def initialize(options, format)
  2527. @options = options
  2528. @format = format
  2529. @tabs = 0
  2530. end
  2531. def visit_children(parent)
  2532. @tabs += 1
  2533. return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
  2534. (@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : " }\n")
  2535. ensure
  2536. @tabs -= 1
  2537. end
  2538. # Ensures proper spacing between top-level nodes.
  2539. def visit_root(node)
  2540. Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
  2541. visit(child) +
  2542. if nxt &&
  2543. (child.is_a?(Sass::Tree::CommentNode) &&
  2544. child.line + child.value.count("\n") + 1 == nxt.line) ||
  2545. (child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
  2546. child.line + 1 == nxt.line) ||
  2547. (child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
  2548. child.line + 1 == nxt.line)
  2549. ""
  2550. else
  2551. "\n"
  2552. end
  2553. end.join.rstrip + "\n"
  2554. end
  2555. def visit_charset(node)
  2556. "#{tab_str}@charset \"#{node.name}\"#{semi}\n"
  2557. end
  2558. def visit_comment(node)
  2559. content = if @format == :sass
  2560. content = node.value.gsub(/\*\/$/, '').rstrip
  2561. if content =~ /\A[ \t]/
  2562. # Re-indent SCSS comments like this:
  2563. # /* foo
  2564. # bar
  2565. # baz */
  2566. content.gsub!(/^/, ' ')
  2567. content.sub!(/\A([ \t]*)\/\*/, '/*\1')
  2568. end
  2569. content =
  2570. unless content.include?("\n")
  2571. content
  2572. else
  2573. content.gsub!(/\n( \*|\/\/)/, "\n ")
  2574. spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
  2575. sep = node.silent ? "\n//" : "\n *"
  2576. if spaces >= 2
  2577. content.gsub(/\n /, sep)
  2578. else
  2579. content.gsub(/\n#{' ' * spaces}/, sep)
  2580. end
  2581. end
  2582. content.gsub!(/\A\/\*/, '//') if node.silent
  2583. content.gsub!(/^/, tab_str)
  2584. content.rstrip + "\n"
  2585. else
  2586. spaces = (' ' * [@tabs - node.value[/^ */].size, 0].max)
  2587. content = if node.silent
  2588. node.value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
  2589. else
  2590. node.value
  2591. end.gsub(/^/, spaces) + "\n"
  2592. content
  2593. end
  2594. if node.loud
  2595. if node.silent
  2596. content.gsub!(%r{^\s*(//!?)}, '//!')
  2597. else
  2598. content.sub!(%r{^\s*(/\*)}, '/*!')
  2599. end
  2600. end
  2601. content
  2602. end
  2603. def visit_debug(node)
  2604. "#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
  2605. end
  2606. def visit_directive(node)
  2607. res = "#{tab_str}#{node.value}"
  2608. return res + "#{semi}\n" unless node.has_children
  2609. res + yield + "\n"
  2610. end
  2611. def visit_each(node)
  2612. "#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
  2613. end
  2614. def visit_extend(node)
  2615. "#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}\n"
  2616. end
  2617. def visit_for(node)
  2618. "#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
  2619. "#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
  2620. end
  2621. def visit_function(node)
  2622. args = node.args.map do |v, d|
  2623. d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
  2624. end.join(", ")
  2625. "#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
  2626. end
  2627. def visit_if(node)
  2628. name =
  2629. if !@is_else; "if"
  2630. elsif node.expr; "else if"
  2631. else; "else"
  2632. end
  2633. str = "#{tab_str}@#{name}"
  2634. str << " #{node.expr.to_sass(@options)}" if node.expr
  2635. str << yield
  2636. @is_else = true
  2637. str << visit(node.else) if node.else
  2638. str
  2639. ensure
  2640. @is_else = false
  2641. end
  2642. def visit_import(node)
  2643. quote = @format == :scss ? '"' : ''
  2644. "#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
  2645. end
  2646. def visit_media(node)
  2647. "#{tab_str}@media #{node.query}#{yield}"
  2648. end
  2649. def visit_mixindef(node)
  2650. args =
  2651. if node.args.empty?
  2652. ""
  2653. else
  2654. '(' + node.args.map do |v, d|
  2655. if d
  2656. "#{v.to_sass(@options)}: #{d.to_sass(@options)}"
  2657. else
  2658. v.to_sass(@options)
  2659. end
  2660. end.join(", ") + ')'
  2661. end
  2662. "#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
  2663. end
  2664. def visit_mixin(node)
  2665. unless node.args.empty? && node.keywords.empty?
  2666. args = node.args.map {|a| a.to_sass(@options)}.join(", ")
  2667. keywords = node.keywords.map {|k, v| "$#{dasherize(k)}: #{v.to_sass(@options)}"}.join(', ')
  2668. arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
  2669. end
  2670. "#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{semi}\n"
  2671. end
  2672. def visit_prop(node)
  2673. res = tab_str + node.declaration(@options, @format)
  2674. return res + semi + "\n" if node.children.empty?
  2675. res + yield.rstrip + semi + "\n"
  2676. end
  2677. def visit_return(node)
  2678. "#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
  2679. end
  2680. def visit_rule(node)
  2681. if @format == :sass
  2682. name = selector_to_sass(node.rule)
  2683. name = "\\" + name if name[0] == ?:
  2684. name.gsub(/^/, tab_str) + yield
  2685. elsif @format == :scss
  2686. name = selector_to_scss(node.rule)
  2687. res = name + yield
  2688. if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.silent
  2689. res.slice!(-3..-1)
  2690. res << "\n" << tab_str << "}\n"
  2691. end
  2692. res
  2693. end
  2694. end
  2695. def visit_variable(node)
  2696. "#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
  2697. end
  2698. def visit_warn(node)
  2699. "#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
  2700. end
  2701. def visit_while(node)
  2702. "#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
  2703. end
  2704. private
  2705. def selector_to_src(sel)
  2706. @format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
  2707. end
  2708. def selector_to_sass(sel)
  2709. sel.map do |r|
  2710. if r.is_a?(String)
  2711. r.gsub(/(,[ \t]*)?\n\s*/) {$1 ? $1 + "\n" : " "}
  2712. else
  2713. "\#{#{r.to_sass(@options)}}"
  2714. end
  2715. end.join
  2716. end
  2717. def selector_to_scss(sel)
  2718. sel.map {|r| r.is_a?(String) ? r : "\#{#{r.to_sass(@options)}}"}.
  2719. join.gsub(/^[ \t]*/, tab_str)
  2720. end
  2721. def semi
  2722. @format == :sass ? "" : ";"
  2723. end
  2724. def tab_str
  2725. ' ' * @tabs
  2726. end
  2727. def dasherize(s)
  2728. if @options[:dasherize]
  2729. s.gsub('_', '-')
  2730. else
  2731. s
  2732. end
  2733. end
  2734. end
  2735. # A visitor for converting a Sass tree into CSS.
  2736. class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
  2737. protected
  2738. def initialize
  2739. @tabs = 0
  2740. end
  2741. def visit(node)
  2742. super
  2743. rescue Sass::SyntaxError => e
  2744. e.modify_backtrace(:filename => node.filename, :line => node.line)
  2745. raise e
  2746. end
  2747. def with_tabs(tabs)
  2748. old_tabs, @tabs = @tabs, tabs
  2749. yield
  2750. ensure
  2751. @tabs = old_tabs
  2752. end
  2753. def visit_root(node)
  2754. result = String.new
  2755. node.children.each do |child|
  2756. next if child.invisible?
  2757. child_str = visit(child)
  2758. result << child_str + (node.style == :compressed ? '' : "\n")
  2759. end
  2760. result.rstrip!
  2761. return "" if result.empty?
  2762. result << "\n"
  2763. unless Sass::Util.ruby1_8? || result.ascii_only?
  2764. if node.children.first.is_a?(Sass::Tree::CharsetNode)
  2765. begin
  2766. encoding = node.children.first.name
  2767. # Default to big-endian encoding, because we have to decide somehow
  2768. encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
  2769. result = result.encode(Encoding.find(encoding))
  2770. rescue EncodingError
  2771. end
  2772. end
  2773. result = "@charset \"#{result.encoding.name}\";#{
  2774. node.style == :compressed ? '' : "\n"
  2775. }".encode(result.encoding) + result
  2776. end
  2777. result
  2778. rescue Sass::SyntaxError => e
  2779. e.sass_template ||= node.template
  2780. raise e
  2781. end
  2782. def visit_charset(node)
  2783. "@charset \"#{node.name}\";"
  2784. end
  2785. def visit_comment(node)
  2786. return if node.invisible?
  2787. spaces = (' ' * [@tabs - node.value[/^ */].size, 0].max)
  2788. content = node.value.gsub(/^/, spaces).gsub(%r{^(\s*)//(.*)$}) do |md|
  2789. "#{$1}/*#{$2} */"
  2790. end
  2791. if content =~ /[^\\]\#\{.*\}/
  2792. Sass::Util.sass_warn <<MESSAGE
  2793. WARNING:
  2794. On line #{node.line}#{" of '#{node.filename}'" if node.filename}
  2795. Comments will evaluate the contents of interpolations (\#{ ... }) in Sass 3.2.
  2796. Please escape the interpolation by adding a backslash before the hash sign.
  2797. MESSAGE
  2798. elsif content =~ /\\\#\{.*\}/
  2799. content.gsub!(/\\(\#\{.*\})/, '\1')
  2800. end
  2801. content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && !node.loud
  2802. content
  2803. end
  2804. def visit_directive(node)
  2805. return node.value + ";" unless node.has_children
  2806. return node.value + " {}" if node.children.empty?
  2807. result = if node.style == :compressed
  2808. "#{node.value}{"
  2809. else
  2810. "#{' ' * @tabs}#{node.value} {" + (node.style == :compact ? ' ' : "\n")
  2811. end
  2812. was_prop = false
  2813. first = true
  2814. node.children.each do |child|
  2815. next if child.invisible?
  2816. if node.style == :compact
  2817. if child.is_a?(Sass::Tree::PropNode)
  2818. with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
  2819. else
  2820. result[-1] = "\n" if was_prop
  2821. rendered = with_tabs(@tabs + 1) {visit(child).dup}
  2822. rendered = rendered.lstrip if first
  2823. result << rendered.rstrip + "\n"
  2824. end
  2825. was_prop = child.is_a?(Sass::Tree::PropNode)
  2826. first = false
  2827. elsif node.style == :compressed
  2828. result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
  2829. was_prop = child.is_a?(Sass::Tree::PropNode)
  2830. else
  2831. result << with_tabs(@tabs + 1) {visit(child)} + "\n"
  2832. end
  2833. end
  2834. result.rstrip + if node.style == :compressed
  2835. "}"
  2836. else
  2837. (node.style == :expanded ? "\n" : " ") + "}\n"
  2838. end
  2839. end
  2840. def visit_media(node)
  2841. str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
  2842. str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
  2843. str
  2844. end
  2845. def visit_prop(node)
  2846. tab_str = ' ' * (@tabs + node.tabs)
  2847. if node.style == :compressed
  2848. "#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
  2849. else
  2850. "#{tab_str}#{node.resolved_name}: #{node.resolved_value};"
  2851. end
  2852. end
  2853. def visit_rule(node)
  2854. with_tabs(@tabs + node.tabs) do
  2855. rule_separator = node.style == :compressed ? ',' : ', '
  2856. line_separator =
  2857. case node.style
  2858. when :nested, :expanded; "\n"
  2859. when :compressed; ""
  2860. else; " "
  2861. end
  2862. rule_indent = ' ' * @tabs
  2863. per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
  2864. joined_rules = node.resolved_rules.members.map do |seq|
  2865. rule_part = seq.to_a.join
  2866. rule_part.gsub!(/\s*([^,])\s*\n\s*/m, '\1 ') if node.style == :compressed
  2867. rule_part
  2868. end.join(rule_separator)
  2869. joined_rules.sub!(/\A\s*/, per_rule_indent)
  2870. joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
  2871. total_rule = total_indent << joined_rules
  2872. to_return = ''
  2873. old_spaces = ' ' * @tabs
  2874. spaces = ' ' * (@tabs + 1)
  2875. if node.style != :compressed
  2876. if node.options[:debug_info]
  2877. to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
  2878. elsif node.options[:line_comments]
  2879. to_return << "#{old_spaces}/* line #{node.line}"
  2880. if node.filename
  2881. relative_filename = if node.options[:css_filename]
  2882. begin
  2883. Pathname.new(node.filename).relative_path_from(
  2884. Pathname.new(File.dirname(node.options[:css_filename]))).to_s
  2885. rescue ArgumentError
  2886. nil
  2887. end
  2888. end
  2889. relative_filename ||= node.filename
  2890. to_return << ", #{relative_filename}"
  2891. end
  2892. to_return << " */\n"
  2893. end
  2894. end
  2895. if node.style == :compact
  2896. properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
  2897. to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
  2898. elsif node.style == :compressed
  2899. properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
  2900. to_return << "#{total_rule}{#{properties}}"
  2901. else
  2902. properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
  2903. end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
  2904. to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
  2905. end
  2906. to_return
  2907. end
  2908. end
  2909. private
  2910. def debug_info_rule(debug_info, options)
  2911. node = Sass::Tree::DirectiveNode.new("@media -sass-debug-info")
  2912. debug_info.map {|k, v| [k.to_s, v.to_s]}.sort.each do |k, v|
  2913. rule = Sass::Tree::RuleNode.new([""])
  2914. rule.resolved_rules = Sass::Selector::CommaSequence.new(
  2915. [Sass::Selector::Sequence.new(
  2916. [Sass::Selector::SimpleSequence.new(
  2917. [Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)])
  2918. ])
  2919. ])
  2920. prop = Sass::Tree::PropNode.new([""], "", :new)
  2921. prop.resolved_name = "font-family"
  2922. prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
  2923. rule << prop
  2924. node << rule
  2925. end
  2926. node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
  2927. node
  2928. end
  2929. end
  2930. # A visitor for checking that all nodes are properly nested.
  2931. class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
  2932. protected
  2933. def visit(node)
  2934. if error = (@parent && (
  2935. try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
  2936. try_send("invalid_#{node_name node}_parent?", @parent, node))) ||
  2937. (@real_parent && (
  2938. try_send("invalid_#{node_name @real_parent}_real_child?", @real_parent, node) ||
  2939. try_send("invalid_#{node_name node}_real_parent?", @real_parent, node)))
  2940. raise Sass::SyntaxError.new(error)
  2941. end
  2942. super
  2943. rescue Sass::SyntaxError => e
  2944. e.modify_backtrace(:filename => node.filename, :line => node.line)
  2945. raise e
  2946. end
  2947. PARENT_CLASSES = [ Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
  2948. Sass::Tree::ImportNode, Sass::Tree::MixinNode, Sass::Tree::WhileNode]
  2949. def visit_children(parent)
  2950. old_parent = @parent
  2951. @parent = parent unless is_any_of?(parent, PARENT_CLASSES)
  2952. old_real_parent, @real_parent = @real_parent, parent
  2953. super
  2954. ensure
  2955. @parent = old_parent
  2956. @real_parent = old_real_parent
  2957. end
  2958. def visit_root(node)
  2959. yield
  2960. rescue Sass::SyntaxError => e
  2961. e.sass_template ||= node.template
  2962. raise e
  2963. end
  2964. def visit_import(node)
  2965. yield
  2966. rescue Sass::SyntaxError => e
  2967. e.modify_backtrace(:filename => node.children.first.filename)
  2968. e.add_backtrace(:filename => node.filename, :line => node.line)
  2969. raise e
  2970. end
  2971. def invalid_charset_parent?(parent, child)
  2972. "@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
  2973. end
  2974. INVALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode]
  2975. def invalid_extend_parent?(parent, child)
  2976. unless is_any_of?(parent, INVALID_EXTEND_PARENTS)
  2977. "Extend directives may only be used within rules."
  2978. end
  2979. end
  2980. def invalid_function_parent?(parent, child)
  2981. "Functions may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
  2982. end
  2983. INVALID_FUNCTION_CHILDREN = [
  2984. Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::EachNode,
  2985. Sass::Tree::ForNode, Sass::Tree::IfNode, Sass::Tree::ReturnNode,
  2986. Sass::Tree::VariableNode, Sass::Tree::WarnNode, Sass::Tree::WhileNode
  2987. ]
  2988. def invalid_function_child?(parent, child)
  2989. unless is_any_of?(child, INVALID_FUNCTION_CHILDREN)
  2990. "Functions can only contain variable declarations and control directives."
  2991. end
  2992. end
  2993. INVALID_IMPORT_PARENTS = [
  2994. Sass::Tree::IfNode, Sass::Tree::ForNode, Sass::Tree::WhileNode,
  2995. Sass::Tree::EachNode, Sass::Tree::MixinDefNode
  2996. ]
  2997. def invalid_import_parent?(parent, child)
  2998. if is_any_of?(@real_parent, INVALID_IMPORT_PARENTS)
  2999. return "Import directives may not be used within control directives or mixins."
  3000. end
  3001. return if parent.is_a?(Sass::Tree::RootNode)
  3002. return "CSS import directives may only be used at the root of a document." if child.css_import?
  3003. # If this is a nested @import, we need to make sure it doesn't have anything
  3004. # that's legal at top-level but not in the current context (e.g. mixin defs).
  3005. child.imported_file.to_tree.children.each {|c| visit(c)}
  3006. nil
  3007. rescue Sass::SyntaxError => e
  3008. e.modify_backtrace(:filename => child.imported_file.options[:filename])
  3009. e.add_backtrace(:filename => child.filename, :line => child.line)
  3010. raise e
  3011. end
  3012. def invalid_import_real_parent?(parent, child)
  3013. end
  3014. def invalid_mixindef_parent?(parent, child)
  3015. "Mixins may only be defined at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
  3016. end
  3017. INVALID_PROP_CHILDREN = [Sass::Tree::CommentNode, Sass::Tree::PropNode]
  3018. def invalid_prop_child?(parent, child)
  3019. unless is_any_of?(child, INVALID_PROP_CHILDREN)
  3020. "Illegal nesting: Only properties may be nested beneath properties."
  3021. end
  3022. end
  3023. INVALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
  3024. Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode]
  3025. def invalid_prop_parent?(parent, child)
  3026. unless is_any_of?(parent, INVALID_PROP_PARENTS)
  3027. "Properties are only allowed within rules, directives, or other properties." + child.pseudo_class_selector_message
  3028. end
  3029. end
  3030. def invalid_return_parent?(parent, child)
  3031. "@return may only be used within a function." unless parent.is_a?(Sass::Tree::FunctionNode)
  3032. end
  3033. private
  3034. def is_any_of?(val, classes)
  3035. for c in classes
  3036. return true if val.is_a?(c)
  3037. end
  3038. return false
  3039. end
  3040. def try_send(method, *args, &block)
  3041. return unless respond_to?(method)
  3042. send(method, *args, &block)
  3043. end
  3044. end
  3045. module Sass
  3046. module Selector
  3047. # The abstract superclass for simple selectors
  3048. # (that is, those that don't compose multiple selectors).
  3049. class Simple
  3050. # The line of the Sass template on which this selector was declared.
  3051. #
  3052. # @return [Fixnum]
  3053. attr_accessor :line
  3054. # The name of the file in which this selector was declared,
  3055. # or `nil` if it was not declared in a file (e.g. on stdin).
  3056. #
  3057. # @return [String, nil]
  3058. attr_accessor :filename
  3059. # Returns a representation of the node
  3060. # as an array of strings and potentially {Sass::Script::Node}s
  3061. # (if there's interpolation in the selector).
  3062. # When the interpolation is resolved and the strings are joined together,
  3063. # this will be the string representation of this node.
  3064. #
  3065. # @return [Array<String, Sass::Script::Node>]
  3066. def to_a
  3067. Sass::Util.abstract(self)
  3068. end
  3069. # Returns a string representation of the node.
  3070. # This is basically the selector string.
  3071. #
  3072. # @return [String]
  3073. def inspect
  3074. to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
  3075. end
  3076. # Returns a hash code for this selector object.
  3077. #
  3078. # By default, this is based on the value of \{#to\_a},
  3079. # so if that contains information irrelevant to the identity of the selector,
  3080. # this should be overridden.
  3081. #
  3082. # @return [Fixnum]
  3083. def hash
  3084. @_hash ||= to_a.hash
  3085. end
  3086. # Checks equality between this and another object.
  3087. #
  3088. # By default, this is based on the value of \{#to\_a},
  3089. # so if that contains information irrelevant to the identity of the selector,
  3090. # this should be overridden.
  3091. #
  3092. # @param other [Object] The object to test equality against
  3093. # @return [Boolean] Whether or not this is equal to `other`
  3094. def eql?(other)
  3095. other.class == self.class && other.hash == self.hash && other.to_a.eql?(to_a)
  3096. end
  3097. alias_method :==, :eql?
  3098. # Unifies this selector with a {SimpleSequence}'s {SimpleSequence#members members array},
  3099. # returning another `SimpleSequence` members array
  3100. # that matches both this selector and the input selector.
  3101. #
  3102. # By default, this just appends this selector to the end of the array
  3103. # (or returns the original array if this selector already exists in it).
  3104. #
  3105. # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
  3106. # @return [Array<Simple>, nil] A {SimpleSequence} {SimpleSequence#members members array}
  3107. # matching both `sels` and this selector,
  3108. # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
  3109. # @raise [Sass::SyntaxError] If this selector cannot be unified.
  3110. # This will only ever occur when a dynamic selector,
  3111. # such as {Parent} or {Interpolation}, is used in unification.
  3112. # Since these selectors should be resolved
  3113. # by the time extension and unification happen,
  3114. # this exception will only ever be raised as a result of programmer error
  3115. def unify(sels)
  3116. return sels if sels.any? {|sel2| eql?(sel2)}
  3117. sels_with_ix = Sass::Util.enum_with_index(sels)
  3118. _, i =
  3119. if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
  3120. sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && sels.last.type == :element}
  3121. else
  3122. sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
  3123. end
  3124. return sels + [self] unless i
  3125. return sels[0...i] + [self] + sels[i..-1]
  3126. end
  3127. protected
  3128. # Unifies two namespaces,
  3129. # returning a namespace that works for both of them if possible.
  3130. #
  3131. # @param ns1 [String, nil] The first namespace.
  3132. # `nil` means none specified, e.g. `foo`.
  3133. # The empty string means no namespace specified, e.g. `|foo`.
  3134. # `"*"` means any namespace is allowed, e.g. `*|foo`.
  3135. # @param ns2 [String, nil] The second namespace. See `ns1`.
  3136. # @return [Array(String or nil, Boolean)]
  3137. # The first value is the unified namespace, or `nil` for no namespace.
  3138. # The second value is whether or not a namespace that works for both inputs
  3139. # could be found at all.
  3140. # If the second value is `false`, the first should be ignored.
  3141. def unify_namespaces(ns1, ns2)
  3142. return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == ['*'] || ns2.nil? || ns2 == ['*']
  3143. return ns2, true if ns1 == ['*']
  3144. return ns1, true if ns2 == ['*']
  3145. return ns1 || ns2, true
  3146. end
  3147. end
  3148. end
  3149. end
  3150. module Sass
  3151. module Selector
  3152. # The abstract parent class of the various selector sequence classes.
  3153. #
  3154. # All subclasses should implement a `members` method
  3155. # that returns an array of object that respond to `#line=` and `#filename=`.
  3156. class AbstractSequence
  3157. # The line of the Sass template on which this selector was declared.
  3158. #
  3159. # @return [Fixnum]
  3160. attr_reader :line
  3161. # The name of the file in which this selector was declared.
  3162. #
  3163. # @return [String, nil]
  3164. attr_reader :filename
  3165. # Sets the line of the Sass template on which this selector was declared.
  3166. # This also sets the line for all child selectors.
  3167. #
  3168. # @param line [Fixnum]
  3169. # @return [Fixnum]
  3170. def line=(line)
  3171. members.each {|m| m.line = line}
  3172. @line = line
  3173. end
  3174. # Sets the name of the file in which this selector was declared,
  3175. # or `nil` if it was not declared in a file (e.g. on stdin).
  3176. # This also sets the filename for all child selectors.
  3177. #
  3178. # @param filename [String, nil]
  3179. # @return [String, nil]
  3180. def filename=(filename)
  3181. members.each {|m| m.filename = filename}
  3182. @filename = filename
  3183. end
  3184. # Returns a hash code for this sequence.
  3185. #
  3186. # Subclasses should define `#_hash` rather than overriding this method,
  3187. # which automatically handles memoizing the result.
  3188. #
  3189. # @return [Fixnum]
  3190. def hash
  3191. @_hash ||= _hash
  3192. end
  3193. # Checks equality between this and another object.
  3194. #
  3195. # Subclasses should define `#_eql?` rather than overriding this method,
  3196. # which handles checking class equality and hash equality.
  3197. #
  3198. # @param other [Object] The object to test equality against
  3199. # @return [Boolean] Whether or not this is equal to `other`
  3200. def eql?(other)
  3201. other.class == self.class && other.hash == self.hash && _eql?(other)
  3202. end
  3203. alias_method :==, :eql?
  3204. end
  3205. end
  3206. end
  3207. module Sass
  3208. module Selector
  3209. # A comma-separated sequence of selectors.
  3210. class CommaSequence < AbstractSequence
  3211. # The comma-separated selector sequences
  3212. # represented by this class.
  3213. #
  3214. # @return [Array<Sequence>]
  3215. attr_reader :members
  3216. # @param seqs [Array<Sequence>] See \{#members}
  3217. def initialize(seqs)
  3218. @members = seqs
  3219. end
  3220. # Resolves the {Parent} selectors within this selector
  3221. # by replacing them with the given parent selector,
  3222. # handling commas appropriately.
  3223. #
  3224. # @param super_cseq [CommaSequence] The parent selector
  3225. # @return [CommaSequence] This selector, with parent references resolved
  3226. # @raise [Sass::SyntaxError] If a parent selector is invalid
  3227. def resolve_parent_refs(super_cseq)
  3228. if super_cseq.nil?
  3229. if @members.any? do |sel|
  3230. sel.members.any? do |sel_or_op|
  3231. sel_or_op.is_a?(SimpleSequence) && sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
  3232. end
  3233. end
  3234. raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '&'.")
  3235. end
  3236. return self
  3237. end
  3238. CommaSequence.new(
  3239. super_cseq.members.map do |super_seq|
  3240. @members.map {|seq| seq.resolve_parent_refs(super_seq)}
  3241. end.flatten)
  3242. end
  3243. # Non-destrucively extends this selector with the extensions specified in a hash
  3244. # (which should come from {Sass::Tree::Visitors::Cssize}).
  3245. #
  3246. # @todo Link this to the reference documentation on `@extend`
  3247. # when such a thing exists.
  3248. #
  3249. # @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  3250. # The extensions to perform on this selector
  3251. # @return [CommaSequence] A copy of this selector,
  3252. # with extensions made according to `extends`
  3253. def do_extend(extends)
  3254. CommaSequence.new(members.map {|seq| seq.do_extend(extends)}.flatten)
  3255. end
  3256. # Returns a string representation of the sequence.
  3257. # This is basically the selector string.
  3258. #
  3259. # @return [String]
  3260. def inspect
  3261. members.map {|m| m.inspect}.join(", ")
  3262. end
  3263. # @see Simple#to_a
  3264. def to_a
  3265. arr = Sass::Util.intersperse(@members.map {|m| m.to_a}, ", ").flatten
  3266. arr.delete("\n")
  3267. arr
  3268. end
  3269. private
  3270. def _hash
  3271. members.hash
  3272. end
  3273. def _eql?(other)
  3274. other.class == self.class && other.members.eql?(self.members)
  3275. end
  3276. end
  3277. end
  3278. end
  3279. module Sass
  3280. module Selector
  3281. # An operator-separated sequence of
  3282. # {SimpleSequence simple selector sequences}.
  3283. class Sequence < AbstractSequence
  3284. # Sets the line of the Sass template on which this selector was declared.
  3285. # This also sets the line for all child selectors.
  3286. #
  3287. # @param line [Fixnum]
  3288. # @return [Fixnum]
  3289. def line=(line)
  3290. members.each {|m| m.line = line if m.is_a?(SimpleSequence)}
  3291. line
  3292. end
  3293. # Sets the name of the file in which this selector was declared,
  3294. # or `nil` if it was not declared in a file (e.g. on stdin).
  3295. # This also sets the filename for all child selectors.
  3296. #
  3297. # @param filename [String, nil]
  3298. # @return [String, nil]
  3299. def filename=(filename)
  3300. members.each {|m| m.filename = filename if m.is_a?(SimpleSequence)}
  3301. filename
  3302. end
  3303. # The array of {SimpleSequence simple selector sequences}, operators, and newlines.
  3304. # The operators are strings such as `"+"` and `">"`
  3305. # representing the corresponding CSS operators.
  3306. # Newlines are also newline strings;
  3307. # these aren't semantically relevant,
  3308. # but they do affect formatting.
  3309. #
  3310. # @return [Array<SimpleSequence, String>]
  3311. attr_reader :members
  3312. # @param seqs_and_ops [Array<SimpleSequence, String>] See \{#members}
  3313. def initialize(seqs_and_ops)
  3314. @members = seqs_and_ops
  3315. end
  3316. # Resolves the {Parent} selectors within this selector
  3317. # by replacing them with the given parent selector,
  3318. # handling commas appropriately.
  3319. #
  3320. # @param super_seq [Sequence] The parent selector sequence
  3321. # @return [Sequence] This selector, with parent references resolved
  3322. # @raise [Sass::SyntaxError] If a parent selector is invalid
  3323. def resolve_parent_refs(super_seq)
  3324. members = @members
  3325. nl = (members.first == "\n" && members.shift)
  3326. unless members.any? do |seq_or_op|
  3327. seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
  3328. end
  3329. members = []
  3330. members << nl if nl
  3331. members << SimpleSequence.new([Parent.new])
  3332. members += @members
  3333. end
  3334. Sequence.new(
  3335. members.map do |seq_or_op|
  3336. next seq_or_op unless seq_or_op.is_a?(SimpleSequence)
  3337. seq_or_op.resolve_parent_refs(super_seq)
  3338. end.flatten)
  3339. end
  3340. # Non-destructively extends this selector with the extensions specified in a hash
  3341. # (which should come from {Sass::Tree::Visitors::Cssize}).
  3342. #
  3343. # @overload def do_extend(extends)
  3344. # @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]
  3345. # The extensions to perform on this selector
  3346. # @return [Array<Sequence>] A list of selectors generated
  3347. # by extending this selector with `extends`.
  3348. # These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
  3349. # @see CommaSequence#do_extend
  3350. def do_extend(extends, seen = Set.new)
  3351. paths = Sass::Util.paths(members.map do |sseq_or_op|
  3352. next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
  3353. extended = sseq_or_op.do_extend(extends, seen)
  3354. choices = extended.map {|seq| seq.members}
  3355. choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
  3356. choices
  3357. end)
  3358. Sass::Util.flatten(paths.map {|path| weave(path)}, 1).map {|p| Sequence.new(p)}
  3359. end
  3360. # Returns whether or not this selector matches all elements
  3361. # that the given selector matches (as well as possibly more).
  3362. #
  3363. # @example
  3364. # (.foo).superselector?(.foo.bar) #=> true
  3365. # (.foo).superselector?(.bar) #=> false
  3366. # (.bar .foo).superselector?(.foo) #=> false
  3367. # @param sseq [SimpleSequence]
  3368. # @return [Boolean]
  3369. def superselector?(sseq)
  3370. return false unless members.size == 1
  3371. members.last.superselector?(sseq)
  3372. end
  3373. # @see Simple#to_a
  3374. def to_a
  3375. ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
  3376. Sass::Util.intersperse(ary, " ").flatten.compact
  3377. end
  3378. # Returns a string representation of the sequence.
  3379. # This is basically the selector string.
  3380. #
  3381. # @return [String]
  3382. def inspect
  3383. members.map {|m| m.inspect}.join(" ")
  3384. end
  3385. private
  3386. # Conceptually, this expands "parenthesized selectors".
  3387. # That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
  3388. # this conceptually expands into `.D .C, .D (.A .B)`,
  3389. # and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
  3390. #
  3391. # @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
  3392. # @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
  3393. def weave(path)
  3394. befores = [[]]
  3395. afters = path.dup
  3396. until afters.empty?
  3397. current = afters.shift.dup
  3398. last_current = [current.pop]
  3399. while !current.empty? && last_current.first.is_a?(String) || current.last.is_a?(String)
  3400. last_current.unshift(current.pop)
  3401. end
  3402. befores = Sass::Util.flatten(befores.map do |before|
  3403. subweave(before, current).map {|seqs| seqs + last_current}
  3404. end, 1)
  3405. return befores if afters.empty?
  3406. end
  3407. end
  3408. # This interweaves two lists of selectors,
  3409. # returning all possible orderings of them (including using unification)
  3410. # that maintain the relative ordering of the input arrays.
  3411. #
  3412. # For example, given `.foo .bar` and `.baz .bang`,
  3413. # this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,
  3414. # `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
  3415. # and so on until `.baz .bang .foo .bar`.
  3416. #
  3417. # @overload def subweave(seq1, seq2)
  3418. # @param seq1 [Array<SimpleSequence or String>]
  3419. # @param seq2 [Array<SimpleSequence or String>]
  3420. # @return [Array<Array<SimpleSequence or String>>]
  3421. def subweave(seq1, seq2, cache = {})
  3422. return [seq2] if seq1.empty?
  3423. return [seq1] if seq2.empty?
  3424. seq1 = group_selectors(seq1)
  3425. seq2 = group_selectors(seq2)
  3426. lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
  3427. next s1 if s1 == s2
  3428. next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
  3429. next s2 if subweave_superselector?(s1, s2)
  3430. next s1 if subweave_superselector?(s2, s1)
  3431. end
  3432. diff = []
  3433. until lcs.empty?
  3434. diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
  3435. seq1.shift
  3436. seq2.shift
  3437. end
  3438. diff << chunks(seq1, seq2) {|s| s.empty?}
  3439. diff.reject! {|c| c.empty?}
  3440. Sass::Util.paths(diff).map {|p| p.flatten}
  3441. end
  3442. def chunks(seq1, seq2)
  3443. chunk1 = []
  3444. chunk1 << seq1.shift until yield seq1
  3445. chunk2 = []
  3446. chunk2 << seq2.shift until yield seq2
  3447. return [] if chunk1.empty? && chunk2.empty?
  3448. return [chunk2] if chunk1.empty?
  3449. return [chunk1] if chunk2.empty?
  3450. [chunk1 + chunk2, chunk2 + chunk1]
  3451. end
  3452. def group_selectors(seq)
  3453. newseq = []
  3454. tail = seq.dup
  3455. until tail.empty?
  3456. head = []
  3457. begin
  3458. head << tail.shift
  3459. end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
  3460. newseq << head
  3461. end
  3462. return newseq
  3463. end
  3464. def subweave_superselector?(sseq1, sseq2)
  3465. if sseq1.size > 1
  3466. # More complex selectors are never superselectors of less complex ones
  3467. return unless sseq2.size > 1
  3468. # .foo ~ .bar is a superselector of .foo + .bar
  3469. return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
  3470. return unless sseq1.first.superselector?(sseq2.first)
  3471. return true if sseq1.size == 2
  3472. return false if sseq2.size == 2
  3473. return subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
  3474. elsif sseq2.size > 1
  3475. return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
  3476. return false if sseq2.size == 2
  3477. return subweave_superselector?(sseq1, sseq2[2..-1])
  3478. else
  3479. sseq1.first.superselector?(sseq2.first)
  3480. end
  3481. end
  3482. def _hash
  3483. members.reject {|m| m == "\n"}.hash
  3484. end
  3485. def _eql?(other)
  3486. other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
  3487. end
  3488. end
  3489. end
  3490. end
  3491. module Sass
  3492. module Selector
  3493. # A unseparated sequence of selectors
  3494. # that all apply to a single element.
  3495. # For example, `.foo#bar[attr=baz]` is a simple sequence
  3496. # of the selectors `.foo`, `#bar`, and `[attr=baz]`.
  3497. class SimpleSequence < AbstractSequence
  3498. # The array of individual selectors.
  3499. #
  3500. # @return [Array<Simple>]
  3501. attr_reader :members
  3502. # Returns the element or universal selector in this sequence,
  3503. # if it exists.
  3504. #
  3505. # @return [Element, Universal, nil]
  3506. def base
  3507. @base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
  3508. end
  3509. # Returns the non-base selectors in this sequence.
  3510. #
  3511. # @return [Set<Simple>]
  3512. def rest
  3513. @rest ||= Set.new(base ? members[1..-1] : members)
  3514. end
  3515. # @param selectors [Array<Simple>] See \{#members}
  3516. def initialize(selectors)
  3517. @members = selectors
  3518. end
  3519. # Resolves the {Parent} selectors within this selector
  3520. # by replacing them with the given parent selector,
  3521. # handling commas appropriately.
  3522. #
  3523. # @param super_seq [Sequence] The parent selector sequence
  3524. # @return [Array<SimpleSequence>] This selector, with parent references resolved.
  3525. # This is an array because the parent selector is itself a {Sequence}
  3526. # @raise [Sass::SyntaxError] If a parent selector is invalid
  3527. def resolve_parent_refs(super_seq)
  3528. # Parent selector only appears as the first selector in the sequence
  3529. return [self] unless @members.first.is_a?(Parent)
  3530. return super_seq.members if @members.size == 1
  3531. unless super_seq.members.last.is_a?(SimpleSequence)
  3532. raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
  3533. end
  3534. super_seq.members[0...-1] +
  3535. [SimpleSequence.new(super_seq.members.last.members + @members[1..-1])]
  3536. end
  3537. # Non-destrucively extends this selector with the extensions specified in a hash
  3538. # (which should come from {Sass::Tree::Visitors::Cssize}).
  3539. #
  3540. # @overload def do_extend(extends)
  3541. # @param extends [{Selector::Simple => Selector::Sequence}]
  3542. # The extensions to perform on this selector
  3543. # @return [Array<Sequence>] A list of selectors generated
  3544. # by extending this selector with `extends`.
  3545. # @see CommaSequence#do_extend
  3546. def do_extend(extends, seen = Set.new)
  3547. extends.get(members.to_set).map do |seq, sels|
  3548. # If A {@extend B} and C {...},
  3549. # seq is A, sels is B, and self is C
  3550. self_without_sel = self.members - sels
  3551. next unless unified = seq.members.last.unify(self_without_sel)
  3552. [sels, seq.members[0...-1] + [unified]]
  3553. end.compact.map do |sels, seq|
  3554. seq = Sequence.new(seq)
  3555. seen.include?(sels) ? [] : seq.do_extend(extends, seen + [sels])
  3556. end.flatten.uniq
  3557. end
  3558. # Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
  3559. # returning another `SimpleSequence`
  3560. # that matches both this selector and the input selector.
  3561. #
  3562. # @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
  3563. # @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
  3564. # or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
  3565. # @raise [Sass::SyntaxError] If this selector cannot be unified.
  3566. # This will only ever occur when a dynamic selector,
  3567. # such as {Parent} or {Interpolation}, is used in unification.
  3568. # Since these selectors should be resolved
  3569. # by the time extension and unification happen,
  3570. # this exception will only ever be raised as a result of programmer error
  3571. def unify(sels)
  3572. return unless sseq = members.inject(sels) do |sseq, sel|
  3573. return unless sseq
  3574. sel.unify(sseq)
  3575. end
  3576. SimpleSequence.new(sseq)
  3577. end
  3578. # Returns whether or not this selector matches all elements
  3579. # that the given selector matches (as well as possibly more).
  3580. #
  3581. # @example
  3582. # (.foo).superselector?(.foo.bar) #=> true
  3583. # (.foo).superselector?(.bar) #=> false
  3584. # @param sseq [SimpleSequence]
  3585. # @return [Boolean]
  3586. def superselector?(sseq)
  3587. (base.nil? || base.eql?(sseq.base)) && rest.subset?(sseq.rest)
  3588. end
  3589. # @see Simple#to_a
  3590. def to_a
  3591. @members.map {|sel| sel.to_a}.flatten
  3592. end
  3593. # Returns a string representation of the sequence.
  3594. # This is basically the selector string.
  3595. #
  3596. # @return [String]
  3597. def inspect
  3598. members.map {|m| m.inspect}.join
  3599. end
  3600. private
  3601. def _hash
  3602. [base, Sass::Util.set_hash(rest)].hash
  3603. end
  3604. def _eql?(other)
  3605. other.base.eql?(self.base) && Sass::Util.set_eql?(other.rest, self.rest)
  3606. end
  3607. end
  3608. end
  3609. end
  3610. module Sass
  3611. # A namespace for nodes in the parse tree for selectors.
  3612. #
  3613. # {CommaSequence} is the toplevel seelctor,
  3614. # representing a comma-separated sequence of {Sequence}s,
  3615. # such as `foo bar, baz bang`.
  3616. # {Sequence} is the next level,
  3617. # representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
  3618. # such as `foo bar` or `foo > bar baz`.
  3619. # {SimpleSequence} is a sequence of selectors that all apply to a single element,
  3620. # such as `foo.bar[attr=val]`.
  3621. # Finally, {Simple} is the superclass of the simplest selectors,
  3622. # such as `.foo` or `#bar`.
  3623. module Selector
  3624. # A parent-referencing selector (`&` in Sass).
  3625. # The function of this is to be replaced by the parent selector
  3626. # in the nested hierarchy.
  3627. class Parent < Simple
  3628. # @see Selector#to_a
  3629. def to_a
  3630. ["&"]
  3631. end
  3632. # Always raises an exception.
  3633. #
  3634. # @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
  3635. # @see Selector#unify
  3636. def unify(sels)
  3637. raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
  3638. end
  3639. end
  3640. # A class selector (e.g. `.foo`).
  3641. class Class < Simple
  3642. # The class name.
  3643. #
  3644. # @return [Array<String, Sass::Script::Node>]
  3645. attr_reader :name
  3646. # @param name [Array<String, Sass::Script::Node>] The class name
  3647. def initialize(name)
  3648. @name = name
  3649. end
  3650. # @see Selector#to_a
  3651. def to_a
  3652. [".", *@name]
  3653. end
  3654. end
  3655. # An id selector (e.g. `#foo`).
  3656. class Id < Simple
  3657. # The id name.
  3658. #
  3659. # @return [Array<String, Sass::Script::Node>]
  3660. attr_reader :name
  3661. # @param name [Array<String, Sass::Script::Node>] The id name
  3662. def initialize(name)
  3663. @name = name
  3664. end
  3665. # @see Selector#to_a
  3666. def to_a
  3667. ["#", *@name]
  3668. end
  3669. # Returns `nil` if `sels` contains an {Id} selector
  3670. # with a different name than this one.
  3671. #
  3672. # @see Selector#unify
  3673. def unify(sels)
  3674. return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
  3675. super
  3676. end
  3677. end
  3678. # A universal selector (`*` in CSS).
  3679. class Universal < Simple
  3680. # The selector namespace.
  3681. # `nil` means the default namespace,
  3682. # `[""]` means no namespace,
  3683. # `["*"]` means any namespace.
  3684. #
  3685. # @return [Array<String, Sass::Script::Node>, nil]
  3686. attr_reader :namespace
  3687. # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
  3688. def initialize(namespace)
  3689. @namespace = namespace
  3690. end
  3691. # @see Selector#to_a
  3692. def to_a
  3693. @namespace ? @namespace + ["|*"] : ["*"]
  3694. end
  3695. # Unification of a universal selector is somewhat complicated,
  3696. # especially when a namespace is specified.
  3697. # If there is no namespace specified
  3698. # or any namespace is specified (namespace `"*"`),
  3699. # then `sel` is returned without change
  3700. # (unless it's empty, in which case `"*"` is required).
  3701. #
  3702. # If a namespace is specified
  3703. # but `sel` does not specify a namespace,
  3704. # then the given namespace is applied to `sel`,
  3705. # either by adding this {Universal} selector
  3706. # or applying this namespace to an existing {Element} selector.
  3707. #
  3708. # If both this selector *and* `sel` specify namespaces,
  3709. # those namespaces are unified via {Simple#unify_namespaces}
  3710. # and the unified namespace is used, if possible.
  3711. #
  3712. # @todo There are lots of cases that this documentation specifies;
  3713. # make sure we thoroughly test **all of them**.
  3714. # @todo Keep track of whether a default namespace has been declared
  3715. # and handle namespace-unspecified selectors accordingly.
  3716. # @todo If any branch of a CommaSequence ends up being just `"*"`,
  3717. # then all other branches should be eliminated
  3718. #
  3719. # @see Selector#unify
  3720. def unify(sels)
  3721. name =
  3722. case sels.first
  3723. when Universal; :universal
  3724. when Element; sels.first.name
  3725. else
  3726. return [self] + sels unless namespace.nil? || namespace == ['*']
  3727. return sels unless sels.empty?
  3728. return [self]
  3729. end
  3730. ns, accept = unify_namespaces(namespace, sels.first.namespace)
  3731. return unless accept
  3732. [name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
  3733. end
  3734. end
  3735. # An element selector (e.g. `h1`).
  3736. class Element < Simple
  3737. # The element name.
  3738. #
  3739. # @return [Array<String, Sass::Script::Node>]
  3740. attr_reader :name
  3741. # The selector namespace.
  3742. # `nil` means the default namespace,
  3743. # `[""]` means no namespace,
  3744. # `["*"]` means any namespace.
  3745. #
  3746. # @return [Array<String, Sass::Script::Node>, nil]
  3747. attr_reader :namespace
  3748. # @param name [Array<String, Sass::Script::Node>] The element name
  3749. # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
  3750. def initialize(name, namespace)
  3751. @name = name
  3752. @namespace = namespace
  3753. end
  3754. # @see Selector#to_a
  3755. def to_a
  3756. @namespace ? @namespace + ["|"] + @name : @name
  3757. end
  3758. # Unification of an element selector is somewhat complicated,
  3759. # especially when a namespace is specified.
  3760. # First, if `sel` contains another {Element} with a different \{#name},
  3761. # then the selectors can't be unified and `nil` is returned.
  3762. #
  3763. # Otherwise, if `sel` doesn't specify a namespace,
  3764. # or it specifies any namespace (via `"*"`),
  3765. # then it's returned with this element selector
  3766. # (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
  3767. # Similarly, if this selector doesn't specify a namespace,
  3768. # the namespace from `sel` is used.
  3769. #
  3770. # If both this selector *and* `sel` specify namespaces,
  3771. # those namespaces are unified via {Simple#unify_namespaces}
  3772. # and the unified namespace is used, if possible.
  3773. #
  3774. # @todo There are lots of cases that this documentation specifies;
  3775. # make sure we thoroughly test **all of them**.
  3776. # @todo Keep track of whether a default namespace has been declared
  3777. # and handle namespace-unspecified selectors accordingly.
  3778. #
  3779. # @see Selector#unify
  3780. def unify(sels)
  3781. case sels.first
  3782. when Universal;
  3783. when Element; return unless name == sels.first.name
  3784. else return [self] + sels
  3785. end
  3786. ns, accept = unify_namespaces(namespace, sels.first.namespace)
  3787. return unless accept
  3788. [Element.new(name, ns)] + sels[1..-1]
  3789. end
  3790. end
  3791. # Selector interpolation (`#{}` in Sass).
  3792. class Interpolation < Simple
  3793. # The script to run.
  3794. #
  3795. # @return [Sass::Script::Node]
  3796. attr_reader :script
  3797. # @param script [Sass::Script::Node] The script to run
  3798. def initialize(script)
  3799. @script = script
  3800. end
  3801. # @see Selector#to_a
  3802. def to_a
  3803. [@script]
  3804. end
  3805. # Always raises an exception.
  3806. #
  3807. # @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
  3808. # @see Selector#unify
  3809. def unify(sels)
  3810. raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
  3811. end
  3812. end
  3813. # An attribute selector (e.g. `[href^="http://"]`).
  3814. class Attribute < Simple
  3815. # The attribute name.
  3816. #
  3817. # @return [Array<String, Sass::Script::Node>]
  3818. attr_reader :name
  3819. # The attribute namespace.
  3820. # `nil` means the default namespace,
  3821. # `[""]` means no namespace,
  3822. # `["*"]` means any namespace.
  3823. #
  3824. # @return [Array<String, Sass::Script::Node>, nil]
  3825. attr_reader :namespace
  3826. # The matching operator, e.g. `"="` or `"^="`.
  3827. #
  3828. # @return [String]
  3829. attr_reader :operator
  3830. # The right-hand side of the operator.
  3831. #
  3832. # @return [Array<String, Sass::Script::Node>]
  3833. attr_reader :value
  3834. # @param name [Array<String, Sass::Script::Node>] The attribute name
  3835. # @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
  3836. # @param operator [String] The matching operator, e.g. `"="` or `"^="`
  3837. # @param value [Array<String, Sass::Script::Node>] See \{#value}
  3838. def initialize(name, namespace, operator, value)
  3839. @name = name
  3840. @namespace = namespace
  3841. @operator = operator
  3842. @value = value
  3843. end
  3844. # @see Selector#to_a
  3845. def to_a
  3846. res = ["["]
  3847. res.concat(@namespace) << "|" if @namespace
  3848. res.concat @name
  3849. (res << @operator).concat @value if @value
  3850. res << "]"
  3851. end
  3852. end
  3853. # A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
  3854. # It can have arguments (e.g. `:nth-child(2n+1)`).
  3855. class Pseudo < Simple
  3856. # The type of the selector.
  3857. # `:class` if this is a pseudoclass selector,
  3858. # `:element` if it's a pseudoelement.
  3859. #
  3860. # @return [Symbol]
  3861. attr_reader :type
  3862. # The name of the selector.
  3863. #
  3864. # @return [Array<String, Sass::Script::Node>]
  3865. attr_reader :name
  3866. # The argument to the selector,
  3867. # or `nil` if no argument was given.
  3868. #
  3869. # This may include SassScript nodes that will be run during resolution.
  3870. # Note that this should not include SassScript nodes
  3871. # after resolution has taken place.
  3872. #
  3873. # @return [Array<String, Sass::Script::Node>, nil]
  3874. attr_reader :arg
  3875. # @param type [Symbol] See \{#type}
  3876. # @param name [Array<String, Sass::Script::Node>] The name of the selector
  3877. # @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
  3878. # or nil if no argument was given
  3879. def initialize(type, name, arg)
  3880. @type = type
  3881. @name = name
  3882. @arg = arg
  3883. end
  3884. # @see Selector#to_a
  3885. def to_a
  3886. res = [@type == :class ? ":" : "::"] + @name
  3887. (res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
  3888. res
  3889. end
  3890. # Returns `nil` if this is a pseudoclass selector
  3891. # and `sels` contains a pseudoclass selector different than this one.
  3892. #
  3893. # @see Selector#unify
  3894. def unify(sels)
  3895. return if type == :element && sels.any? do |sel|
  3896. sel.is_a?(Pseudo) && sel.type == :element &&
  3897. (sel.name != self.name || sel.arg != self.arg)
  3898. end
  3899. super
  3900. end
  3901. end
  3902. # A pseudoclass selector whose argument is itself a selector
  3903. # (e.g. `:not(.foo)` or `:-moz-all(.foo, .bar)`).
  3904. class SelectorPseudoClass < Simple
  3905. # The name of the pseudoclass.
  3906. #
  3907. # @return [String]
  3908. attr_reader :name
  3909. # The selector argument.
  3910. #
  3911. # @return [Selector::Sequence]
  3912. attr_reader :selector
  3913. # @param [String] The name of the pseudoclass
  3914. # @param [Selector::Sequence] The selector argument
  3915. def initialize(name, selector)
  3916. @name = name
  3917. @selector = selector
  3918. end
  3919. # @see Selector#to_a
  3920. def to_a
  3921. [":", @name, "("] + @selector.to_a + [")"]
  3922. end
  3923. end
  3924. end
  3925. end
  3926. require 'set'
  3927. module Sass
  3928. # The lexical environment for SassScript.
  3929. # This keeps track of variable, mixin, and function definitions.
  3930. #
  3931. # A new environment is created for each level of Sass nesting.
  3932. # This allows variables to be lexically scoped.
  3933. # The new environment refers to the environment in the upper scope,
  3934. # so it has access to variables defined in enclosing scopes,
  3935. # but new variables are defined locally.
  3936. #
  3937. # Environment also keeps track of the {Engine} options
  3938. # so that they can be made available to {Sass::Script::Functions}.
  3939. class Environment
  3940. # The enclosing environment,
  3941. # or nil if this is the global environment.
  3942. #
  3943. # @return [Environment]
  3944. attr_reader :parent
  3945. attr_writer :options
  3946. # @param parent [Environment] See \{#parent}
  3947. def initialize(parent = nil)
  3948. @parent = parent
  3949. unless parent
  3950. @stack = []
  3951. @mixins_in_use = Set.new
  3952. set_var("important", Script::String.new("!important"))
  3953. end
  3954. end
  3955. # The options hash.
  3956. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  3957. #
  3958. # @return [{Symbol => Object}]
  3959. def options
  3960. @options || parent_options || {}
  3961. end
  3962. # Push a new stack frame onto the mixin/include stack.
  3963. #
  3964. # @param frame_info [{Symbol => Object}]
  3965. # Frame information has the following keys:
  3966. #
  3967. # `:filename`
  3968. # : The name of the file in which the lexical scope changed.
  3969. #
  3970. # `:mixin`
  3971. # : The name of the mixin in which the lexical scope changed,
  3972. # or `nil` if it wasn't within in a mixin.
  3973. #
  3974. # `:line`
  3975. # : The line of the file on which the lexical scope changed. Never nil.
  3976. def push_frame(frame_info)
  3977. top_of_stack = stack.last
  3978. if top_of_stack && top_of_stack.delete(:prepared)
  3979. top_of_stack.merge!(frame_info)
  3980. else
  3981. stack.push(top_of_stack = frame_info)
  3982. end
  3983. mixins_in_use << top_of_stack[:mixin] if top_of_stack[:mixin] && !top_of_stack[:prepared]
  3984. end
  3985. # Like \{#push\_frame}, but next time a stack frame is pushed,
  3986. # it will be merged with this frame.
  3987. #
  3988. # @param frame_info [{Symbol => Object}] Same as for \{#push\_frame}.
  3989. def prepare_frame(frame_info)
  3990. push_frame(frame_info.merge(:prepared => true))
  3991. end
  3992. # Pop a stack frame from the mixin/include stack.
  3993. def pop_frame
  3994. stack.pop if stack.last && stack.last[:prepared]
  3995. popped = stack.pop
  3996. mixins_in_use.delete(popped[:mixin]) if popped && popped[:mixin]
  3997. end
  3998. # A list of stack frames in the mixin/include stack.
  3999. # The last element in the list is the most deeply-nested frame.
  4000. #
  4001. # @return [Array<{Symbol => Object}>] The stack frames,
  4002. # of the form passed to \{#push\_frame}.
  4003. def stack
  4004. @stack ||= @parent.stack
  4005. end
  4006. # A set of names of mixins currently present in the stack.
  4007. #
  4008. # @return [Set<String>] The mixin names.
  4009. def mixins_in_use
  4010. @mixins_in_use ||= @parent.mixins_in_use
  4011. end
  4012. private
  4013. def parent_options
  4014. @parent_options ||= @parent && @parent.options
  4015. end
  4016. class << self
  4017. private
  4018. UNDERSCORE, DASH = '_', '-'
  4019. # Note: when updating this,
  4020. # update sass/yard/inherited_hash.rb as well.
  4021. def inherited_hash(name)
  4022. class_eval <<RUBY, __FILE__, __LINE__ + 1
  4023. def #{name}(name)
  4024. _#{name}(name.tr(UNDERSCORE, DASH))
  4025. end
  4026. def _#{name}(name)
  4027. (@#{name}s && @#{name}s[name]) || @parent && @parent._#{name}(name)
  4028. end
  4029. protected :_#{name}
  4030. def set_#{name}(name, value)
  4031. name = name.tr(UNDERSCORE, DASH)
  4032. @#{name}s[name] = value unless try_set_#{name}(name, value)
  4033. end
  4034. def try_set_#{name}(name, value)
  4035. @#{name}s ||= {}
  4036. if @#{name}s.include?(name)
  4037. @#{name}s[name] = value
  4038. true
  4039. elsif @parent
  4040. @parent.try_set_#{name}(name, value)
  4041. else
  4042. false
  4043. end
  4044. end
  4045. protected :try_set_#{name}
  4046. def set_local_#{name}(name, value)
  4047. @#{name}s ||= {}
  4048. @#{name}s[name.tr(UNDERSCORE, DASH)] = value
  4049. end
  4050. RUBY
  4051. end
  4052. end
  4053. # variable
  4054. # Script::Literal
  4055. inherited_hash :var
  4056. # mixin
  4057. # Sass::Callable
  4058. inherited_hash :mixin
  4059. # function
  4060. # Sass::Callable
  4061. inherited_hash :function
  4062. end
  4063. end
  4064. require 'strscan'
  4065. module Sass::Script
  4066. # The abstract superclass for SassScript parse tree nodes.
  4067. #
  4068. # Use \{#perform} to evaluate a parse tree.
  4069. class Node
  4070. # The options hash for this node.
  4071. #
  4072. # @return [{Symbol => Object}]
  4073. attr_reader :options
  4074. # The line of the document on which this node appeared.
  4075. #
  4076. # @return [Fixnum]
  4077. attr_accessor :line
  4078. # Sets the options hash for this node,
  4079. # as well as for all child nodes.
  4080. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  4081. #
  4082. # @param options [{Symbol => Object}] The options
  4083. def options=(options)
  4084. @options = options
  4085. children.each do |c|
  4086. if c.is_a? Hash
  4087. c.values.each {|v| v.options = options }
  4088. else
  4089. c.options = options
  4090. end
  4091. end
  4092. end
  4093. # Evaluates the node.
  4094. #
  4095. # \{#perform} shouldn't be overridden directly;
  4096. # instead, override \{#\_perform}.
  4097. #
  4098. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  4099. # @return [Literal] The SassScript object that is the value of the SassScript
  4100. def perform(environment)
  4101. _perform(environment)
  4102. rescue Sass::SyntaxError => e
  4103. e.modify_backtrace(:line => line)
  4104. raise e
  4105. end
  4106. # Returns all child nodes of this node.
  4107. #
  4108. # @return [Array<Node>]
  4109. def children
  4110. Sass::Util.abstract(self)
  4111. end
  4112. # Returns the text of this SassScript expression.
  4113. #
  4114. # @return [String]
  4115. def to_sass(opts = {})
  4116. Sass::Util.abstract(self)
  4117. end
  4118. protected
  4119. # Converts underscores to dashes if the :dasherize option is set.
  4120. def dasherize(s, opts)
  4121. if opts[:dasherize]
  4122. s.gsub(/_/,'-')
  4123. else
  4124. s
  4125. end
  4126. end
  4127. # Evaluates this node.
  4128. # Note that all {Literal} objects created within this method
  4129. # should have their \{#options} attribute set, probably via \{#opts}.
  4130. #
  4131. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  4132. # @return [Literal] The SassScript object that is the value of the SassScript
  4133. # @see #perform
  4134. def _perform(environment)
  4135. Sass::Util.abstract(self)
  4136. end
  4137. # Sets the \{#options} field on the given literal and returns it
  4138. #
  4139. # @param literal [Literal]
  4140. # @return [Literal]
  4141. def opts(literal)
  4142. literal.options = options
  4143. literal
  4144. end
  4145. end
  4146. end
  4147. module Sass
  4148. module Script
  4149. # A SassScript parse node representing a variable.
  4150. class Variable < Node
  4151. # The name of the variable.
  4152. #
  4153. # @return [String]
  4154. attr_reader :name
  4155. # The underscored name of the variable.
  4156. #
  4157. # @return [String]
  4158. attr_reader :underscored_name
  4159. # @param name [String] See \{#name}
  4160. def initialize(name)
  4161. @name = name
  4162. @underscored_name = name.gsub(/-/,"_")
  4163. super()
  4164. end
  4165. # @return [String] A string representation of the variable
  4166. def inspect(opts = {})
  4167. return "!important" if name == "important"
  4168. "$#{dasherize(name, opts)}"
  4169. end
  4170. alias_method :to_sass, :inspect
  4171. # Returns an empty array.
  4172. #
  4173. # @return [Array<Node>] empty
  4174. # @see Node#children
  4175. def children
  4176. []
  4177. end
  4178. protected
  4179. # Evaluates the variable.
  4180. #
  4181. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  4182. # @return [Literal] The SassScript object that is the value of the variable
  4183. # @raise [Sass::SyntaxError] if the variable is undefined
  4184. def _perform(environment)
  4185. raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
  4186. if val.is_a?(Number)
  4187. val = val.dup
  4188. val.original = nil
  4189. end
  4190. return val
  4191. end
  4192. end
  4193. end
  4194. end
  4195. module Sass::Script
  4196. # Methods in this module are accessible from the SassScript context.
  4197. # For example, you can write
  4198. #
  4199. # $color = hsl(120deg, 100%, 50%)
  4200. #
  4201. # and it will call {Sass::Script::Functions#hsl}.
  4202. #
  4203. # The following functions are provided:
  4204. #
  4205. # *Note: These functions are described in more detail below.*
  4206. #
  4207. # ## RGB Functions
  4208. #
  4209. # \{#rgb rgb($red, $green, $blue)}
  4210. # : Converts an `rgb(red, green, blue)` triplet into a color.
  4211. #
  4212. # \{#rgba rgba($red, $green, $blue, $alpha)}
  4213. # : Converts an `rgba(red, green, blue, alpha)` quadruplet into a color.
  4214. #
  4215. # \{#rgba rgba($color, $alpha)}
  4216. # : Adds an alpha layer to any color value.
  4217. #
  4218. # \{#red red($color)}
  4219. # : Gets the red component of a color.
  4220. #
  4221. # \{#green green($color)}
  4222. # : Gets the green component of a color.
  4223. #
  4224. # \{#blue blue($color)}
  4225. # : Gets the blue component of a color.
  4226. #
  4227. # \{#mix mix($color-1, $color-2, \[$weight\])}
  4228. # : Mixes two colors together.
  4229. #
  4230. # ## HSL Functions
  4231. #
  4232. # \{#hsl hsl($hue, $saturation, $lightness)}
  4233. # : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
  4234. #
  4235. # \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
  4236. # : Converts an `hsla(hue, saturation, lightness, alpha)` quadruplet into a color.
  4237. #
  4238. # \{#hue hue($color)}
  4239. # : Gets the hue component of a color.
  4240. #
  4241. # \{#saturation saturation($color)}
  4242. # : Gets the saturation component of a color.
  4243. #
  4244. # \{#lightness lightness($color)}
  4245. # : Gets the lightness component of a color.
  4246. #
  4247. # \{#adjust_hue adjust-hue($color, $degrees)}
  4248. # : Changes the hue of a color.
  4249. #
  4250. # \{#lighten lighten($color, $amount)}
  4251. # : Makes a color lighter.
  4252. #
  4253. # \{#darken darken($color, $amount)}
  4254. # : Makes a color darker.
  4255. #
  4256. # \{#saturate saturate($color, $amount)}
  4257. # : Makes a color more saturated.
  4258. #
  4259. # \{#desaturate desaturate($color, $amount)}
  4260. # : Makes a color less saturated.
  4261. #
  4262. # \{#grayscale grayscale($color)}
  4263. # : Converts a color to grayscale.
  4264. #
  4265. # \{#complement complement($color)}
  4266. # : Returns the complement of a color.
  4267. #
  4268. # \{#invert invert($color)}
  4269. # : Returns the inverse of a color.
  4270. #
  4271. # ## Opacity Functions
  4272. #
  4273. # \{#alpha alpha($color)} / \{#opacity opacity($color)}
  4274. # : Gets the alpha component (opacity) of a color.
  4275. #
  4276. # \{#rgba rgba($color, $alpha)}
  4277. # : Add or change an alpha layer for any color value.
  4278. #
  4279. # \{#opacify opacify($color, $amount)} / \{#fade_in fade-in($color, $amount)}
  4280. # : Makes a color more opaque.
  4281. #
  4282. # \{#transparentize transparentize($color, $amount)} / \{#fade_out fade-out($color, $amount)}
  4283. # : Makes a color more transparent.
  4284. #
  4285. # ## Other Color Functions
  4286. #
  4287. # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
  4288. # : Increase or decrease any of the components of a color.
  4289. #
  4290. # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
  4291. # : Fluidly scale one or more components of a color.
  4292. #
  4293. # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\]}
  4294. # : Changes one or more properties of a color.
  4295. #
  4296. # ## String Functions
  4297. #
  4298. # \{#unquote unquote($string)}
  4299. # : Removes the quotes from a string.
  4300. #
  4301. # \{#quote quote($string)}
  4302. # : Adds quotes to a string.
  4303. #
  4304. # ## Number Functions
  4305. #
  4306. # \{#percentage percentage($value)}
  4307. # : Converts a unitless number to a percentage.
  4308. #
  4309. # \{#round round($value)}
  4310. # : Rounds a number to the nearest whole number.
  4311. #
  4312. # \{#ceil ceil($value)}
  4313. # : Rounds a number up to the nearest whole number.
  4314. #
  4315. # \{#floor floor($value)}
  4316. # : Rounds a number down to the nearest whole number.
  4317. #
  4318. # \{#abs abs($value)}
  4319. # : Returns the absolute value of a number.
  4320. #
  4321. # ## List Functions {#list-functions}
  4322. #
  4323. # \{#length length($list)}
  4324. # : Returns the length of a list.
  4325. #
  4326. # \{#nth nth($list, $n)}
  4327. # : Returns a specific item in a list.
  4328. #
  4329. # \{#join join($list1, $list2, \[$separator\])}
  4330. # : Joins together two lists into one.
  4331. #
  4332. # ## Introspection Functions
  4333. #
  4334. # \{#type_of type-of($value)}
  4335. # : Returns the type of a value.
  4336. #
  4337. # \{#unit unit($number)}
  4338. # : Returns the units associated with a number.
  4339. #
  4340. # \{#unitless unitless($number)}
  4341. # : Returns whether a number has units or not.
  4342. #
  4343. # \{#comparable comparable($number-1, $number-2)}
  4344. # : Returns whether two numbers can be added or compared.
  4345. #
  4346. # ## Miscellaneous Functions
  4347. #
  4348. # \{#if if($condition, $if-true, $if-false)}
  4349. # : Returns one of two values, depending on whether or not a condition is true.
  4350. #
  4351. # ## Adding Custom Functions
  4352. #
  4353. # New Sass functions can be added by adding Ruby methods to this module.
  4354. # For example:
  4355. #
  4356. # module Sass::Script::Functions
  4357. # def reverse(string)
  4358. # assert_type string, :String
  4359. # Sass::Script::String.new(string.value.reverse)
  4360. # end
  4361. # declare :reverse, :args => [:string]
  4362. # end
  4363. #
  4364. # Calling {declare} tells Sass the argument names for your function.
  4365. # If omitted, the function will still work, but will not be able to accept keyword arguments.
  4366. # {declare} can also allow your function to take arbitrary keyword arguments.
  4367. #
  4368. # There are a few things to keep in mind when modifying this module.
  4369. # First of all, the arguments passed are {Sass::Script::Literal} objects.
  4370. # Literal objects are also expected to be returned.
  4371. # This means that Ruby values must be unwrapped and wrapped.
  4372. #
  4373. # Most Literal objects support the {Sass::Script::Literal#value value} accessor
  4374. # for getting their Ruby values.
  4375. # Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
  4376. # {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
  4377. #
  4378. # Second, making Ruby functions accessible from Sass introduces the temptation
  4379. # to do things like database access within stylesheets.
  4380. # This is generally a bad idea;
  4381. # since Sass files are by default only compiled once,
  4382. # dynamic code is not a great fit.
  4383. #
  4384. # If you really, really need to compile Sass on each request,
  4385. # first make sure you have adequate caching set up.
  4386. # Then you can use {Sass::Engine} to render the code,
  4387. # using the {file:SASS_REFERENCE.md#custom-option `options` parameter}
  4388. # to pass in data that {EvaluationContext#options can be accessed}
  4389. # from your Sass functions.
  4390. #
  4391. # Within one of the functions in this module,
  4392. # methods of {EvaluationContext} can be used.
  4393. #
  4394. # ### Caveats
  4395. #
  4396. # When creating new {Literal} objects within functions,
  4397. # be aware that it's not safe to call {Literal#to_s #to_s}
  4398. # (or other methods that use the string representation)
  4399. # on those objects without first setting {Node#options= the #options attribute}.
  4400. module Functions
  4401. @signatures = {}
  4402. # A class representing a Sass function signature.
  4403. #
  4404. # @attr args [Array<Symbol>] The names of the arguments to the function.
  4405. # @attr var_args [Boolean] Whether the function takes a variable number of arguments.
  4406. # @attr var_kwargs [Boolean] Whether the function takes an arbitrary set of keyword arguments.
  4407. Signature = Struct.new(:args, :var_args, :var_kwargs)
  4408. # Declare a Sass signature for a Ruby-defined function.
  4409. # This includes the names of the arguments,
  4410. # whether the function takes a variable number of arguments,
  4411. # and whether the function takes an arbitrary set of keyword arguments.
  4412. #
  4413. # It's not necessary to declare a signature for a function.
  4414. # However, without a signature it won't support keyword arguments.
  4415. #
  4416. # A single function can have multiple signatures declared
  4417. # as long as each one takes a different number of arguments.
  4418. # It's also possible to declare multiple signatures
  4419. # that all take the same number of arguments,
  4420. # but none of them but the first will be used
  4421. # unless the user uses keyword arguments.
  4422. #
  4423. # @param method_name [Symbol] The name of the method
  4424. # whose signature is being declared.
  4425. # @param args [Array<Symbol>] The names of the arguments for the function signature.
  4426. # @option options :var_args [Boolean] (false)
  4427. # Whether the function accepts a variable number of (unnamed) arguments
  4428. # in addition to the named arguments.
  4429. # @option options :var_kwargs [Boolean] (false)
  4430. # Whether the function accepts other keyword arguments
  4431. # in addition to those in `:args`.
  4432. # If this is true, the Ruby function will be passed a hash from strings
  4433. # to {Sass::Script::Literal}s as the last argument.
  4434. # In addition, if this is true and `:var_args` is not,
  4435. # Sass will ensure that the last argument passed is a hash.
  4436. #
  4437. # @example
  4438. # declare :rgba, [:hex, :alpha]
  4439. # declare :rgba, [:red, :green, :blue, :alpha]
  4440. # declare :accepts_anything, [], :var_args => true, :var_kwargs => true
  4441. # declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
  4442. def self.declare(method_name, args, options = {})
  4443. @signatures[method_name] ||= []
  4444. @signatures[method_name] << Signature.new(
  4445. args.map {|s| s.to_s},
  4446. options[:var_args],
  4447. options[:var_kwargs])
  4448. end
  4449. # Determine the correct signature for the number of arguments
  4450. # passed in for a given function.
  4451. # If no signatures match, the first signature is returned for error messaging.
  4452. #
  4453. # @param method_name [Symbol] The name of the Ruby function to be called.
  4454. # @param arg_arity [Number] The number of unnamed arguments the function was passed.
  4455. # @param kwarg_arity [Number] The number of keyword arguments the function was passed.
  4456. #
  4457. # @return [{Symbol => Object}, nil]
  4458. # The signature options for the matching signature,
  4459. # or nil if no signatures are declared for this function. See {declare}.
  4460. def self.signature(method_name, arg_arity, kwarg_arity)
  4461. return unless @signatures[method_name]
  4462. @signatures[method_name].each do |signature|
  4463. return signature if signature.args.size == arg_arity + kwarg_arity
  4464. next unless signature.args.size < arg_arity + kwarg_arity
  4465. # We have enough args.
  4466. # Now we need to figure out which args are varargs
  4467. # and if the signature allows them.
  4468. t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
  4469. if signature.args.size > t_arg_arity
  4470. # we transfer some kwargs arity to args arity
  4471. # if it does not have enough args -- assuming the names will work out.
  4472. t_kwarg_arity -= (signature.args.size - t_arg_arity)
  4473. t_arg_arity = signature.args.size
  4474. end
  4475. if ( t_arg_arity == signature.args.size || t_arg_arity > signature.args.size && signature.var_args ) &&
  4476. (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
  4477. return signature
  4478. end
  4479. end
  4480. @signatures[method_name].first
  4481. end
  4482. # The context in which methods in {Script::Functions} are evaluated.
  4483. # That means that all instance methods of {EvaluationContext}
  4484. # are available to use in functions.
  4485. class EvaluationContext
  4486. include Functions
  4487. # The options hash for the {Sass::Engine} that is processing the function call
  4488. #
  4489. # @return [{Symbol => Object}]
  4490. attr_reader :options
  4491. # @param options [{Symbol => Object}] See \{#options}
  4492. def initialize(options)
  4493. @options = options
  4494. end
  4495. # Asserts that the type of a given SassScript value
  4496. # is the expected type (designated by a symbol).
  4497. #
  4498. # Valid types are `:Bool`, `:Color`, `:Number`, and `:String`.
  4499. # Note that `:String` will match both double-quoted strings
  4500. # and unquoted identifiers.
  4501. #
  4502. # @example
  4503. # assert_type value, :String
  4504. # assert_type value, :Number
  4505. # @param value [Sass::Script::Literal] A SassScript value
  4506. # @param type [Symbol] The name of the type the value is expected to be
  4507. # @param name [String, nil] The name of the argument.
  4508. def assert_type(value, type, name = nil)
  4509. return if value.is_a?(Sass::Script.const_get(type))
  4510. err = "#{value.inspect} is not a #{type.to_s.downcase}"
  4511. err = "$#{name}: " + err if name
  4512. raise ArgumentError.new(err)
  4513. end
  4514. end
  4515. class << self
  4516. # Returns whether user function with a given name exists.
  4517. #
  4518. # @param function_name [String]
  4519. # @return [Boolean]
  4520. alias_method :callable?, :public_method_defined?
  4521. private
  4522. def include(*args)
  4523. r = super
  4524. # We have to re-include ourselves into EvaluationContext to work around
  4525. # an icky Ruby restriction.
  4526. EvaluationContext.send :include, self
  4527. r
  4528. end
  4529. end
  4530. # Creates a {Color} object from red, green, and blue values.
  4531. #
  4532. # @param red [Number]
  4533. # A number between 0 and 255 inclusive,
  4534. # or between 0% and 100% inclusive
  4535. # @param green [Number]
  4536. # A number between 0 and 255 inclusive,
  4537. # or between 0% and 100% inclusive
  4538. # @param blue [Number]
  4539. # A number between 0 and 255 inclusive,
  4540. # or between 0% and 100% inclusive
  4541. # @see #rgba
  4542. # @return [Color]
  4543. def rgb(red, green, blue)
  4544. assert_type red, :Number
  4545. assert_type green, :Number
  4546. assert_type blue, :Number
  4547. Color.new([red, green, blue].map do |c|
  4548. v = c.value
  4549. if c.numerator_units == ["%"] && c.denominator_units.empty?
  4550. next v * 255 / 100.0 if (0..100).include?(v)
  4551. raise ArgumentError.new("Color value #{c} must be between 0% and 100% inclusive")
  4552. else
  4553. next v if (0..255).include?(v)
  4554. raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
  4555. end
  4556. end)
  4557. end
  4558. declare :rgb, [:red, :green, :blue]
  4559. # @see #rgb
  4560. # @overload rgba(red, green, blue, alpha)
  4561. # Creates a {Color} object from red, green, and blue values,
  4562. # as well as an alpha channel indicating opacity.
  4563. #
  4564. # @param red [Number]
  4565. # A number between 0 and 255 inclusive
  4566. # @param green [Number]
  4567. # A number between 0 and 255 inclusive
  4568. # @param blue [Number]
  4569. # A number between 0 and 255 inclusive
  4570. # @param alpha [Number]
  4571. # A number between 0 and 1
  4572. # @return [Color]
  4573. #
  4574. # @overload rgba(color, alpha)
  4575. # Sets the opacity of a color.
  4576. #
  4577. # @example
  4578. # rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
  4579. # rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
  4580. #
  4581. # @param color [Color]
  4582. # @param alpha [Number]
  4583. # A number between 0 and 1
  4584. # @return [Color]
  4585. def rgba(*args)
  4586. case args.size
  4587. when 2
  4588. color, alpha = args
  4589. assert_type color, :Color
  4590. assert_type alpha, :Number
  4591. unless (0..1).include?(alpha.value)
  4592. raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1 inclusive")
  4593. end
  4594. color.with(:alpha => alpha.value)
  4595. when 4
  4596. red, green, blue, alpha = args
  4597. rgba(rgb(red, green, blue), alpha)
  4598. else
  4599. raise ArgumentError.new("wrong number of arguments (#{args.size} for 4)")
  4600. end
  4601. end
  4602. declare :rgba, [:red, :green, :blue, :alpha]
  4603. declare :rgba, [:color, :alpha]
  4604. # Creates a {Color} object from hue, saturation, and lightness.
  4605. # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
  4606. #
  4607. # @param hue [Number] The hue of the color.
  4608. # Should be between 0 and 360 degrees, inclusive
  4609. # @param saturation [Number] The saturation of the color.
  4610. # Must be between `0%` and `100%`, inclusive
  4611. # @param lightness [Number] The lightness of the color.
  4612. # Must be between `0%` and `100%`, inclusive
  4613. # @return [Color] The resulting color
  4614. # @see #hsla
  4615. # @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
  4616. def hsl(hue, saturation, lightness)
  4617. hsla(hue, saturation, lightness, Number.new(1))
  4618. end
  4619. declare :hsl, [:hue, :saturation, :lightness]
  4620. # Creates a {Color} object from hue, saturation, and lightness,
  4621. # as well as an alpha channel indicating opacity.
  4622. # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
  4623. #
  4624. # @param hue [Number] The hue of the color.
  4625. # Should be between 0 and 360 degrees, inclusive
  4626. # @param saturation [Number] The saturation of the color.
  4627. # Must be between `0%` and `100%`, inclusive
  4628. # @param lightness [Number] The lightness of the color.
  4629. # Must be between `0%` and `100%`, inclusive
  4630. # @param alpha [Number] The opacity of the color.
  4631. # Must be between 0 and 1, inclusive
  4632. # @return [Color] The resulting color
  4633. # @see #hsl
  4634. # @raise [ArgumentError] if `saturation`, `lightness`, or `alpha` are out of bounds
  4635. def hsla(hue, saturation, lightness, alpha)
  4636. assert_type hue, :Number
  4637. assert_type saturation, :Number
  4638. assert_type lightness, :Number
  4639. assert_type alpha, :Number
  4640. unless (0..1).include?(alpha.value)
  4641. raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1")
  4642. end
  4643. original_s = saturation
  4644. original_l = lightness
  4645. # This algorithm is from http://www.w3.org/TR/css3-color#hsl-color
  4646. h, s, l = [hue, saturation, lightness].map { |a| a.value }
  4647. raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") unless (0..100).include?(s)
  4648. raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") unless (0..100).include?(l)
  4649. Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
  4650. end
  4651. declare :hsla, [:hue, :saturation, :lightness, :alpha]
  4652. # Returns the red component of a color.
  4653. #
  4654. # @param color [Color]
  4655. # @return [Number]
  4656. # @raise [ArgumentError] If `color` isn't a color
  4657. def red(color)
  4658. assert_type color, :Color
  4659. Sass::Script::Number.new(color.red)
  4660. end
  4661. declare :red, [:color]
  4662. # Returns the green component of a color.
  4663. #
  4664. # @param color [Color]
  4665. # @return [Number]
  4666. # @raise [ArgumentError] If `color` isn't a color
  4667. def green(color)
  4668. assert_type color, :Color
  4669. Sass::Script::Number.new(color.green)
  4670. end
  4671. declare :green, [:color]
  4672. # Returns the blue component of a color.
  4673. #
  4674. # @param color [Color]
  4675. # @return [Number]
  4676. # @raise [ArgumentError] If `color` isn't a color
  4677. def blue(color)
  4678. assert_type color, :Color
  4679. Sass::Script::Number.new(color.blue)
  4680. end
  4681. declare :blue, [:color]
  4682. # Returns the hue component of a color.
  4683. #
  4684. # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4685. #
  4686. # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4687. #
  4688. # @param color [Color]
  4689. # @return [Number] between 0deg and 360deg
  4690. # @see #adjust_hue
  4691. # @raise [ArgumentError] if `color` isn't a color
  4692. def hue(color)
  4693. assert_type color, :Color
  4694. Sass::Script::Number.new(color.hue, ["deg"])
  4695. end
  4696. declare :hue, [:color]
  4697. # Returns the saturation component of a color.
  4698. #
  4699. # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4700. #
  4701. # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4702. #
  4703. # @param color [Color]
  4704. # @return [Number] between 0% and 100%
  4705. # @see #saturate
  4706. # @see #desaturate
  4707. # @raise [ArgumentError] if `color` isn't a color
  4708. def saturation(color)
  4709. assert_type color, :Color
  4710. Sass::Script::Number.new(color.saturation, ["%"])
  4711. end
  4712. declare :saturation, [:color]
  4713. # Returns the hue component of a color.
  4714. #
  4715. # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4716. #
  4717. # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
  4718. #
  4719. # @param color [Color]
  4720. # @return [Number] between 0% and 100%
  4721. # @see #lighten
  4722. # @see #darken
  4723. # @raise [ArgumentError] if `color` isn't a color
  4724. def lightness(color)
  4725. assert_type color, :Color
  4726. Sass::Script::Number.new(color.lightness, ["%"])
  4727. end
  4728. declare :lightness, [:color]
  4729. # Returns the alpha component (opacity) of a color.
  4730. # This is 1 unless otherwise specified.
  4731. #
  4732. # This function also supports the proprietary Microsoft
  4733. # `alpha(opacity=20)` syntax.
  4734. #
  4735. # @overload def alpha(color)
  4736. # @param color [Color]
  4737. # @return [Number]
  4738. # @see #opacify
  4739. # @see #transparentize
  4740. # @raise [ArgumentError] If `color` isn't a color
  4741. def alpha(*args)
  4742. if args.all? do |a|
  4743. a.is_a?(Sass::Script::String) && a.type == :identifier &&
  4744. a.value =~ /^[a-zA-Z]+\s*=/
  4745. end
  4746. # Support the proprietary MS alpha() function
  4747. return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
  4748. end
  4749. opacity(*args)
  4750. end
  4751. declare :alpha, [:color]
  4752. # Returns the alpha component (opacity) of a color.
  4753. # This is 1 unless otherwise specified.
  4754. #
  4755. # @param color [Color]
  4756. # @return [Number]
  4757. # @see #opacify
  4758. # @see #transparentize
  4759. # @raise [ArgumentError] If `color` isn't a color
  4760. def opacity(color)
  4761. assert_type color, :Color
  4762. Sass::Script::Number.new(color.alpha)
  4763. end
  4764. declare :opacity, [:color]
  4765. # Makes a color more opaque.
  4766. # Takes a color and an amount between 0 and 1,
  4767. # and returns a color with the opacity increased by that value.
  4768. #
  4769. # @example
  4770. # opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
  4771. # opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
  4772. # @param color [Color]
  4773. # @param amount [Number]
  4774. # @return [Color]
  4775. # @see #transparentize
  4776. # @raise [ArgumentError] If `color` isn't a color,
  4777. # or `number` isn't a number between 0 and 1
  4778. def opacify(color, amount)
  4779. _adjust(color, amount, :alpha, 0..1, :+)
  4780. end
  4781. declare :opacify, [:color, :amount]
  4782. alias_method :fade_in, :opacify
  4783. declare :fade_in, [:color, :amount]
  4784. # Makes a color more transparent.
  4785. # Takes a color and an amount between 0 and 1,
  4786. # and returns a color with the opacity decreased by that value.
  4787. #
  4788. # @example
  4789. # transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
  4790. # transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
  4791. # @param color [Color]
  4792. # @param amount [Number]
  4793. # @return [Color]
  4794. # @see #opacify
  4795. # @raise [ArgumentError] If `color` isn't a color,
  4796. # or `number` isn't a number between 0 and 1
  4797. def transparentize(color, amount)
  4798. _adjust(color, amount, :alpha, 0..1, :-)
  4799. end
  4800. declare :transparentize, [:color, :amount]
  4801. alias_method :fade_out, :transparentize
  4802. declare :fade_out, [:color, :amount]
  4803. # Makes a color lighter.
  4804. # Takes a color and an amount between 0% and 100%,
  4805. # and returns a color with the lightness increased by that value.
  4806. #
  4807. # @example
  4808. # lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
  4809. # lighten(#800, 20%) => #e00
  4810. # @param color [Color]
  4811. # @param amount [Number]
  4812. # @return [Color]
  4813. # @see #darken
  4814. # @raise [ArgumentError] If `color` isn't a color,
  4815. # or `number` isn't a number between 0% and 100%
  4816. def lighten(color, amount)
  4817. _adjust(color, amount, :lightness, 0..100, :+, "%")
  4818. end
  4819. declare :lighten, [:color, :amount]
  4820. # Makes a color darker.
  4821. # Takes a color and an amount between 0% and 100%,
  4822. # and returns a color with the lightness decreased by that value.
  4823. #
  4824. # @example
  4825. # darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
  4826. # darken(#800, 20%) => #200
  4827. # @param color [Color]
  4828. # @param amount [Number]
  4829. # @return [Color]
  4830. # @see #lighten
  4831. # @raise [ArgumentError] If `color` isn't a color,
  4832. # or `number` isn't a number between 0% and 100%
  4833. def darken(color, amount)
  4834. _adjust(color, amount, :lightness, 0..100, :-, "%")
  4835. end
  4836. declare :darken, [:color, :amount]
  4837. # Makes a color more saturated.
  4838. # Takes a color and an amount between 0% and 100%,
  4839. # and returns a color with the saturation increased by that value.
  4840. #
  4841. # @example
  4842. # saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
  4843. # saturate(#855, 20%) => #9e3f3f
  4844. # @param color [Color]
  4845. # @param amount [Number]
  4846. # @return [Color]
  4847. # @see #desaturate
  4848. # @raise [ArgumentError] If `color` isn't a color,
  4849. # or `number` isn't a number between 0% and 100%
  4850. def saturate(color, amount)
  4851. _adjust(color, amount, :saturation, 0..100, :+, "%")
  4852. end
  4853. declare :saturate, [:color, :amount]
  4854. # Makes a color less saturated.
  4855. # Takes a color and an amount between 0% and 100%,
  4856. # and returns a color with the saturation decreased by that value.
  4857. #
  4858. # @example
  4859. # desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
  4860. # desaturate(#855, 20%) => #726b6b
  4861. # @param color [Color]
  4862. # @param amount [Number]
  4863. # @return [Color]
  4864. # @see #saturate
  4865. # @raise [ArgumentError] If `color` isn't a color,
  4866. # or `number` isn't a number between 0% and 100%
  4867. def desaturate(color, amount)
  4868. _adjust(color, amount, :saturation, 0..100, :-, "%")
  4869. end
  4870. declare :desaturate, [:color, :amount]
  4871. # Changes the hue of a color while retaining the lightness and saturation.
  4872. # Takes a color and a number of degrees (usually between -360deg and 360deg),
  4873. # and returns a color with the hue rotated by that value.
  4874. #
  4875. # @example
  4876. # adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
  4877. # adjust-hue(hsl(120, 30%, 90%), 060deg) => hsl(60, 30%, 90%)
  4878. # adjust-hue(#811, 45deg) => #886a11
  4879. # @param color [Color]
  4880. # @param amount [Number]
  4881. # @return [Color]
  4882. # @raise [ArgumentError] If `color` isn't a color, or `number` isn't a number
  4883. def adjust_hue(color, degrees)
  4884. assert_type color, :Color
  4885. assert_type degrees, :Number
  4886. color.with(:hue => color.hue + degrees.value)
  4887. end
  4888. declare :adjust_hue, [:color, :degrees]
  4889. # Adjusts one or more properties of a color.
  4890. # This can change the red, green, blue, hue, saturation, value, and alpha properties.
  4891. # The properties are specified as keyword arguments,
  4892. # and are added to or subtracted from the color's current value for that property.
  4893. #
  4894. # `$red`, `$green`, and `$blue` properties should be between 0 and 255.
  4895. # `$saturation` and `$lightness` should be between 0% and 100%.
  4896. # `$alpha` should be between 0 and 1.
  4897. #
  4898. # All properties are optional.
  4899. # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
  4900. # and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
  4901. #
  4902. # @example
  4903. # adjust-color(#102030, $blue: 5) => #102035
  4904. # adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
  4905. # adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
  4906. # @param color [Color]
  4907. # @param red [Number]
  4908. # @param green [Number]
  4909. # @param blue [Number]
  4910. # @param hue [Number]
  4911. # @param saturation [Number]
  4912. # @param lightness [Number]
  4913. # @param alpha [Number]
  4914. # @return [Color]
  4915. # @raise [ArgumentError] if `color` is not a color,
  4916. # if any keyword argument is not a number,
  4917. # if any keyword argument is not in the legal range,
  4918. # if an unexpected keyword argument is given,
  4919. # or if both HSL and RGB properties are given.
  4920. def adjust_color(color, kwargs)
  4921. assert_type color, :Color
  4922. with = Sass::Util.map_hash({
  4923. "red" => [-255..255, ""],
  4924. "green" => [-255..255, ""],
  4925. "blue" => [-255..255, ""],
  4926. "hue" => nil,
  4927. "saturation" => [-100..100, "%"],
  4928. "lightness" => [-100..100, "%"],
  4929. "alpha" => [-1..1, ""]
  4930. }) do |name, (range, units)|
  4931. next unless val = kwargs.delete(name)
  4932. assert_type val, :Number, name
  4933. if range && !range.include?(val.value)
  4934. raise ArgumentError.new("$#{name}: Amount #{val} must be between #{range.first}#{units} and #{range.last}#{units}")
  4935. end
  4936. adjusted = color.send(name) + val.value
  4937. adjusted = [0, Sass::Util.restrict(adjusted, range)].max if range
  4938. [name.to_sym, adjusted]
  4939. end
  4940. unless kwargs.empty?
  4941. name, val = kwargs.to_a.first
  4942. raise ArgumentError.new("Unknown argument $#{name} (#{val})")
  4943. end
  4944. color.with(with)
  4945. end
  4946. declare :adjust_color, [:color], :var_kwargs => true
  4947. # Scales one or more properties of a color by a percentage value.
  4948. # Unlike \{#adjust_color adjust-color}, which changes a color's properties by fixed amounts,
  4949. # \{#scale_color scale-color} fluidly changes them based on how high or low they already are.
  4950. # That means that lightening an already-light color with \{#scale_color scale-color}
  4951. # won't change the lightness much,
  4952. # but lightening a dark color by the same amount will change it more dramatically.
  4953. # This has the benefit of making `scale-color($color, ...)` have a similar effect
  4954. # regardless of what `$color` is.
  4955. #
  4956. # For example, the lightness of a color can be anywhere between 0 and 100.
  4957. # If `scale-color($color, $lightness: 40%)` is called, the resulting color's lightness
  4958. # will be 40% of the way between its original lightness and 100.
  4959. # If `scale-color($color, $lightness: -40%)` is called instead,
  4960. # the lightness will be 40% of the way between the original and 0.
  4961. #
  4962. # This can change the red, green, blue, saturation, value, and alpha properties.
  4963. # The properties are specified as keyword arguments.
  4964. # All arguments should be percentages between 0% and 100%.
  4965. #
  4966. # All properties are optional.
  4967. # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
  4968. # and HSL properties (`$saturation`, `$value`) at the same time.
  4969. #
  4970. # @example
  4971. # scale-color(hsl(120, 70, 80), $lightness: 50%) => hsl(120, 70, 90)
  4972. # scale-color(rgb(200, 150, 170), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
  4973. # scale-color(hsl(200, 70, 80), $saturation: -90%, $alpha: -30%) => hsla(200, 7, 80, 0.7)
  4974. # @param color [Color]
  4975. # @param red [Number]
  4976. # @param green [Number]
  4977. # @param blue [Number]
  4978. # @param saturation [Number]
  4979. # @param lightness [Number]
  4980. # @param alpha [Number]
  4981. # @return [Color]
  4982. # @raise [ArgumentError] if `color` is not a color,
  4983. # if any keyword argument is not a percentage between 0% and 100%,
  4984. # if an unexpected keyword argument is given,
  4985. # or if both HSL and RGB properties are given.
  4986. def scale_color(color, kwargs)
  4987. assert_type color, :Color
  4988. with = Sass::Util.map_hash({
  4989. "red" => 255,
  4990. "green" => 255,
  4991. "blue" => 255,
  4992. "saturation" => 100,
  4993. "lightness" => 100,
  4994. "alpha" => 1
  4995. }) do |name, max|
  4996. next unless val = kwargs.delete(name)
  4997. assert_type val, :Number, name
  4998. if !(val.numerator_units == ['%'] && val.denominator_units.empty?)
  4999. raise ArgumentError.new("$#{name}: Amount #{val} must be a % (e.g. #{val.value}%)")
  5000. elsif !(-100..100).include?(val.value)
  5001. raise ArgumentError.new("$#{name}: Amount #{val} must be between -100% and 100%")
  5002. end
  5003. current = color.send(name)
  5004. scale = val.value/100.0
  5005. diff = scale > 0 ? max - current : current
  5006. [name.to_sym, current + diff*scale]
  5007. end
  5008. unless kwargs.empty?
  5009. name, val = kwargs.to_a.first
  5010. raise ArgumentError.new("Unknown argument $#{name} (#{val})")
  5011. end
  5012. color.with(with)
  5013. end
  5014. declare :scale_color, [:color], :var_kwargs => true
  5015. # Changes one or more properties of a color.
  5016. # This can change the red, green, blue, hue, saturation, value, and alpha properties.
  5017. # The properties are specified as keyword arguments,
  5018. # and replace the color's current value for that property.
  5019. #
  5020. # `$red`, `$green`, and `$blue` properties should be between 0 and 255.
  5021. # `$saturation` and `$lightness` should be between 0% and 100%.
  5022. # `$alpha` should be between 0 and 1.
  5023. #
  5024. # All properties are optional.
  5025. # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
  5026. # and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
  5027. #
  5028. # @example
  5029. # change-color(#102030, $blue: 5) => #102005
  5030. # change-color(#102030, $red: 120, $blue: 5) => #782005
  5031. # change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
  5032. # @param color [Color]
  5033. # @param red [Number]
  5034. # @param green [Number]
  5035. # @param blue [Number]
  5036. # @param hue [Number]
  5037. # @param saturation [Number]
  5038. # @param lightness [Number]
  5039. # @param alpha [Number]
  5040. # @return [Color]
  5041. # @raise [ArgumentError] if `color` is not a color,
  5042. # if any keyword argument is not a number,
  5043. # if any keyword argument is not in the legal range,
  5044. # if an unexpected keyword argument is given,
  5045. # or if both HSL and RGB properties are given.
  5046. def change_color(color, kwargs)
  5047. assert_type color, :Color
  5048. with = Sass::Util.map_hash(%w[red green blue hue saturation lightness alpha]) do |name, max|
  5049. next unless val = kwargs.delete(name)
  5050. assert_type val, :Number, name
  5051. [name.to_sym, val.value]
  5052. end
  5053. unless kwargs.empty?
  5054. name, val = kwargs.to_a.first
  5055. raise ArgumentError.new("Unknown argument $#{name} (#{val})")
  5056. end
  5057. color.with(with)
  5058. end
  5059. declare :change_color, [:color], :var_kwargs => true
  5060. # Mixes together two colors.
  5061. # Specifically, takes the average of each of the RGB components,
  5062. # optionally weighted by the given percentage.
  5063. # The opacity of the colors is also considered when weighting the components.
  5064. #
  5065. # The weight specifies the amount of the first color that should be included
  5066. # in the returned color.
  5067. # The default, 50%, means that half the first color
  5068. # and half the second color should be used.
  5069. # 25% means that a quarter of the first color
  5070. # and three quarters of the second color should be used.
  5071. #
  5072. # @example
  5073. # mix(#f00, #00f) => #7f007f
  5074. # mix(#f00, #00f, 25%) => #3f00bf
  5075. # mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
  5076. # @overload mix(color1, color2, weight: 50%)
  5077. # @param color1 [Color]
  5078. # @param color2 [Color]
  5079. # @param weight [Number] between 0% and 100%
  5080. # @return [Color]
  5081. # @raise [ArgumentError] if `color1` or `color2` aren't colors,
  5082. # or `weight` isn't a number between 0% and 100%
  5083. def mix(color1, color2, weight = Number.new(50))
  5084. assert_type color1, :Color
  5085. assert_type color2, :Color
  5086. assert_type weight, :Number
  5087. unless (0..100).include?(weight.value)
  5088. raise ArgumentError.new("Weight #{weight} must be between 0% and 100%")
  5089. end
  5090. # This algorithm factors in both the user-provided weight
  5091. # and the difference between the alpha values of the two colors
  5092. # to decide how to perform the weighted average of the two RGB values.
  5093. #
  5094. # It works by first normalizing both parameters to be within [-1, 1],
  5095. # where 1 indicates "only use color1", -1 indicates "only use color 0",
  5096. # and all values in between indicated a proportionately weighted average.
  5097. #
  5098. # Once we have the normalized variables w and a,
  5099. # we apply the formula (w + a)/(1 + w*a)
  5100. # to get the combined weight (in [-1, 1]) of color1.
  5101. # This formula has two especially nice properties:
  5102. #
  5103. # * When either w or a are -1 or 1, the combined weight is also that number
  5104. # (cases where w * a == -1 are undefined, and handled as a special case).
  5105. #
  5106. # * When a is 0, the combined weight is w, and vice versa
  5107. #
  5108. # Finally, the weight of color1 is renormalized to be within [0, 1]
  5109. # and the weight of color2 is given by 1 minus the weight of color1.
  5110. p = weight.value/100.0
  5111. w = p*2 - 1
  5112. a = color1.alpha - color2.alpha
  5113. w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
  5114. w2 = 1 - w1
  5115. rgb = color1.rgb.zip(color2.rgb).map {|v1, v2| v1*w1 + v2*w2}
  5116. alpha = color1.alpha*p + color2.alpha*(1-p)
  5117. Color.new(rgb + [alpha])
  5118. end
  5119. declare :mix, [:color_1, :color_2]
  5120. declare :mix, [:color_1, :color_2, :weight]
  5121. # Converts a color to grayscale.
  5122. # This is identical to `desaturate(color, 100%)`.
  5123. #
  5124. # @param color [Color]
  5125. # @return [Color]
  5126. # @raise [ArgumentError] if `color` isn't a color
  5127. # @see #desaturate
  5128. def grayscale(color)
  5129. desaturate color, Number.new(100)
  5130. end
  5131. declare :grayscale, [:color]
  5132. # Returns the complement of a color.
  5133. # This is identical to `adjust-hue(color, 180deg)`.
  5134. #
  5135. # @param color [Color]
  5136. # @return [Color]
  5137. # @raise [ArgumentError] if `color` isn't a color
  5138. # @see #adjust_hue #adjust-hue
  5139. def complement(color)
  5140. adjust_hue color, Number.new(180)
  5141. end
  5142. declare :complement, [:color]
  5143. # Returns the inverse (negative) of a color.
  5144. # The red, green, and blue values are inverted, while the opacity is left alone.
  5145. #
  5146. # @param color [Color]
  5147. # @return [Color]
  5148. # @raise [ArgumentError] if `color` isn't a color
  5149. def invert(color)
  5150. assert_type color, :Color
  5151. color.with(
  5152. :red => (255 - color.red),
  5153. :green => (255 - color.green),
  5154. :blue => (255 - color.blue))
  5155. end
  5156. # Removes quotes from a string if the string is quoted,
  5157. # or returns the same string if it's not.
  5158. #
  5159. # @param string [String]
  5160. # @return [String]
  5161. # @raise [ArgumentError] if `string` isn't a string
  5162. # @see #quote
  5163. # @example
  5164. # unquote("foo") => foo
  5165. # unquote(foo) => foo
  5166. def unquote(string)
  5167. if string.is_a?(Sass::Script::String)
  5168. Sass::Script::String.new(string.value, :identifier)
  5169. else
  5170. string
  5171. end
  5172. end
  5173. declare :unquote, [:string]
  5174. # Add quotes to a string if the string isn't quoted,
  5175. # or returns the same string if it is.
  5176. #
  5177. # @param string [String]
  5178. # @return [String]
  5179. # @raise [ArgumentError] if `string` isn't a string
  5180. # @see #unquote
  5181. # @example
  5182. # quote("foo") => "foo"
  5183. # quote(foo) => "foo"
  5184. def quote(string)
  5185. assert_type string, :String
  5186. Sass::Script::String.new(string.value, :string)
  5187. end
  5188. declare :quote, [:string]
  5189. # Inspects the type of the argument, returning it as an unquoted string.
  5190. #
  5191. # @example
  5192. # type-of(100px) => number
  5193. # type-of(asdf) => string
  5194. # type-of("asdf") => string
  5195. # type-of(true) => bool
  5196. # type-of(#fff) => color
  5197. # type-of(blue) => color
  5198. # @param value [Literal] The object to inspect
  5199. # @return [String] The unquoted string name of the literal's type
  5200. def type_of(value)
  5201. Sass::Script::String.new(value.class.name.gsub(/Sass::Script::/,'').downcase)
  5202. end
  5203. declare :type_of, [:value]
  5204. # Inspects the unit of the number, returning it as a quoted string.
  5205. # Complex units are sorted in alphabetical order by numerator and denominator.
  5206. #
  5207. # @example
  5208. # unit(100) => ""
  5209. # unit(100px) => "px"
  5210. # unit(3em) => "em"
  5211. # unit(10px * 5em) => "em*px"
  5212. # unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
  5213. # @param number [Literal] The number to inspect
  5214. # @return [String] The unit(s) of the number
  5215. # @raise [ArgumentError] if `number` isn't a number
  5216. def unit(number)
  5217. assert_type number, :Number
  5218. Sass::Script::String.new(number.unit_str, :string)
  5219. end
  5220. declare :unit, [:number]
  5221. # Inspects the unit of the number, returning a boolean indicating if it is unitless.
  5222. #
  5223. # @example
  5224. # unitless(100) => true
  5225. # unitless(100px) => false
  5226. # @param number [Literal] The number to inspect
  5227. # @return [Bool] Whether or not the number is unitless
  5228. # @raise [ArgumentError] if `number` isn't a number
  5229. def unitless(number)
  5230. assert_type number, :Number
  5231. Sass::Script::Bool.new(number.unitless?)
  5232. end
  5233. declare :unitless, [:number]
  5234. # Returns true if two numbers are similar enough to be added, subtracted, or compared.
  5235. #
  5236. # @example
  5237. # comparable(2px, 1px) => true
  5238. # comparable(100px, 3em) => false
  5239. # comparable(10cm, 3mm) => true
  5240. # @param number_1 [Number]
  5241. # @param number_2 [Number]
  5242. # @return [Bool] indicating if the numbers can be compared.
  5243. # @raise [ArgumentError] if `number_1` or `number_2` aren't numbers
  5244. def comparable(number_1, number_2)
  5245. assert_type number_1, :Number
  5246. assert_type number_2, :Number
  5247. Sass::Script::Bool.new(number_1.comparable_to?(number_2))
  5248. end
  5249. declare :comparable, [:number_1, :number_2]
  5250. # Converts a decimal number to a percentage.
  5251. #
  5252. # @example
  5253. # percentage(100px / 50px) => 200%
  5254. # @param value [Number] The decimal number to convert to a percentage
  5255. # @return [Number] The percentage
  5256. # @raise [ArgumentError] If `value` isn't a unitless number
  5257. def percentage(value)
  5258. unless value.is_a?(Sass::Script::Number) && value.unitless?
  5259. raise ArgumentError.new("#{value.inspect} is not a unitless number")
  5260. end
  5261. Sass::Script::Number.new(value.value * 100, ['%'])
  5262. end
  5263. declare :percentage, [:value]
  5264. # Rounds a number to the nearest whole number.
  5265. #
  5266. # @example
  5267. # round(10.4px) => 10px
  5268. # round(10.6px) => 11px
  5269. # @param value [Number] The number
  5270. # @return [Number] The rounded number
  5271. # @raise [ArgumentError] if `value` isn't a number
  5272. def round(value)
  5273. numeric_transformation(value) {|n| n.round}
  5274. end
  5275. declare :round, [:value]
  5276. # Rounds a number up to the nearest whole number.
  5277. #
  5278. # @example
  5279. # ciel(10.4px) => 11px
  5280. # ciel(10.6px) => 11px
  5281. # @param value [Number] The number
  5282. # @return [Number] The rounded number
  5283. # @raise [ArgumentError] if `value` isn't a number
  5284. def ceil(value)
  5285. numeric_transformation(value) {|n| n.ceil}
  5286. end
  5287. declare :ceil, [:value]
  5288. # Rounds down to the nearest whole number.
  5289. #
  5290. # @example
  5291. # floor(10.4px) => 10px
  5292. # floor(10.6px) => 10px
  5293. # @param value [Number] The number
  5294. # @return [Number] The rounded number
  5295. # @raise [ArgumentError] if `value` isn't a number
  5296. def floor(value)
  5297. numeric_transformation(value) {|n| n.floor}
  5298. end
  5299. declare :floor, [:value]
  5300. # Finds the absolute value of a number.
  5301. #
  5302. # @example
  5303. # abs(10px) => 10px
  5304. # abs(-10px) => 10px
  5305. # @param value [Number] The number
  5306. # @return [Number] The absolute value
  5307. # @raise [ArgumentError] if `value` isn't a number
  5308. def abs(value)
  5309. numeric_transformation(value) {|n| n.abs}
  5310. end
  5311. declare :abs, [:value]
  5312. # Return the length of a list.
  5313. #
  5314. # @example
  5315. # length(10px) => 1
  5316. # length(10px 20px 30px) => 3
  5317. # @param list [Literal] The list
  5318. # @return [Number] The length
  5319. def length(list)
  5320. Sass::Script::Number.new(list.to_a.size)
  5321. end
  5322. declare :length, [:list]
  5323. # Gets the nth item in a list.
  5324. #
  5325. # Note that unlike some languages, the first item in a Sass list is number 1,
  5326. # the second number 2, and so forth.
  5327. #
  5328. # @example
  5329. # nth(10px 20px 30px, 1) => 10px
  5330. # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
  5331. # @param list [Literal] The list
  5332. # @param n [Number] The index into the list
  5333. # @return [Literal] The nth item in the list
  5334. # @raise [ArgumentError] If `n` isn't an integer between 1 and the list's length.
  5335. def nth(list, n)
  5336. assert_type n, :Number
  5337. if !n.int?
  5338. raise ArgumentError.new("List index #{n} must be an integer")
  5339. elsif n.to_i < 1
  5340. raise ArgumentError.new("List index #{n} must be greater than or equal to 1")
  5341. elsif list.to_a.size == 0
  5342. raise ArgumentError.new("List index is #{n} but list has no items")
  5343. elsif n.to_i > (size = list.to_a.size)
  5344. raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
  5345. end
  5346. list.to_a[n.to_i - 1]
  5347. end
  5348. declare :nth, [:list, :n]
  5349. # Joins together two lists into a new list.
  5350. #
  5351. # Unless the `$separator` argument is passed,
  5352. # if one list is comma-separated and one is space-separated,
  5353. # the first parameter's separator is used for the resulting list.
  5354. # If the lists have only one item each, spaces are used for the resulting list.
  5355. #
  5356. # @example
  5357. # join(10px 20px, 30px 40px) => 10px 20px 30px 40px
  5358. # join((blue, red), (#abc, #def)) => blue, red, #abc, #def
  5359. # join(10px, 20px) => 10px 20px
  5360. # join(10px, 20px, comma) => 10px, 20px
  5361. # join((blue, red), (#abc, #def), space) => blue red #abc #def
  5362. # @overload join(list1, list2, separator: auto)
  5363. # @param list1 [Literal] The first list to join
  5364. # @param list2 [Literal] The second list to join
  5365. # @param separator [String] How the list separator (comma or space) should be determined.
  5366. # If this is `comma` or `space`, that is always the separator;
  5367. # if this is `auto` (the default), the separator is determined as explained above.
  5368. def join(list1, list2, separator = Sass::Script::String.new("auto"))
  5369. assert_type separator, :String
  5370. unless %w[auto space comma].include?(separator.value)
  5371. raise ArgumentError.new("Separator name must be space, comma, or auto")
  5372. end
  5373. sep1 = list1.separator if list1.is_a?(Sass::Script::List) && !list1.value.empty?
  5374. sep2 = list2.separator if list2.is_a?(Sass::Script::List) && !list2.value.empty?
  5375. Sass::Script::List.new(
  5376. list1.to_a + list2.to_a,
  5377. if separator.value == 'auto'
  5378. sep1 || sep2 || :space
  5379. else
  5380. separator.value.to_sym
  5381. end)
  5382. end
  5383. declare :join, [:list1, :list2]
  5384. declare :join, [:list1, :list2, :separator]
  5385. # Appends a single value onto the end of a list.
  5386. #
  5387. # Unless the `$separator` argument is passed,
  5388. # if the list has only one item,
  5389. # the resulting list will be space-separated.
  5390. #
  5391. # @example
  5392. # append(10px 20px, 30px) => 10px 20px 30px
  5393. # append((blue, red), green) => blue, red, green
  5394. # append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
  5395. # join(10px, 20px, comma) => 10px, 20px
  5396. # join((blue, red), green, space) => blue red green
  5397. # @overload join(list, val, separator: auto)
  5398. # @param list1 [Literal] The first list to join
  5399. # @param list2 [Literal] The second list to join
  5400. # @param separator [String] How the list separator (comma or space) should be determined.
  5401. # If this is `comma` or `space`, that is always the separator;
  5402. # if this is `auto` (the default), the separator is determined as explained above.
  5403. def append(list, val, separator = Sass::Script::String.new("auto"))
  5404. assert_type separator, :String
  5405. unless %w[auto space comma].include?(separator.value)
  5406. raise ArgumentError.new("Separator name must be space, comma, or auto")
  5407. end
  5408. sep = list.separator if list.is_a?(Sass::Script::List)
  5409. Sass::Script::List.new(
  5410. list.to_a + [val],
  5411. if separator.value == 'auto'
  5412. sep || :space
  5413. else
  5414. separator.value.to_sym
  5415. end)
  5416. end
  5417. declare :append, [:list, :val]
  5418. declare :append, [:list, :val, :separator]
  5419. # Returns one of two values based on the truth value of the first argument.
  5420. #
  5421. # @example
  5422. # if(true, 1px, 2px) => 1px
  5423. # if(false, 1px, 2px) => 2px
  5424. # @param condition [Bool] Whether the first or second value will be returned.
  5425. # @param if_true [Literal] The value that will be returned if `$condition` is true.
  5426. # @param if_false [Literal] The value that will be returned if `$condition` is false.
  5427. def if(condition, if_true, if_false)
  5428. if condition.to_bool
  5429. if_true
  5430. else
  5431. if_false
  5432. end
  5433. end
  5434. declare :if, [:condition, :if_true, :if_false]
  5435. private
  5436. # This method implements the pattern of transforming a numeric value into
  5437. # another numeric value with the same units.
  5438. # It yields a number to a block to perform the operation and return a number
  5439. def numeric_transformation(value)
  5440. assert_type value, :Number
  5441. Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
  5442. end
  5443. def _adjust(color, amount, attr, range, op, units = "")
  5444. assert_type color, :Color
  5445. assert_type amount, :Number
  5446. unless range.include?(amount.value)
  5447. raise ArgumentError.new("Amount #{amount} must be between #{range.first}#{units} and #{range.last}#{units}")
  5448. end
  5449. # TODO: is it worth restricting here,
  5450. # or should we do so in the Color constructor itself,
  5451. # and allow clipping in rgb() et al?
  5452. color.with(attr => Sass::Util.restrict(
  5453. color.send(attr).send(op, amount.value), range))
  5454. end
  5455. end
  5456. end
  5457. module Sass
  5458. module Script
  5459. # A SassScript parse node representing a function call.
  5460. #
  5461. # A function call either calls one of the functions in {Script::Functions},
  5462. # or if no function with the given name exists
  5463. # it returns a string representation of the function call.
  5464. class Funcall < Node
  5465. # The name of the function.
  5466. #
  5467. # @return [String]
  5468. attr_reader :name
  5469. # The arguments to the function.
  5470. #
  5471. # @return [Array<Script::Node>]
  5472. attr_reader :args
  5473. # The keyword arguments to the function.
  5474. #
  5475. # @return [{String => Script::Node}]
  5476. attr_reader :keywords
  5477. # @param name [String] See \{#name}
  5478. # @param args [Array<Script::Node>] See \{#args}
  5479. # @param keywords [{String => Script::Node}] See \{#keywords}
  5480. def initialize(name, args, keywords)
  5481. @name = name
  5482. @args = args
  5483. @keywords = keywords
  5484. super()
  5485. end
  5486. # @return [String] A string representation of the function call
  5487. def inspect
  5488. args = @args.map {|a| a.inspect}.join(', ')
  5489. keywords = @keywords.sort_by {|k, v| k}.
  5490. map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
  5491. "#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
  5492. end
  5493. # @see Node#to_sass
  5494. def to_sass(opts = {})
  5495. args = @args.map {|a| a.to_sass(opts)}.join(', ')
  5496. keywords = @keywords.sort_by {|k, v| k}.
  5497. map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
  5498. "#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
  5499. end
  5500. # Returns the arguments to the function.
  5501. #
  5502. # @return [Array<Node>]
  5503. # @see Node#children
  5504. def children
  5505. @args + @keywords.values
  5506. end
  5507. protected
  5508. # Evaluates the function call.
  5509. #
  5510. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  5511. # @return [Literal] The SassScript object that is the value of the function call
  5512. # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
  5513. def _perform(environment)
  5514. args = @args.map {|a| a.perform(environment)}
  5515. if fn = environment.function(@name)
  5516. keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
  5517. return perform_sass_fn(fn, args, keywords)
  5518. end
  5519. ruby_name = @name.tr('-', '_')
  5520. args = construct_ruby_args(ruby_name, args, environment)
  5521. unless Functions.callable?(ruby_name)
  5522. opts(to_literal(args))
  5523. else
  5524. opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
  5525. end
  5526. rescue ArgumentError => e
  5527. raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
  5528. raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
  5529. end
  5530. # This method is factored out from `_perform` so that compass can override
  5531. # it with a cross-browser implementation for functions that require vendor prefixes
  5532. # in the generated css.
  5533. def to_literal(args)
  5534. Script::String.new("#{name}(#{args.join(', ')})")
  5535. end
  5536. private
  5537. def construct_ruby_args(name, args, environment)
  5538. unless signature = Functions.signature(name.to_sym, args.size, @keywords.size)
  5539. return args if keywords.empty?
  5540. raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
  5541. end
  5542. keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
  5543. # If the user passes more non-keyword args than the function expects,
  5544. # but it does expect keyword args, Ruby's arg handling won't raise an error.
  5545. # Since we don't want to make functions think about this,
  5546. # we'll handle it for them here.
  5547. if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
  5548. raise Sass::SyntaxError.new(
  5549. "#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
  5550. elsif keywords.empty?
  5551. return args
  5552. end
  5553. args = args + signature.args[args.size..-1].map do |argname|
  5554. if keywords.has_key?(argname)
  5555. keywords.delete(argname)
  5556. else
  5557. raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
  5558. end
  5559. end
  5560. if keywords.size > 0
  5561. if signature.var_kwargs
  5562. args << keywords
  5563. else
  5564. raise Sass::SyntaxError.new("Function #{name} doesn't take an argument named $#{keywords.keys.sort.first}")
  5565. end
  5566. end
  5567. args
  5568. end
  5569. def perform_sass_fn(function, args, keywords)
  5570. # TODO: merge with mixin arg evaluation?
  5571. keywords.each do |name, value|
  5572. # TODO: Make this fast
  5573. unless function.args.find {|(var, default)| var.underscored_name == name}
  5574. raise Sass::SyntaxError.new("Function #{@name} doesn't have an argument named $#{name}")
  5575. end
  5576. end
  5577. if args.size > function.args.size
  5578. raise ArgumentError.new("Wrong number of arguments (#{args.size} for #{function.args.size})")
  5579. end
  5580. environment = function.args.zip(args).
  5581. inject(Sass::Environment.new(function.environment)) do |env, ((var, default), value)|
  5582. env.set_local_var(var.name,
  5583. value || keywords[var.underscored_name] || (default && default.perform(env)))
  5584. raise Sass::SyntaxError.new("Function #{@name} is missing parameter #{var.inspect}.") unless env.var(var.name)
  5585. env
  5586. end
  5587. val = catch :_sass_return do
  5588. function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, environment)}
  5589. raise Sass::SyntaxError.new("Function #{@name} finished without @return")
  5590. end
  5591. val
  5592. end
  5593. end
  5594. end
  5595. end
  5596. require 'set'
  5597. module Sass::Script
  5598. # The abstract superclass for SassScript objects.
  5599. #
  5600. # Many of these methods, especially the ones that correspond to SassScript operations,
  5601. # are designed to be overridden by subclasses which may change the semantics somewhat.
  5602. # The operations listed here are just the defaults.
  5603. class Literal < Node
  5604. module Sass::Script
  5605. # A SassScript object representing a number.
  5606. # SassScript numbers can have decimal values,
  5607. # and can also have units.
  5608. # For example, `12`, `1px`, and `10.45em`
  5609. # are all valid values.
  5610. #
  5611. # Numbers can also have more complex units, such as `1px*em/in`.
  5612. # These cannot be inputted directly in Sass code at the moment.
  5613. class Number < Literal
  5614. # The Ruby value of the number.
  5615. #
  5616. # @return [Numeric]
  5617. attr_reader :value
  5618. # A list of units in the numerator of the number.
  5619. # For example, `1px*em/in*cm` would return `["px", "em"]`
  5620. # @return [Array<String>]
  5621. attr_reader :numerator_units
  5622. # A list of units in the denominator of the number.
  5623. # For example, `1px*em/in*cm` would return `["in", "cm"]`
  5624. # @return [Array<String>]
  5625. attr_reader :denominator_units
  5626. # The original representation of this number.
  5627. # For example, although the result of `1px/2px` is `0.5`,
  5628. # the value of `#original` is `"1px/2px"`.
  5629. #
  5630. # This is only non-nil when the original value should be used as the CSS value,
  5631. # as in `font: 1px/2px`.
  5632. #
  5633. # @return [Boolean, nil]
  5634. attr_accessor :original
  5635. # The precision with which numbers will be printed to CSS files.
  5636. # For example, if this is `1000.0`,
  5637. # `3.1415926` will be printed as `3.142`.
  5638. # @api public
  5639. PRECISION = 1000.0
  5640. # Used so we don't allocate two new arrays for each new number.
  5641. NO_UNITS = []
  5642. # @param value [Numeric] The value of the number
  5643. # @param numerator_units [Array<String>] See \{#numerator\_units}
  5644. # @param denominator_units [Array<String>] See \{#denominator\_units}
  5645. def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
  5646. super(value)
  5647. @numerator_units = numerator_units
  5648. @denominator_units = denominator_units
  5649. normalize!
  5650. end
  5651. # The SassScript `+` operation.
  5652. # Its functionality depends on the type of its argument:
  5653. #
  5654. # {Number}
  5655. # : Adds the two numbers together, converting units if possible.
  5656. #
  5657. # {Color}
  5658. # : Adds this number to each of the RGB color channels.
  5659. #
  5660. # {Literal}
  5661. # : See {Literal#plus}.
  5662. #
  5663. # @param other [Literal] The right-hand side of the operator
  5664. # @return [Literal] The result of the operation
  5665. # @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
  5666. def plus(other)
  5667. if other.is_a? Number
  5668. operate(other, :+)
  5669. elsif other.is_a?(Color)
  5670. other.plus(self)
  5671. else
  5672. super
  5673. end
  5674. end
  5675. # The SassScript binary `-` operation (e.g. `$a - $b`).
  5676. # Its functionality depends on the type of its argument:
  5677. #
  5678. # {Number}
  5679. # : Subtracts this number from the other, converting units if possible.
  5680. #
  5681. # {Literal}
  5682. # : See {Literal#minus}.
  5683. #
  5684. # @param other [Literal] The right-hand side of the operator
  5685. # @return [Literal] The result of the operation
  5686. # @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
  5687. def minus(other)
  5688. if other.is_a? Number
  5689. operate(other, :-)
  5690. else
  5691. super
  5692. end
  5693. end
  5694. # The SassScript unary `+` operation (e.g. `+$a`).
  5695. #
  5696. # @return [Number] The value of this number
  5697. def unary_plus
  5698. self
  5699. end
  5700. # The SassScript unary `-` operation (e.g. `-$a`).
  5701. #
  5702. # @return [Number] The negative value of this number
  5703. def unary_minus
  5704. Number.new(-value, @numerator_units, @denominator_units)
  5705. end
  5706. # The SassScript `*` operation.
  5707. # Its functionality depends on the type of its argument:
  5708. #
  5709. # {Number}
  5710. # : Multiplies the two numbers together, converting units appropriately.
  5711. #
  5712. # {Color}
  5713. # : Multiplies each of the RGB color channels by this number.
  5714. #
  5715. # @param other [Number, Color] The right-hand side of the operator
  5716. # @return [Number, Color] The result of the operation
  5717. # @raise [NoMethodError] if `other` is an invalid type
  5718. def times(other)
  5719. if other.is_a? Number
  5720. operate(other, :*)
  5721. elsif other.is_a? Color
  5722. other.times(self)
  5723. else
  5724. raise NoMethodError.new(nil, :times)
  5725. end
  5726. end
  5727. # The SassScript `/` operation.
  5728. # Its functionality depends on the type of its argument:
  5729. #
  5730. # {Number}
  5731. # : Divides this number by the other, converting units appropriately.
  5732. #
  5733. # {Literal}
  5734. # : See {Literal#div}.
  5735. #
  5736. # @param other [Literal] The right-hand side of the operator
  5737. # @return [Literal] The result of the operation
  5738. def div(other)
  5739. if other.is_a? Number
  5740. res = operate(other, :/)
  5741. if self.original && other.original
  5742. res.original = "#{self.original}/#{other.original}"
  5743. end
  5744. res
  5745. else
  5746. super
  5747. end
  5748. end
  5749. # The SassScript `%` operation.
  5750. #
  5751. # @param other [Number] The right-hand side of the operator
  5752. # @return [Number] This number modulo the other
  5753. # @raise [NoMethodError] if `other` is an invalid type
  5754. # @raise [Sass::UnitConversionError] if `other` has any units
  5755. def mod(other)
  5756. if other.is_a?(Number)
  5757. unless other.unitless?
  5758. raise Sass::UnitConversionError.new("Cannot modulo by a number with units: #{other.inspect}.")
  5759. end
  5760. operate(other, :%)
  5761. else
  5762. raise NoMethodError.new(nil, :mod)
  5763. end
  5764. end
  5765. # The SassScript `==` operation.
  5766. #
  5767. # @param other [Literal] The right-hand side of the operator
  5768. # @return [Boolean] Whether this number is equal to the other object
  5769. def eq(other)
  5770. return Sass::Script::Bool.new(false) unless other.is_a?(Sass::Script::Number)
  5771. this = self
  5772. begin
  5773. if unitless?
  5774. this = this.coerce(other.numerator_units, other.denominator_units)
  5775. else
  5776. other = other.coerce(@numerator_units, @denominator_units)
  5777. end
  5778. rescue Sass::UnitConversionError
  5779. return Sass::Script::Bool.new(false)
  5780. end
  5781. Sass::Script::Bool.new(this.value == other.value)
  5782. end
  5783. # The SassScript `>` operation.
  5784. #
  5785. # @param other [Number] The right-hand side of the operator
  5786. # @return [Boolean] Whether this number is greater than the other
  5787. # @raise [NoMethodError] if `other` is an invalid type
  5788. def gt(other)
  5789. raise NoMethodError.new(nil, :gt) unless other.is_a?(Number)
  5790. operate(other, :>)
  5791. end
  5792. # The SassScript `>=` operation.
  5793. #
  5794. # @param other [Number] The right-hand side of the operator
  5795. # @return [Boolean] Whether this number is greater than or equal to the other
  5796. # @raise [NoMethodError] if `other` is an invalid type
  5797. def gte(other)
  5798. raise NoMethodError.new(nil, :gte) unless other.is_a?(Number)
  5799. operate(other, :>=)
  5800. end
  5801. # The SassScript `<` operation.
  5802. #
  5803. # @param other [Number] The right-hand side of the operator
  5804. # @return [Boolean] Whether this number is less than the other
  5805. # @raise [NoMethodError] if `other` is an invalid type
  5806. def lt(other)
  5807. raise NoMethodError.new(nil, :lt) unless other.is_a?(Number)
  5808. operate(other, :<)
  5809. end
  5810. # The SassScript `<=` operation.
  5811. #
  5812. # @param other [Number] The right-hand side of the operator
  5813. # @return [Boolean] Whether this number is less than or equal to the other
  5814. # @raise [NoMethodError] if `other` is an invalid type
  5815. def lte(other)
  5816. raise NoMethodError.new(nil, :lte) unless other.is_a?(Number)
  5817. operate(other, :<=)
  5818. end
  5819. # @return [String] The CSS representation of this number
  5820. # @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
  5821. # (e.g. `px*in`)
  5822. def to_s(opts = {})
  5823. return original if original
  5824. raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
  5825. inspect
  5826. end
  5827. # Returns a readable representation of this number.
  5828. #
  5829. # This representation is valid CSS (and valid SassScript)
  5830. # as long as there is only one unit.
  5831. #
  5832. # @return [String] The representation
  5833. def inspect(opts = {})
  5834. value = self.class.round(self.value)
  5835. unitless? ? value.to_s : "#{value}#{unit_str}"
  5836. end
  5837. alias_method :to_sass, :inspect
  5838. # @return [Fixnum] The integer value of the number
  5839. # @raise [Sass::SyntaxError] if the number isn't an integer
  5840. def to_i
  5841. super unless int?
  5842. return value
  5843. end
  5844. # @return [Boolean] Whether or not this number is an integer.
  5845. def int?
  5846. value % 1 == 0.0
  5847. end
  5848. # @return [Boolean] Whether or not this number has no units.
  5849. def unitless?
  5850. @numerator_units.empty? && @denominator_units.empty?
  5851. end
  5852. # @return [Boolean] Whether or not this number has units that can be represented in CSS
  5853. # (that is, zero or one \{#numerator\_units}).
  5854. def legal_units?
  5855. (@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
  5856. end
  5857. # Returns this number converted to other units.
  5858. # The conversion takes into account the relationship between e.g. mm and cm,
  5859. # as well as between e.g. in and cm.
  5860. #
  5861. # If this number has no units, it will simply return itself
  5862. # with the given units.
  5863. #
  5864. # An incompatible coercion, e.g. between px and cm, will raise an error.
  5865. #
  5866. # @param num_units [Array<String>] The numerator units to coerce this number into.
  5867. # See {\#numerator\_units}
  5868. # @param den_units [Array<String>] The denominator units to coerce this number into.
  5869. # See {\#denominator\_units}
  5870. # @return [Number] The number with the new units
  5871. # @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
  5872. # current units
  5873. def coerce(num_units, den_units)
  5874. Number.new(if unitless?
  5875. self.value
  5876. else
  5877. self.value * coercion_factor(@numerator_units, num_units) /
  5878. coercion_factor(@denominator_units, den_units)
  5879. end, num_units, den_units)
  5880. end
  5881. # @param other [Number] A number to decide if it can be compared with this number.
  5882. # @return [Boolean] Whether or not this number can be compared with the other.
  5883. def comparable_to?(other)
  5884. begin
  5885. operate(other, :+)
  5886. true
  5887. rescue Sass::UnitConversionError
  5888. false
  5889. end
  5890. end
  5891. # Returns a human readable representation of the units in this number.
  5892. # For complex units this takes the form of:
  5893. # numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
  5894. # @return [String] a string that represents the units in this number
  5895. def unit_str
  5896. rv = @numerator_units.sort.join("*")
  5897. if @denominator_units.any?
  5898. rv << "/"
  5899. rv << @denominator_units.sort.join("*")
  5900. end
  5901. rv
  5902. end
  5903. private
  5904. # @private
  5905. def self.round(num)
  5906. if num.is_a?(Float) && (num.infinite? || num.nan?)
  5907. num
  5908. elsif num % 1 == 0.0
  5909. num.to_i
  5910. else
  5911. (num * PRECISION).round / PRECISION
  5912. end
  5913. end
  5914. OPERATIONS = [:+, :-, :<=, :<, :>, :>=]
  5915. def operate(other, operation)
  5916. this = self
  5917. if OPERATIONS.include?(operation)
  5918. if unitless?
  5919. this = this.coerce(other.numerator_units, other.denominator_units)
  5920. else
  5921. other = other.coerce(@numerator_units, @denominator_units)
  5922. end
  5923. end
  5924. # avoid integer division
  5925. value = (:/ == operation) ? this.value.to_f : this.value
  5926. result = value.send(operation, other.value)
  5927. if result.is_a?(Numeric)
  5928. Number.new(result, *compute_units(this, other, operation))
  5929. else # Boolean op
  5930. Bool.new(result)
  5931. end
  5932. end
  5933. def coercion_factor(from_units, to_units)
  5934. # get a list of unmatched units
  5935. from_units, to_units = sans_common_units(from_units, to_units)
  5936. if from_units.size != to_units.size || !convertable?(from_units | to_units)
  5937. raise Sass::UnitConversionError.new("Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
  5938. end
  5939. from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1]) }
  5940. end
  5941. def compute_units(this, other, operation)
  5942. case operation
  5943. when :*
  5944. [this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units]
  5945. when :/
  5946. [this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units]
  5947. else
  5948. [this.numerator_units, this.denominator_units]
  5949. end
  5950. end
  5951. def normalize!
  5952. return if unitless?
  5953. @numerator_units, @denominator_units = sans_common_units(@numerator_units, @denominator_units)
  5954. @denominator_units.each_with_index do |d, i|
  5955. if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?)))
  5956. @value /= conversion_factor(d, u)
  5957. @denominator_units.delete_at(i)
  5958. @numerator_units.delete_at(@numerator_units.index(u))
  5959. end
  5960. end
  5961. end
  5962. # A hash of unit names to their index in the conversion table
  5963. CONVERTABLE_UNITS = {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4}
  5964. CONVERSION_TABLE = [[ 1, 2.54, 6, 25.4, 72 ], # in
  5965. [ nil, 1, 2.36220473, 10, 28.3464567], # cm
  5966. [ nil, nil, 1, 4.23333333, 12 ], # pc
  5967. [ nil, nil, nil, 1, 2.83464567], # mm
  5968. [ nil, nil, nil, nil, 1 ]] # pt
  5969. def conversion_factor(from_unit, to_unit)
  5970. res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]]
  5971. return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
  5972. res
  5973. end
  5974. def convertable?(units)
  5975. Array(units).all? {|u| CONVERTABLE_UNITS.include?(u)}
  5976. end
  5977. def sans_common_units(units1, units2)
  5978. units2 = units2.dup
  5979. # Can't just use -, because we want px*px to coerce properly to px*mm
  5980. return units1.map do |u|
  5981. next u unless j = units2.index(u)
  5982. units2.delete_at(j)
  5983. nil
  5984. end.compact, units2
  5985. end
  5986. end
  5987. end
  5988. module Sass::Script
  5989. # A SassScript object representing a CSS color.
  5990. #
  5991. # A color may be represented internally as RGBA, HSLA, or both.
  5992. # It's originally represented as whatever its input is;
  5993. # if it's created with RGB values, it's represented as RGBA,
  5994. # and if it's created with HSL values, it's represented as HSLA.
  5995. # Once a property is accessed that requires the other representation --
  5996. # for example, \{#red} for an HSL color --
  5997. # that component is calculated and cached.
  5998. #
  5999. # The alpha channel of a color is independent of its RGB or HSL representation.
  6000. # It's always stored, as 1 if nothing else is specified.
  6001. # If only the alpha channel is modified using \{#with},
  6002. # the cached RGB and HSL values are retained.
  6003. class Color < Literal
  6004. class << self; include Sass::Util; end
  6005. # A hash from color names to `[red, green, blue]` value arrays.
  6006. HTML4_COLORS = map_vals({
  6007. 'black' => 0x000000,
  6008. 'silver' => 0xc0c0c0,
  6009. 'gray' => 0x808080,
  6010. 'white' => 0xffffff,
  6011. 'maroon' => 0x800000,
  6012. 'red' => 0xff0000,
  6013. 'purple' => 0x800080,
  6014. 'fuchsia' => 0xff00ff,
  6015. 'green' => 0x008000,
  6016. 'lime' => 0x00ff00,
  6017. 'olive' => 0x808000,
  6018. 'yellow' => 0xffff00,
  6019. 'navy' => 0x000080,
  6020. 'blue' => 0x0000ff,
  6021. 'teal' => 0x008080,
  6022. 'aqua' => 0x00ffff
  6023. }) {|color| (0..2).map {|n| color >> (n << 3) & 0xff}.reverse}
  6024. # A hash from `[red, green, blue]` value arrays to color names.
  6025. HTML4_COLORS_REVERSE = map_hash(HTML4_COLORS) {|k, v| [v, k]}
  6026. # Constructs an RGB or HSL color object,
  6027. # optionally with an alpha channel.
  6028. #
  6029. # The RGB values must be between 0 and 255.
  6030. # The saturation and lightness values must be between 0 and 100.
  6031. # The alpha value must be between 0 and 1.
  6032. #
  6033. # @raise [Sass::SyntaxError] if any color value isn't in the specified range
  6034. #
  6035. # @overload initialize(attrs)
  6036. # The attributes are specified as a hash.
  6037. # This hash must contain either `:hue`, `:saturation`, and `:value` keys,
  6038. # or `:red`, `:green`, and `:blue` keys.
  6039. # It cannot contain both HSL and RGB keys.
  6040. # It may also optionally contain an `:alpha` key.
  6041. #
  6042. # @param attrs [{Symbol => Numeric}] A hash of color attributes to values
  6043. # @raise [ArgumentError] if not enough attributes are specified,
  6044. # or both RGB and HSL attributes are specified
  6045. #
  6046. # @overload initialize(rgba)
  6047. # The attributes are specified as an array.
  6048. # This overload only supports RGB or RGBA colors.
  6049. #
  6050. # @param rgba [Array<Numeric>] A three- or four-element array
  6051. # of the red, green, blue, and optionally alpha values (respectively)
  6052. # of the color
  6053. # @raise [ArgumentError] if not enough attributes are specified
  6054. def initialize(attrs, allow_both_rgb_and_hsl = false)
  6055. super(nil)
  6056. if attrs.is_a?(Array)
  6057. unless (3..4).include?(attrs.size)
  6058. raise ArgumentError.new("Color.new(array) expects a three- or four-element array")
  6059. end
  6060. red, green, blue = attrs[0...3].map {|c| c.to_i}
  6061. @attrs = {:red => red, :green => green, :blue => blue}
  6062. @attrs[:alpha] = attrs[3] ? attrs[3].to_f : 1
  6063. else
  6064. attrs = attrs.reject {|k, v| v.nil?}
  6065. hsl = [:hue, :saturation, :lightness] & attrs.keys
  6066. rgb = [:red, :green, :blue] & attrs.keys
  6067. if !allow_both_rgb_and_hsl && !hsl.empty? && !rgb.empty?
  6068. raise ArgumentError.new("Color.new(hash) may not have both HSL and RGB keys specified")
  6069. elsif hsl.empty? && rgb.empty?
  6070. raise ArgumentError.new("Color.new(hash) must have either HSL or RGB keys specified")
  6071. elsif !hsl.empty? && hsl.size != 3
  6072. raise ArgumentError.new("Color.new(hash) must have all three HSL values specified")
  6073. elsif !rgb.empty? && rgb.size != 3
  6074. raise ArgumentError.new("Color.new(hash) must have all three RGB values specified")
  6075. end
  6076. @attrs = attrs
  6077. @attrs[:hue] %= 360 if @attrs[:hue]
  6078. @attrs[:alpha] ||= 1
  6079. end
  6080. [:red, :green, :blue].each do |k|
  6081. next if @attrs[k].nil?
  6082. @attrs[k] = @attrs[k].to_i
  6083. next if (0..255).include?(@attrs[k])
  6084. raise ArgumentError.new("#{k.to_s.capitalize} value must be between 0 and 255")
  6085. end
  6086. [:saturation, :lightness].each do |k|
  6087. next if @attrs[k].nil?
  6088. @attrs[k] = 0 if @attrs[k] < 0.00001 && @attrs[k] > -0.00001
  6089. @attrs[k] = 100 if @attrs[k] - 100 < 0.00001 && @attrs[k] - 100 > -0.00001
  6090. next if (0..100).include?(@attrs[k])
  6091. raise ArgumentError.new("#{k.to_s.capitalize} must be between 0 and 100")
  6092. end
  6093. unless (0..1).include?(@attrs[:alpha])
  6094. raise ArgumentError.new("Alpha channel must be between 0 and 1")
  6095. end
  6096. end
  6097. # The red component of the color.
  6098. #
  6099. # @return [Fixnum]
  6100. def red
  6101. hsl_to_rgb!
  6102. @attrs[:red]
  6103. end
  6104. # The green component of the color.
  6105. #
  6106. # @return [Fixnum]
  6107. def green
  6108. hsl_to_rgb!
  6109. @attrs[:green]
  6110. end
  6111. # The blue component of the color.
  6112. #
  6113. # @return [Fixnum]
  6114. def blue
  6115. hsl_to_rgb!
  6116. @attrs[:blue]
  6117. end
  6118. # The hue component of the color.
  6119. #
  6120. # @return [Numeric]
  6121. def hue
  6122. rgb_to_hsl!
  6123. @attrs[:hue]
  6124. end
  6125. # The saturation component of the color.
  6126. #
  6127. # @return [Numeric]
  6128. def saturation
  6129. rgb_to_hsl!
  6130. @attrs[:saturation]
  6131. end
  6132. # The lightness component of the color.
  6133. #
  6134. # @return [Numeric]
  6135. def lightness
  6136. rgb_to_hsl!
  6137. @attrs[:lightness]
  6138. end
  6139. # The alpha channel (opacity) of the color.
  6140. # This is 1 unless otherwise defined.
  6141. #
  6142. # @return [Fixnum]
  6143. def alpha
  6144. @attrs[:alpha]
  6145. end
  6146. # Returns whether this color object is translucent;
  6147. # that is, whether the alpha channel is non-1.
  6148. #
  6149. # @return [Boolean]
  6150. def alpha?
  6151. alpha < 1
  6152. end
  6153. # Returns the red, green, and blue components of the color.
  6154. #
  6155. # @return [Array<Fixnum>] A frozen three-element array of the red, green, and blue
  6156. # values (respectively) of the color
  6157. def rgb
  6158. [red, green, blue].freeze
  6159. end
  6160. # Returns the hue, saturation, and lightness components of the color.
  6161. #
  6162. # @return [Array<Fixnum>] A frozen three-element array of the
  6163. # hue, saturation, and lightness values (respectively) of the color
  6164. def hsl
  6165. [hue, saturation, lightness].freeze
  6166. end
  6167. # The SassScript `==` operation.
  6168. # **Note that this returns a {Sass::Script::Bool} object,
  6169. # not a Ruby boolean**.
  6170. #
  6171. # @param other [Literal] The right-hand side of the operator
  6172. # @return [Bool] True if this literal is the same as the other,
  6173. # false otherwise
  6174. def eq(other)
  6175. Sass::Script::Bool.new(
  6176. other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha)
  6177. end
  6178. # Returns a copy of this color with one or more channels changed.
  6179. # RGB or HSL colors may be changed, but not both at once.
  6180. #
  6181. # For example:
  6182. #
  6183. # Color.new([10, 20, 30]).with(:blue => 40)
  6184. # #=> rgb(10, 40, 30)
  6185. # Color.new([126, 126, 126]).with(:red => 0, :green => 255)
  6186. # #=> rgb(0, 255, 126)
  6187. # Color.new([255, 0, 127]).with(:saturation => 60)
  6188. # #=> rgb(204, 51, 127)
  6189. # Color.new([1, 2, 3]).with(:alpha => 0.4)
  6190. # #=> rgba(1, 2, 3, 0.4)
  6191. #
  6192. # @param attrs [{Symbol => Numeric}]
  6193. # A map of channel names (`:red`, `:green`, `:blue`,
  6194. # `:hue`, `:saturation`, `:lightness`, or `:alpha`) to values
  6195. # @return [Color] The new Color object
  6196. # @raise [ArgumentError] if both RGB and HSL keys are specified
  6197. def with(attrs)
  6198. attrs = attrs.reject {|k, v| v.nil?}
  6199. hsl = !([:hue, :saturation, :lightness] & attrs.keys).empty?
  6200. rgb = !([:red, :green, :blue] & attrs.keys).empty?
  6201. if hsl && rgb
  6202. raise ArgumentError.new("Cannot specify HSL and RGB values for a color at the same time")
  6203. end
  6204. if hsl
  6205. [:hue, :saturation, :lightness].each {|k| attrs[k] ||= send(k)}
  6206. elsif rgb
  6207. [:red, :green, :blue].each {|k| attrs[k] ||= send(k)}
  6208. else
  6209. # If we're just changing the alpha channel,
  6210. # keep all the HSL/RGB stuff we've calculated
  6211. attrs = @attrs.merge(attrs)
  6212. end
  6213. attrs[:alpha] ||= alpha
  6214. Color.new(attrs, :allow_both_rgb_and_hsl)
  6215. end
  6216. # The SassScript `+` operation.
  6217. # Its functionality depends on the type of its argument:
  6218. #
  6219. # {Number}
  6220. # : Adds the number to each of the RGB color channels.
  6221. #
  6222. # {Color}
  6223. # : Adds each of the RGB color channels together.
  6224. #
  6225. # {Literal}
  6226. # : See {Literal#plus}.
  6227. #
  6228. # @param other [Literal] The right-hand side of the operator
  6229. # @return [Color] The resulting color
  6230. # @raise [Sass::SyntaxError] if `other` is a number with units
  6231. def plus(other)
  6232. if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
  6233. piecewise(other, :+)
  6234. else
  6235. super
  6236. end
  6237. end
  6238. # The SassScript `-` operation.
  6239. # Its functionality depends on the type of its argument:
  6240. #
  6241. # {Number}
  6242. # : Subtracts the number from each of the RGB color channels.
  6243. #
  6244. # {Color}
  6245. # : Subtracts each of the other color's RGB color channels from this color's.
  6246. #
  6247. # {Literal}
  6248. # : See {Literal#minus}.
  6249. #
  6250. # @param other [Literal] The right-hand side of the operator
  6251. # @return [Color] The resulting color
  6252. # @raise [Sass::SyntaxError] if `other` is a number with units
  6253. def minus(other)
  6254. if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
  6255. piecewise(other, :-)
  6256. else
  6257. super
  6258. end
  6259. end
  6260. # The SassScript `*` operation.
  6261. # Its functionality depends on the type of its argument:
  6262. #
  6263. # {Number}
  6264. # : Multiplies the number by each of the RGB color channels.
  6265. #
  6266. # {Color}
  6267. # : Multiplies each of the RGB color channels together.
  6268. #
  6269. # @param other [Number, Color] The right-hand side of the operator
  6270. # @return [Color] The resulting color
  6271. # @raise [Sass::SyntaxError] if `other` is a number with units
  6272. def times(other)
  6273. if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
  6274. piecewise(other, :*)
  6275. else
  6276. raise NoMethodError.new(nil, :times)
  6277. end
  6278. end
  6279. # The SassScript `/` operation.
  6280. # Its functionality depends on the type of its argument:
  6281. #
  6282. # {Number}
  6283. # : Divides each of the RGB color channels by the number.
  6284. #
  6285. # {Color}
  6286. # : Divides each of this color's RGB color channels by the other color's.
  6287. #
  6288. # {Literal}
  6289. # : See {Literal#div}.
  6290. #
  6291. # @param other [Literal] The right-hand side of the operator
  6292. # @return [Color] The resulting color
  6293. # @raise [Sass::SyntaxError] if `other` is a number with units
  6294. def div(other)
  6295. if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
  6296. piecewise(other, :/)
  6297. else
  6298. super
  6299. end
  6300. end
  6301. # The SassScript `%` operation.
  6302. # Its functionality depends on the type of its argument:
  6303. #
  6304. # {Number}
  6305. # : Takes each of the RGB color channels module the number.
  6306. #
  6307. # {Color}
  6308. # : Takes each of this color's RGB color channels modulo the other color's.
  6309. #
  6310. # @param other [Number, Color] The right-hand side of the operator
  6311. # @return [Color] The resulting color
  6312. # @raise [Sass::SyntaxError] if `other` is a number with units
  6313. def mod(other)
  6314. if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
  6315. piecewise(other, :%)
  6316. else
  6317. raise NoMethodError.new(nil, :mod)
  6318. end
  6319. end
  6320. # Returns a string representation of the color.
  6321. # This is usually the color's hex value,
  6322. # but if the color has a name that's used instead.
  6323. #
  6324. # @return [String] The string representation
  6325. def to_s(opts = {})
  6326. return rgba_str if alpha?
  6327. return smallest if options[:style] == :compressed
  6328. return HTML4_COLORS_REVERSE[rgb] if HTML4_COLORS_REVERSE[rgb]
  6329. hex_str
  6330. end
  6331. alias_method :to_sass, :to_s
  6332. # Returns a string representation of the color.
  6333. #
  6334. # @return [String] The hex value
  6335. def inspect
  6336. alpha? ? rgba_str : hex_str
  6337. end
  6338. private
  6339. def smallest
  6340. small_hex_str = hex_str.gsub(/^#(.)\1(.)\2(.)\3$/, '#\1\2\3')
  6341. return small_hex_str unless (color = HTML4_COLORS_REVERSE[rgb]) &&
  6342. color.size <= small_hex_str.size
  6343. return color
  6344. end
  6345. def rgba_str
  6346. split = options[:style] == :compressed ? ',' : ', '
  6347. "rgba(#{rgb.join(split)}#{split}#{Number.round(alpha)})"
  6348. end
  6349. def hex_str
  6350. red, green, blue = rgb.map { |num| num.to_s(16).rjust(2, '0') }
  6351. "##{red}#{green}#{blue}"
  6352. end
  6353. def piecewise(other, operation)
  6354. other_num = other.is_a? Number
  6355. if other_num && !other.unitless?
  6356. raise Sass::SyntaxError.new("Cannot add a number with units (#{other}) to a color (#{self}).")
  6357. end
  6358. result = []
  6359. for i in (0...3)
  6360. res = rgb[i].send(operation, other_num ? other.value : other.rgb[i])
  6361. result[i] = [ [res, 255].min, 0 ].max
  6362. end
  6363. if !other_num && other.alpha != alpha
  6364. raise Sass::SyntaxError.new("Alpha channels must be equal: #{self} #{operation} #{other}")
  6365. end
  6366. with(:red => result[0], :green => result[1], :blue => result[2])
  6367. end
  6368. def hsl_to_rgb!
  6369. return if @attrs[:red] && @attrs[:blue] && @attrs[:green]
  6370. h = @attrs[:hue] / 360.0
  6371. s = @attrs[:saturation] / 100.0
  6372. l = @attrs[:lightness] / 100.0
  6373. # Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
  6374. m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
  6375. m1 = l * 2 - m2
  6376. @attrs[:red], @attrs[:green], @attrs[:blue] = [
  6377. hue_to_rgb(m1, m2, h + 1.0/3),
  6378. hue_to_rgb(m1, m2, h),
  6379. hue_to_rgb(m1, m2, h - 1.0/3)
  6380. ].map {|c| (c * 0xff).round}
  6381. end
  6382. def hue_to_rgb(m1, m2, h)
  6383. h += 1 if h < 0
  6384. h -= 1 if h > 1
  6385. return m1 + (m2 - m1) * h * 6 if h * 6 < 1
  6386. return m2 if h * 2 < 1
  6387. return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
  6388. return m1
  6389. end
  6390. def rgb_to_hsl!
  6391. return if @attrs[:hue] && @attrs[:saturation] && @attrs[:lightness]
  6392. r, g, b = [:red, :green, :blue].map {|k| @attrs[k] / 255.0}
  6393. # Algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
  6394. max = [r, g, b].max
  6395. min = [r, g, b].min
  6396. d = max - min
  6397. h =
  6398. case max
  6399. when min; 0
  6400. when r; 60 * (g-b)/d
  6401. when g; 60 * (b-r)/d + 120
  6402. when b; 60 * (r-g)/d + 240
  6403. end
  6404. l = (max + min)/2.0
  6405. s =
  6406. if max == min
  6407. 0
  6408. elsif l < 0.5
  6409. d/(2*l)
  6410. else
  6411. d/(2 - 2*l)
  6412. end
  6413. @attrs[:hue] = h % 360
  6414. @attrs[:saturation] = s * 100
  6415. @attrs[:lightness] = l * 100
  6416. end
  6417. end
  6418. end
  6419. module Sass::Script
  6420. # A SassScript object representing a boolean (true or false) value.
  6421. class Bool < Literal
  6422. # The Ruby value of the boolean.
  6423. #
  6424. # @return [Boolean]
  6425. attr_reader :value
  6426. alias_method :to_bool, :value
  6427. # @return [String] "true" or "false"
  6428. def to_s(opts = {})
  6429. @value.to_s
  6430. end
  6431. alias_method :to_sass, :to_s
  6432. end
  6433. end
  6434. module Sass::Script
  6435. # A SassScript object representing a CSS list.
  6436. # This includes both comma-separated lists and space-separated lists.
  6437. class List < Literal
  6438. # The Ruby array containing the contents of the list.
  6439. #
  6440. # @return [Array<Literal>]
  6441. attr_reader :value
  6442. alias_method :children, :value
  6443. alias_method :to_a, :value
  6444. # The operator separating the values of the list.
  6445. # Either `:comma` or `:space`.
  6446. #
  6447. # @return [Symbol]
  6448. attr_reader :separator
  6449. # Creates a new list.
  6450. #
  6451. # @param value [Array<Literal>] See \{#value}
  6452. # @param separator [String] See \{#separator}
  6453. def initialize(value, separator)
  6454. super(value)
  6455. @separator = separator
  6456. end
  6457. # @see Node#eq
  6458. def eq(other)
  6459. Sass::Script::Bool.new(
  6460. self.class == other.class && self.value == other.value &&
  6461. self.separator == other.separator)
  6462. end
  6463. # @see Node#to_s
  6464. def to_s(opts = {})
  6465. raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
  6466. return value.reject {|e| e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
  6467. end
  6468. # @see Node#to_sass
  6469. def to_sass(opts = {})
  6470. precedence = Sass::Script::Parser.precedence_of(separator)
  6471. value.map do |v|
  6472. if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence
  6473. "(#{v.to_sass(opts)})"
  6474. else
  6475. v.to_sass(opts)
  6476. end
  6477. end.join(sep_str(nil))
  6478. end
  6479. # @see Node#inspect
  6480. def inspect
  6481. "(#{to_sass})"
  6482. end
  6483. protected
  6484. # @see Node#_perform
  6485. def _perform(environment)
  6486. list = Sass::Script::List.new(
  6487. value.map {|e| e.perform(environment)},
  6488. separator)
  6489. list.options = self.options
  6490. list
  6491. end
  6492. private
  6493. def sep_str(opts = self.options)
  6494. return ' ' if separator == :space
  6495. return ',' if opts && opts[:style] == :compressed
  6496. return ', '
  6497. end
  6498. end
  6499. end
  6500. # Returns the Ruby value of the literal.
  6501. # The type of this value varies based on the subclass.
  6502. #
  6503. # @return [Object]
  6504. attr_reader :value
  6505. # Creates a new literal.
  6506. #
  6507. # @param value [Object] The object for \{#value}
  6508. def initialize(value = nil)
  6509. @value = value
  6510. super()
  6511. end
  6512. # Returns an empty array.
  6513. #
  6514. # @return [Array<Node>] empty
  6515. # @see Node#children
  6516. def children
  6517. []
  6518. end
  6519. # Returns the options hash for this node.
  6520. #
  6521. # @return [{Symbol => Object}]
  6522. # @raise [Sass::SyntaxError] if the options hash hasn't been set.
  6523. # This should only happen when the literal was created
  6524. # outside of the parser and \{#to\_s} was called on it
  6525. def options
  6526. opts = super
  6527. return opts if opts
  6528. raise Sass::SyntaxError.new(<<MSG)
  6529. The #options attribute is not set on this #{self.class}.
  6530. This error is probably occurring because #to_s was called
  6531. on this literal within a custom Sass function without first
  6532. setting the #option attribute.
  6533. MSG
  6534. end
  6535. # The SassScript `and` operation.
  6536. #
  6537. # @param other [Literal] The right-hand side of the operator
  6538. # @return [Literal] The result of a logical and:
  6539. # `other` if this literal isn't a false {Bool},
  6540. # and this literal otherwise
  6541. def and(other)
  6542. to_bool ? other : self
  6543. end
  6544. # The SassScript `or` operation.
  6545. #
  6546. # @param other [Literal] The right-hand side of the operator
  6547. # @return [Literal] The result of the logical or:
  6548. # this literal if it isn't a false {Bool},
  6549. # and `other` otherwise
  6550. def or(other)
  6551. to_bool ? self : other
  6552. end
  6553. # The SassScript `==` operation.
  6554. # **Note that this returns a {Sass::Script::Bool} object,
  6555. # not a Ruby boolean**.
  6556. #
  6557. # @param other [Literal] The right-hand side of the operator
  6558. # @return [Bool] True if this literal is the same as the other,
  6559. # false otherwise
  6560. def eq(other)
  6561. Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
  6562. end
  6563. # The SassScript `!=` operation.
  6564. # **Note that this returns a {Sass::Script::Bool} object,
  6565. # not a Ruby boolean**.
  6566. #
  6567. # @param other [Literal] The right-hand side of the operator
  6568. # @return [Bool] False if this literal is the same as the other,
  6569. # true otherwise
  6570. def neq(other)
  6571. Sass::Script::Bool.new(!eq(other).to_bool)
  6572. end
  6573. # The SassScript `==` operation.
  6574. # **Note that this returns a {Sass::Script::Bool} object,
  6575. # not a Ruby boolean**.
  6576. #
  6577. # @param other [Literal] The right-hand side of the operator
  6578. # @return [Bool] True if this literal is the same as the other,
  6579. # false otherwise
  6580. def unary_not
  6581. Sass::Script::Bool.new(!to_bool)
  6582. end
  6583. # The SassScript default operation (e.g. `$a $b`, `"foo" "bar"`).
  6584. #
  6585. # @param other [Literal] The right-hand side of the operator
  6586. # @return [Script::String] A string containing both literals
  6587. # separated by a space
  6588. def space(other)
  6589. Sass::Script::String.new("#{self.to_s} #{other.to_s}")
  6590. end
  6591. # The SassScript `,` operation (e.g. `$a, $b`, `"foo", "bar"`).
  6592. #
  6593. # @param other [Literal] The right-hand side of the operator
  6594. # @return [Script::String] A string containing both literals
  6595. # separated by `", "`
  6596. def comma(other)
  6597. Sass::Script::String.new("#{self.to_s},#{' ' unless options[:style] == :compressed}#{other.to_s}")
  6598. end
  6599. # The SassScript `=` operation
  6600. # (used for proprietary MS syntax like `alpha(opacity=20)`).
  6601. #
  6602. # @param other [Literal] The right-hand side of the operator
  6603. # @return [Script::String] A string containing both literals
  6604. # separated by `"="`
  6605. def single_eq(other)
  6606. Sass::Script::String.new("#{self.to_s}=#{other.to_s}")
  6607. end
  6608. # The SassScript `+` operation.
  6609. #
  6610. # @param other [Literal] The right-hand side of the operator
  6611. # @return [Script::String] A string containing both literals
  6612. # without any separation
  6613. def plus(other)
  6614. if other.is_a?(Sass::Script::String)
  6615. return Sass::Script::String.new(self.to_s + other.value, other.type)
  6616. end
  6617. Sass::Script::String.new(self.to_s + other.to_s)
  6618. end
  6619. # The SassScript `-` operation.
  6620. #
  6621. # @param other [Literal] The right-hand side of the operator
  6622. # @return [Script::String] A string containing both literals
  6623. # separated by `"-"`
  6624. def minus(other)
  6625. Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
  6626. end
  6627. # The SassScript `/` operation.
  6628. #
  6629. # @param other [Literal] The right-hand side of the operator
  6630. # @return [Script::String] A string containing both literals
  6631. # separated by `"/"`
  6632. def div(other)
  6633. Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
  6634. end
  6635. # The SassScript unary `+` operation (e.g. `+$a`).
  6636. #
  6637. # @param other [Literal] The right-hand side of the operator
  6638. # @return [Script::String] A string containing the literal
  6639. # preceded by `"+"`
  6640. def unary_plus
  6641. Sass::Script::String.new("+#{self.to_s}")
  6642. end
  6643. # The SassScript unary `-` operation (e.g. `-$a`).
  6644. #
  6645. # @param other [Literal] The right-hand side of the operator
  6646. # @return [Script::String] A string containing the literal
  6647. # preceded by `"-"`
  6648. def unary_minus
  6649. Sass::Script::String.new("-#{self.to_s}")
  6650. end
  6651. # The SassScript unary `/` operation (e.g. `/$a`).
  6652. #
  6653. # @param other [Literal] The right-hand side of the operator
  6654. # @return [Script::String] A string containing the literal
  6655. # preceded by `"/"`
  6656. def unary_div
  6657. Sass::Script::String.new("/#{self.to_s}")
  6658. end
  6659. # @return [String] A readable representation of the literal
  6660. def inspect
  6661. value.inspect
  6662. end
  6663. # @return [Boolean] `true` (the Ruby boolean value)
  6664. def to_bool
  6665. true
  6666. end
  6667. # Compares this object with another.
  6668. #
  6669. # @param other [Object] The object to compare with
  6670. # @return [Boolean] Whether or not this literal is equivalent to `other`
  6671. def ==(other)
  6672. eq(other).to_bool
  6673. end
  6674. # @return [Fixnum] The integer value of this literal
  6675. # @raise [Sass::SyntaxError] if this literal isn't an integer
  6676. def to_i
  6677. raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
  6678. end
  6679. # @raise [Sass::SyntaxError] if this literal isn't an integer
  6680. def assert_int!; to_i; end
  6681. # Returns the value of this literal as a list.
  6682. # Single literals are considered the same as single-element lists.
  6683. #
  6684. # @return [Array<Literal>] The of this literal as a list
  6685. def to_a
  6686. [self]
  6687. end
  6688. # Returns the string representation of this literal
  6689. # as it would be output to the CSS document.
  6690. #
  6691. # @return [String]
  6692. def to_s(opts = {})
  6693. raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
  6694. end
  6695. alias_method :to_sass, :to_s
  6696. protected
  6697. # Evaluates the literal.
  6698. #
  6699. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  6700. # @return [Literal] This literal
  6701. def _perform(environment)
  6702. self
  6703. end
  6704. end
  6705. end
  6706. module Sass::Script
  6707. # A SassScript object representing a CSS string *or* a CSS identifier.
  6708. class String < Literal
  6709. # The Ruby value of the string.
  6710. #
  6711. # @return [String]
  6712. attr_reader :value
  6713. # Whether this is a CSS string or a CSS identifier.
  6714. # The difference is that strings are written with double-quotes,
  6715. # while identifiers aren't.
  6716. #
  6717. # @return [Symbol] `:string` or `:identifier`
  6718. attr_reader :type
  6719. # Creates a new string.
  6720. #
  6721. # @param value [String] See \{#value}
  6722. # @param type [Symbol] See \{#type}
  6723. def initialize(value, type = :identifier)
  6724. super(value)
  6725. @type = type
  6726. end
  6727. # @see Literal#plus
  6728. def plus(other)
  6729. other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
  6730. Sass::Script::String.new(self.value + other_str, self.type)
  6731. end
  6732. # @see Node#to_s
  6733. def to_s(opts = {})
  6734. if @type == :identifier
  6735. return @value.tr("\n", " ")
  6736. end
  6737. return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
  6738. return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
  6739. return "\"#{value}\"" unless value.include?('"')
  6740. return "'#{value}'" unless value.include?("'")
  6741. "\"#{value.gsub('"', "\\\"")}\"" #'
  6742. end
  6743. # @see Node#to_sass
  6744. def to_sass(opts = {})
  6745. to_s
  6746. end
  6747. end
  6748. end
  6749. module Sass::Script
  6750. # A SassScript parse node representing a unary operation,
  6751. # such as `-$b` or `not true`.
  6752. #
  6753. # Currently only `-`, `/`, and `not` are unary operators.
  6754. class UnaryOperation < Node
  6755. # @param operand [Script::Node] The parse-tree node
  6756. # for the object of the operator
  6757. # @param operator [Symbol] The operator to perform
  6758. def initialize(operand, operator)
  6759. @operand = operand
  6760. @operator = operator
  6761. super()
  6762. end
  6763. # @return [String] A human-readable s-expression representation of the operation
  6764. def inspect
  6765. "(#{@operator.inspect} #{@operand.inspect})"
  6766. end
  6767. # @see Node#to_sass
  6768. def to_sass(opts = {})
  6769. operand = @operand.to_sass(opts)
  6770. if @operand.is_a?(Operation) ||
  6771. (@operator == :minus &&
  6772. (operand =~ Sass::SCSS::RX::IDENT) == 0)
  6773. operand = "(#{@operand.to_sass(opts)})"
  6774. end
  6775. op = Lexer::OPERATORS_REVERSE[@operator]
  6776. op + (op =~ /[a-z]/ ? " " : "") + operand
  6777. end
  6778. # Returns the operand of the operation.
  6779. #
  6780. # @return [Array<Node>]
  6781. # @see Node#children
  6782. def children
  6783. [@operand]
  6784. end
  6785. protected
  6786. # Evaluates the operation.
  6787. #
  6788. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  6789. # @return [Literal] The SassScript object that is the value of the operation
  6790. # @raise [Sass::SyntaxError] if the operation is undefined for the operand
  6791. def _perform(environment)
  6792. operator = "unary_#{@operator}"
  6793. literal = @operand.perform(environment)
  6794. literal.send(operator)
  6795. rescue NoMethodError => e
  6796. raise e unless e.name.to_s == operator.to_s
  6797. raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
  6798. end
  6799. end
  6800. end
  6801. module Sass::Script
  6802. # A SassScript object representing `#{}` interpolation outside a string.
  6803. #
  6804. # @see StringInterpolation
  6805. class Interpolation < Node
  6806. # Interpolation in a property is of the form `before #{mid} after`.
  6807. #
  6808. # @param before [Node] The SassScript before the interpolation
  6809. # @param mid [Node] The SassScript within the interpolation
  6810. # @param after [Node] The SassScript after the interpolation
  6811. # @param wb [Boolean] Whether there was whitespace between `before` and `#{`
  6812. # @param wa [Boolean] Whether there was whitespace between `}` and `after`
  6813. # @param originally_text [Boolean]
  6814. # Whether the original format of the interpolation was plain text,
  6815. # not an interpolation.
  6816. # This is used when converting back to SassScript.
  6817. def initialize(before, mid, after, wb, wa, originally_text = false)
  6818. @before = before
  6819. @mid = mid
  6820. @after = after
  6821. @whitespace_before = wb
  6822. @whitespace_after = wa
  6823. @originally_text = originally_text
  6824. end
  6825. # @return [String] A human-readable s-expression representation of the interpolation
  6826. def inspect
  6827. "(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
  6828. end
  6829. # @see Node#to_sass
  6830. def to_sass(opts = {})
  6831. res = ""
  6832. res << @before.to_sass(opts) if @before
  6833. res << ' ' if @before && @whitespace_before
  6834. res << '#{' unless @originally_text
  6835. res << @mid.to_sass(opts)
  6836. res << '}' unless @originally_text
  6837. res << ' ' if @after && @whitespace_after
  6838. res << @after.to_sass(opts) if @after
  6839. res
  6840. end
  6841. # Returns the three components of the interpolation, `before`, `mid`, and `after`.
  6842. #
  6843. # @return [Array<Node>]
  6844. # @see #initialize
  6845. # @see Node#children
  6846. def children
  6847. [@before, @mid, @after].compact
  6848. end
  6849. protected
  6850. # Evaluates the interpolation.
  6851. #
  6852. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  6853. # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
  6854. def _perform(environment)
  6855. res = ""
  6856. res << @before.perform(environment).to_s if @before
  6857. res << " " if @before && @whitespace_before
  6858. val = @mid.perform(environment)
  6859. res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
  6860. res << " " if @after && @whitespace_after
  6861. res << @after.perform(environment).to_s if @after
  6862. opts(Sass::Script::String.new(res))
  6863. end
  6864. end
  6865. end
  6866. module Sass::Script
  6867. # A SassScript object representing `#{}` interpolation within a string.
  6868. #
  6869. # @see Interpolation
  6870. class StringInterpolation < Node
  6871. # Interpolation in a string is of the form `"before #{mid} after"`,
  6872. # where `before` and `after` may include more interpolation.
  6873. #
  6874. # @param before [Node] The string before the interpolation
  6875. # @param mid [Node] The SassScript within the interpolation
  6876. # @param after [Node] The string after the interpolation
  6877. def initialize(before, mid, after)
  6878. @before = before
  6879. @mid = mid
  6880. @after = after
  6881. end
  6882. # @return [String] A human-readable s-expression representation of the interpolation
  6883. def inspect
  6884. "(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
  6885. end
  6886. # @see Node#to_sass
  6887. def to_sass(opts = {})
  6888. # We can get rid of all of this when we remove the deprecated :equals context
  6889. # XXX CE: It's gone now but I'm not sure what can be removed now.
  6890. before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
  6891. after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
  6892. unquote = before_unquote || after_unquote ||
  6893. (before_quote_char && !after_quote_char && !after_str.empty?) ||
  6894. (!before_quote_char && after_quote_char && !before_str.empty?)
  6895. quote_char =
  6896. if before_quote_char && after_quote_char && before_quote_char != after_quote_char
  6897. before_str.gsub!("\\'", "'")
  6898. before_str.gsub!('"', "\\\"")
  6899. after_str.gsub!("\\'", "'")
  6900. after_str.gsub!('"', "\\\"")
  6901. '"'
  6902. else
  6903. before_quote_char || after_quote_char
  6904. end
  6905. res = ""
  6906. res << 'unquote(' if unquote
  6907. res << quote_char if quote_char
  6908. res << before_str
  6909. res << '#{' << @mid.to_sass(opts) << '}'
  6910. res << after_str
  6911. res << quote_char if quote_char
  6912. res << ')' if unquote
  6913. res
  6914. end
  6915. # Returns the three components of the interpolation, `before`, `mid`, and `after`.
  6916. #
  6917. # @return [Array<Node>]
  6918. # @see #initialize
  6919. # @see Node#children
  6920. def children
  6921. [@before, @mid, @after].compact
  6922. end
  6923. protected
  6924. # Evaluates the interpolation.
  6925. #
  6926. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  6927. # @return [Sass::Script::String] The SassScript string that is the value of the interpolation
  6928. def _perform(environment)
  6929. res = ""
  6930. before = @before.perform(environment)
  6931. res << before.value
  6932. mid = @mid.perform(environment)
  6933. res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
  6934. res << @after.perform(environment).value
  6935. opts(Sass::Script::String.new(res, before.type))
  6936. end
  6937. private
  6938. def parse_str(str)
  6939. case str
  6940. when /^unquote\((["'])(.*)\1\)$/
  6941. return true, $1, $2
  6942. when '""'
  6943. return false, nil, ""
  6944. when /^(["'])(.*)\1$/
  6945. return false, $1, $2
  6946. else
  6947. return false, nil, str
  6948. end
  6949. end
  6950. end
  6951. end
  6952. module Sass::Script
  6953. # A SassScript parse node representing a binary operation,
  6954. # such as `$a + $b` or `"foo" + 1`.
  6955. class Operation < Node
  6956. attr_reader :operand1
  6957. attr_reader :operand2
  6958. attr_reader :operator
  6959. # @param operand1 [Script::Node] The parse-tree node
  6960. # for the right-hand side of the operator
  6961. # @param operand2 [Script::Node] The parse-tree node
  6962. # for the left-hand side of the operator
  6963. # @param operator [Symbol] The operator to perform.
  6964. # This should be one of the binary operator names in {Lexer::OPERATORS}
  6965. def initialize(operand1, operand2, operator)
  6966. @operand1 = operand1
  6967. @operand2 = operand2
  6968. @operator = operator
  6969. super()
  6970. end
  6971. # @return [String] A human-readable s-expression representation of the operation
  6972. def inspect
  6973. "(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
  6974. end
  6975. # @see Node#to_sass
  6976. def to_sass(opts = {})
  6977. pred = Sass::Script::Parser.precedence_of(@operator)
  6978. o1 = operand_to_sass @operand1, :left, opts
  6979. o2 = operand_to_sass @operand2, :right, opts
  6980. sep =
  6981. case @operator
  6982. when :comma; ", "
  6983. when :space; " "
  6984. else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
  6985. end
  6986. "#{o1}#{sep}#{o2}"
  6987. end
  6988. # Returns the operands for this operation.
  6989. #
  6990. # @return [Array<Node>]
  6991. # @see Node#children
  6992. def children
  6993. [@operand1, @operand2]
  6994. end
  6995. protected
  6996. # Evaluates the operation.
  6997. #
  6998. # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
  6999. # @return [Literal] The SassScript object that is the value of the operation
  7000. # @raise [Sass::SyntaxError] if the operation is undefined for the operands
  7001. def _perform(environment)
  7002. literal1 = @operand1.perform(environment)
  7003. literal2 = @operand2.perform(environment)
  7004. begin
  7005. opts(literal1.send(@operator, literal2))
  7006. rescue NoMethodError => e
  7007. raise e unless e.name.to_s == @operator.to_s
  7008. raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
  7009. end
  7010. end
  7011. private
  7012. def operand_to_sass(op, side, opts)
  7013. return "(#{op.to_sass(opts)})" if op.is_a?(List)
  7014. return op.to_sass(opts) unless op.is_a?(Operation)
  7015. pred = Sass::Script::Parser.precedence_of(@operator)
  7016. sub_pred = Sass::Script::Parser.precedence_of(op.operator)
  7017. assoc = Sass::Script::Parser.associative?(@operator)
  7018. return "(#{op.to_sass(opts)})" if sub_pred < pred ||
  7019. (side == :right && sub_pred == pred && !assoc)
  7020. op.to_sass(opts)
  7021. end
  7022. end
  7023. end
  7024. module Sass
  7025. module SCSS
  7026. # A module containing regular expressions used
  7027. # for lexing tokens in an SCSS document.
  7028. # Most of these are taken from [the CSS3 spec](http://www.w3.org/TR/css3-syntax/#lexical),
  7029. # although some have been modified for various reasons.
  7030. module RX
  7031. # Takes a string and returns a CSS identifier
  7032. # that will have the value of the given string.
  7033. #
  7034. # @param str [String] The string to escape
  7035. # @return [String] The escaped string
  7036. def self.escape_ident(str)
  7037. return "" if str.empty?
  7038. return "\\#{str}" if str == '-' || str == '_'
  7039. out = ""
  7040. value = str.dup
  7041. out << value.slice!(0...1) if value =~ /^[-_]/
  7042. if value[0...1] =~ NMSTART
  7043. out << value.slice!(0...1)
  7044. else
  7045. out << escape_char(value.slice!(0...1))
  7046. end
  7047. out << value.gsub(/[^a-zA-Z0-9_-]/) {|c| escape_char c}
  7048. return out
  7049. end
  7050. # Escapes a single character for a CSS identifier.
  7051. #
  7052. # @param c [String] The character to escape. Should have length 1
  7053. # @return [String] The escaped character
  7054. # @private
  7055. def self.escape_char(c)
  7056. return "\\%06x" % Sass::Util.ord(c) unless c =~ /[ -\/:-~]/
  7057. return "\\#{c}"
  7058. end
  7059. # Creates a Regexp from a plain text string,
  7060. # escaping all significant characters.
  7061. #
  7062. # @param str [String] The text of the regexp
  7063. # @param flags [Fixnum] Flags for the created regular expression
  7064. # @return [Regexp]
  7065. # @private
  7066. def self.quote(str, flags = 0)
  7067. Regexp.new(Regexp.quote(str), flags)
  7068. end
  7069. H = /[0-9a-fA-F]/
  7070. NL = /\n|\r\n|\r|\f/
  7071. UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
  7072. s = if Sass::Util.ruby1_8?
  7073. '\200-\377'
  7074. else
  7075. '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
  7076. end
  7077. NONASCII = /[#{s}]/
  7078. ESCAPE = /#{UNICODE}|\\[ -~#{s}]/
  7079. NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
  7080. NMCHAR = /[a-zA-Z0-9_-]|#{NONASCII}|#{ESCAPE}/
  7081. STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
  7082. STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
  7083. IDENT = /-?#{NMSTART}#{NMCHAR}*/
  7084. NAME = /#{NMCHAR}+/
  7085. NUM = /[0-9]+|[0-9]*\.[0-9]+/
  7086. STRING = /#{STRING1}|#{STRING2}/
  7087. URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
  7088. URL = /(#{URLCHAR}*)/
  7089. W = /[ \t\r\n\f]*/
  7090. VARIABLE = /(\$)(#{Sass::SCSS::RX::IDENT})/
  7091. # This is more liberal than the spec's definition,
  7092. # but that definition didn't work well with the greediness rules
  7093. RANGE = /(?:#{H}|\?){1,6}/
  7094. ##
  7095. S = /[ \t\r\n\f]+/
  7096. COMMENT = /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\//
  7097. SINGLE_LINE_COMMENT = /\/\/.*(\n[ \t]*\/\/.*)*/
  7098. CDO = quote("<!--")
  7099. CDC = quote("-->")
  7100. INCLUDES = quote("~=")
  7101. DASHMATCH = quote("|=")
  7102. PREFIXMATCH = quote("^=")
  7103. SUFFIXMATCH = quote("$=")
  7104. SUBSTRINGMATCH = quote("*=")
  7105. HASH = /##{NAME}/
  7106. IMPORTANT = /!#{W}important/i
  7107. DEFAULT = /!#{W}default/i
  7108. NUMBER = /#{NUM}(?:#{IDENT}|%)?/
  7109. URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
  7110. FUNCTION = /#{IDENT}\(/
  7111. UNICODERANGE = /u\+(?:#{H}{1,6}-#{H}{1,6}|#{RANGE})/i
  7112. # Defined in http://www.w3.org/TR/css3-selectors/#lex
  7113. PLUS = /#{W}\+/
  7114. GREATER = /#{W}>/
  7115. TILDE = /#{W}~/
  7116. NOT = quote(":not(", Regexp::IGNORECASE)
  7117. # Custom
  7118. HEXCOLOR = /\#[0-9a-fA-F]+/
  7119. INTERP_START = /#\{/
  7120. MOZ_ANY = quote(":-moz-any(", Regexp::IGNORECASE)
  7121. STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
  7122. STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
  7123. STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
  7124. # Can't use IDENT here, because it seems to take exponential time on 1.8.
  7125. # We could use it for 1.9 only, but I don't want to introduce a cross-version
  7126. # behavior difference.
  7127. # In any case, almost all CSS idents will be matched by this.
  7128. STATIC_VALUE = /(-?#{NMSTART}|#{STRING_NOINTERP}|\s(?!%)|#[a-f0-9]|[,%]|#{NUM}|\!important)+(?=[;}])/i
  7129. STATIC_SELECTOR = /(#{NMCHAR}|\s|[,>+*]|[:#.]#{NMSTART})+(?=[{])/i
  7130. end
  7131. end
  7132. end
  7133. require 'strscan'
  7134. module Sass
  7135. module Script
  7136. # The lexical analyzer for SassScript.
  7137. # It takes a raw string and converts it to individual tokens
  7138. # that are easier to parse.
  7139. class Lexer
  7140. include Sass::SCSS::RX
  7141. # A struct containing information about an individual token.
  7142. #
  7143. # `type`: \[`Symbol`\]
  7144. # : The type of token.
  7145. #
  7146. # `value`: \[`Object`\]
  7147. # : The Ruby object corresponding to the value of the token.
  7148. #
  7149. # `line`: \[`Fixnum`\]
  7150. # : The line of the source file on which the token appears.
  7151. #
  7152. # `offset`: \[`Fixnum`\]
  7153. # : The number of bytes into the line the SassScript token appeared.
  7154. #
  7155. # `pos`: \[`Fixnum`\]
  7156. # : The scanner position at which the SassScript token appeared.
  7157. Token = Struct.new(:type, :value, :line, :offset, :pos)
  7158. # The line number of the lexer's current position.
  7159. #
  7160. # @return [Fixnum]
  7161. attr_reader :line
  7162. # The number of bytes into the current line
  7163. # of the lexer's current position.
  7164. #
  7165. # @return [Fixnum]
  7166. attr_reader :offset
  7167. # A hash from operator strings to the corresponding token types.
  7168. OPERATORS = {
  7169. '+' => :plus,
  7170. '-' => :minus,
  7171. '*' => :times,
  7172. '/' => :div,
  7173. '%' => :mod,
  7174. '=' => :single_eq,
  7175. ':' => :colon,
  7176. '(' => :lparen,
  7177. ')' => :rparen,
  7178. ',' => :comma,
  7179. 'and' => :and,
  7180. 'or' => :or,
  7181. 'not' => :not,
  7182. '==' => :eq,
  7183. '!=' => :neq,
  7184. '>=' => :gte,
  7185. '<=' => :lte,
  7186. '>' => :gt,
  7187. '<' => :lt,
  7188. '#{' => :begin_interpolation,
  7189. '}' => :end_interpolation,
  7190. ';' => :semicolon,
  7191. '{' => :lcurly,
  7192. }
  7193. OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
  7194. TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
  7195. :const => "variable (e.g. $foo)",
  7196. :ident => "identifier (e.g. middle)",
  7197. :bool => "boolean (e.g. true, false)",
  7198. })
  7199. # A list of operator strings ordered with longer names first
  7200. # so that `>` and `<` don't clobber `>=` and `<=`.
  7201. OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
  7202. # A sub-list of {OP_NAMES} that only includes operators
  7203. # with identifier names.
  7204. IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
  7205. # A hash of regular expressions that are used for tokenizing.
  7206. REGULAR_EXPRESSIONS = {
  7207. :whitespace => /\s+/,
  7208. :comment => COMMENT,
  7209. :single_line_comment => SINGLE_LINE_COMMENT,
  7210. :variable => /(\$)(#{IDENT})/,
  7211. :ident => /(#{IDENT})(\()?/,
  7212. :number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
  7213. :color => HEXCOLOR,
  7214. :bool => /(true|false)\b/,
  7215. :ident_op => %r{(#{Regexp.union(*IDENT_OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")})})},
  7216. :op => %r{(#{Regexp.union(*OP_NAMES)})},
  7217. }
  7218. class << self
  7219. private
  7220. def string_re(open, close)
  7221. /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/
  7222. end
  7223. end
  7224. # A hash of regular expressions that are used for tokenizing strings.
  7225. #
  7226. # The key is a `[Symbol, Boolean]` pair.
  7227. # The symbol represents which style of quotation to use,
  7228. # while the boolean represents whether or not the string
  7229. # is following an interpolated segment.
  7230. STRING_REGULAR_EXPRESSIONS = {
  7231. [:double, false] => string_re('"', '"'),
  7232. [:single, false] => string_re("'", "'"),
  7233. [:double, true] => string_re('', '"'),
  7234. [:single, true] => string_re('', "'"),
  7235. [:uri, false] => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
  7236. [:uri, true] => /(#{URLCHAR}*?)(#{W}\)|#\{)/,
  7237. }
  7238. # @param str [String, StringScanner] The source text to lex
  7239. # @param line [Fixnum] The line on which the SassScript appears.
  7240. # Used for error reporting
  7241. # @param offset [Fixnum] The number of characters in on which the SassScript appears.
  7242. # Used for error reporting
  7243. # @param options [{Symbol => Object}] An options hash;
  7244. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  7245. def initialize(str, line, offset, options)
  7246. @scanner = str.is_a?(StringScanner) ? str : StringScanner.new(str)
  7247. @line = line
  7248. @offset = offset
  7249. @options = options
  7250. @interpolation_stack = []
  7251. @prev = nil
  7252. end
  7253. # Moves the lexer forward one token.
  7254. #
  7255. # @return [Token] The token that was moved past
  7256. def next
  7257. @tok ||= read_token
  7258. @tok, tok = nil, @tok
  7259. @prev = tok
  7260. return tok
  7261. end
  7262. # Returns whether or not there's whitespace before the next token.
  7263. #
  7264. # @return [Boolean]
  7265. def whitespace?(tok = @tok)
  7266. if tok
  7267. @scanner.string[0...tok.pos] =~ /\s\Z/
  7268. else
  7269. @scanner.string[@scanner.pos, 1] =~ /^\s/ ||
  7270. @scanner.string[@scanner.pos - 1, 1] =~ /\s\Z/
  7271. end
  7272. end
  7273. # Returns the next token without moving the lexer forward.
  7274. #
  7275. # @return [Token] The next token
  7276. def peek
  7277. @tok ||= read_token
  7278. end
  7279. # Rewinds the underlying StringScanner
  7280. # to before the token returned by \{#peek}.
  7281. def unpeek!
  7282. @scanner.pos = @tok.pos if @tok
  7283. end
  7284. # @return [Boolean] Whether or not there's more source text to lex.
  7285. def done?
  7286. whitespace unless after_interpolation? && @interpolation_stack.last
  7287. @scanner.eos? && @tok.nil?
  7288. end
  7289. # @return [Boolean] Whether or not the last token lexed was `:end_interpolation`.
  7290. def after_interpolation?
  7291. @prev && @prev.type == :end_interpolation
  7292. end
  7293. # Raise an error to the effect that `name` was expected in the input stream
  7294. # and wasn't found.
  7295. #
  7296. # This calls \{#unpeek!} to rewind the scanner to immediately after
  7297. # the last returned token.
  7298. #
  7299. # @param name [String] The name of the entity that was expected but not found
  7300. # @raise [Sass::SyntaxError]
  7301. def expected!(name)
  7302. unpeek!
  7303. Sass::SCSS::Parser.expected(@scanner, name, @line)
  7304. end
  7305. # Records all non-comment text the lexer consumes within the block
  7306. # and returns it as a string.
  7307. #
  7308. # @yield A block in which text is recorded
  7309. # @return [String]
  7310. def str
  7311. old_pos = @tok ? @tok.pos : @scanner.pos
  7312. yield
  7313. new_pos = @tok ? @tok.pos : @scanner.pos
  7314. @scanner.string[old_pos...new_pos]
  7315. end
  7316. private
  7317. def read_token
  7318. return if done?
  7319. return unless value = token
  7320. type, val, size = value
  7321. size ||= @scanner.matched_size
  7322. val.line = @line if val.is_a?(Script::Node)
  7323. Token.new(type, val, @line,
  7324. current_position - size, @scanner.pos - size)
  7325. end
  7326. def whitespace
  7327. nil while scan(REGULAR_EXPRESSIONS[:whitespace]) ||
  7328. scan(REGULAR_EXPRESSIONS[:comment]) ||
  7329. scan(REGULAR_EXPRESSIONS[:single_line_comment])
  7330. end
  7331. def token
  7332. if after_interpolation? && (interp_type = @interpolation_stack.pop)
  7333. return string(interp_type, true)
  7334. end
  7335. variable || string(:double, false) || string(:single, false) || number ||
  7336. color || bool || string(:uri, false) || raw(UNICODERANGE) ||
  7337. special_fun || special_val || ident_op || ident || op
  7338. end
  7339. def variable
  7340. _variable(REGULAR_EXPRESSIONS[:variable])
  7341. end
  7342. def _variable(rx)
  7343. line = @line
  7344. offset = @offset
  7345. return unless scan(rx)
  7346. [:const, @scanner[2]]
  7347. end
  7348. def ident
  7349. return unless scan(REGULAR_EXPRESSIONS[:ident])
  7350. [@scanner[2] ? :funcall : :ident, @scanner[1]]
  7351. end
  7352. def string(re, open)
  7353. return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
  7354. if @scanner[2] == '#{' #'
  7355. @scanner.pos -= 2 # Don't actually consume the #{
  7356. @interpolation_stack << re
  7357. end
  7358. str =
  7359. if re == :uri
  7360. Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
  7361. else
  7362. Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
  7363. end
  7364. [:string, str]
  7365. end
  7366. def number
  7367. return unless scan(REGULAR_EXPRESSIONS[:number])
  7368. value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
  7369. value = -value if @scanner[1]
  7370. [:number, Script::Number.new(value, Array(@scanner[4]))]
  7371. end
  7372. def color
  7373. return unless s = scan(REGULAR_EXPRESSIONS[:color])
  7374. raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
  7375. Colors must have either three or six digits: '#{s}'
  7376. MESSAGE
  7377. value = s.scan(/^#(..?)(..?)(..?)$/).first.
  7378. map {|num| num.ljust(2, num).to_i(16)}
  7379. [:color, Script::Color.new(value)]
  7380. end
  7381. def bool
  7382. return unless s = scan(REGULAR_EXPRESSIONS[:bool])
  7383. [:bool, Script::Bool.new(s == 'true')]
  7384. end
  7385. def special_fun
  7386. return unless str1 = scan(/((-[\w-]+-)?calc|expression|progid:[a-z\.]*)\(/i)
  7387. str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
  7388. c = str2.count("\n")
  7389. old_line = @line
  7390. old_offset = @offset
  7391. @line += c
  7392. @offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
  7393. [:special_fun,
  7394. Sass::Util.merge_adjacent_strings(
  7395. [str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
  7396. str1.size + str2.size]
  7397. end
  7398. def special_val
  7399. return unless scan(/!important/i)
  7400. [:string, Script::String.new("!important")]
  7401. end
  7402. def ident_op
  7403. return unless op = scan(REGULAR_EXPRESSIONS[:ident_op])
  7404. [OPERATORS[op]]
  7405. end
  7406. def op
  7407. return unless op = scan(REGULAR_EXPRESSIONS[:op])
  7408. @interpolation_stack << nil if op == :begin_interpolation
  7409. [OPERATORS[op]]
  7410. end
  7411. def raw(rx)
  7412. return unless val = scan(rx)
  7413. [:raw, val]
  7414. end
  7415. def scan(re)
  7416. return unless str = @scanner.scan(re)
  7417. c = str.count("\n")
  7418. @line += c
  7419. @offset = (c == 0 ? @offset + str.size : str[/\n(.*)/, 1].size)
  7420. str
  7421. end
  7422. def current_position
  7423. @offset + 1
  7424. end
  7425. end
  7426. end
  7427. end
  7428. module Sass
  7429. module Script
  7430. # The parser for SassScript.
  7431. # It parses a string of code into a tree of {Script::Node}s.
  7432. class Parser
  7433. # The line number of the parser's current position.
  7434. #
  7435. # @return [Fixnum]
  7436. def line
  7437. @lexer.line
  7438. end
  7439. # @param str [String, StringScanner] The source text to parse
  7440. # @param line [Fixnum] The line on which the SassScript appears.
  7441. # Used for error reporting
  7442. # @param offset [Fixnum] The number of characters in on which the SassScript appears.
  7443. # Used for error reporting
  7444. # @param options [{Symbol => Object}] An options hash;
  7445. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  7446. def initialize(str, line, offset, options = {})
  7447. @options = options
  7448. @lexer = lexer_class.new(str, line, offset, options)
  7449. end
  7450. # Parses a SassScript expression within an interpolated segment (`#{}`).
  7451. # This means that it stops when it comes across an unmatched `}`,
  7452. # which signals the end of an interpolated segment,
  7453. # it returns rather than throwing an error.
  7454. #
  7455. # @return [Script::Node] The root node of the parse tree
  7456. # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
  7457. def parse_interpolated
  7458. expr = assert_expr :expr
  7459. assert_tok :end_interpolation
  7460. expr.options = @options
  7461. expr
  7462. rescue Sass::SyntaxError => e
  7463. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7464. raise e
  7465. end
  7466. # Parses a SassScript expression.
  7467. #
  7468. # @return [Script::Node] The root node of the parse tree
  7469. # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
  7470. def parse
  7471. expr = assert_expr :expr
  7472. assert_done
  7473. expr.options = @options
  7474. expr
  7475. rescue Sass::SyntaxError => e
  7476. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7477. raise e
  7478. end
  7479. # Parses a SassScript expression,
  7480. # ending it when it encounters one of the given identifier tokens.
  7481. #
  7482. # @param [#include?(String)] A set of strings that delimit the expression.
  7483. # @return [Script::Node] The root node of the parse tree
  7484. # @raise [Sass::SyntaxError] if the expression isn't valid SassScript
  7485. def parse_until(tokens)
  7486. @stop_at = tokens
  7487. expr = assert_expr :expr
  7488. assert_done
  7489. expr.options = @options
  7490. expr
  7491. rescue Sass::SyntaxError => e
  7492. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7493. raise e
  7494. end
  7495. # Parses the argument list for a mixin include.
  7496. #
  7497. # @return [(Array<Script::Node>, {String => Script::Note})]
  7498. # The root nodes of the arguments.
  7499. # Keyword arguments are in a hash from names to values.
  7500. # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
  7501. def parse_mixin_include_arglist
  7502. args, keywords = [], {}
  7503. if try_tok(:lparen)
  7504. args, keywords = mixin_arglist || [[], {}]
  7505. assert_tok(:rparen)
  7506. end
  7507. assert_done
  7508. args.each {|a| a.options = @options}
  7509. keywords.each {|k, v| v.options = @options}
  7510. return args, keywords
  7511. rescue Sass::SyntaxError => e
  7512. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7513. raise e
  7514. end
  7515. # Parses the argument list for a mixin definition.
  7516. #
  7517. # @return [Array<Script::Node>] The root nodes of the arguments.
  7518. # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
  7519. def parse_mixin_definition_arglist
  7520. args = defn_arglist!(false)
  7521. assert_done
  7522. args.each do |k, v|
  7523. k.options = @options
  7524. v.options = @options if v
  7525. end
  7526. args
  7527. rescue Sass::SyntaxError => e
  7528. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7529. raise e
  7530. end
  7531. # Parses the argument list for a function definition.
  7532. #
  7533. # @return [Array<Script::Node>] The root nodes of the arguments.
  7534. # @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
  7535. def parse_function_definition_arglist
  7536. args = defn_arglist!(true)
  7537. assert_done
  7538. args.each do |k, v|
  7539. k.options = @options
  7540. v.options = @options if v
  7541. end
  7542. args
  7543. rescue Sass::SyntaxError => e
  7544. e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
  7545. raise e
  7546. end
  7547. # Parses a SassScript expression.
  7548. #
  7549. # @overload parse(str, line, offset, filename = nil)
  7550. # @return [Script::Node] The root node of the parse tree
  7551. # @see Parser#initialize
  7552. # @see Parser#parse
  7553. def self.parse(*args)
  7554. new(*args).parse
  7555. end
  7556. PRECEDENCE = [
  7557. :comma, :single_eq, :space, :or, :and,
  7558. [:eq, :neq],
  7559. [:gt, :gte, :lt, :lte],
  7560. [:plus, :minus],
  7561. [:times, :div, :mod],
  7562. ]
  7563. ASSOCIATIVE = [:plus, :times]
  7564. class << self
  7565. # Returns an integer representing the precedence
  7566. # of the given operator.
  7567. # A lower integer indicates a looser binding.
  7568. #
  7569. # @private
  7570. def precedence_of(op)
  7571. PRECEDENCE.each_with_index do |e, i|
  7572. return i if Array(e).include?(op)
  7573. end
  7574. raise "[BUG] Unknown operator #{op}"
  7575. end
  7576. # Returns whether or not the given operation is associative.
  7577. #
  7578. # @private
  7579. def associative?(op)
  7580. ASSOCIATIVE.include?(op)
  7581. end
  7582. private
  7583. # Defines a simple left-associative production.
  7584. # name is the name of the production,
  7585. # sub is the name of the production beneath it,
  7586. # and ops is a list of operators for this precedence level
  7587. def production(name, sub, *ops)
  7588. class_eval <<RUBY
  7589. def #{name}
  7590. interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
  7591. return unless e = #{sub}
  7592. while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
  7593. interp = try_op_before_interp(tok, e) and return interp
  7594. line = @lexer.line
  7595. e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
  7596. e.line = line
  7597. end
  7598. e
  7599. end
  7600. RUBY
  7601. end
  7602. def unary(op, sub)
  7603. class_eval <<RUBY
  7604. def unary_#{op}
  7605. return #{sub} unless tok = try_tok(:#{op})
  7606. interp = try_op_before_interp(tok) and return interp
  7607. line = @lexer.line
  7608. op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
  7609. op.line = line
  7610. op
  7611. end
  7612. RUBY
  7613. end
  7614. end
  7615. private
  7616. # @private
  7617. def lexer_class; Lexer; end
  7618. def expr
  7619. interp = try_ops_after_interp([:comma], :expr) and return interp
  7620. line = @lexer.line
  7621. return unless e = interpolation
  7622. arr = [e]
  7623. while tok = try_tok(:comma)
  7624. interp = try_op_before_interp(tok, e) and return interp
  7625. arr << assert_expr(:interpolation)
  7626. end
  7627. arr.size == 1 ? arr.first : node(List.new(arr, :comma), line)
  7628. end
  7629. production :equals, :interpolation, :single_eq
  7630. def try_op_before_interp(op, prev = nil)
  7631. return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
  7632. wb = @lexer.whitespace?(op)
  7633. str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
  7634. str.line = @lexer.line
  7635. interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
  7636. interp.line = @lexer.line
  7637. interpolation(interp)
  7638. end
  7639. def try_ops_after_interp(ops, name)
  7640. return unless @lexer.after_interpolation?
  7641. return unless op = try_tok(*ops)
  7642. interp = try_op_before_interp(op) and return interp
  7643. wa = @lexer.whitespace?
  7644. str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
  7645. str.line = @lexer.line
  7646. interp = Script::Interpolation.new(nil, str, assert_expr(name), !:wb, wa, :originally_text)
  7647. interp.line = @lexer.line
  7648. return interp
  7649. end
  7650. def interpolation(first = space)
  7651. e = first
  7652. while interp = try_tok(:begin_interpolation)
  7653. wb = @lexer.whitespace?(interp)
  7654. line = @lexer.line
  7655. mid = parse_interpolated
  7656. wa = @lexer.whitespace?
  7657. e = Script::Interpolation.new(e, mid, space, wb, wa)
  7658. e.line = line
  7659. end
  7660. e
  7661. end
  7662. def space
  7663. line = @lexer.line
  7664. return unless e = or_expr
  7665. arr = [e]
  7666. while e = or_expr
  7667. arr << e
  7668. end
  7669. arr.size == 1 ? arr.first : node(List.new(arr, :space), line)
  7670. end
  7671. production :or_expr, :and_expr, :or
  7672. production :and_expr, :eq_or_neq, :and
  7673. production :eq_or_neq, :relational, :eq, :neq
  7674. production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
  7675. production :plus_or_minus, :times_div_or_mod, :plus, :minus
  7676. production :times_div_or_mod, :unary_plus, :times, :div, :mod
  7677. unary :plus, :unary_minus
  7678. unary :minus, :unary_div
  7679. unary :div, :unary_not # For strings, so /foo/bar works
  7680. unary :not, :ident
  7681. def ident
  7682. return funcall unless @lexer.peek && @lexer.peek.type == :ident
  7683. return if @stop_at && @stop_at.include?(@lexer.peek.value)
  7684. name = @lexer.next
  7685. if color = Color::HTML4_COLORS[name.value.downcase]
  7686. return node(Color.new(color))
  7687. end
  7688. node(Script::String.new(name.value, :identifier))
  7689. end
  7690. def funcall
  7691. return raw unless tok = try_tok(:funcall)
  7692. args, keywords = fn_arglist || [[], {}]
  7693. assert_tok(:rparen)
  7694. node(Script::Funcall.new(tok.value, args, keywords))
  7695. end
  7696. def defn_arglist!(must_have_parens)
  7697. if must_have_parens
  7698. assert_tok(:lparen)
  7699. else
  7700. return [] unless try_tok(:lparen)
  7701. end
  7702. return [] if try_tok(:rparen)
  7703. res = []
  7704. must_have_default = false
  7705. loop do
  7706. line = @lexer.line
  7707. offset = @lexer.offset + 1
  7708. c = assert_tok(:const)
  7709. var = Script::Variable.new(c.value)
  7710. if tok = try_tok(:colon)
  7711. val = assert_expr(:space)
  7712. must_have_default = true
  7713. elsif must_have_default
  7714. raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
  7715. end
  7716. res << [var, val]
  7717. break unless try_tok(:comma)
  7718. end
  7719. assert_tok(:rparen)
  7720. res
  7721. end
  7722. def fn_arglist
  7723. arglist(:fn_arglist, :equals)
  7724. end
  7725. def mixin_arglist
  7726. arglist(:mixin_arglist, :interpolation)
  7727. end
  7728. def arglist(type, subexpr)
  7729. return unless e = send(subexpr)
  7730. if @lexer.peek && @lexer.peek.type == :colon
  7731. name = e
  7732. @lexer.expected!("comma") unless name.is_a?(Variable)
  7733. assert_tok(:colon)
  7734. keywords = {name.underscored_name => assert_expr(subexpr, EXPR_NAMES[type])}
  7735. end
  7736. unless try_tok(:comma)
  7737. return [], keywords if keywords
  7738. return [e], {}
  7739. end
  7740. other_args, other_keywords = assert_expr(type)
  7741. if keywords
  7742. if other_keywords[name.underscored_name]
  7743. raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
  7744. end
  7745. return other_args, keywords.merge(other_keywords)
  7746. else
  7747. return [e, *other_args], other_keywords
  7748. end
  7749. end
  7750. def keyword_arglist
  7751. return unless var = try_tok(:const)
  7752. unless try_tok(:colon)
  7753. return_tok!
  7754. return
  7755. end
  7756. name = var[1]
  7757. value = interpolation
  7758. return {name => value} unless try_tok(:comma)
  7759. {name => value}.merge(assert_expr(:keyword_arglist))
  7760. end
  7761. def raw
  7762. return special_fun unless tok = try_tok(:raw)
  7763. node(Script::String.new(tok.value))
  7764. end
  7765. def special_fun
  7766. return paren unless tok = try_tok(:special_fun)
  7767. first = node(Script::String.new(tok.value.first))
  7768. Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
  7769. Script::Interpolation.new(
  7770. l, i, r && node(Script::String.new(r)),
  7771. false, false)
  7772. end
  7773. end
  7774. def paren
  7775. return variable unless try_tok(:lparen)
  7776. was_in_parens = @in_parens
  7777. @in_parens = true
  7778. line = @lexer.line
  7779. e = expr
  7780. assert_tok(:rparen)
  7781. return e || node(List.new([], :space), line)
  7782. ensure
  7783. @in_parens = was_in_parens
  7784. end
  7785. def variable
  7786. return string unless c = try_tok(:const)
  7787. node(Variable.new(*c.value))
  7788. end
  7789. def string
  7790. return number unless first = try_tok(:string)
  7791. return first.value unless try_tok(:begin_interpolation)
  7792. line = @lexer.line
  7793. mid = parse_interpolated
  7794. last = assert_expr(:string)
  7795. interp = StringInterpolation.new(first.value, mid, last)
  7796. interp.line = line
  7797. interp
  7798. end
  7799. def number
  7800. return literal unless tok = try_tok(:number)
  7801. num = tok.value
  7802. num.original = num.to_s unless @in_parens
  7803. num
  7804. end
  7805. def literal
  7806. (t = try_tok(:color, :bool)) && (return t.value)
  7807. end
  7808. # It would be possible to have unified #assert and #try methods,
  7809. # but detecting the method/token difference turns out to be quite expensive.
  7810. EXPR_NAMES = {
  7811. :string => "string",
  7812. :default => "expression (e.g. 1px, bold)",
  7813. :mixin_arglist => "mixin argument",
  7814. :fn_arglist => "function argument",
  7815. }
  7816. def assert_expr(name, expected = nil)
  7817. (e = send(name)) && (return e)
  7818. @lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default])
  7819. end
  7820. def assert_tok(*names)
  7821. (t = try_tok(*names)) && (return t)
  7822. @lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
  7823. end
  7824. def try_tok(*names)
  7825. peeked = @lexer.peek
  7826. peeked && names.include?(peeked.type) && @lexer.next
  7827. end
  7828. def assert_done
  7829. return if @lexer.done?
  7830. @lexer.expected!(EXPR_NAMES[:default])
  7831. end
  7832. def node(node, line = @lexer.line)
  7833. node.line = line
  7834. node
  7835. end
  7836. end
  7837. end
  7838. end
  7839. module Sass
  7840. # SassScript is code that's embedded in Sass documents
  7841. # to allow for property values to be computed from variables.
  7842. #
  7843. # This module contains code that handles the parsing and evaluation of SassScript.
  7844. module Script
  7845. # The regular expression used to parse variables.
  7846. MATCH = /^\$(#{Sass::SCSS::RX::IDENT})\s*:\s*(.+?)(!(?i:default))?$/
  7847. # The regular expression used to validate variables without matching.
  7848. VALIDATE = /^\$#{Sass::SCSS::RX::IDENT}$/
  7849. # Parses a string of SassScript
  7850. #
  7851. # @param value [String] The SassScript
  7852. # @param line [Fixnum] The number of the line on which the SassScript appeared.
  7853. # Used for error reporting
  7854. # @param offset [Fixnum] The number of characters in on `line` that the SassScript started.
  7855. # Used for error reporting
  7856. # @param options [{Symbol => Object}] An options hash;
  7857. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  7858. # @return [Script::Node] The root node of the parse tree
  7859. def self.parse(value, line, offset, options = {})
  7860. Parser.parse(value, line, offset, options)
  7861. rescue Sass::SyntaxError => e
  7862. e.message << ": #{value.inspect}." if e.message == "SassScript error"
  7863. e.modify_backtrace(:line => line, :filename => options[:filename])
  7864. raise e
  7865. end
  7866. end
  7867. end
  7868. module Sass
  7869. module SCSS
  7870. # A mixin for subclasses of {Sass::Script::Lexer}
  7871. # that makes them usable by {SCSS::Parser} to parse SassScript.
  7872. # In particular, the lexer doesn't support `!` for a variable prefix.
  7873. module ScriptLexer
  7874. private
  7875. def variable
  7876. return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
  7877. _variable(Sass::SCSS::RX::VARIABLE)
  7878. end
  7879. end
  7880. end
  7881. end
  7882. module Sass
  7883. module SCSS
  7884. # A mixin for subclasses of {Sass::Script::Parser}
  7885. # that makes them usable by {SCSS::Parser} to parse SassScript.
  7886. # In particular, the parser won't raise an error
  7887. # when there's more content in the lexer once lexing is done.
  7888. # In addition, the parser doesn't support `!` for a variable prefix.
  7889. module ScriptParser
  7890. private
  7891. # @private
  7892. def lexer_class
  7893. klass = Class.new(super)
  7894. klass.send(:include, ScriptLexer)
  7895. klass
  7896. end
  7897. # Instead of raising an error when the parser is done,
  7898. # rewind the StringScanner so that it hasn't consumed the final token.
  7899. def assert_done
  7900. @lexer.unpeek!
  7901. end
  7902. end
  7903. end
  7904. end
  7905. require 'strscan'
  7906. require 'set'
  7907. module Sass
  7908. module SCSS
  7909. # The parser for SCSS.
  7910. # It parses a string of code into a tree of {Sass::Tree::Node}s.
  7911. class Parser
  7912. # @param str [String, StringScanner] The source document to parse.
  7913. # Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
  7914. # for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
  7915. # @param line [Fixnum] The line on which the source string appeared,
  7916. # if it's part of another document
  7917. def initialize(str, line = 1)
  7918. @template = str
  7919. @line = line
  7920. @strs = []
  7921. end
  7922. # Parses an SCSS document.
  7923. #
  7924. # @return [Sass::Tree::RootNode] The root node of the document tree
  7925. # @raise [Sass::SyntaxError] if there's a syntax error in the document
  7926. def parse
  7927. init_scanner!
  7928. root = stylesheet
  7929. expected("selector or at-rule") unless @scanner.eos?
  7930. root
  7931. end
  7932. # Parses an identifier with interpolation.
  7933. # Note that this won't assert that the identifier takes up the entire input string;
  7934. # it's meant to be used with `StringScanner`s as part of other parsers.
  7935. #
  7936. # @return [Array<String, Sass::Script::Node>, nil]
  7937. # The interpolated identifier, or nil if none could be parsed
  7938. def parse_interp_ident
  7939. init_scanner!
  7940. interp_ident
  7941. end
  7942. private
  7943. include Sass::SCSS::RX
  7944. def init_scanner!
  7945. @scanner =
  7946. if @template.is_a?(StringScanner)
  7947. @template
  7948. else
  7949. StringScanner.new(@template.gsub("\r", ""))
  7950. end
  7951. end
  7952. def stylesheet
  7953. node = node(Sass::Tree::RootNode.new(@scanner.string))
  7954. block_contents(node, :stylesheet) {s(node)}
  7955. end
  7956. def s(node)
  7957. while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
  7958. next unless c
  7959. process_comment c, node
  7960. c = nil
  7961. end
  7962. true
  7963. end
  7964. def ss
  7965. nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
  7966. true
  7967. end
  7968. def ss_comments(node)
  7969. while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
  7970. next unless c
  7971. process_comment c, node
  7972. c = nil
  7973. end
  7974. true
  7975. end
  7976. def whitespace
  7977. return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
  7978. ss
  7979. end
  7980. def process_comment(text, node)
  7981. single_line = text =~ /^\/\//
  7982. pre_str = single_line ? "" : @scanner.
  7983. string[0...@scanner.pos].
  7984. reverse[/.*?\*\/(.*?)($|\Z)/, 1].
  7985. reverse.gsub(/[^\s]/, ' ')
  7986. text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
  7987. comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
  7988. comment.line = @line - text.count("\n")
  7989. node << comment
  7990. end
  7991. DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
  7992. :each, :while, :if, :else, :extend, :import, :media, :charset]
  7993. def directive
  7994. return unless tok(/@/)
  7995. name = tok!(IDENT)
  7996. ss
  7997. if dir = special_directive(name)
  7998. return dir
  7999. end
  8000. # Most at-rules take expressions (e.g. @import),
  8001. # but some (e.g. @page) take selector-like arguments
  8002. val = str {break unless expr}
  8003. val ||= CssParser.new(@scanner, @line).parse_selector_string
  8004. node = node(Sass::Tree::DirectiveNode.new("@#{name} #{val}".strip))
  8005. if tok(/\{/)
  8006. node.has_children = true
  8007. block_contents(node, :directive)
  8008. tok!(/\}/)
  8009. end
  8010. node
  8011. end
  8012. def special_directive(name)
  8013. sym = name.gsub('-', '_').to_sym
  8014. DIRECTIVES.include?(sym) && send("#{sym}_directive")
  8015. end
  8016. def mixin_directive
  8017. name = tok! IDENT
  8018. args = sass_script(:parse_mixin_definition_arglist)
  8019. ss
  8020. block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
  8021. end
  8022. def include_directive
  8023. name = tok! IDENT
  8024. args, keywords = sass_script(:parse_mixin_include_arglist)
  8025. ss
  8026. node(Sass::Tree::MixinNode.new(name, args, keywords))
  8027. end
  8028. def function_directive
  8029. name = tok! IDENT
  8030. args = sass_script(:parse_function_definition_arglist)
  8031. ss
  8032. block(node(Sass::Tree::FunctionNode.new(name, args)), :function)
  8033. end
  8034. def return_directive
  8035. node(Sass::Tree::ReturnNode.new(sass_script(:parse)))
  8036. end
  8037. def debug_directive
  8038. node(Sass::Tree::DebugNode.new(sass_script(:parse)))
  8039. end
  8040. def warn_directive
  8041. node(Sass::Tree::WarnNode.new(sass_script(:parse)))
  8042. end
  8043. def for_directive
  8044. tok!(/\$/)
  8045. var = tok! IDENT
  8046. ss
  8047. tok!(/from/)
  8048. from = sass_script(:parse_until, Set["to", "through"])
  8049. ss
  8050. @expected = '"to" or "through"'
  8051. exclusive = (tok(/to/) || tok!(/through/)) == 'to'
  8052. to = sass_script(:parse)
  8053. ss
  8054. block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
  8055. end
  8056. def each_directive
  8057. tok!(/\$/)
  8058. var = tok! IDENT
  8059. ss
  8060. tok!(/in/)
  8061. list = sass_script(:parse)
  8062. ss
  8063. block(node(Sass::Tree::EachNode.new(var, list)), :directive)
  8064. end
  8065. def while_directive
  8066. expr = sass_script(:parse)
  8067. ss
  8068. block(node(Sass::Tree::WhileNode.new(expr)), :directive)
  8069. end
  8070. def if_directive
  8071. expr = sass_script(:parse)
  8072. ss
  8073. node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
  8074. pos = @scanner.pos
  8075. line = @line
  8076. ss
  8077. else_block(node) ||
  8078. begin
  8079. # Backtrack in case there are any comments we want to parse
  8080. @scanner.pos = pos
  8081. @line = line
  8082. node
  8083. end
  8084. end
  8085. def else_block(node)
  8086. return unless tok(/@else/)
  8087. ss
  8088. else_node = block(
  8089. Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
  8090. :directive)
  8091. node.add_else(else_node)
  8092. pos = @scanner.pos
  8093. line = @line
  8094. ss
  8095. else_block(node) ||
  8096. begin
  8097. # Backtrack in case there are any comments we want to parse
  8098. @scanner.pos = pos
  8099. @line = line
  8100. node
  8101. end
  8102. end
  8103. def else_directive
  8104. err("Invalid CSS: @else must come after @if")
  8105. end
  8106. def extend_directive
  8107. node(Sass::Tree::ExtendNode.new(expr!(:selector)))
  8108. end
  8109. def import_directive
  8110. values = []
  8111. loop do
  8112. values << expr!(:import_arg)
  8113. break if use_css_import? || !tok(/,\s*/)
  8114. end
  8115. return values
  8116. end
  8117. def import_arg
  8118. return unless arg = tok(STRING) || (uri = tok!(URI))
  8119. path = @scanner[1] || @scanner[2] || @scanner[3]
  8120. ss
  8121. media = str {media_query_list}.strip
  8122. if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
  8123. return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
  8124. end
  8125. node(Sass::Tree::ImportNode.new(path.strip))
  8126. end
  8127. def use_css_import?; false; end
  8128. def media_directive
  8129. val = str {media_query_list}.strip
  8130. block(node(Sass::Tree::MediaNode.new(val)), :directive)
  8131. end
  8132. # http://www.w3.org/TR/css3-mediaqueries/#syntax
  8133. def media_query_list
  8134. return unless media_query
  8135. ss
  8136. while tok(/,/)
  8137. ss; expr!(:media_query); ss
  8138. end
  8139. true
  8140. end
  8141. def media_query
  8142. if tok(/only|not/i)
  8143. ss
  8144. @expected = "media type (e.g. print, screen)"
  8145. tok!(IDENT)
  8146. ss
  8147. elsif !tok(IDENT) && !media_expr
  8148. return
  8149. end
  8150. ss
  8151. while tok(/and/i)
  8152. ss; expr!(:media_expr); ss
  8153. end
  8154. true
  8155. end
  8156. def media_expr
  8157. return unless tok(/\(/)
  8158. ss
  8159. @expected = "media feature (e.g. min-device-width, color)"
  8160. tok!(IDENT)
  8161. ss
  8162. if tok(/:/)
  8163. ss; expr!(:expr)
  8164. end
  8165. tok!(/\)/)
  8166. ss
  8167. true
  8168. end
  8169. def charset_directive
  8170. tok! STRING
  8171. name = @scanner[1] || @scanner[2]
  8172. ss
  8173. node(Sass::Tree::CharsetNode.new(name))
  8174. end
  8175. def variable
  8176. return unless tok(/\$/)
  8177. name = tok!(IDENT)
  8178. ss; tok!(/:/); ss
  8179. expr = sass_script(:parse)
  8180. guarded = tok(DEFAULT)
  8181. node(Sass::Tree::VariableNode.new(name, expr, guarded))
  8182. end
  8183. def operator
  8184. # Many of these operators (all except / and ,)
  8185. # are disallowed by the CSS spec,
  8186. # but they're included here for compatibility
  8187. # with some proprietary MS properties
  8188. str {ss if tok(/[\/,:.=]/)}
  8189. end
  8190. def unary_operator
  8191. tok(/[+-]/)
  8192. end
  8193. def ruleset
  8194. return unless rules = selector_sequence
  8195. block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
  8196. end
  8197. def block(node, context)
  8198. node.has_children = true
  8199. tok!(/\{/)
  8200. block_contents(node, context)
  8201. tok!(/\}/)
  8202. node
  8203. end
  8204. # A block may contain declarations and/or rulesets
  8205. def block_contents(node, context)
  8206. block_given? ? yield : ss_comments(node)
  8207. node << (child = block_child(context))
  8208. while tok(/;/) || has_children?(child)
  8209. block_given? ? yield : ss_comments(node)
  8210. node << (child = block_child(context))
  8211. end
  8212. node
  8213. end
  8214. def block_child(context)
  8215. return variable || directive if context == :function
  8216. return variable || directive || ruleset if context == :stylesheet
  8217. variable || directive || declaration_or_ruleset
  8218. end
  8219. def has_children?(child_or_array)
  8220. return false unless child_or_array
  8221. return child_or_array.last.has_children if child_or_array.is_a?(Array)
  8222. return child_or_array.has_children
  8223. end
  8224. # This is a nasty hack, and the only place in the parser
  8225. # that requires backtracking.
  8226. # The reason is that we can't figure out if certain strings
  8227. # are declarations or rulesets with fixed finite lookahead.
  8228. # For example, "foo:bar baz baz baz..." could be either a property
  8229. # or a selector.
  8230. #
  8231. # To handle this, we simply check if it works as a property
  8232. # (which is the most common case)
  8233. # and, if it doesn't, try it as a ruleset.
  8234. #
  8235. # We could eke some more efficiency out of this
  8236. # by handling some easy cases (first token isn't an identifier,
  8237. # no colon after the identifier, whitespace after the colon),
  8238. # but I'm not sure the gains would be worth the added complexity.
  8239. def declaration_or_ruleset
  8240. old_use_property_exception, @use_property_exception =
  8241. @use_property_exception, false
  8242. decl_err = catch_error do
  8243. decl = declaration
  8244. unless decl && decl.has_children
  8245. # We want an exception if it's not there,
  8246. # but we don't want to consume if it is
  8247. tok!(/[;}]/) unless tok?(/[;}]/)
  8248. end
  8249. return decl
  8250. end
  8251. ruleset_err = catch_error {return ruleset}
  8252. rethrow(@use_property_exception ? decl_err : ruleset_err)
  8253. ensure
  8254. @use_property_exception = old_use_property_exception
  8255. end
  8256. def selector_sequence
  8257. if sel = tok(STATIC_SELECTOR)
  8258. return [sel]
  8259. end
  8260. rules = []
  8261. return unless v = selector
  8262. rules.concat v
  8263. ws = ''
  8264. while tok(/,/)
  8265. ws << str {ss}
  8266. if v = selector
  8267. rules << ',' << ws
  8268. rules.concat v
  8269. ws = ''
  8270. end
  8271. end
  8272. rules
  8273. end
  8274. def selector
  8275. return unless sel = _selector
  8276. sel.to_a
  8277. end
  8278. def selector_comma_sequence
  8279. return unless sel = _selector
  8280. selectors = [sel]
  8281. ws = ''
  8282. while tok(/,/)
  8283. ws << str{ss}
  8284. if sel = _selector
  8285. selectors << sel
  8286. selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
  8287. ws = ''
  8288. end
  8289. end
  8290. Selector::CommaSequence.new(selectors)
  8291. end
  8292. def _selector
  8293. # The combinator here allows the "> E" hack
  8294. return unless val = combinator || simple_selector_sequence
  8295. nl = str{ss}.include?("\n")
  8296. res = []
  8297. res << val
  8298. res << "\n" if nl
  8299. while val = combinator || simple_selector_sequence
  8300. res << val
  8301. res << "\n" if str{ss}.include?("\n")
  8302. end
  8303. Selector::Sequence.new(res.compact)
  8304. end
  8305. def combinator
  8306. tok(PLUS) || tok(GREATER) || tok(TILDE)
  8307. end
  8308. def simple_selector_sequence
  8309. # This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
  8310. return expr unless e = element_name || id_selector || class_selector ||
  8311. attrib || negation || pseudo || parent_selector || interpolation_selector
  8312. res = [e]
  8313. # The tok(/\*/) allows the "E*" hack
  8314. while v = element_name || id_selector || class_selector ||
  8315. attrib || negation || pseudo || interpolation_selector ||
  8316. (tok(/\*/) && Selector::Universal.new(nil))
  8317. res << v
  8318. end
  8319. if tok?(/&/)
  8320. begin
  8321. expected('"{"')
  8322. rescue Sass::SyntaxError => e
  8323. e.message << "\n\n" << <<MESSAGE
  8324. In Sass 3, the parent selector & can only be used where element names are valid,
  8325. since it could potentially be replaced by an element name.
  8326. MESSAGE
  8327. raise e
  8328. end
  8329. end
  8330. Selector::SimpleSequence.new(res)
  8331. end
  8332. def parent_selector
  8333. return unless tok(/&/)
  8334. Selector::Parent.new
  8335. end
  8336. def class_selector
  8337. return unless tok(/\./)
  8338. @expected = "class name"
  8339. Selector::Class.new(merge(expr!(:interp_ident)))
  8340. end
  8341. def id_selector
  8342. return unless tok(/#(?!\{)/)
  8343. @expected = "id name"
  8344. Selector::Id.new(merge(expr!(:interp_name)))
  8345. end
  8346. def element_name
  8347. return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
  8348. if tok(/\|/)
  8349. @expected = "element name or *"
  8350. ns = name
  8351. name = interp_ident || tok!(/\*/)
  8352. end
  8353. if name == '*'
  8354. Selector::Universal.new(merge(ns))
  8355. else
  8356. Selector::Element.new(merge(name), merge(ns))
  8357. end
  8358. end
  8359. def interpolation_selector
  8360. return unless script = interpolation
  8361. Selector::Interpolation.new(script)
  8362. end
  8363. def attrib
  8364. return unless tok(/\[/)
  8365. ss
  8366. ns, name = attrib_name!
  8367. ss
  8368. if op = tok(/=/) ||
  8369. tok(INCLUDES) ||
  8370. tok(DASHMATCH) ||
  8371. tok(PREFIXMATCH) ||
  8372. tok(SUFFIXMATCH) ||
  8373. tok(SUBSTRINGMATCH)
  8374. @expected = "identifier or string"
  8375. ss
  8376. if val = tok(IDENT)
  8377. val = [val]
  8378. else
  8379. val = expr!(:interp_string)
  8380. end
  8381. ss
  8382. end
  8383. tok(/\]/)
  8384. Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
  8385. end
  8386. def attrib_name!
  8387. if name_or_ns = interp_ident
  8388. # E, E|E
  8389. if tok(/\|(?!=)/)
  8390. ns = name_or_ns
  8391. name = interp_ident
  8392. else
  8393. name = name_or_ns
  8394. end
  8395. else
  8396. # *|E or |E
  8397. ns = [tok(/\*/) || ""]
  8398. tok!(/\|/)
  8399. name = expr!(:interp_ident)
  8400. end
  8401. return ns, name
  8402. end
  8403. def pseudo
  8404. return unless s = tok(/::?/)
  8405. @expected = "pseudoclass or pseudoelement"
  8406. name = expr!(:interp_ident)
  8407. if tok(/\(/)
  8408. ss
  8409. arg = expr!(:pseudo_expr)
  8410. tok!(/\)/)
  8411. end
  8412. Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
  8413. end
  8414. def pseudo_expr
  8415. return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
  8416. interp_string || tok(IDENT) || interpolation
  8417. res = [e, str{ss}]
  8418. while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
  8419. interp_string || tok(IDENT) || interpolation
  8420. res << e << str{ss}
  8421. end
  8422. res
  8423. end
  8424. def negation
  8425. return unless name = tok(NOT) || tok(MOZ_ANY)
  8426. ss
  8427. @expected = "selector"
  8428. sel = selector_comma_sequence
  8429. tok!(/\)/)
  8430. Selector::SelectorPseudoClass.new(name[1...-1], sel)
  8431. end
  8432. def declaration
  8433. # This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
  8434. if s = tok(/[:\*\.]|\#(?!\{)/)
  8435. @use_property_exception = s !~ /[\.\#]/
  8436. name = [s, str{ss}, *expr!(:interp_ident)]
  8437. else
  8438. return unless name = interp_ident
  8439. name = [name] if name.is_a?(String)
  8440. end
  8441. if comment = tok(COMMENT)
  8442. name << comment
  8443. end
  8444. ss
  8445. tok!(/:/)
  8446. space, value = value!
  8447. ss
  8448. require_block = tok?(/\{/)
  8449. node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
  8450. return node unless require_block
  8451. nested_properties! node, space
  8452. end
  8453. def value!
  8454. space = !str {ss}.empty?
  8455. @use_property_exception ||= space || !tok?(IDENT)
  8456. return true, Sass::Script::String.new("") if tok?(/\{/)
  8457. # This is a bit of a dirty trick:
  8458. # if the value is completely static,
  8459. # we don't parse it at all, and instead return a plain old string
  8460. # containing the value.
  8461. # This results in a dramatic speed increase.
  8462. if val = tok(STATIC_VALUE)
  8463. return space, Sass::Script::String.new(val.strip)
  8464. end
  8465. return space, sass_script(:parse)
  8466. end
  8467. def plain_value
  8468. return unless tok(/:/)
  8469. space = !str {ss}.empty?
  8470. @use_property_exception ||= space || !tok?(IDENT)
  8471. expression = expr
  8472. expression << tok(IMPORTANT) if expression
  8473. # expression, space, value
  8474. return expression, space, expression || [""]
  8475. end
  8476. def nested_properties!(node, space)
  8477. err(<<MESSAGE) unless space
  8478. Invalid CSS: a space is required between a property and its definition
  8479. when it has other properties nested beneath it.
  8480. MESSAGE
  8481. @use_property_exception = true
  8482. @expected = 'expression (e.g. 1px, bold) or "{"'
  8483. block(node, :property)
  8484. end
  8485. def expr
  8486. return unless t = term
  8487. res = [t, str{ss}]
  8488. while (o = operator) && (t = term)
  8489. res << o << t << str{ss}
  8490. end
  8491. res
  8492. end
  8493. def term
  8494. unless e = tok(NUMBER) ||
  8495. tok(URI) ||
  8496. function ||
  8497. tok(STRING) ||
  8498. tok(UNICODERANGE) ||
  8499. tok(IDENT) ||
  8500. tok(HEXCOLOR)
  8501. return unless op = unary_operator
  8502. @expected = "number or function"
  8503. return [op, tok(NUMBER) || expr!(:function)]
  8504. end
  8505. e
  8506. end
  8507. def function
  8508. return unless name = tok(FUNCTION)
  8509. if name == "expression(" || name == "calc("
  8510. str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
  8511. [name, str]
  8512. else
  8513. [name, str{ss}, expr, tok!(/\)/)]
  8514. end
  8515. end
  8516. def interpolation
  8517. return unless tok(INTERP_START)
  8518. sass_script(:parse_interpolated)
  8519. end
  8520. def interp_string
  8521. _interp_string(:double) || _interp_string(:single)
  8522. end
  8523. def _interp_string(type)
  8524. return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
  8525. res = [start]
  8526. mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
  8527. # @scanner[2].empty? means we've started an interpolated section
  8528. while @scanner[2] == '#{'
  8529. @scanner.pos -= 2 # Don't consume the #{
  8530. res.last.slice!(-2..-1)
  8531. res << expr!(:interpolation) << tok(mid_re)
  8532. end
  8533. res
  8534. end
  8535. def interp_ident(start = IDENT)
  8536. return unless val = tok(start) || interpolation
  8537. res = [val]
  8538. while val = tok(NAME) || interpolation
  8539. res << val
  8540. end
  8541. res
  8542. end
  8543. def interp_name
  8544. interp_ident NAME
  8545. end
  8546. def str
  8547. @strs.push ""
  8548. yield
  8549. @strs.last
  8550. ensure
  8551. @strs.pop
  8552. end
  8553. def str?
  8554. @strs.push ""
  8555. yield && @strs.last
  8556. ensure
  8557. @strs.pop
  8558. end
  8559. def node(node)
  8560. node.line = @line
  8561. node
  8562. end
  8563. @sass_script_parser = Class.new(Sass::Script::Parser)
  8564. @sass_script_parser.send(:include, ScriptParser)
  8565. # @private
  8566. def self.sass_script_parser; @sass_script_parser; end
  8567. def sass_script(*args)
  8568. parser = self.class.sass_script_parser.new(@scanner, @line,
  8569. @scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
  8570. result = parser.send(*args)
  8571. @line = parser.line
  8572. result
  8573. rescue Sass::SyntaxError => e
  8574. throw(:_sass_parser_error, true) if @throw_error
  8575. raise e
  8576. end
  8577. def merge(arr)
  8578. arr && Sass::Util.merge_adjacent_strings([arr].flatten)
  8579. end
  8580. EXPR_NAMES = {
  8581. :media_query => "media query (e.g. print, screen, print and screen)",
  8582. :media_expr => "media expression (e.g. (min-device-width: 800px)))",
  8583. :pseudo_expr => "expression (e.g. fr, 2n+1)",
  8584. :interp_ident => "identifier",
  8585. :interp_name => "identifier",
  8586. :expr => "expression (e.g. 1px, bold)",
  8587. :_selector => "selector",
  8588. :selector_comma_sequence => "selector",
  8589. :simple_selector_sequence => "selector",
  8590. :import_arg => "file to import (string or url())",
  8591. }
  8592. TOK_NAMES = Sass::Util.to_hash(
  8593. Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
  8594. merge(IDENT => "identifier", /[;}]/ => '";"')
  8595. def tok?(rx)
  8596. @scanner.match?(rx)
  8597. end
  8598. def expr!(name)
  8599. (e = send(name)) && (return e)
  8600. expected(EXPR_NAMES[name] || name.to_s)
  8601. end
  8602. def tok!(rx)
  8603. (t = tok(rx)) && (return t)
  8604. name = TOK_NAMES[rx]
  8605. unless name
  8606. # Display basic regexps as plain old strings
  8607. string = rx.source.gsub(/\\(.)/, '\1')
  8608. name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
  8609. end
  8610. expected(name)
  8611. end
  8612. def expected(name)
  8613. throw(:_sass_parser_error, true) if @throw_error
  8614. self.class.expected(@scanner, @expected || name, @line)
  8615. end
  8616. def err(msg)
  8617. throw(:_sass_parser_error, true) if @throw_error
  8618. raise Sass::SyntaxError.new(msg, :line => @line)
  8619. end
  8620. def catch_error(&block)
  8621. old_throw_error, @throw_error = @throw_error, true
  8622. pos = @scanner.pos
  8623. line = @line
  8624. expected = @expected
  8625. if catch(:_sass_parser_error, &block)
  8626. @scanner.pos = pos
  8627. @line = line
  8628. @expected = expected
  8629. {:pos => pos, :line => line, :expected => @expected, :block => block}
  8630. end
  8631. ensure
  8632. @throw_error = old_throw_error
  8633. end
  8634. def rethrow(err)
  8635. if @throw_err
  8636. throw :_sass_parser_error, err
  8637. else
  8638. @scanner = StringScanner.new(@scanner.string)
  8639. @scanner.pos = err[:pos]
  8640. @line = err[:line]
  8641. @expected = err[:expected]
  8642. err[:block].call
  8643. end
  8644. end
  8645. # @private
  8646. def self.expected(scanner, expected, line)
  8647. pos = scanner.pos
  8648. after = scanner.string[0...pos]
  8649. # Get rid of whitespace between pos and the last token,
  8650. # but only if there's a newline in there
  8651. after.gsub!(/\s*\n\s*$/, '')
  8652. # Also get rid of stuff before the last newline
  8653. after.gsub!(/.*\n/, '')
  8654. after = "..." + after[-15..-1] if after.size > 18
  8655. was = scanner.rest.dup
  8656. # Get rid of whitespace between pos and the next token,
  8657. # but only if there's a newline in there
  8658. was.gsub!(/^\s*\n\s*/, '')
  8659. # Also get rid of stuff after the next newline
  8660. was.gsub!(/\n.*/, '')
  8661. was = was[0...15] + "..." if was.size > 18
  8662. raise Sass::SyntaxError.new(
  8663. "Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
  8664. :line => line)
  8665. end
  8666. # Avoid allocating lots of new strings for `#tok`.
  8667. # This is important because `#tok` is called all the time.
  8668. NEWLINE = "\n"
  8669. def tok(rx)
  8670. res = @scanner.scan(rx)
  8671. if res
  8672. @line += res.count(NEWLINE)
  8673. @expected = nil
  8674. if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
  8675. @strs.each {|s| s << res}
  8676. end
  8677. res
  8678. end
  8679. end
  8680. end
  8681. end
  8682. end
  8683. module Sass
  8684. module SCSS
  8685. # A subclass of {Parser} that parses code in Sass documents
  8686. # using some SCSS constructs.
  8687. # This is necessary because SassScript in Sass supports `!`-style variables,
  8688. # whereas in SCSS it doesn't.
  8689. class SassParser < Parser
  8690. @sass_script_parser = Sass::Script::Parser
  8691. end
  8692. end
  8693. end
  8694. module Sass
  8695. module SCSS
  8696. # A parser for a static SCSS tree.
  8697. # Parses with SCSS extensions, like nested rules and parent selectors,
  8698. # but without dynamic SassScript.
  8699. # This is useful for e.g. \{#parse\_selector parsing selectors}
  8700. # after resolving the interpolation.
  8701. class StaticParser < Parser
  8702. # Parses the text as a selector.
  8703. #
  8704. # @param filename [String, nil] The file in which the selector appears,
  8705. # or nil if there is no such file.
  8706. # Used for error reporting.
  8707. # @return [Selector::CommaSequence] The parsed selector
  8708. # @raise [Sass::SyntaxError] if there's a syntax error in the selector
  8709. def parse_selector(filename)
  8710. init_scanner!
  8711. seq = expr!(:selector_comma_sequence)
  8712. expected("selector") unless @scanner.eos?
  8713. seq.line = @line
  8714. seq.filename = filename
  8715. seq
  8716. end
  8717. private
  8718. def variable; nil; end
  8719. def script_value; nil; end
  8720. def interpolation; nil; end
  8721. def interp_string; s = tok(STRING) and [s]; end
  8722. def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
  8723. def use_css_import?; true; end
  8724. def special_directive(name)
  8725. return unless %w[media import charset].include?(name)
  8726. super
  8727. end
  8728. end
  8729. end
  8730. end
  8731. module Sass
  8732. module Script
  8733. # This is a subclass of {Lexer} for use in parsing plain CSS properties.
  8734. #
  8735. # @see Sass::SCSS::CssParser
  8736. class CssLexer < Lexer
  8737. private
  8738. def token
  8739. important || super
  8740. end
  8741. def string(re, *args)
  8742. if re == :uri
  8743. return unless uri = scan(URI)
  8744. return [:string, Script::String.new(uri)]
  8745. end
  8746. return unless scan(STRING)
  8747. [:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
  8748. end
  8749. def important
  8750. return unless s = scan(IMPORTANT)
  8751. [:raw, s]
  8752. end
  8753. end
  8754. end
  8755. end
  8756. module Sass
  8757. module Script
  8758. # This is a subclass of {Parser} for use in parsing plain CSS properties.
  8759. #
  8760. # @see Sass::SCSS::CssParser
  8761. class CssParser < Parser
  8762. private
  8763. # @private
  8764. def lexer_class; CssLexer; end
  8765. # We need a production that only does /,
  8766. # since * and % aren't allowed in plain CSS
  8767. production :div, :unary_plus, :div
  8768. def string
  8769. return number unless tok = try_tok(:string)
  8770. return tok.value unless @lexer.peek && @lexer.peek.type == :begin_interpolation
  8771. end
  8772. # Short-circuit all the SassScript-only productions
  8773. alias_method :interpolation, :space
  8774. alias_method :or_expr, :div
  8775. alias_method :unary_div, :ident
  8776. alias_method :paren, :string
  8777. end
  8778. end
  8779. end
  8780. module Sass
  8781. module SCSS
  8782. # This is a subclass of {Parser} which only parses plain CSS.
  8783. # It doesn't support any Sass extensions, such as interpolation,
  8784. # parent references, nested selectors, and so forth.
  8785. # It does support all the same CSS hacks as the SCSS parser, though.
  8786. class CssParser < StaticParser
  8787. # Parse a selector, and return its value as a string.
  8788. #
  8789. # @return [String, nil] The parsed selector, or nil if no selector was parsed
  8790. # @raise [Sass::SyntaxError] if there's a syntax error in the selector
  8791. def parse_selector_string
  8792. init_scanner!
  8793. str {return unless selector}
  8794. end
  8795. private
  8796. def parent_selector; nil; end
  8797. def interpolation; nil; end
  8798. def interp_string; tok(STRING); end
  8799. def interp_ident(ident = IDENT); tok(ident); end
  8800. def use_css_import?; true; end
  8801. def block_child(context)
  8802. case context
  8803. when :ruleset
  8804. declaration
  8805. when :stylesheet
  8806. directive || ruleset
  8807. when :directive
  8808. directive || declaration_or_ruleset
  8809. end
  8810. end
  8811. def nested_properties!(node, space)
  8812. expected('expression (e.g. 1px, bold)');
  8813. end
  8814. @sass_script_parser = Class.new(Sass::Script::CssParser)
  8815. @sass_script_parser.send(:include, ScriptParser)
  8816. end
  8817. end
  8818. end
  8819. module Sass
  8820. # SCSS is the CSS syntax for Sass.
  8821. # It parses into the same syntax tree as Sass,
  8822. # and generates the same sort of output CSS.
  8823. #
  8824. # This module contains code for the parsing of SCSS.
  8825. # The evaluation is handled by the broader {Sass} module.
  8826. module SCSS; end
  8827. end
  8828. module Sass
  8829. # An exception class that keeps track of
  8830. # the line of the Sass template it was raised on
  8831. # and the Sass file that was being parsed (if applicable).
  8832. #
  8833. # All Sass errors are raised as {Sass::SyntaxError}s.
  8834. #
  8835. # When dealing with SyntaxErrors,
  8836. # it's important to provide filename and line number information.
  8837. # This will be used in various error reports to users, including backtraces;
  8838. # see \{#sass\_backtrace} for details.
  8839. #
  8840. # Some of this information is usually provided as part of the constructor.
  8841. # New backtrace entries can be added with \{#add\_backtrace},
  8842. # which is called when an exception is raised between files (e.g. with `@import`).
  8843. #
  8844. # Often, a chunk of code will all have similar backtrace information -
  8845. # the same filename or even line.
  8846. # It may also be useful to have a default line number set.
  8847. # In those situations, the default values can be used
  8848. # by omitting the information on the original exception,
  8849. # and then calling \{#modify\_backtrace} in a wrapper `rescue`.
  8850. # When doing this, be sure that all exceptions ultimately end up
  8851. # with the information filled in.
  8852. class SyntaxError < StandardError
  8853. # The backtrace of the error within Sass files.
  8854. # This is an array of hashes containing information for a single entry.
  8855. # The hashes have the following keys:
  8856. #
  8857. # `:filename`
  8858. # : The name of the file in which the exception was raised,
  8859. # or `nil` if no filename is available.
  8860. #
  8861. # `:mixin`
  8862. # : The name of the mixin in which the exception was raised,
  8863. # or `nil` if it wasn't raised in a mixin.
  8864. #
  8865. # `:line`
  8866. # : The line of the file on which the error occurred. Never nil.
  8867. #
  8868. # This information is also included in standard backtrace format
  8869. # in the output of \{#backtrace}.
  8870. #
  8871. # @return [Aray<{Symbol => Object>}]
  8872. attr_accessor :sass_backtrace
  8873. # The text of the template where this error was raised.
  8874. #
  8875. # @return [String]
  8876. attr_accessor :sass_template
  8877. # @param msg [String] The error message
  8878. # @param attrs [{Symbol => Object}] The information in the backtrace entry.
  8879. # See \{#sass\_backtrace}
  8880. def initialize(msg, attrs = {})
  8881. @message = msg
  8882. @sass_backtrace = []
  8883. add_backtrace(attrs)
  8884. end
  8885. # The name of the file in which the exception was raised.
  8886. # This could be `nil` if no filename is available.
  8887. #
  8888. # @return [String, nil]
  8889. def sass_filename
  8890. sass_backtrace.first[:filename]
  8891. end
  8892. # The name of the mixin in which the error occurred.
  8893. # This could be `nil` if the error occurred outside a mixin.
  8894. #
  8895. # @return [Fixnum]
  8896. def sass_mixin
  8897. sass_backtrace.first[:mixin]
  8898. end
  8899. # The line of the Sass template on which the error occurred.
  8900. #
  8901. # @return [Fixnum]
  8902. def sass_line
  8903. sass_backtrace.first[:line]
  8904. end
  8905. # Adds an entry to the exception's Sass backtrace.
  8906. #
  8907. # @param attrs [{Symbol => Object}] The information in the backtrace entry.
  8908. # See \{#sass\_backtrace}
  8909. def add_backtrace(attrs)
  8910. sass_backtrace << attrs.reject {|k, v| v.nil?}
  8911. end
  8912. # Modify the top Sass backtrace entries
  8913. # (that is, the most deeply nested ones)
  8914. # to have the given attributes.
  8915. #
  8916. # Specifically, this goes through the backtrace entries
  8917. # from most deeply nested to least,
  8918. # setting the given attributes for each entry.
  8919. # If an entry already has one of the given attributes set,
  8920. # the pre-existing attribute takes precedence
  8921. # and is not used for less deeply-nested entries
  8922. # (even if they don't have that attribute set).
  8923. #
  8924. # @param attrs [{Symbol => Object}] The information to add to the backtrace entry.
  8925. # See \{#sass\_backtrace}
  8926. def modify_backtrace(attrs)
  8927. attrs = attrs.reject {|k, v| v.nil?}
  8928. # Move backwards through the backtrace
  8929. (0...sass_backtrace.size).to_a.reverse.each do |i|
  8930. entry = sass_backtrace[i]
  8931. sass_backtrace[i] = attrs.merge(entry)
  8932. attrs.reject! {|k, v| entry.include?(k)}
  8933. break if attrs.empty?
  8934. end
  8935. end
  8936. # @return [String] The error message
  8937. def to_s
  8938. @message
  8939. end
  8940. # Returns the standard exception backtrace,
  8941. # including the Sass backtrace.
  8942. #
  8943. # @return [Array<String>]
  8944. def backtrace
  8945. return nil if super.nil?
  8946. return super if sass_backtrace.all? {|h| h.empty?}
  8947. sass_backtrace.map do |h|
  8948. "#{h[:filename] || "(sass)"}:#{h[:line]}" +
  8949. (h[:mixin] ? ":in `#{h[:mixin]}'" : "")
  8950. end + super
  8951. end
  8952. # Returns a string representation of the Sass backtrace.
  8953. #
  8954. # @param default_filename [String] The filename to use for unknown files
  8955. # @see #sass_backtrace
  8956. # @return [String]
  8957. def sass_backtrace_str(default_filename = "an unknown file")
  8958. lines = self.message.split("\n")
  8959. msg = lines[0] + lines[1..-1].
  8960. map {|l| "\n" + (" " * "Syntax error: ".size) + l}.join
  8961. "Syntax error: #{msg}" +
  8962. Sass::Util.enum_with_index(sass_backtrace).map do |entry, i|
  8963. "\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" +
  8964. " of #{entry[:filename] || default_filename}" +
  8965. (entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
  8966. end.join
  8967. end
  8968. class << self
  8969. # Returns an error report for an exception in CSS format.
  8970. #
  8971. # @param e [Exception]
  8972. # @param options [{Symbol => Object}] The options passed to {Sass::Engine#initialize}
  8973. # @return [String] The error report
  8974. # @raise [Exception] `e`, if the
  8975. # {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
  8976. # is set to false.
  8977. def exception_to_css(e, options)
  8978. raise e unless options[:full_exception]
  8979. header = header_string(e, options)
  8980. <<END
  8981. /*
  8982. #{header}
  8983. Backtrace:\n#{e.backtrace.join("\n")}
  8984. */
  8985. body:before {
  8986. white-space: pre;
  8987. font-family: monospace;
  8988. content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; }
  8989. END
  8990. end
  8991. private
  8992. def header_string(e, options)
  8993. unless e.is_a?(Sass::SyntaxError) && e.sass_line && e.sass_template
  8994. return "#{e.class}: #{e.message}"
  8995. end
  8996. line_offset = options[:line] || 1
  8997. line_num = e.sass_line + 1 - line_offset
  8998. min = [line_num - 6, 0].max
  8999. section = e.sass_template.rstrip.split("\n")[min ... line_num + 5]
  9000. return e.sass_backtrace_str if section.nil? || section.empty?
  9001. e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
  9002. map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
  9003. end
  9004. end
  9005. end
  9006. # The class for Sass errors that are raised due to invalid unit conversions
  9007. # in SassScript.
  9008. class UnitConversionError < SyntaxError; end
  9009. end
  9010. module Sass
  9011. # Sass importers are in charge of taking paths passed to `@import`
  9012. # and finding the appropriate Sass code for those paths.
  9013. # By default, this code is always loaded from the filesystem,
  9014. # but importers could be added to load from a database or over HTTP.
  9015. #
  9016. # Each importer is in charge of a single load path
  9017. # (or whatever the corresponding notion is for the backend).
  9018. # Importers can be placed in the {file:SASS_REFERENCE.md#load_paths-option `:load_paths` array}
  9019. # alongside normal filesystem paths.
  9020. #
  9021. # When resolving an `@import`, Sass will go through the load paths
  9022. # looking for an importer that successfully imports the path.
  9023. # Once one is found, the imported file is used.
  9024. #
  9025. # User-created importers must inherit from {Importers::Base}.
  9026. module Importers
  9027. end
  9028. end
  9029. module Sass
  9030. module Importers
  9031. # The abstract base class for Sass importers.
  9032. # All importers should inherit from this.
  9033. #
  9034. # At the most basic level, an importer is given a string
  9035. # and must return a {Sass::Engine} containing some Sass code.
  9036. # This string can be interpreted however the importer wants;
  9037. # however, subclasses are encouraged to use the URI format
  9038. # for pathnames.
  9039. #
  9040. # Importers that have some notion of "relative imports"
  9041. # should take a single load path in their constructor,
  9042. # and interpret paths as relative to that.
  9043. # They should also implement the \{#find\_relative} method.
  9044. #
  9045. # Importers should be serializable via `Marshal.dump`.
  9046. # In addition to the standard `_dump` and `_load` methods,
  9047. # importers can define `_before_dump`, `_after_dump`, `_around_dump`,
  9048. # and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
  9049. #
  9050. # @abstract
  9051. class Base
  9052. # Find a Sass file relative to another file.
  9053. # Importers without a notion of "relative paths"
  9054. # should just return nil here.
  9055. #
  9056. # If the importer does have a notion of "relative paths",
  9057. # it should ignore its load path during this method.
  9058. #
  9059. # See \{#find} for important information on how this method should behave.
  9060. #
  9061. # The `:filename` option passed to the returned {Sass::Engine}
  9062. # should be of a format that could be passed to \{#find}.
  9063. #
  9064. # @param uri [String] The URI to import. This is not necessarily relative,
  9065. # but this method should only return true if it is.
  9066. # @param base [String] The base filename. If `uri` is relative,
  9067. # it should be interpreted as relative to `base`.
  9068. # `base` is guaranteed to be in a format importable by this importer.
  9069. # @param options [{Symbol => Object}] Options for the Sass file
  9070. # containing the `@import` that's currently being resolved.
  9071. # @return [Sass::Engine, nil] An Engine containing the imported file,
  9072. # or nil if it couldn't be found or was in the wrong format.
  9073. def find_relative(uri, base, options)
  9074. Sass::Util.abstract(self)
  9075. end
  9076. # Find a Sass file, if it exists.
  9077. #
  9078. # This is the primary entry point of the Importer.
  9079. # It corresponds directly to an `@import` statement in Sass.
  9080. # It should do three basic things:
  9081. #
  9082. # * Determine if the URI is in this importer's format.
  9083. # If not, return nil.
  9084. # * Determine if the file indicated by the URI actually exists and is readable.
  9085. # If not, return nil.
  9086. # * Read the file and place the contents in a {Sass::Engine}.
  9087. # Return that engine.
  9088. #
  9089. # If this importer's format allows for file extensions,
  9090. # it should treat them the same way as the default {Filesystem} importer.
  9091. # If the URI explicitly has a `.sass` or `.scss` filename,
  9092. # the importer should look for that exact file
  9093. # and import it as the syntax indicated.
  9094. # If it doesn't exist, the importer should return nil.
  9095. #
  9096. # If the URI doesn't have either of these extensions,
  9097. # the importer should look for files with the extensions.
  9098. # If no such files exist, it should return nil.
  9099. #
  9100. # The {Sass::Engine} to be returned should be passed `options`,
  9101. # with a few modifications. `:filename` and `:syntax` should be set appropriately,
  9102. # and `:importer` should be set to this importer.
  9103. #
  9104. # @param uri [String] The URI to import.
  9105. # @param options [{Symbol => Object}] Options for the Sass file
  9106. # containing the `@import` that's currently being resolved.
  9107. # This is safe for subclasses to modify destructively.
  9108. # Callers should only pass in a value they don't mind being destructively modified.
  9109. # @return [Sass::Engine, nil] An Engine containing the imported file,
  9110. # or nil if it couldn't be found or was in the wrong format.
  9111. def find(uri, options)
  9112. Sass::Util.abstract(self)
  9113. end
  9114. # Returns the time the given Sass file was last modified.
  9115. #
  9116. # If the given file has been deleted or the time can't be accessed
  9117. # for some other reason, this should return nil.
  9118. #
  9119. # @param uri [String] The URI of the file to check.
  9120. # Comes from a `:filename` option set on an engine returned by this importer.
  9121. # @param options [{Symbol => Objet}] Options for the Sass file
  9122. # containing the `@import` currently being checked.
  9123. # @return [Time, nil]
  9124. def mtime(uri, options)
  9125. Sass::Util.abstract(self)
  9126. end
  9127. # Get the cache key pair for the given Sass URI.
  9128. # The URI need not be checked for validity.
  9129. #
  9130. # The only strict requirement is that the returned pair of strings
  9131. # uniquely identify the file at the given URI.
  9132. # However, the first component generally corresponds roughly to the directory,
  9133. # and the second to the basename, of the URI.
  9134. #
  9135. # Note that keys must be unique *across importers*.
  9136. # Thus it's probably a good idea to include the importer name
  9137. # at the beginning of the first component.
  9138. #
  9139. # @param uri [String] A URI known to be valid for this importer.
  9140. # @param options [{Symbol => Object}] Options for the Sass file
  9141. # containing the `@import` currently being checked.
  9142. # @return [(String, String)] The key pair which uniquely identifies
  9143. # the file at the given URI.
  9144. def key(uri, options)
  9145. Sass::Util.abstract(self)
  9146. end
  9147. # A string representation of the importer.
  9148. # Should be overridden by subclasses.
  9149. #
  9150. # This is used to help debugging,
  9151. # and should usually just show the load path encapsulated by this importer.
  9152. #
  9153. # @return [String]
  9154. def to_s
  9155. Sass::Util.abstract(self)
  9156. end
  9157. end
  9158. end
  9159. end
  9160. require 'pathname'
  9161. module Sass
  9162. module Importers
  9163. # The default importer, used for any strings found in the load path.
  9164. # Simply loads Sass files from the filesystem using the default logic.
  9165. class Filesystem < Base
  9166. attr_accessor :root
  9167. # Creates a new filesystem importer that imports files relative to a given path.
  9168. #
  9169. # @param root [String] The root path.
  9170. # This importer will import files relative to this path.
  9171. def initialize(root)
  9172. @root = root
  9173. end
  9174. # @see Base#find_relative
  9175. def find_relative(name, base, options)
  9176. _find(File.dirname(base), name, options)
  9177. end
  9178. # @see Base#find
  9179. def find(name, options)
  9180. _find(@root, name, options)
  9181. end
  9182. # @see Base#mtime
  9183. def mtime(name, options)
  9184. file, s = find_real_file(@root, name)
  9185. File.mtime(file) if file
  9186. rescue Errno::ENOENT
  9187. nil
  9188. end
  9189. # @see Base#key
  9190. def key(name, options)
  9191. [self.class.name + ":" + File.dirname(File.expand_path(name)),
  9192. File.basename(name)]
  9193. end
  9194. # @see Base#to_s
  9195. def to_s
  9196. @root
  9197. end
  9198. protected
  9199. # If a full uri is passed, this removes the root from it
  9200. # otherwise returns the name unchanged
  9201. def remove_root(name)
  9202. root = @root.end_with?('/') ? @root : @root + '/'
  9203. if name.index(root) == 0
  9204. name[root.length..-1]
  9205. else
  9206. name
  9207. end
  9208. end
  9209. # A hash from file extensions to the syntaxes for those extensions.
  9210. # The syntaxes must be `:sass` or `:scss`.
  9211. #
  9212. # This can be overridden by subclasses that want normal filesystem importing
  9213. # with unusual extensions.
  9214. #
  9215. # @return [{String => Symbol}]
  9216. def extensions
  9217. {'sass' => :sass, 'scss' => :scss}
  9218. end
  9219. # Given an `@import`ed path, returns an array of possible
  9220. # on-disk filenames and their corresponding syntaxes for that path.
  9221. #
  9222. # @param name [String] The filename.
  9223. # @return [Array(String, Symbol)] An array of pairs.
  9224. # The first element of each pair is a filename to look for;
  9225. # the second element is the syntax that file would be in (`:sass` or `:scss`).
  9226. def possible_files(name)
  9227. dirname, basename, extname = split(name)
  9228. sorted_exts = extensions.sort
  9229. syntax = extensions[extname]
  9230. return [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]] if syntax
  9231. sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
  9232. end
  9233. REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
  9234. # Given a base directory and an `@import`ed name,
  9235. # finds an existant file that matches the name.
  9236. #
  9237. # @param dir [String] The directory relative to which to search.
  9238. # @param name [String] The filename to search for.
  9239. # @return [(String, Symbol)] A filename-syntax pair.
  9240. def find_real_file(dir, name)
  9241. for (f,s) in possible_files(remove_root(name))
  9242. path = (dir == ".") ? f : "#{dir}/#{f}"
  9243. if full_path = Dir[path].first
  9244. full_path.gsub!(REDUNDANT_DIRECTORY,File::SEPARATOR)
  9245. return full_path, s
  9246. end
  9247. end
  9248. nil
  9249. end
  9250. # Splits a filename into three parts, a directory part, a basename, and an extension
  9251. # Only the known extensions returned from the extensions method will be recognized as such.
  9252. def split(name)
  9253. extension = nil
  9254. dirname, basename = File.dirname(name), File.basename(name)
  9255. if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
  9256. basename = $1
  9257. extension = $2
  9258. end
  9259. [dirname, basename, extension]
  9260. end
  9261. def hash
  9262. @root.hash
  9263. end
  9264. def eql?(other)
  9265. root.eql?(other.root)
  9266. end
  9267. private
  9268. def _find(dir, name, options)
  9269. full_filename, syntax = find_real_file(dir, name)
  9270. return unless full_filename && File.readable?(full_filename)
  9271. options[:syntax] = syntax
  9272. options[:filename] = full_filename
  9273. options[:importer] = self
  9274. Sass::Engine.new(File.read(full_filename), options)
  9275. end
  9276. def join(base, path)
  9277. Pathname.new(base).join(path).to_s
  9278. end
  9279. end
  9280. end
  9281. end
  9282. require 'strscan'
  9283. module Sass
  9284. # This module contains functionality that's shared between Haml and Sass.
  9285. module Shared
  9286. extend self
  9287. # Scans through a string looking for the interoplation-opening `#{`
  9288. # and, when it's found, yields the scanner to the calling code
  9289. # so it can handle it properly.
  9290. #
  9291. # The scanner will have any backslashes immediately in front of the `#{`
  9292. # as the second capture group (`scan[2]`),
  9293. # and the text prior to that as the first (`scan[1]`).
  9294. #
  9295. # @yieldparam scan [StringScanner] The scanner scanning through the string
  9296. # @return [String] The text remaining in the scanner after all `#{`s have been processed
  9297. def handle_interpolation(str)
  9298. scan = StringScanner.new(str)
  9299. yield scan while scan.scan(/(.*?)(\\*)\#\{/)
  9300. scan.rest
  9301. end
  9302. # Moves a scanner through a balanced pair of characters.
  9303. # For example:
  9304. #
  9305. # Foo (Bar (Baz bang) bop) (Bang (bop bip))
  9306. # ^ ^
  9307. # from to
  9308. #
  9309. # @param scanner [StringScanner] The string scanner to move
  9310. # @param start [Character] The character opening the balanced pair.
  9311. # A `Fixnum` in 1.8, a `String` in 1.9
  9312. # @param finish [Character] The character closing the balanced pair.
  9313. # A `Fixnum` in 1.8, a `String` in 1.9
  9314. # @param count [Fixnum] The number of opening characters matched
  9315. # before calling this method
  9316. # @return [(String, String)] The string matched within the balanced pair
  9317. # and the rest of the string.
  9318. # `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
  9319. def balance(scanner, start, finish, count = 0)
  9320. str = ''
  9321. scanner = StringScanner.new(scanner) unless scanner.is_a? StringScanner
  9322. regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
  9323. while scanner.scan(regexp)
  9324. str << scanner.matched
  9325. count += 1 if scanner.matched[-1] == start
  9326. count -= 1 if scanner.matched[-1] == finish
  9327. return [str.strip, scanner.rest] if count == 0
  9328. end
  9329. end
  9330. # Formats a string for use in error messages about indentation.
  9331. #
  9332. # @param indentation [String] The string used for indentation
  9333. # @param was [Boolean] Whether or not to add `"was"` or `"were"`
  9334. # (depending on how many characters were in `indentation`)
  9335. # @return [String] The name of the indentation (e.g. `"12 spaces"`, `"1 tab"`)
  9336. def human_indentation(indentation, was = false)
  9337. if !indentation.include?(?\t)
  9338. noun = 'space'
  9339. elsif !indentation.include?(?\s)
  9340. noun = 'tab'
  9341. else
  9342. return indentation.inspect + (was ? ' was' : '')
  9343. end
  9344. singular = indentation.length == 1
  9345. if was
  9346. was = singular ? ' was' : ' were'
  9347. else
  9348. was = ''
  9349. end
  9350. "#{indentation.length} #{noun}#{'s' unless singular}#{was}"
  9351. end
  9352. end
  9353. end
  9354. module Sass
  9355. # A Sass mixin or function.
  9356. #
  9357. # `name`: `String`
  9358. # : The name of the mixin/function.
  9359. #
  9360. # `args`: `Array<(String, Script::Node)>`
  9361. # : The arguments for the mixin/function.
  9362. # Each element is a tuple containing the name of the argument
  9363. # and the parse tree for the default value of the argument.
  9364. #
  9365. # `environment`: {Sass::Environment}
  9366. # : The environment in which the mixin/function was defined.
  9367. # This is captured so that the mixin/function can have access
  9368. # to local variables defined in its scope.
  9369. #
  9370. # `tree`: `Array<Tree::Node>`
  9371. # : The parse tree for the mixin/function.
  9372. Callable = Struct.new(:name, :args, :environment, :tree)
  9373. # This class handles the parsing and compilation of the Sass template.
  9374. # Example usage:
  9375. #
  9376. # template = File.load('stylesheets/sassy.sass')
  9377. # sass_engine = Sass::Engine.new(template)
  9378. # output = sass_engine.render
  9379. # puts output
  9380. class Engine
  9381. include Sass::Util
  9382. # A line of Sass code.
  9383. #
  9384. # `text`: `String`
  9385. # : The text in the line, without any whitespace at the beginning or end.
  9386. #
  9387. # `tabs`: `Fixnum`
  9388. # : The level of indentation of the line.
  9389. #
  9390. # `index`: `Fixnum`
  9391. # : The line number in the original document.
  9392. #
  9393. # `offset`: `Fixnum`
  9394. # : The number of bytes in on the line that the text begins.
  9395. # This ends up being the number of bytes of leading whitespace.
  9396. #
  9397. # `filename`: `String`
  9398. # : The name of the file in which this line appeared.
  9399. #
  9400. # `children`: `Array<Line>`
  9401. # : The lines nested below this one.
  9402. class Line < Struct.new(:text, :tabs, :index, :offset, :filename, :children)
  9403. def comment?
  9404. text[0] == COMMENT_CHAR && (text[1] == SASS_COMMENT_CHAR || text[1] == CSS_COMMENT_CHAR)
  9405. end
  9406. end
  9407. # The character that begins a CSS property.
  9408. PROPERTY_CHAR = ?:
  9409. # The character that designates the beginning of a comment,
  9410. # either Sass or CSS.
  9411. COMMENT_CHAR = ?/
  9412. # The character that follows the general COMMENT_CHAR and designates a Sass comment,
  9413. # which is not output as a CSS comment.
  9414. SASS_COMMENT_CHAR = ?/
  9415. # The character that follows the general COMMENT_CHAR and designates a CSS comment,
  9416. # which is embedded in the CSS document.
  9417. CSS_COMMENT_CHAR = ?*
  9418. # The character used to denote a compiler directive.
  9419. DIRECTIVE_CHAR = ?@
  9420. # Designates a non-parsed rule.
  9421. ESCAPE_CHAR = ?\\
  9422. # Designates block as mixin definition rather than CSS rules to output
  9423. MIXIN_DEFINITION_CHAR = ?=
  9424. # Includes named mixin declared using MIXIN_DEFINITION_CHAR
  9425. MIXIN_INCLUDE_CHAR = ?+
  9426. # The regex that matches and extracts data from
  9427. # properties of the form `:name prop`.
  9428. PROPERTY_OLD = /^:([^\s=:"]+)\s*(?:\s+|$)(.*)/
  9429. # The default options for Sass::Engine.
  9430. # @api public
  9431. DEFAULT_OPTIONS = {
  9432. :style => :nested,
  9433. :load_paths => ['.'],
  9434. :cache => true,
  9435. :cache_location => './.sass-cache',
  9436. :syntax => :sass,
  9437. :filesystem_importer => Sass::Importers::Filesystem
  9438. }.freeze
  9439. # Converts a Sass options hash into a standard form, filling in
  9440. # default values and resolving aliases.
  9441. #
  9442. # @param options [{Symbol => Object}] The options hash;
  9443. # see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
  9444. # @return [{Symbol => Object}] The normalized options hash.
  9445. # @private
  9446. def self.normalize_options(options)
  9447. options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
  9448. # If the `:filename` option is passed in without an importer,
  9449. # assume it's using the default filesystem importer.
  9450. options[:importer] ||= options[:filesystem_importer].new(".") if options[:filename]
  9451. # Tracks the original filename of the top-level Sass file
  9452. options[:original_filename] ||= options[:filename]
  9453. options[:cache_store] ||= Sass::CacheStores::Chain.new(
  9454. Sass::CacheStores::Memory.new, Sass::CacheStores::Filesystem.new(options[:cache_location]))
  9455. # Support both, because the docs said one and the other actually worked
  9456. # for quite a long time.
  9457. options[:line_comments] ||= options[:line_numbers]
  9458. options[:load_paths] = options[:load_paths].map do |p|
  9459. next p unless p.is_a?(String) || (defined?(Pathname) && p.is_a?(Pathname))
  9460. options[:filesystem_importer].new(p.to_s)
  9461. end
  9462. # Backwards compatibility
  9463. options[:property_syntax] ||= options[:attribute_syntax]
  9464. case options[:property_syntax]
  9465. when :alternate; options[:property_syntax] = :new
  9466. when :normal; options[:property_syntax] = :old
  9467. end
  9468. options
  9469. end
  9470. # Returns the {Sass::Engine} for the given file.
  9471. # This is preferable to Sass::Engine.new when reading from a file
  9472. # because it properly sets up the Engine's metadata,
  9473. # enables parse-tree caching,
  9474. # and infers the syntax from the filename.
  9475. #
  9476. # @param filename [String] The path to the Sass or SCSS file
  9477. # @param options [{Symbol => Object}] The options hash;
  9478. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  9479. # @return [Sass::Engine] The Engine for the given Sass or SCSS file.
  9480. # @raise [Sass::SyntaxError] if there's an error in the document.
  9481. def self.for_file(filename, options)
  9482. had_syntax = options[:syntax]
  9483. if had_syntax
  9484. # Use what was explicitly specificed
  9485. elsif filename =~ /\.scss$/
  9486. options.merge!(:syntax => :scss)
  9487. elsif filename =~ /\.sass$/
  9488. options.merge!(:syntax => :sass)
  9489. end
  9490. Sass::Engine.new(File.read(filename), options.merge(:filename => filename))
  9491. end
  9492. # The options for the Sass engine.
  9493. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  9494. #
  9495. # @return [{Symbol => Object}]
  9496. attr_reader :options
  9497. # Creates a new Engine. Note that Engine should only be used directly
  9498. # when compiling in-memory Sass code.
  9499. # If you're compiling a single Sass file from the filesystem,
  9500. # use \{Sass::Engine.for\_file}.
  9501. # If you're compiling multiple files from the filesystem,
  9502. # use {Sass::Plugin.
  9503. #
  9504. # @param template [String] The Sass template.
  9505. # This template can be encoded using any encoding
  9506. # that can be converted to Unicode.
  9507. # If the template contains an `@charset` declaration,
  9508. # that overrides the Ruby encoding
  9509. # (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
  9510. # @param options [{Symbol => Object}] An options hash.
  9511. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  9512. # @see {Sass::Engine.for_file}
  9513. # @see {Sass::Plugin}
  9514. def initialize(template, options={})
  9515. @options = self.class.normalize_options(options)
  9516. @template = template
  9517. end
  9518. # Render the template to CSS.
  9519. #
  9520. # @return [String] The CSS
  9521. # @raise [Sass::SyntaxError] if there's an error in the document
  9522. # @raise [Encoding::UndefinedConversionError] if the source encoding
  9523. # cannot be converted to UTF-8
  9524. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  9525. def render
  9526. return _render unless @options[:quiet]
  9527. Sass::Util.silence_sass_warnings {_render}
  9528. end
  9529. alias_method :to_css, :render
  9530. # Parses the document into its parse tree. Memoized.
  9531. #
  9532. # @return [Sass::Tree::Node] The root of the parse tree.
  9533. # @raise [Sass::SyntaxError] if there's an error in the document
  9534. def to_tree
  9535. @tree ||= @options[:quiet] ?
  9536. Sass::Util.silence_sass_warnings {_to_tree} :
  9537. _to_tree
  9538. end
  9539. # Returns the original encoding of the document,
  9540. # or `nil` under Ruby 1.8.
  9541. #
  9542. # @return [Encoding, nil]
  9543. # @raise [Encoding::UndefinedConversionError] if the source encoding
  9544. # cannot be converted to UTF-8
  9545. # @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
  9546. def source_encoding
  9547. check_encoding!
  9548. @original_encoding
  9549. end
  9550. # Gets a set of all the documents
  9551. # that are (transitive) dependencies of this document,
  9552. # not including the document itself.
  9553. #
  9554. # @return [[Sass::Engine]] The dependency documents.
  9555. def dependencies
  9556. _dependencies(Set.new, engines = Set.new)
  9557. engines - [self]
  9558. end
  9559. # Helper for \{#dependencies}.
  9560. #
  9561. # @private
  9562. def _dependencies(seen, engines)
  9563. return if seen.include?(key = [@options[:filename], @options[:importer]])
  9564. seen << key
  9565. engines << self
  9566. to_tree.grep(Tree::ImportNode) do |n|
  9567. next if n.css_import?
  9568. n.imported_file._dependencies(seen, engines)
  9569. end
  9570. end
  9571. private
  9572. def _render
  9573. rendered = _to_tree.render
  9574. return rendered if ruby1_8?
  9575. begin
  9576. # Try to convert the result to the original encoding,
  9577. # but if that doesn't work fall back on UTF-8
  9578. rendered = rendered.encode(source_encoding)
  9579. rescue EncodingError
  9580. end
  9581. rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
  9582. "@charset \"#{source_encoding.name}\"".encode(source_encoding))
  9583. end
  9584. def _to_tree
  9585. if (@options[:cache] || @options[:read_cache]) &&
  9586. @options[:filename] && @options[:importer]
  9587. key = sassc_key
  9588. sha = Digest::SHA1.hexdigest(@template)
  9589. if root = @options[:cache_store].retrieve(key, sha)
  9590. @options = root.options.merge(@options)
  9591. root.options = @options
  9592. return root
  9593. end
  9594. end
  9595. check_encoding!
  9596. if @options[:syntax] == :scss
  9597. root = Sass::SCSS::Parser.new(@template).parse
  9598. else
  9599. root = Tree::RootNode.new(@template)
  9600. append_children(root, tree(tabulate(@template)).first, true)
  9601. end
  9602. root.options = @options
  9603. if @options[:cache] && key && sha
  9604. begin
  9605. old_options = root.options
  9606. root.options = {:importer => root.options[:importer]}
  9607. @options[:cache_store].store(key, sha, root)
  9608. ensure
  9609. root.options = old_options
  9610. end
  9611. end
  9612. root
  9613. rescue SyntaxError => e
  9614. e.modify_backtrace(:filename => @options[:filename], :line => @line)
  9615. e.sass_template = @template
  9616. raise e
  9617. end
  9618. def sassc_key
  9619. @options[:cache_store].key(*@options[:importer].key(@options[:filename], @options))
  9620. end
  9621. def check_encoding!
  9622. return if @checked_encoding
  9623. @checked_encoding = true
  9624. @template, @original_encoding = check_sass_encoding(@template) do |msg, line|
  9625. raise Sass::SyntaxError.new(msg, :line => line)
  9626. end
  9627. end
  9628. def tabulate(string)
  9629. tab_str = nil
  9630. comment_tab_str = nil
  9631. first = true
  9632. lines = []
  9633. string.gsub(/\r|\n|\r\n|\r\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
  9634. index += (@options[:line] || 1)
  9635. if line.strip.empty?
  9636. lines.last.text << "\n" if lines.last && lines.last.comment?
  9637. next
  9638. end
  9639. line_tab_str = line[/^\s*/]
  9640. unless line_tab_str.empty?
  9641. if tab_str.nil?
  9642. comment_tab_str ||= line_tab_str
  9643. next if try_comment(line, lines.last, "", comment_tab_str, index)
  9644. comment_tab_str = nil
  9645. end
  9646. tab_str ||= line_tab_str
  9647. raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
  9648. :line => index) if first
  9649. raise SyntaxError.new("Indentation can't use both tabs and spaces.",
  9650. :line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
  9651. end
  9652. first &&= !tab_str.nil?
  9653. if tab_str.nil?
  9654. lines << Line.new(line.strip, 0, index, 0, @options[:filename], [])
  9655. next
  9656. end
  9657. comment_tab_str ||= line_tab_str
  9658. if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
  9659. next
  9660. else
  9661. comment_tab_str = nil
  9662. end
  9663. line_tabs = line_tab_str.scan(tab_str).size
  9664. if tab_str * line_tabs != line_tab_str
  9665. message = <<END.strip.gsub("\n", ' ')
  9666. Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
  9667. but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
  9668. END
  9669. raise SyntaxError.new(message, :line => index)
  9670. end
  9671. lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
  9672. end
  9673. lines
  9674. end
  9675. def try_comment(line, last, tab_str, comment_tab_str, index)
  9676. return unless last && last.comment?
  9677. # Nested comment stuff must be at least one whitespace char deeper
  9678. # than the normal indentation
  9679. return unless line =~ /^#{tab_str}\s/
  9680. unless line =~ /^(?:#{comment_tab_str})(.*)$/
  9681. raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
  9682. Inconsistent indentation:
  9683. previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
  9684. but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
  9685. MSG
  9686. end
  9687. last.text << "\n" << $1
  9688. true
  9689. end
  9690. def tree(arr, i = 0)
  9691. return [], i if arr[i].nil?
  9692. base = arr[i].tabs
  9693. nodes = []
  9694. while (line = arr[i]) && line.tabs >= base
  9695. if line.tabs > base
  9696. raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
  9697. :line => line.index) if line.tabs > base + 1
  9698. nodes.last.children, i = tree(arr, i)
  9699. else
  9700. nodes << line
  9701. i += 1
  9702. end
  9703. end
  9704. return nodes, i
  9705. end
  9706. def build_tree(parent, line, root = false)
  9707. @line = line.index
  9708. node_or_nodes = parse_line(parent, line, root)
  9709. Array(node_or_nodes).each do |node|
  9710. # Node is a symbol if it's non-outputting, like a variable assignment
  9711. next unless node.is_a? Tree::Node
  9712. node.line = line.index
  9713. node.filename = line.filename
  9714. append_children(node, line.children, false)
  9715. end
  9716. node_or_nodes
  9717. end
  9718. def append_children(parent, children, root)
  9719. continued_rule = nil
  9720. continued_comment = nil
  9721. children.each do |line|
  9722. child = build_tree(parent, line, root)
  9723. if child.is_a?(Tree::RuleNode)
  9724. if child.continued? && child.children.empty?
  9725. if continued_rule
  9726. continued_rule.add_rules child
  9727. else
  9728. continued_rule = child
  9729. end
  9730. next
  9731. elsif continued_rule
  9732. continued_rule.add_rules child
  9733. continued_rule.children = child.children
  9734. continued_rule, child = nil, continued_rule
  9735. end
  9736. elsif continued_rule
  9737. continued_rule = nil
  9738. end
  9739. if child.is_a?(Tree::CommentNode) && child.silent
  9740. if continued_comment &&
  9741. child.line == continued_comment.line +
  9742. continued_comment.value.count("\n") + 1
  9743. continued_comment.value << "\n" << child.value
  9744. next
  9745. end
  9746. continued_comment = child
  9747. end
  9748. check_for_no_children(child)
  9749. validate_and_append_child(parent, child, line, root)
  9750. end
  9751. parent
  9752. end
  9753. def validate_and_append_child(parent, child, line, root)
  9754. case child
  9755. when Array
  9756. child.each {|c| validate_and_append_child(parent, c, line, root)}
  9757. when Tree::Node
  9758. parent << child
  9759. end
  9760. end
  9761. def check_for_no_children(node)
  9762. return unless node.is_a?(Tree::RuleNode) && node.children.empty?
  9763. Sass::Util.sass_warn(<<WARNING.strip)
  9764. WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
  9765. This selector doesn't have any properties and will not be rendered.
  9766. WARNING
  9767. end
  9768. def parse_line(parent, line, root)
  9769. case line.text[0]
  9770. when PROPERTY_CHAR
  9771. if line.text[1] == PROPERTY_CHAR ||
  9772. (@options[:property_syntax] == :new &&
  9773. line.text =~ PROPERTY_OLD && $2.empty?)
  9774. # Support CSS3-style pseudo-elements,
  9775. # which begin with ::,
  9776. # as well as pseudo-classes
  9777. # if we're using the new property syntax
  9778. Tree::RuleNode.new(parse_interp(line.text))
  9779. else
  9780. name, value = line.text.scan(PROPERTY_OLD)[0]
  9781. raise SyntaxError.new("Invalid property: \"#{line.text}\".",
  9782. :line => @line) if name.nil? || value.nil?
  9783. parse_property(name, parse_interp(name), value, :old, line)
  9784. end
  9785. when ?$
  9786. parse_variable(line)
  9787. when COMMENT_CHAR
  9788. parse_comment(line.text)
  9789. when DIRECTIVE_CHAR
  9790. parse_directive(parent, line, root)
  9791. when ESCAPE_CHAR
  9792. Tree::RuleNode.new(parse_interp(line.text[1..-1]))
  9793. when MIXIN_DEFINITION_CHAR
  9794. parse_mixin_definition(line)
  9795. when MIXIN_INCLUDE_CHAR
  9796. if line.text[1].nil? || line.text[1] == ?\s
  9797. Tree::RuleNode.new(parse_interp(line.text))
  9798. else
  9799. parse_mixin_include(line, root)
  9800. end
  9801. else
  9802. parse_property_or_rule(line)
  9803. end
  9804. end
  9805. def parse_property_or_rule(line)
  9806. scanner = StringScanner.new(line.text)
  9807. hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
  9808. parser = Sass::SCSS::SassParser.new(scanner, @line)
  9809. unless res = parser.parse_interp_ident
  9810. return Tree::RuleNode.new(parse_interp(line.text))
  9811. end
  9812. res.unshift(hack_char) if hack_char
  9813. if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
  9814. res << comment
  9815. end
  9816. name = line.text[0...scanner.pos]
  9817. if scanner.scan(/\s*:(?:\s|$)/)
  9818. parse_property(name, res, scanner.rest, :new, line)
  9819. else
  9820. res.pop if comment
  9821. Tree::RuleNode.new(res + parse_interp(scanner.rest))
  9822. end
  9823. end
  9824. def parse_property(name, parsed_name, value, prop, line)
  9825. if value.strip.empty?
  9826. expr = Sass::Script::String.new("")
  9827. else
  9828. expr = parse_script(value, :offset => line.offset + line.text.index(value))
  9829. end
  9830. Tree::PropNode.new(parse_interp(name), expr, prop)
  9831. end
  9832. def parse_variable(line)
  9833. name, value, default = line.text.scan(Script::MATCH)[0]
  9834. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
  9835. :line => @line + 1) unless line.children.empty?
  9836. raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
  9837. :line => @line) unless name && value
  9838. expr = parse_script(value, :offset => line.offset + line.text.index(value))
  9839. Tree::VariableNode.new(name, expr, default)
  9840. end
  9841. def parse_comment(line)
  9842. if line[1] == CSS_COMMENT_CHAR || line[1] == SASS_COMMENT_CHAR
  9843. silent = line[1] == SASS_COMMENT_CHAR
  9844. Tree::CommentNode.new(
  9845. format_comment_text(line[2..-1], silent),
  9846. silent)
  9847. else
  9848. Tree::RuleNode.new(parse_interp(line))
  9849. end
  9850. end
  9851. def parse_directive(parent, line, root)
  9852. directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
  9853. offset = directive.size + whitespace.size + 1 if whitespace
  9854. # If value begins with url( or ",
  9855. # it's a CSS @import rule and we don't want to touch it.
  9856. if directive == "import"
  9857. parse_import(line, value)
  9858. elsif directive == "mixin"
  9859. parse_mixin_definition(line)
  9860. elsif directive == "include"
  9861. parse_mixin_include(line, root)
  9862. elsif directive == "function"
  9863. parse_function(line, root)
  9864. elsif directive == "for"
  9865. parse_for(line, root, value)
  9866. elsif directive == "each"
  9867. parse_each(line, root, value)
  9868. elsif directive == "else"
  9869. parse_else(parent, line, value)
  9870. elsif directive == "while"
  9871. raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
  9872. Tree::WhileNode.new(parse_script(value, :offset => offset))
  9873. elsif directive == "if"
  9874. raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
  9875. Tree::IfNode.new(parse_script(value, :offset => offset))
  9876. elsif directive == "debug"
  9877. raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
  9878. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
  9879. :line => @line + 1) unless line.children.empty?
  9880. offset = line.offset + line.text.index(value).to_i
  9881. Tree::DebugNode.new(parse_script(value, :offset => offset))
  9882. elsif directive == "extend"
  9883. raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
  9884. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
  9885. :line => @line + 1) unless line.children.empty?
  9886. offset = line.offset + line.text.index(value).to_i
  9887. Tree::ExtendNode.new(parse_interp(value, offset))
  9888. elsif directive == "warn"
  9889. raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
  9890. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
  9891. :line => @line + 1) unless line.children.empty?
  9892. offset = line.offset + line.text.index(value).to_i
  9893. Tree::WarnNode.new(parse_script(value, :offset => offset))
  9894. elsif directive == "return"
  9895. raise SyntaxError.new("Invalid @return: expected expression.") unless value
  9896. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
  9897. :line => @line + 1) unless line.children.empty?
  9898. offset = line.offset + line.text.index(value).to_i
  9899. Tree::ReturnNode.new(parse_script(value, :offset => offset))
  9900. elsif directive == "charset"
  9901. name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
  9902. raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
  9903. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
  9904. :line => @line + 1) unless line.children.empty?
  9905. Tree::CharsetNode.new(name)
  9906. elsif directive == "media"
  9907. Tree::MediaNode.new(value)
  9908. else
  9909. Tree::DirectiveNode.new(line.text)
  9910. end
  9911. end
  9912. def parse_for(line, root, text)
  9913. var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
  9914. if var.nil? # scan failed, try to figure out why for error message
  9915. if text !~ /^[^\s]+/
  9916. expected = "variable name"
  9917. elsif text !~ /^[^\s]+\s+from\s+.+/
  9918. expected = "'from <expr>'"
  9919. else
  9920. expected = "'to <expr>' or 'through <expr>'"
  9921. end
  9922. raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
  9923. end
  9924. raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
  9925. var = var[1..-1]
  9926. parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
  9927. parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
  9928. Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
  9929. end
  9930. def parse_each(line, root, text)
  9931. var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
  9932. if var.nil? # scan failed, try to figure out why for error message
  9933. if text !~ /^[^\s]+/
  9934. expected = "variable name"
  9935. elsif text !~ /^[^\s]+\s+from\s+.+/
  9936. expected = "'in <expr>'"
  9937. end
  9938. raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
  9939. end
  9940. raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
  9941. var = var[1..-1]
  9942. parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
  9943. Tree::EachNode.new(var, parsed_list)
  9944. end
  9945. def parse_else(parent, line, text)
  9946. previous = parent.children.last
  9947. raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
  9948. if text
  9949. if text !~ /^if\s+(.+)/
  9950. raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
  9951. end
  9952. expr = parse_script($1, :offset => line.offset + line.text.index($1))
  9953. end
  9954. node = Tree::IfNode.new(expr)
  9955. append_children(node, line.children, false)
  9956. previous.add_else node
  9957. nil
  9958. end
  9959. def parse_import(line, value)
  9960. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
  9961. :line => @line + 1) unless line.children.empty?
  9962. scanner = StringScanner.new(value)
  9963. values = []
  9964. loop do
  9965. unless node = parse_import_arg(scanner)
  9966. raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
  9967. :line => @line)
  9968. end
  9969. values << node
  9970. break unless scanner.scan(/,\s*/)
  9971. end
  9972. return values
  9973. end
  9974. def parse_import_arg(scanner)
  9975. return if scanner.eos?
  9976. unless (str = scanner.scan(Sass::SCSS::RX::STRING)) ||
  9977. (uri = scanner.scan(Sass::SCSS::RX::URI))
  9978. return Tree::ImportNode.new(scanner.scan(/[^,]+/))
  9979. end
  9980. val = scanner[1] || scanner[2]
  9981. scanner.scan(/\s*/)
  9982. if media = scanner.scan(/[^,].*/)
  9983. Tree::DirectiveNode.new("@import #{str || uri} #{media}")
  9984. elsif uri
  9985. Tree::DirectiveNode.new("@import #{uri}")
  9986. elsif val =~ /^http:\/\//
  9987. Tree::DirectiveNode.new("@import url(#{val})")
  9988. else
  9989. Tree::ImportNode.new(val)
  9990. end
  9991. end
  9992. MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
  9993. def parse_mixin_definition(line)
  9994. name, arg_string = line.text.scan(MIXIN_DEF_RE).first
  9995. raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
  9996. offset = line.offset + line.text.size - arg_string.size
  9997. args = Script::Parser.new(arg_string.strip, @line, offset, @options).
  9998. parse_mixin_definition_arglist
  9999. Tree::MixinDefNode.new(name, args)
  10000. end
  10001. MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
  10002. def parse_mixin_include(line, root)
  10003. name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
  10004. raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
  10005. offset = line.offset + line.text.size - arg_string.size
  10006. args, keywords = Script::Parser.new(arg_string.strip, @line, offset, @options).
  10007. parse_mixin_include_arglist
  10008. raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath mixin directives.",
  10009. :line => @line + 1) unless line.children.empty?
  10010. Tree::MixinNode.new(name, args, keywords)
  10011. end
  10012. FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
  10013. def parse_function(line, root)
  10014. name, arg_string = line.text.scan(FUNCTION_RE).first
  10015. raise SyntaxError.new("Invalid function definition \"#{line.text}\".") if name.nil?
  10016. offset = line.offset + line.text.size - arg_string.size
  10017. args = Script::Parser.new(arg_string.strip, @line, offset, @options).
  10018. parse_function_definition_arglist
  10019. Tree::FunctionNode.new(name, args)
  10020. end
  10021. def parse_script(script, options = {})
  10022. line = options[:line] || @line
  10023. offset = options[:offset] || 0
  10024. Script.parse(script, line, offset, @options)
  10025. end
  10026. def format_comment_text(text, silent)
  10027. content = text.split("\n")
  10028. if content.first && content.first.strip.empty?
  10029. removed_first = true
  10030. content.shift
  10031. end
  10032. return silent ? "//" : "/* */" if content.empty?
  10033. content.last.gsub!(%r{ ?\*/ *$}, '')
  10034. content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
  10035. content.first.gsub!(/^ /, '') unless removed_first
  10036. if silent
  10037. "//" + content.join("\n//")
  10038. else
  10039. # The #gsub fixes the case of a trailing */
  10040. "/*" + content.join("\n *").gsub(/ \*\Z/, '') + " */"
  10041. end
  10042. end
  10043. def parse_interp(text, offset = 0)
  10044. self.class.parse_interp(text, @line, offset, :filename => @filename)
  10045. end
  10046. # It's important that this have strings (at least)
  10047. # at the beginning, the end, and between each Script::Node.
  10048. #
  10049. # @private
  10050. def self.parse_interp(text, line, offset, options)
  10051. res = []
  10052. rest = Sass::Shared.handle_interpolation text do |scan|
  10053. escapes = scan[2].size
  10054. res << scan.matched[0...-2 - escapes]
  10055. if escapes % 2 == 1
  10056. res << "\\" * (escapes - 1) << '#{'
  10057. else
  10058. res << "\\" * [0, escapes - 1].max
  10059. res << Script::Parser.new(
  10060. scan, line, offset + scan.pos - scan.matched_size, options).
  10061. parse_interpolated
  10062. end
  10063. end
  10064. res << rest
  10065. end
  10066. end
  10067. end
  10068. require 'fileutils'
  10069. require 'fileutils'
  10070. # XXX CE: is this still necessary now that we have the compiler class?
  10071. module Sass
  10072. # A lightweight infrastructure for defining and running callbacks.
  10073. # Callbacks are defined using \{#define\_callback\} at the class level,
  10074. # and called using `run_#{name}` at the instance level.
  10075. #
  10076. # Clients can add callbacks by calling the generated `on_#{name}` method,
  10077. # and passing in a block that's run when the callback is activated.
  10078. #
  10079. # @example Define a callback
  10080. # class Munger
  10081. # extend Sass::Callbacks
  10082. # define_callback :string_munged
  10083. #
  10084. # def munge(str)
  10085. # res = str.gsub(/[a-z]/, '\1\1')
  10086. # run_string_munged str, res
  10087. # res
  10088. # end
  10089. # end
  10090. #
  10091. # @example Use a callback
  10092. # m = Munger.new
  10093. # m.on_string_munged {|str, res| puts "#{str} was munged into #{res}!"}
  10094. # m.munge "bar" #=> bar was munged into bbaarr!
  10095. module Callbacks
  10096. # Automatically includes {InstanceMethods}
  10097. # when something extends this module.
  10098. #
  10099. # @param base [Module]
  10100. def self.extended(base)
  10101. base.send(:include, InstanceMethods)
  10102. end
  10103. protected
  10104. module InstanceMethods
  10105. # Removes all callbacks registered against this object.
  10106. def clear_callbacks!
  10107. @_sass_callbacks = {}
  10108. end
  10109. end
  10110. # Define a callback with the given name.
  10111. # This will define an `on_#{name}` method
  10112. # that registers a block,
  10113. # and a `run_#{name}` method that runs that block
  10114. # (optionall with some arguments).
  10115. #
  10116. # @param name [Symbol] The name of the callback
  10117. # @return [void]
  10118. def define_callback(name)
  10119. class_eval <<RUBY
  10120. def on_#{name}(&block)
  10121. @_sass_callbacks ||= {}
  10122. (@_sass_callbacks[#{name.inspect}] ||= []) << block
  10123. end
  10124. def run_#{name}(*args)
  10125. return unless @_sass_callbacks
  10126. return unless @_sass_callbacks[#{name.inspect}]
  10127. @_sass_callbacks[#{name.inspect}].each {|c| c[*args]}
  10128. end
  10129. private :run_#{name}
  10130. RUBY
  10131. end
  10132. end
  10133. end
  10134. # We keep configuration in its own self-contained file
  10135. # so that we can load it independently in Rails 3,
  10136. # where the full plugin stuff is lazy-loaded.
  10137. module Sass
  10138. module Plugin
  10139. module Configuration
  10140. # Returns the default options for a {Sass::Plugin::Compiler}.
  10141. #
  10142. # @return [{Symbol => Object}]
  10143. def default_options
  10144. @default_options ||= {
  10145. :css_location => './public/stylesheets',
  10146. :always_update => false,
  10147. :always_check => true,
  10148. :full_exception => true,
  10149. :cache_location => ".sass-cache"
  10150. }.freeze
  10151. end
  10152. # Resets the options and {Sass::Callbacks::InstanceMethods#clear_callbacks! clears all callbacks}.
  10153. def reset!
  10154. @options = nil
  10155. clear_callbacks!
  10156. end
  10157. # An options hash.
  10158. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  10159. #
  10160. # @return [{Symbol => Object}]
  10161. def options
  10162. @options ||= default_options.dup
  10163. @options[:cache_store] ||= Sass::CacheStores::Filesystem.new(@options[:cache_location])
  10164. @options
  10165. end
  10166. # Sets the options hash.
  10167. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  10168. # See {Sass::Plugin::Configuration#reset!}
  10169. # @deprecated Instead, modify the options hash in-place.
  10170. # @param value [{Symbol => Object}] The options hash
  10171. def options=(value)
  10172. Sass::Util.sass_warn("Setting Sass::Plugin.options is deprecated " +
  10173. "and will be removed in a future release.")
  10174. options.merge!(value)
  10175. end
  10176. # Adds a new template-location/css-location mapping.
  10177. # This means that Sass/SCSS files in `template_location`
  10178. # will be compiled to CSS files in `css_location`.
  10179. #
  10180. # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
  10181. # since the option can be in multiple formats.
  10182. #
  10183. # Note that this method will change `options[:template_location]`
  10184. # to be in the Array format.
  10185. # This means that even if `options[:template_location]`
  10186. # had previously been a Hash or a String,
  10187. # it will now be an Array.
  10188. #
  10189. # @param template_location [String] The location where Sass/SCSS files will be.
  10190. # @param css_location [String] The location where compiled CSS files will go.
  10191. def add_template_location(template_location, css_location = options[:css_location])
  10192. normalize_template_location!
  10193. template_location_array << [template_location, css_location]
  10194. end
  10195. # Removes a template-location/css-location mapping.
  10196. # This means that Sass/SCSS files in `template_location`
  10197. # will no longer be compiled to CSS files in `css_location`.
  10198. #
  10199. # This is preferred over manually manipulating the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
  10200. # since the option can be in multiple formats.
  10201. #
  10202. # Note that this method will change `options[:template_location]`
  10203. # to be in the Array format.
  10204. # This means that even if `options[:template_location]`
  10205. # had previously been a Hash or a String,
  10206. # it will now be an Array.
  10207. #
  10208. # @param template_location [String]
  10209. # The location where Sass/SCSS files were,
  10210. # which is now going to be ignored.
  10211. # @param css_location [String]
  10212. # The location where compiled CSS files went, but will no longer go.
  10213. # @return [Boolean]
  10214. # Non-`nil` if the given mapping already existed and was removed,
  10215. # or `nil` if nothing was changed.
  10216. def remove_template_location(template_location, css_location = options[:css_location])
  10217. normalize_template_location!
  10218. template_location_array.delete([template_location, css_location])
  10219. end
  10220. # Returns the template locations configured for Sass
  10221. # as an array of `[template_location, css_location]` pairs.
  10222. # See the {file:SASS_REFERENCE.md#template_location-option `:template_location` option}
  10223. # for details.
  10224. #
  10225. # @return [Array<(String, String)>]
  10226. # An array of `[template_location, css_location]` pairs.
  10227. def template_location_array
  10228. old_template_location = options[:template_location]
  10229. normalize_template_location!
  10230. options[:template_location]
  10231. ensure
  10232. options[:template_location] = old_template_location
  10233. end
  10234. private
  10235. def normalize_template_location!
  10236. return if options[:template_location].is_a?(Array)
  10237. options[:template_location] =
  10238. case options[:template_location]
  10239. when nil
  10240. options[:css_location] ?
  10241. [[File.join(options[:css_location], 'sass'), options[:css_location]]] : []
  10242. when String; [[options[:template_location], options[:css_location]]]
  10243. else; options[:template_location].to_a
  10244. end
  10245. end
  10246. end
  10247. end
  10248. end
  10249. module Sass
  10250. module Plugin
  10251. # The class handles `.s[ca]ss` file staleness checks via their mtime timestamps.
  10252. #
  10253. # To speed things up two level of caches are employed:
  10254. #
  10255. # * A class-level dependency cache which stores @import paths for each file.
  10256. # This is a long-lived cache that is reused by every StalenessChecker instance.
  10257. # * Three short-lived instance-level caches, one for file mtimes,
  10258. # one for whether a file is stale during this particular run.
  10259. # and one for the parse tree for a file.
  10260. # These are only used by a single StalenessChecker instance.
  10261. #
  10262. # Usage:
  10263. #
  10264. # * For a one-off staleness check of a single `.s[ca]ss` file,
  10265. # the class-level {stylesheet_needs_update?} method
  10266. # should be used.
  10267. # * For a series of staleness checks (e.g. checking all files for staleness)
  10268. # a StalenessChecker instance should be created,
  10269. # and the instance-level \{#stylesheet\_needs\_update?} method should be used.
  10270. # the caches should make the whole process significantly faster.
  10271. # *WARNING*: It is important not to retain the instance for too long,
  10272. # as its instance-level caches are never explicitly expired.
  10273. class StalenessChecker
  10274. @dependencies_cache = {}
  10275. class << self
  10276. # TODO: attach this to a compiler instance.
  10277. # @private
  10278. attr_accessor :dependencies_cache
  10279. end
  10280. # Creates a new StalenessChecker
  10281. # for checking the staleness of several stylesheets at once.
  10282. #
  10283. # @param options [{Symbol => Object}]
  10284. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  10285. def initialize(options)
  10286. @dependencies = self.class.dependencies_cache
  10287. # Entries in the following instance-level caches are never explicitly expired.
  10288. # Instead they are supposed to automaticaly go out of scope when a series of staleness checks
  10289. # (this instance of StalenessChecker was created for) is finished.
  10290. @mtimes, @dependencies_stale, @parse_trees = {}, {}, {}
  10291. @options = Sass::Engine.normalize_options(options)
  10292. end
  10293. # Returns whether or not a given CSS file is out of date
  10294. # and needs to be regenerated.
  10295. #
  10296. # @param css_file [String] The location of the CSS file to check.
  10297. # @param template_file [String] The location of the Sass or SCSS template
  10298. # that is compiled to `css_file`.
  10299. # @return [Boolean] Whether the stylesheet needs to be updated.
  10300. def stylesheet_needs_update?(css_file, template_file, importer = nil)
  10301. template_file = File.expand_path(template_file)
  10302. begin
  10303. css_mtime = File.mtime(css_file)
  10304. rescue Errno::ENOENT
  10305. return true
  10306. end
  10307. stylesheet_modified_since?(template_file, css_mtime, importer)
  10308. end
  10309. # Returns whether a Sass or SCSS stylesheet has been modified since a given time.
  10310. #
  10311. # @param template_file [String] The location of the Sass or SCSS template.
  10312. # @param mtime [Fixnum] The modification time to check against.
  10313. # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
  10314. # Defaults to the filesystem importer.
  10315. # @return [Boolean] Whether the stylesheet has been modified.
  10316. def stylesheet_modified_since?(template_file, mtime, importer = nil)
  10317. importer ||= @options[:filesystem_importer].new(".")
  10318. dependency_updated?(mtime).call(template_file, importer)
  10319. end
  10320. # Returns whether or not a given CSS file is out of date
  10321. # and needs to be regenerated.
  10322. #
  10323. # The distinction between this method and the instance-level \{#stylesheet\_needs\_update?}
  10324. # is that the instance method preserves mtime and stale-dependency caches,
  10325. # so it's better to use when checking multiple stylesheets at once.
  10326. #
  10327. # @param css_file [String] The location of the CSS file to check.
  10328. # @param template_file [String] The location of the Sass or SCSS template
  10329. # that is compiled to `css_file`.
  10330. # @return [Boolean] Whether the stylesheet needs to be updated.
  10331. def self.stylesheet_needs_update?(css_file, template_file, importer = nil)
  10332. new(Plugin.engine_options).stylesheet_needs_update?(css_file, template_file, importer)
  10333. end
  10334. # Returns whether a Sass or SCSS stylesheet has been modified since a given time.
  10335. #
  10336. # The distinction between this method and the instance-level \{#stylesheet\_modified\_since?}
  10337. # is that the instance method preserves mtime and stale-dependency caches,
  10338. # so it's better to use when checking multiple stylesheets at once.
  10339. #
  10340. # @param template_file [String] The location of the Sass or SCSS template.
  10341. # @param mtime [Fixnum] The modification time to check against.
  10342. # @param importer [Sass::Importers::Base] The importer used to locate the stylesheet.
  10343. # Defaults to the filesystem importer.
  10344. # @return [Boolean] Whether the stylesheet has been modified.
  10345. def self.stylesheet_modified_since?(template_file, mtime, importer = nil)
  10346. new(Plugin.engine_options).stylesheet_modified_since?(template_file, mtime, importer)
  10347. end
  10348. private
  10349. def dependencies_stale?(uri, importer, css_mtime)
  10350. timestamps = @dependencies_stale[[uri, importer]] ||= {}
  10351. timestamps.each_pair do |checked_css_mtime, is_stale|
  10352. if checked_css_mtime <= css_mtime && !is_stale
  10353. return false
  10354. elsif checked_css_mtime > css_mtime && is_stale
  10355. return true
  10356. end
  10357. end
  10358. timestamps[css_mtime] = dependencies(uri, importer).any?(&dependency_updated?(css_mtime))
  10359. rescue Sass::SyntaxError
  10360. # If there's an error finding dependencies, default to recompiling.
  10361. true
  10362. end
  10363. def mtime(uri, importer)
  10364. @mtimes[[uri, importer]] ||=
  10365. begin
  10366. mtime = importer.mtime(uri, @options)
  10367. if mtime.nil?
  10368. @dependencies.delete([uri, importer])
  10369. nil
  10370. else
  10371. mtime
  10372. end
  10373. end
  10374. end
  10375. def dependencies(uri, importer)
  10376. stored_mtime, dependencies = @dependencies[[uri, importer]]
  10377. if !stored_mtime || stored_mtime < mtime(uri, importer)
  10378. dependencies = compute_dependencies(uri, importer)
  10379. @dependencies[[uri, importer]] = [mtime(uri, importer), dependencies]
  10380. end
  10381. dependencies
  10382. end
  10383. def dependency_updated?(css_mtime)
  10384. Proc.new do |uri, importer|
  10385. sass_mtime = mtime(uri, importer)
  10386. !sass_mtime ||
  10387. sass_mtime > css_mtime ||
  10388. dependencies_stale?(uri, importer, css_mtime)
  10389. end
  10390. end
  10391. def compute_dependencies(uri, importer)
  10392. tree(uri, importer).grep(Tree::ImportNode) do |n|
  10393. next if n.css_import?
  10394. file = n.imported_file
  10395. key = [file.options[:filename], file.options[:importer]]
  10396. @parse_trees[key] = file.to_tree
  10397. key
  10398. end.compact
  10399. end
  10400. def tree(uri, importer)
  10401. @parse_trees[[uri, importer]] ||= importer.find(uri, @options).to_tree
  10402. end
  10403. end
  10404. end
  10405. end
  10406. module Sass::Plugin
  10407. # The Compiler class handles compilation of multiple files and/or directories,
  10408. # including checking which CSS files are out-of-date and need to be updated
  10409. # and calling Sass to perform the compilation on those files.
  10410. #
  10411. # {Sass::Plugin} uses this class to update stylesheets for a single application.
  10412. # Unlike {Sass::Plugin}, though, the Compiler class has no global state,
  10413. # and so multiple instances may be created and used independently.
  10414. #
  10415. # If you need to compile a Sass string into CSS,
  10416. # please see the {Sass::Engine} class.
  10417. #
  10418. # Unlike {Sass::Plugin}, this class doesn't keep track of
  10419. # whether or how many times a stylesheet should be updated.
  10420. # Therefore, the following `Sass::Plugin` options are ignored by the Compiler:
  10421. #
  10422. # * `:never_update`
  10423. # * `:always_check`
  10424. class Compiler
  10425. include Sass::Util
  10426. include Configuration
  10427. extend Sass::Callbacks
  10428. # Creates a new compiler.
  10429. #
  10430. # @param options [{Symbol => Object}]
  10431. # See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
  10432. def initialize(options = {})
  10433. self.options.merge!(options)
  10434. end
  10435. # Register a callback to be run before stylesheets are mass-updated.
  10436. # This is run whenever \{#update\_stylesheets} is called,
  10437. # unless the \{file:SASS_REFERENCE.md#never_update-option `:never_update` option}
  10438. # is enabled.
  10439. #
  10440. # @yield [individual_files]
  10441. # @yieldparam individual_files [<(String, String)>]
  10442. # Individual files to be updated, in addition to the directories
  10443. # specified in the options.
  10444. # The first element of each pair is the source file,
  10445. # the second is the target CSS file.
  10446. define_callback :updating_stylesheets
  10447. # Register a callback to be run before a single stylesheet is updated.
  10448. # The callback is only run if the stylesheet is guaranteed to be updated;
  10449. # if the CSS file is fresh, this won't be run.
  10450. #
  10451. # Even if the \{file:SASS_REFERENCE.md#full_exception-option `:full_exception` option}
  10452. # is enabled, this callback won't be run
  10453. # when an exception CSS file is being written.
  10454. # To run an action for those files, use \{#on\_compilation\_error}.
  10455. #
  10456. # @yield [template, css]
  10457. # @yieldparam template [String]
  10458. # The location of the Sass/SCSS file being updated.
  10459. # @yieldparam css [String]
  10460. # The location of the CSS file being generated.
  10461. define_callback :updating_stylesheet
  10462. # Register a callback to be run when Sass decides not to update a stylesheet.
  10463. # In particular, the callback is run when Sass finds that
  10464. # the template file and none of its dependencies
  10465. # have been modified since the last compilation.
  10466. #
  10467. # Note that this is **not** run when the
  10468. # \{file:SASS_REFERENCE.md#never-update_option `:never_update` option} is set,
  10469. # nor when Sass decides not to compile a partial.
  10470. #
  10471. # @yield [template, css]
  10472. # @yieldparam template [String]
  10473. # The location of the Sass/SCSS file not being updated.
  10474. # @yieldparam css [String]
  10475. # The location of the CSS file not being generated.
  10476. define_callback :not_updating_stylesheet
  10477. # Register a callback to be run when there's an error
  10478. # compiling a Sass file.
  10479. # This could include not only errors in the Sass document,
  10480. # but also errors accessing the file at all.
  10481. #
  10482. # @yield [error, template, css]
  10483. # @yieldparam error [Exception] The exception that was raised.
  10484. # @yieldparam template [String]
  10485. # The location of the Sass/SCSS file being updated.
  10486. # @yieldparam css [String]
  10487. # The location of the CSS file being generated.
  10488. define_callback :compilation_error
  10489. # Register a callback to be run when Sass creates a directory
  10490. # into which to put CSS files.
  10491. #
  10492. # Note that even if multiple levels of directories need to be created,
  10493. # the callback may only be run once.
  10494. # For example, if "foo/" exists and "foo/bar/baz/" needs to be created,
  10495. # this may only be run for "foo/bar/baz/".
  10496. # This is not a guarantee, however;
  10497. # it may also be run for "foo/bar/".
  10498. #
  10499. # @yield [dirname]
  10500. # @yieldparam dirname [String]
  10501. # The location of the directory that was created.
  10502. define_callback :creating_directory
  10503. # Register a callback to be run when Sass detects
  10504. # that a template has been modified.
  10505. # This is only run when using \{#watch}.
  10506. #
  10507. # @yield [template]
  10508. # @yieldparam template [String]
  10509. # The location of the template that was modified.
  10510. define_callback :template_modified
  10511. # Register a callback to be run when Sass detects
  10512. # that a new template has been created.
  10513. # This is only run when using \{#watch}.
  10514. #
  10515. # @yield [template]
  10516. # @yieldparam template [String]
  10517. # The location of the template that was created.
  10518. define_callback :template_created
  10519. # Register a callback to be run when Sass detects
  10520. # that a template has been deleted.
  10521. # This is only run when using \{#watch}.
  10522. #
  10523. # @yield [template]
  10524. # @yieldparam template [String]
  10525. # The location of the template that was deleted.
  10526. define_callback :template_deleted
  10527. # Register a callback to be run when Sass deletes a CSS file.
  10528. # This happens when the corresponding Sass/SCSS file has been deleted.
  10529. #
  10530. # @yield [filename]
  10531. # @yieldparam filename [String]
  10532. # The location of the CSS file that was deleted.
  10533. define_callback :deleting_css
  10534. # Updates out-of-date stylesheets.
  10535. #
  10536. # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
  10537. # to see if it's been modified more recently than the corresponding CSS file
  10538. # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
  10539. # If it has, it updates the CSS file.
  10540. #
  10541. # @param individual_files [Array<(String, String)>]
  10542. # A list of files to check for updates
  10543. # **in addition to those specified by the
  10544. # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
  10545. # The first string in each pair is the location of the Sass/SCSS file,
  10546. # the second is the location of the CSS file that it should be compiled to.
  10547. def update_stylesheets(individual_files = [])
  10548. run_updating_stylesheets individual_files
  10549. Sass::Plugin.checked_for_updates = true
  10550. staleness_checker = StalenessChecker.new(engine_options)
  10551. individual_files.each do |t, c|
  10552. if options[:always_update] || staleness_checker.stylesheet_needs_update?(c, t)
  10553. update_stylesheet(t, c)
  10554. end
  10555. end
  10556. template_location_array.each do |template_location, css_location|
  10557. Dir.glob(File.join(template_location, "**", "[^_]*.s[ca]ss")).sort.each do |file|
  10558. # Get the relative path to the file
  10559. name = file.sub(template_location.to_s.sub(/\/*$/, '/'), "")
  10560. css = css_filename(name, css_location)
  10561. if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
  10562. update_stylesheet file, css
  10563. else
  10564. run_not_updating_stylesheet file, css
  10565. end
  10566. end
  10567. end
  10568. end
  10569. # Watches the template directory (or directories)
  10570. # and updates the CSS files whenever the related Sass/SCSS files change.
  10571. # `watch` never returns.
  10572. #
  10573. # Whenever a change is detected to a Sass/SCSS file in
  10574. # {file:SASS_REFERENCE.md#template_location-option `:template_location`},
  10575. # the corresponding CSS file in {file:SASS_REFERENCE.md#css_location-option `:css_location`}
  10576. # will be recompiled.
  10577. # The CSS files of any Sass/SCSS files that import the changed file will also be recompiled.
  10578. #
  10579. # Before the watching starts in earnest, `watch` calls \{#update\_stylesheets}.
  10580. #
  10581. # Note that `watch` uses the [FSSM](http://github.com/ttilley/fssm) library
  10582. # to monitor the filesystem for changes.
  10583. # FSSM isn't loaded until `watch` is run.
  10584. # The version of FSSM distributed with Sass is loaded by default,
  10585. # but if another version has already been loaded that will be used instead.
  10586. #
  10587. # @param individual_files [Array<(String, String)>]
  10588. # A list of files to watch for updates
  10589. # **in addition to those specified by the
  10590. # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
  10591. # The first string in each pair is the location of the Sass/SCSS file,
  10592. # the second is the location of the CSS file that it should be compiled to.
  10593. def watch(individual_files = [])
  10594. update_stylesheets(individual_files)
  10595. begin
  10596. require 'fssm'
  10597. rescue LoadError => e
  10598. dir = Sass::Util.scope("vendor/fssm/lib")
  10599. if $LOAD_PATH.include?(dir)
  10600. e.message << "\n" <<
  10601. if File.exists?(scope(".git"))
  10602. 'Run "git submodule update --init" to get the recommended version.'
  10603. else
  10604. 'Run "gem install fssm" to get it.'
  10605. end
  10606. raise e
  10607. else
  10608. $LOAD_PATH.unshift dir
  10609. retry
  10610. end
  10611. end
  10612. unless individual_files.empty? && FSSM::Backends::Default.name == "FSSM::Backends::FSEvents"
  10613. # As of FSSM 0.1.4, it doesn't support FSevents on individual files,
  10614. # but it also isn't smart enough to switch to polling itself.
  10615. require 'fssm/backends/polling'
  10616. Sass::Util.silence_warnings do
  10617. FSSM::Backends.const_set(:Default, FSSM::Backends::Polling)
  10618. end
  10619. end
  10620. # TODO: Keep better track of what depends on what
  10621. # so we don't have to run a global update every time anything changes.
  10622. FSSM.monitor do |mon|
  10623. template_location_array.each do |template_location, css_location|
  10624. mon.path template_location do |path|
  10625. path.glob '**/*.s[ac]ss'
  10626. path.update do |base, relative|
  10627. run_template_modified File.join(base, relative)
  10628. update_stylesheets(individual_files)
  10629. end
  10630. path.create do |base, relative|
  10631. run_template_created File.join(base, relative)
  10632. update_stylesheets(individual_files)
  10633. end
  10634. path.delete do |base, relative|
  10635. run_template_deleted File.join(base, relative)
  10636. css = File.join(css_location, relative.gsub(/\.s[ac]ss$/, '.css'))
  10637. try_delete_css css
  10638. update_stylesheets(individual_files)
  10639. end
  10640. end
  10641. end
  10642. individual_files.each do |template, css|
  10643. mon.file template do |path|
  10644. path.update do
  10645. run_template_modified template
  10646. update_stylesheets(individual_files)
  10647. end
  10648. path.create do
  10649. run_template_created template
  10650. update_stylesheets(individual_files)
  10651. end
  10652. path.delete do
  10653. run_template_deleted template
  10654. try_delete_css css
  10655. update_stylesheets(individual_files)
  10656. end
  10657. end
  10658. end
  10659. end
  10660. end
  10661. # Non-destructively modifies \{#options} so that default values are properly set,
  10662. # and returns the result.
  10663. #
  10664. # @param additional_options [{Symbol => Object}] An options hash with which to merge \{#options}
  10665. # @return [{Symbol => Object}] The modified options hash
  10666. def engine_options(additional_options = {})
  10667. opts = options.merge(additional_options)
  10668. opts[:load_paths] = load_paths(opts)
  10669. opts
  10670. end
  10671. # Compass expects this to exist
  10672. def stylesheet_needs_update?(css_file, template_file)
  10673. StalenessChecker.stylesheet_needs_update?(css_file, template_file)
  10674. end
  10675. private
  10676. def update_stylesheet(filename, css)
  10677. dir = File.dirname(css)
  10678. unless File.exists?(dir)
  10679. run_creating_directory dir
  10680. FileUtils.mkdir_p dir
  10681. end
  10682. begin
  10683. File.read(filename) unless File.readable?(filename) # triggers an error for handling
  10684. engine_opts = engine_options(:css_filename => css, :filename => filename)
  10685. result = Sass::Engine.for_file(filename, engine_opts).render
  10686. rescue Exception => e
  10687. run_compilation_error e, filename, css
  10688. result = Sass::SyntaxError.exception_to_css(e, options)
  10689. else
  10690. run_updating_stylesheet filename, css
  10691. end
  10692. # Finally, write the file
  10693. flag = 'w'
  10694. flag = 'wb' if Sass::Util.windows? && options[:unix_newlines]
  10695. File.open(css, flag) do |file|
  10696. file.set_encoding(result.encoding) unless Sass::Util.ruby1_8?
  10697. file.print(result)
  10698. end
  10699. end
  10700. def try_delete_css(css)
  10701. return unless File.exists?(css)
  10702. run_deleting_css css
  10703. File.delete css
  10704. end
  10705. def load_paths(opts = options)
  10706. (opts[:load_paths] || []) + template_locations
  10707. end
  10708. def template_locations
  10709. template_location_array.to_a.map {|l| l.first}
  10710. end
  10711. def css_locations
  10712. template_location_array.to_a.map {|l| l.last}
  10713. end
  10714. def css_filename(name, path)
  10715. "#{path}/#{name}".gsub(/\.s[ac]ss$/, '.css')
  10716. end
  10717. end
  10718. end
  10719. module Sass
  10720. # This module provides a single interface to the compilation of Sass/SCSS files
  10721. # for an application. It provides global options and checks whether CSS files
  10722. # need to be updated.
  10723. #
  10724. # This module is used as the primary interface with Sass
  10725. # when it's used as a plugin for various frameworks.
  10726. # All Rack-enabled frameworks are supported out of the box.
  10727. # The plugin is {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
  10728. # Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
  10729. #
  10730. # This module has a large set of callbacks available
  10731. # to allow users to run code (such as logging) when certain things happen.
  10732. # All callback methods are of the form `on_#{name}`,
  10733. # and they all take a block that's called when the given action occurs.
  10734. #
  10735. # Note that this class proxies almost all methods to its {Sass::Plugin::Compiler} instance.
  10736. # See \{#compiler}.
  10737. #
  10738. # @example Using a callback
  10739. # Sass::Plugin.on_updating_stylesheet do |template, css|
  10740. # puts "Compiling #{template} to #{css}"
  10741. # end
  10742. # Sass::Plugin.update_stylesheets
  10743. # #=> Compiling app/sass/screen.scss to public/stylesheets/screen.css
  10744. # #=> Compiling app/sass/print.scss to public/stylesheets/print.css
  10745. # #=> Compiling app/sass/ie.scss to public/stylesheets/ie.css
  10746. # @see Sass::Plugin::Compiler
  10747. module Plugin
  10748. include Sass::Util
  10749. extend self
  10750. @checked_for_updates = false
  10751. # Whether or not Sass has **ever** checked if the stylesheets need to be updated
  10752. # (in this Ruby instance).
  10753. #
  10754. # @return [Boolean]
  10755. attr_accessor :checked_for_updates
  10756. # Same as \{#update\_stylesheets}, but respects \{#checked\_for\_updates}
  10757. # and the {file:SASS_REFERENCE.md#always_update-option `:always_update`}
  10758. # and {file:SASS_REFERENCE.md#always_check-option `:always_check`} options.
  10759. #
  10760. # @see #update_stylesheets
  10761. def check_for_updates
  10762. return unless !Sass::Plugin.checked_for_updates ||
  10763. Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
  10764. update_stylesheets
  10765. end
  10766. # Returns the singleton compiler instance.
  10767. # This compiler has been pre-configured according
  10768. # to the plugin configuration.
  10769. #
  10770. # @return [Sass::Plugin::Compiler]
  10771. def compiler
  10772. @compiler ||= Compiler.new
  10773. end
  10774. # Updates out-of-date stylesheets.
  10775. #
  10776. # Checks each Sass/SCSS file in {file:SASS_REFERENCE.md#template_location-option `:template_location`}
  10777. # to see if it's been modified more recently than the corresponding CSS file
  10778. # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
  10779. # If it has, it updates the CSS file.
  10780. #
  10781. # @param individual_files [Array<(String, String)>]
  10782. # A list of files to check for updates
  10783. # **in addition to those specified by the
  10784. # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
  10785. # The first string in each pair is the location of the Sass/SCSS file,
  10786. # the second is the location of the CSS file that it should be compiled to.
  10787. def update_stylesheets(individual_files = [])
  10788. return if options[:never_update]
  10789. compiler.update_stylesheets(individual_files)
  10790. end
  10791. # Updates all stylesheets, even those that aren't out-of-date.
  10792. # Ignores the cache.
  10793. #
  10794. # @param individual_files [Array<(String, String)>]
  10795. # A list of files to check for updates
  10796. # **in addition to those specified by the
  10797. # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
  10798. # The first string in each pair is the location of the Sass/SCSS file,
  10799. # the second is the location of the CSS file that it should be compiled to.
  10800. # @see #update_stylesheets
  10801. def force_update_stylesheets(individual_files = [])
  10802. old_options = options
  10803. self.options = options.dup
  10804. options[:never_update] = false
  10805. options[:always_update] = true
  10806. options[:cache] = false
  10807. update_stylesheets(individual_files)
  10808. ensure
  10809. self.options = old_options
  10810. end
  10811. # All other method invocations are proxied to the \{#compiler}.
  10812. #
  10813. # @see #compiler
  10814. # @see Sass::Plugin::Compiler
  10815. def method_missing(method, *args, &block)
  10816. if compiler.respond_to?(method)
  10817. compiler.send(method, *args, &block)
  10818. else
  10819. super
  10820. end
  10821. end
  10822. # For parity with method_missing
  10823. def respond_to?(method)
  10824. super || compiler.respond_to?(method)
  10825. end
  10826. # There's a small speedup by not using method missing for frequently delegated methods.
  10827. def options
  10828. compiler.options
  10829. end
  10830. end
  10831. end
  10832. if defined?(ActionController)
  10833. unless defined?(Sass::RAILS_LOADED)
  10834. Sass::RAILS_LOADED = true
  10835. module Sass::Plugin::Configuration
  10836. # Different default options in a rails envirionment.
  10837. def default_options
  10838. return @default_options if @default_options
  10839. opts = {
  10840. :quiet => Sass::Util.rails_env != "production",
  10841. :full_exception => Sass::Util.rails_env != "production",
  10842. :cache_location => Sass::Util.rails_root + '/tmp/sass-cache'
  10843. }
  10844. opts.merge!(
  10845. :always_update => false,
  10846. :template_location => Sass::Util.rails_root + '/public/stylesheets/sass',
  10847. :css_location => Sass::Util.rails_root + '/public/stylesheets',
  10848. :always_check => Sass::Util.rails_env == "development")
  10849. @default_options = opts.freeze
  10850. end
  10851. end
  10852. Sass::Plugin.options.reverse_merge!(Sass::Plugin.default_options)
  10853. # Rails 3.1 loads and handles Sass all on its own
  10854. if defined?(ActionController::Metal)
  10855. # 3.1 > Rails >= 3.0
  10856. module Sass
  10857. module Plugin
  10858. # Rack middleware for compiling Sass code.
  10859. #
  10860. # ## Activate
  10861. #
  10862. # use Sass::Plugin::Rack
  10863. #
  10864. # ## Customize
  10865. #
  10866. # Sass::Plugin.options.merge(
  10867. # :cache_location => './tmp/sass-cache',
  10868. # :never_update => environment != :production,
  10869. # :full_exception => environment != :production)
  10870. #
  10871. # {file:SASS_REFERENCE.md#options See the Reference for more options}.
  10872. #
  10873. # ## Use
  10874. #
  10875. # Put your Sass files in `public/stylesheets/sass`.
  10876. # Your CSS will be generated in `public/stylesheets`,
  10877. # and regenerated every request if necessary.
  10878. # The locations and frequency {file:SASS_REFERENCE.md#options can be customized}.
  10879. # That's all there is to it!
  10880. class Rack
  10881. # The delay, in seconds, between update checks.
  10882. # Useful when many resources are requested for a single page.
  10883. # `nil` means no delay at all.
  10884. #
  10885. # @return [Float]
  10886. attr_accessor :dwell
  10887. # Initialize the middleware.
  10888. #
  10889. # @param app [#call] The Rack application
  10890. # @param dwell [Float] See \{#dwell}
  10891. def initialize(app, dwell = 1.0)
  10892. @app = app
  10893. @dwell = dwell
  10894. @check_after = Time.now.to_f
  10895. end
  10896. # Process a request, checking the Sass stylesheets for changes
  10897. # and updating them if necessary.
  10898. #
  10899. # @param env The Rack request environment
  10900. # @return [(#to_i, {String => String}, Object)] The Rack response
  10901. def call(env)
  10902. if @dwell.nil? || Time.now.to_f > @check_after
  10903. Sass::Plugin.check_for_updates
  10904. @check_after = Time.now.to_f + @dwell if @dwell
  10905. end
  10906. @app.call(env)
  10907. end
  10908. end
  10909. end
  10910. end
  10911. Rails.configuration.middleware.use(Sass::Plugin::Rack)
  10912. elsif defined?(ActionController::Dispatcher) &&
  10913. defined?(ActionController::Dispatcher.middleware)
  10914. # Rails >= 2.3
  10915. ActionController::Dispatcher.middleware.use(Sass::Plugin::Rack)
  10916. else
  10917. module ActionController
  10918. class Base
  10919. alias_method :sass_old_process, :process
  10920. def process(*args)
  10921. Sass::Plugin.check_for_updates
  10922. sass_old_process(*args)
  10923. end
  10924. end
  10925. end
  10926. end
  10927. end
  10928. elsif defined?(Merb::Plugins)
  10929. unless defined?(Sass::MERB_LOADED)
  10930. Sass::MERB_LOADED = true
  10931. module Sass::Plugin::Configuration
  10932. # Different default options in a m envirionment.
  10933. def default_options
  10934. @default_options ||= begin
  10935. version = Merb::VERSION.split('.').map { |n| n.to_i }
  10936. if version[0] <= 0 && version[1] < 5
  10937. root = MERB_ROOT
  10938. env = MERB_ENV
  10939. else
  10940. root = Merb.root.to_s
  10941. env = Merb.environment
  10942. end
  10943. {
  10944. :always_update => false,
  10945. :template_location => root + '/public/stylesheets/sass',
  10946. :css_location => root + '/public/stylesheets',
  10947. :cache_location => root + '/tmp/sass-cache',
  10948. :always_check => env != "production",
  10949. :quiet => env != "production",
  10950. :full_exception => env != "production"
  10951. }.freeze
  10952. end
  10953. end
  10954. end
  10955. config = Merb::Plugins.config[:sass] || Merb::Plugins.config["sass"] || {}
  10956. if defined? config.symbolize_keys!
  10957. config.symbolize_keys!
  10958. end
  10959. Sass::Plugin.options.merge!(config)
  10960. class Sass::Plugin::MerbBootLoader < Merb::BootLoader
  10961. after Merb::BootLoader::RackUpApplication
  10962. def self.run
  10963. # Apparently there's no better way than this to add Sass
  10964. # to Merb's Rack stack.
  10965. Merb::Config[:app] = Sass::Plugin::Rack.new(Merb::Config[:app])
  10966. end
  10967. end
  10968. end
  10969. else
  10970. # The reason some options are declared here rather than in sass/plugin/configuration.rb
  10971. # is that otherwise they'd clobber the Rails-specific options.
  10972. # Since Rails' options are lazy-loaded in Rails 3,
  10973. # they're reverse-merged with the default options
  10974. # so that user configuration is preserved.
  10975. # This means that defaults that differ from Rails'
  10976. # must be declared here.
  10977. unless defined?(Sass::GENERIC_LOADED)
  10978. Sass::GENERIC_LOADED = true
  10979. Sass::Plugin.options.merge!(:css_location => './public/stylesheets',
  10980. :always_update => false,
  10981. :always_check => true)
  10982. end
  10983. end
  10984. # Rails 3.0.0.beta.2+, < 3.1
  10985. if defined?(ActiveSupport) && Sass::Util.has?(:public_method, ActiveSupport, :on_load) &&
  10986. !Sass::Util.ap_geq?('3.1.0.beta')
  10987. ActiveSupport.on_load(:before_configuration) do
  10988. require 'sass'
  10989. end
  10990. end