1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Merge branch 'master' into extend

Conflicts:
	lib/sass/engine.rb
	lib/sass/scss/parser.rb
This commit is contained in:
Nathan Weizenbaum 2010-04-17 14:30:20 -07:00
commit 298862b0f3
42 changed files with 866 additions and 156 deletions

View file

@ -1 +1 @@
3.0.0.beta.1
3.0.0.beta.3

View file

@ -3,10 +3,20 @@
* Table of contents
{:toc}
## 3.0.0.beta.3
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.3).
There were no changes made to Haml between beta 2 and beta 3.
## 3.0.0.beta.2
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.2).
* Make {Haml::Helpers#capture_haml capture\_haml} faster when using `:ugly`.
* Add an `RDFa` doctype shortcut.
## 3.0.0.beta.1
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.1).
@ -263,13 +273,17 @@ Thanks to [Josh Peek](http://joshpeek.com/).
* The `puts` helper has been removed.
Use {Haml::Helpers#haml\_concat} instead.
## 2.2.23 (Unreleased)
## 2.2.23
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.23).
* Don't crash when `rake gems` is run in Rails with Haml installed.
Thanks to [Florian Frank](http://github.com/flori).
* Don't remove `\n` in filters with interpolation.
* Silence those annoying `"regexp match /.../n against to UTF-8 string"` warnings.
## 2.2.22
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.22).

View file

@ -771,6 +771,10 @@ the following doctypes are supported:
: XHTML Mobile 1.2<br/>
`<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">`
`!!! RDFa`
: XHTML+RDFa 1.0<br/>
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">`
When the [`:format`](#format) option is set to `:html4`,
the following doctypes are supported:

View file

@ -3,7 +3,75 @@
* Table of contents
{:toc}
## 3.0.0.beta.2 (Unreleased)
## 3.0.0.beta.4
### Bug Fixes
* Variables are now allowed as arguments to `url()`.
* `#{}` interpolation is now allowed within `url()`.
## 3.0.0.beta.3
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.3).
### `@import` in Sass
The Sass `@import` statement now allows non-CSS files to be specified with quotes,
for similarity with the SCSS syntax. For example, `@import "foo.sass"`
Will now import the `foo.sass` file, rather than compiling to `@import "foo.sass";`.
### Bug Fixes
* Make sure nested comments use the proper prefix when `sass-convert` outputs Sass.
* Allow comments to begin with ` *` in all cases.
## 3.0.0.beta.2
[Tagged on GitHub](http://github.com/nex3/haml/commit/3.0.0.beta.2).
### Stylesheet Updating Speed
Several caching layers were added to Sass's stylesheet updater.
This means that it should run significantly faster.
This benefit will be seen by people using Sass in development mode
with Rails, Rack, and Merb,
as well as people using `sass --watch` from the command line,
and to a lesser (but still significant) extent `sass --update`.
Thanks to [thedarkone](http://github.com/thedarkone).
### New Sass Functions for Introspection
Several new functions were added to make it easier to have
more flexible arguments to mixins and to enable deprecation
of obsolete APIs.
* {Sass::Script::Functions#type_of `type-of`} -- Returns the type of a value.
* {Sass::Script::Functions#unit `unit`} --
Returns the units associated with a number.
* {Sass::Script::Functions#unitless `unitless`} --
Returns whether a number has units or not.
* {Sass::Script::Functions#comparable `comparable`} --
Returns whether two numbers can be added or compared.
### `@warn`
A new directive `@warn` has been added that allows Sass libraries to emit warnings.
This can be used to issue deprecation warnings, discourage sloppy use of mixins, etc.
`@warn` takes a single argument: a SassScript expression that will be
displayed on the console along with a stylesheet trace for locating the warning.
For example:
@mixin blue-text {
@warn "The blue-text mixin is deprecated. Use new-blue-text instead.";
color: #00f;
}
Warnings may be silenced with the new `--quiet` command line option,
or the corresponding {file:SASS_REFERENCE.md#quiet-option `:quiey` Sass option}.
This option will also affect warnings printed by Sass itself.
Warnings are off by default in the Rails, Rack, and Merb production environments.
### `sass-convert`
@ -26,6 +94,23 @@ Note that since underscores may still be used in place of hyphens
when referring to mixins and variables,
this won't cause any backwards-incompatibilities.
#### Sass Comment Format
When converting to the indented syntax,
each line of comments past the first begins with either `//` (for Sass comments)
or ` *` (for CSS comments).
This prevents some whitespace errors.
### Minor Changes
Indented-syntax `/*` comments may now include `*` on lines beyond the first,
which will get stripped out of the output.
This means that the following will render nicely:
/* Foo
* Bar
* Baz
#### Bug Fixes
* Include `;` when rendering nested properties.
@ -53,11 +138,8 @@ this won't cause any backwards-incompatibilities.
* Prevent multiline CSS selectors from getting cut off
in certain rare cases.
### `@import` in Sass
The Sass `@import` statement now allows non-CSS files to be specified with quotes,
for similarity with the SCSS syntax. For example, `@import "foo.sass"`
Will now import the `foo.sass` file, rather than compiling to `@import "foo.sass";`.
* Improve the parsing of mixin argument lists.
This mostly means that error messages will be clearer.
### CSS Parsing
@ -567,7 +649,9 @@ and the hex representation (shortened to the three-letter version if possible).
and `tealbang(12)` now renders as `tealbang(12)`
rather than `teal bang(12)`.
## 2.2.23 (Unreleased)
## 2.2.23
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.23).
* Don't crash when `rake gems` is run in Rails with Sass installed.
Thanks to [Florian Frank](http://github.com/flori).
@ -579,6 +663,9 @@ and the hex representation (shortened to the three-letter version if possible).
{file:SASS_REFERENCE.md#full_exception `:full_exception option`} is enabled,
print the full exception rather than raising it.
* Fix a bug with a weird interaction with Haml, DataMapper, and Rails 3
that caused some tag helpers to go into infinite recursion.
## 2.2.22
[Tagged on GitHub](http://github.com/nex3/haml/commit/2.2.22).

View file

@ -287,6 +287,9 @@ Available options are:
Currently, this just means that strings in mixin arguments
are treated as though they were in [an `=` context](#sass-script-strings).
{#quiet-option} `:quiet`
: When set to true, causes warnings to be disabled.
## CSS Extensions
### Nested Rules
@ -898,8 +901,7 @@ and `_colors.scss` would be imported.
The `@debug` directive prints the value of a SassScript expression
to the standard error output stream.
It's useful for debugging Sass files
that have complicated SassScript going on,
or for printing warnings in libraries.
that have complicated SassScript going on.
For example:
@debug 10em + 12em;
@ -908,6 +910,33 @@ outputs:
Line 1 DEBUG: 22em
### `@warn`
The `@warn` directive prints the value of a SassScript expression
to the standard error output stream.
It's useful for libraries that need to warn users of deprecations
or recovering from minor mixin usage mistakes.
There are two major distinctions between `@warn` and `@debug`:
1. You can turn warnings off with the `--quiet` command-line option
or the `:quiet` Sass option.
2. A stylesheet trace will be printed out along with the message
so that the user being warned can see where their styles caused the warning.
Usage Example:
@mixin adjust-location($x, $y) {
@if unitless($x) {
@warn "Assuming #{$x} to be in pixels";
$x: 1px * $x;
}
@if unitless($y) {
@warn "Assuming #{$y} to be in pixels";
$y: 1px * $y;
}
position: relative; left: $x; top: $y;
}
## Control Directives
SassScript supports basic control directives

View file

@ -276,6 +276,9 @@ END
'Output style. Can be nested (default), compact, compressed, or expanded.') do |name|
@options[:for_engine][:style] = name.to_sym
end
opts.on('-q', '--quiet', 'Silence warnings during compilation.') do
@options[:for_engine][:quiet] = true
end
opts.on('-g', '--debug-info',
'Emit extra information in the generated CSS that can be used by the FireSass Firebug plugin.') do
@options[:for_engine][:debug_info] = true
@ -592,6 +595,9 @@ END
'By default, this is inferred from the input filename.',
'If there is none, defaults to css.') do |name|
@options[:from] = name.downcase.to_sym
unless [:css, :scss, :sass, :sass2].include?(@options[:from])
raise "Unknown format for sass-convert --from: #{name}"
end
end
opts.on('-T', '--to FORMAT',
@ -599,6 +605,9 @@ END
'By default, this is inferred from the output filename.',
'If there is none, defaults to sass.') do |name|
@options[:to] = name.downcase.to_sym
unless [:scss, :sass].include?(@options[:to])
raise "Unknown format for sass-convert --to: #{name}"
end
end
opts.on('-R', '--recursive',

View file

@ -513,7 +513,7 @@ MESSAGE
# @param text [String] The string to sanitize
# @return [String] The sanitized string
def html_escape(text)
text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}
Haml::Util.silence_warnings {text.to_s.gsub(/[\"><&]/n) {|s| HTML_ESCAPE[s]}}
end
# Escapes HTML entities in `text`, but without escaping an ampersand

View file

@ -125,9 +125,8 @@ module ActionView
@template_object.send :is_haml?
end
unless defined?(ActionView::Helpers) && defined?(ActionView::Helpers::ActiveRecordInstanceTag)
alias_method :content_tag_without_haml, :content_tag
alias_method :content_tag, :content_tag_with_haml
def content_tag(*args)
content_tag_with_haml(*args)
end
end

View file

@ -905,6 +905,7 @@ END
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
end

View file

@ -15,7 +15,7 @@ module Sass
extend Haml::Version
# A string representing the version of Sass.
# A more fine-grained representation is available from {Sass.version}.
# A more fine-grained representation is available from {Haml::Version#version Sass.version}.
VERSION = version[:string] unless defined?(Sass::VERSION)
end

View file

@ -14,6 +14,7 @@ require 'sass/tree/if_node'
require 'sass/tree/while_node'
require 'sass/tree/for_node'
require 'sass/tree/debug_node'
require 'sass/tree/warn_node'
require 'sass/tree/import_node'
require 'sass/selector'
require 'sass/environment'
@ -166,9 +167,9 @@ module Sass
# @return [String] The CSS
# @raise [Sass::SyntaxError] if there's an error in the document
def render
to_tree.render
return _to_tree.render unless @options[:quiet]
Haml::Util.silence_haml_warnings {_to_tree.render}
end
alias_method :to_css, :render
# Parses the document into its parse tree.
@ -176,6 +177,13 @@ module Sass
# @return [Sass::Tree::Node] The root of the parse tree.
# @raise [Sass::SyntaxError] if there's an error in the document
def to_tree
return _to_tree unless @options[:quiet]
Haml::Util.silence_haml_warnings {_to_tree}
end
private
def _to_tree
@template = check_encoding(@template) {|msg, line| raise Sass::SyntaxError.new(msg, :line => line)}
if @options[:syntax] == :scss
@ -193,8 +201,6 @@ module Sass
raise e
end
private
def tabulate(string)
tab_str = nil
comment_tab_str = nil
@ -230,7 +236,7 @@ module Sass
end
comment_tab_str ||= line_tab_str
if try_comment(line, lines.last, tab_str * (lines.last.tabs + 1), comment_tab_str, index)
if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
next
else
comment_tab_str = nil
@ -252,7 +258,9 @@ END
def try_comment(line, last, tab_str, comment_tab_str, index)
return unless last && last.comment?
return unless line =~ /^#{tab_str}/
# Nested comment stuff must be at least one whitespace char deeper
# than the normal indentation
return unless line =~ /^#{tab_str}\s/
unless line =~ /^(?:#{comment_tab_str})(.*)$/
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
Inconsistent indentation:
@ -463,9 +471,13 @@ WARNING
# If value begins with url( or ",
# it's a CSS @import rule and we don't want to touch it.
if directive == "import" && value !~ /^(url\(|["'])/
if directive == "import"
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
:line => @line + 1) unless line.children.empty?
if (match = value.match(Sass::SCSS::RX::STRING) || value.match(Sass::SCSS::RX::URI)) &&
!match.post_match.strip.empty? && match.post_match.strip[0] != ?,
return Tree::DirectiveNode.new("@import #{value}")
end
value.split(/,\s*/).map do |f|
f = $1 || $2 || $3 if f =~ Sass::SCSS::RX::STRING || f =~ Sass::SCSS::RX::URI
Tree::ImportNode.new(f)
@ -496,6 +508,12 @@ WARNING
:line => @line + 1) unless line.children.empty?
offset = line.offset + line.text.index(value).to_i
Tree::ExtendNode.new(parse_interp(value, offset))
elsif directive == "warn"
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
:line => @line + 1) unless line.children.empty?
offset = line.offset + line.text.index(value).to_i
Tree::WarnNode.new(parse_script(value, :offset => offset))
else
Tree::DirectiveNode.new(line.text)
end
@ -584,6 +602,7 @@ WARNING
end
return silent ? "//" : "/* */" if content.empty?
content.each {|l| l.gsub!(/^\* /, '')}
content.map! {|l| (l.empty? ? "" : " ") + l}
content.first.gsub!(/^ /, '') unless removed_first
content.last.gsub!(%r{ ?\*/ *$}, '')

View file

@ -23,7 +23,7 @@ module Sass
@vars = {}
@mixins = {}
@parent = parent
@stack = [] unless parent
set_var("important", Script::String.new("!important")) unless @parent
end
@ -35,6 +35,52 @@ module Sass
@options || (parent && parent.options) || {}
end
# Push a new stack frame onto the mixin/include stack.
#
# @param frame_info [{Symbol => Object}]
# Frame information has the following keys:
#
# `:filename`
# : The name of the file in which the lexical scope changed.
#
# `:mixin`
# : The name of the mixin in which the lexical scope changed,
# or `nil` if it wasn't within in a mixin.
#
# `:line`
# : The line of the file on which the lexical scope changed. Never nil.
def push_frame(frame_info)
if stack.last && stack.last[:prepared]
stack.last.delete(:prepared)
stack.last.merge!(frame_info)
else
stack.push(frame_info)
end
end
# Like \{#push\_frame}, but next time a stack frame is pushed,
# it will be merged with this frame.
#
# @param frame_info [{Symbol => Object}] Same as for \{#push\_frame}.
def prepare_frame(frame_info)
push_frame(frame_info.merge(:prepared => true))
end
# Pop a stack frame from the mixin/include stack.
def pop_frame
stack.pop if stack.last[:prepared]
stack.pop
end
# A list of stack frames in the mixin/include stack.
# The last element in the list is the most deeply-nested frame.
#
# @return [Array<{Symbol => Object}>] The stack frames,
# of the form passed to \{#push\_frame}.
def stack
@stack ||= @parent.stack
end
class << self
private

View file

@ -76,8 +76,11 @@ module Sass
return filename
end
new_filename = find_full_path("#{filename}.sass", load_paths) unless was_scss
new_filename ||= find_full_path("#{filename}.scss", load_paths) unless was_sass
new_filename = nil
load_paths.each do |load_path|
new_filename ||= find_full_path("#{filename}.sass", load_path) unless was_scss
new_filename ||= find_full_path("#{filename}.scss", load_path) unless was_sass
end
return new_filename if new_filename
unless was_sass || was_scss
@ -134,7 +137,7 @@ END
end
end
def find_full_path(filename, load_paths)
def find_full_path(filename, load_path)
partial_name = File.join(File.dirname(filename), "_#{File.basename(filename)}")
if Pathname.new(filename).absolute?
@ -144,13 +147,9 @@ END
return nil
end
load_paths.each do |path|
[partial_name, filename].each do |name|
full_path = File.join(path, name)
if File.readable?(full_path)
return full_path
end
end
[partial_name, filename].each do |name|
full_path = File.join(load_path, name)
return full_path if File.readable?(full_path)
end
nil
end

View file

@ -3,6 +3,7 @@ require 'rbconfig'
require 'sass'
require 'sass/callbacks'
require 'sass/plugin/staleness_checker'
module Sass
# This module handles the compilation of Sass/SCSS files.
@ -211,6 +212,8 @@ module Sass
individual_files.each {|t, c| update_stylesheet(t, c)}
@checked_for_updates = true
staleness_checker = StalenessChecker.new
template_locations.zip(css_locations).each do |template_location, css_location|
Dir.glob(File.join(template_location, "**", "*.s[ca]ss")).each do |file|
@ -219,7 +222,7 @@ module Sass
css = css_filename(name, css_location)
next if forbid_update?(name)
if options[:always_update] || stylesheet_needs_update?(css, file)
if options[:always_update] || staleness_checker.stylesheet_needs_update?(css, file)
update_stylesheet file, css
else
run_not_updating_stylesheet file, css
@ -374,33 +377,9 @@ module Sass
name.sub(/^.*\//, '')[0] == ?_
end
# Compass expects this to exist
def stylesheet_needs_update?(css_file, template_file)
return true unless File.exists?(css_file) && File.exists?(template_file)
css_mtime = File.mtime(css_file)
File.mtime(template_file) > css_mtime ||
dependencies(template_file).any?(&dependency_updated?(css_mtime))
end
def dependency_updated?(css_mtime)
lambda do |dep|
begin
File.mtime(dep) > css_mtime ||
dependencies(dep).any?(&dependency_updated?(css_mtime))
rescue Sass::SyntaxError
# If there's an error finding depenencies, default to recompiling.
true
end
end
end
def dependencies(filename)
Files.tree_for(filename, engine_options).select {|n| n.is_a?(Tree::ImportNode)}.map do |n|
next if n.full_filename =~ /\.css$/
n.full_filename
end.compact
rescue Sass::SyntaxError => e
[] # If the file has an error, we assume it has no dependencies
StalenessChecker.stylesheet_needs_update?(css_file, template_file)
end
end
end

View file

@ -14,6 +14,7 @@ unless defined?(Sass::MERB_LOADED)
:css_location => root + '/public/stylesheets',
:cache_location => root + '/tmp/sass-cache',
:always_check => env != "production",
:quiet => env != "production",
:full_exception => env != "production")
config = Merb::Plugins.config[:sass] || Merb::Plugins.config["sass"] || {}

View file

@ -5,6 +5,7 @@ unless defined?(Sass::RAILS_LOADED)
:css_location => Haml::Util.rails_root + '/public/stylesheets',
:cache_location => Haml::Util.rails_root + '/tmp/sass-cache',
:always_check => Haml::Util.rails_env != "production",
:quiet => Haml::Util.rails_env != "production",
:full_exception => Haml::Util.rails_env != "production")
if defined?(Rails.configuration) && defined?(Rails.configuration.middleware)

View file

@ -0,0 +1,123 @@
module Sass
module Plugin
# The class handles `.s[ca]ss` file staleness checks via their mtime timestamps.
#
# To speed things up two level of caches are employed:
#
# * A class-level dependency cache which stores @import paths for each file.
# This is a long-lived cache that is reused by every StalenessChecker instance.
# * Two short-lived instance-level caches, one for file mtimes
# and one for whether a file is stale during this particular run.
# These are only used by a single StalenessChecker instance.
#
# Usage:
#
# * For a one-off staleness check of a single `.s[ca]ss` file,
# the class-level {stylesheet_needs_update?} method
# should be used.
# * For a series of staleness checks (e.g. checking all files for staleness)
# a StalenessChecker instance should be created,
# and the instance-level \{#stylesheet\_needs\_update?} method should be used.
# the caches should make the whole process significantly faster.
# *WARNING*: It is important not to retain the instance for too long,
# as its instance-level caches are never explicitly expired.
class StalenessChecker
@dependencies_cache = {}
class << self
# @private
attr_accessor :dependencies_cache
end
# Creates a new StalenessChecker
# for checking the staleness of several stylesheets at once.
def initialize
@dependencies = self.class.dependencies_cache
# Entries in the following instance-level caches are never explicitly expired.
# Instead they are supposed to automaticaly go out of scope when a series of staleness checks
# (this instance of StalenessChecker was created for) is finished.
@mtimes, @dependencies_stale = {}, {}
end
# Returns whether or not a given CSS file is out of date
# and needs to be regenerated.
#
# @param css_file [String] The location of the CSS file to check.
# @param template_file [String] The location of the Sass or SCSS template
# that is compiled to `css_file`.
def stylesheet_needs_update?(css_file, template_file)
template_file = File.expand_path(template_file)
unless File.exists?(css_file) && File.exists?(template_file)
@dependencies.delete(template_file)
true
else
css_mtime = mtime(css_file)
mtime(template_file) > css_mtime || dependencies_stale?(template_file, css_mtime)
end
end
# Returns whether or not a given CSS file is out of date
# and needs to be regenerated.
#
# The distinction between this method and the instance-level \{#stylesheet\_needs\_update?}
# is that the instance method preserves mtime and stale-dependency caches,
# so it's better to use when checking multiple stylesheets at once.
#
# @param css_file [String] The location of the CSS file to check.
# @param template_file [String] The location of the Sass or SCSS template
# that is compiled to `css_file`.
def self.stylesheet_needs_update?(css_file, template_file)
new.stylesheet_needs_update?(css_file, template_file)
end
private
def dependencies_stale?(template_file, css_mtime)
timestamps = @dependencies_stale[template_file] ||= {}
timestamps.each_pair do |checked_css_mtime, is_stale|
if checked_css_mtime <= css_mtime && !is_stale
return false
elsif checked_css_mtime > css_mtime && is_stale
return true
end
end
timestamps[css_mtime] = dependencies(template_file).any?(&dependency_updated?(css_mtime))
end
def mtime(filename)
@mtimes[filename] ||= File.mtime(filename)
end
def dependencies(filename)
stored_mtime, dependencies = @dependencies[filename]
if !stored_mtime || stored_mtime < mtime(filename)
@dependencies[filename] = [mtime(filename), dependencies = compute_dependencies(filename)]
end
dependencies
end
def dependency_updated?(css_mtime)
lambda do |dep|
begin
mtime(dep) > css_mtime || dependencies_stale?(dep, css_mtime)
rescue Sass::SyntaxError
# If there's an error finding depenencies, default to recompiling.
true
end
end
end
def compute_dependencies(filename)
Files.tree_for(filename, Plugin.engine_options).grep(Tree::ImportNode) do |n|
File.expand_path(n.full_filename) unless n.full_filename =~ /\.css$/
end.compact
rescue Sass::SyntaxError => e
[] # If the file has an error, we assume it has no dependencies
end
end
end
end

View file

@ -5,7 +5,12 @@ module Sass
important || super
end
def string(*args)
def string(re, *args)
if re == :uri
return unless uri = scan(URI)
return [:string, Script::String.new(uri)]
end
return unless scan(STRING)
[:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
end

View file

@ -105,6 +105,20 @@ module Sass::Script
# \{#abs}
# : Returns the absolute value of a number.
#
# ## Introspection Functions
#
# \{#type_of}
# : Returns the type of a value.
#
# \{#unit}
# : Returns the units associated with a number.
#
# \{#unitless}
# : Returns whether a number has units or not.
#
# \{#comparable}
# : Returns whether two numbers can be added or compared.
#
# These functions are described in more detail below.
#
# ## Adding Custom Functions
@ -669,6 +683,71 @@ module Sass::Script
Sass::Script::String.new(str.value, :string)
end
# Inspects the type of the argument, returning it as an unquoted string.
# For example:
#
# type-of(100px) => number
# type-of(asdf) => string
# type-of("asdf") => string
# type-of(true) => bool
# type-of(#fff) => color
# type-of(blue) => color
#
# @param obj [Literal] The object to inspect
# @return [String] The unquoted string name of the literal's type
def type_of(obj)
Sass::Script::String.new(obj.class.name.gsub(/Sass::Script::/,'').downcase)
end
# Inspects the unit of the number, returning it as a quoted string.
# Complex units are sorted in alphabetical order by numerator and denominator.
# For example:
#
# unit(100) => ""
# unit(100px) => "px"
# unit(3em) => "em"
# unit(10px * 5em) => "em*px"
# unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
#
# @param number [Literal] The number to inspect
# @return [String] The unit(s) of the number
# @raise [ArgumentError] if `number` isn't a number
def unit(number)
assert_type number, :Number
Sass::Script::String.new(number.unit_str, :string)
end
# Inspects the unit of the number, returning a boolean indicating if it is unitless.
# For example:
#
# unitless(100) => true
# unitless(100px) => false
#
# @param number [Literal] The number to inspect
# @return [Bool] Whether or not the number is unitless
# @raise [ArgumentError] if `number` isn't a number
def unitless(number)
assert_type number, :Number
Sass::Script::Bool.new(number.unitless?)
end
# Returns true if two numbers are similar enough to be added, subtracted, or compared.
# For example:
#
# comparable(2px, 1px) => true
# comparable(100px, 3em) => false
# comparable(10cm, 3mm) => true
#
# @param number1 [Number]
# @param number2 [Number]
# @return [Bool] indicating if the numbers can be compared.
# @raise [ArgumentError] if `number1` or `number2` aren't numbers
def comparable(number1, number2)
assert_type number1, :Number
assert_type number2, :Number
Sass::Script::Bool.new(number1.comparable_to?(number2))
end
# Converts a decimal number to a percentage.
# For example:
#

View file

@ -111,7 +111,7 @@ module Sass
# A hash of regular expressions that are used for tokenizing strings.
#
# The key is a [Symbol, Boolean] pair.
# The key is a `[Symbol, Boolean]` pair.
# The symbol represents which style of quotation to use,
# while the boolean represents whether or not the string
# is following an interpolated segment.
@ -120,6 +120,8 @@ module Sass
[:single, false] => string_re("'", "'"),
[:double, true] => string_re('', '"'),
[:single, true] => string_re('', "'"),
[:uri, false] => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
[:uri, true] => /(#{URLCHAR}*?)(#{W}\)|(?=#\{))/,
}
# @param str [String, StringScanner] The source text to lex
@ -216,8 +218,8 @@ module Sass
end
variable || string(:double, false) || string(:single, false) || number ||
color || bool || raw(URI) || raw(UNICODERANGE) || special_fun ||
ident_op || ident || op
color || bool || string(:uri, false) || raw(UNICODERANGE) ||
special_fun || ident_op || ident || op
end
def variable
@ -243,7 +245,13 @@ module Sass
def string(re, open)
return unless scan(STRING_REGULAR_EXPRESSIONS[[re, open]])
@interpolation_stack << re if @scanner[2].empty? # Started an interpolated section
[:string, Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)]
str =
if re == :uri
Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2].empty?}")
else
Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
end
[:string, str]
end
def number

View file

@ -299,6 +299,30 @@ module Sass::Script
end, num_units, den_units)
end
# @param other [Number] A number to decide if it can be compared with this number.
# @return [Boolean] Whether or not this number can be compared with the other.
def comparable_to?(other)
begin
self.operate(other, :+)
true
rescue Sass::UnitConversionError
false
end
end
# Returns a human readable representation of the units in this number.
# For complex units this takes the form of:
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
# @return [String] a string that represents the units in this number
def unit_str
rv = numerator_units.sort.join("*")
if denominator_units.any?
rv << "/"
rv << denominator_units.sort.join("*")
end
rv
end
protected
def operate(other, operation)
@ -343,15 +367,6 @@ module Sass::Script
end
end
def unit_str
rv = numerator_units.join("*")
if denominator_units.any?
rv << "/"
rv << denominator_units.join("*")
end
rv
end
def normalize!
return if unitless?
@numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units)

View file

@ -97,12 +97,7 @@ module Sass
# @return [Array<Script::Node>] The root nodes of the arguments.
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
def parse_mixin_definition_arglist
args = []
if try_tok(:lparen)
args = defn_arglist(false) || args
assert_tok(:rparen)
end
args = defn_arglist!(false)
assert_done
args.each do |k, v|
@ -239,26 +234,33 @@ RUBY
end
end
def defn_arglist(must_have_default)
line = @lexer.line
offset = @lexer.offset + 1
return unless c = try_tok(:const)
var = Script::Variable.new(c.value)
if tok = (try_tok(:colon) || try_tok(:single_eq))
val = assert_expr(:concat)
def defn_arglist!(must_have_default)
return [] unless try_tok(:lparen)
return [] if try_tok(:rparen)
res = []
loop do
line = @lexer.line
offset = @lexer.offset + 1
c = assert_tok(:const)
var = Script::Variable.new(c.value)
if tok = (try_tok(:colon) || try_tok(:single_eq))
val = assert_expr(:concat)
if tok.type == :single_eq
val.context = :equals
val.options = @options
Script.equals_warning("mixin argument defaults", "$#{c.value}",
val.to_sass, false, line, offset, @options[:filename])
if tok.type == :single_eq
val.context = :equals
val.options = @options
Script.equals_warning("mixin argument defaults", "$#{c.value}",
val.to_sass, false, line, offset, @options[:filename])
end
must_have_default = true
elsif must_have_default
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
end
elsif must_have_default
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
res << [var, val]
break unless try_tok(:comma)
end
return [[var, val]] unless try_tok(:comma)
[[var, val], *defn_arglist(val)]
assert_tok(:rparen)
res
end
def fn_arglist

View file

@ -81,7 +81,7 @@ module Sass
node << comment
end
DIRECTIVES = Set[:mixin, :include, :debug, :for, :while, :if, :extend, :import, :media]
DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :extend, :import, :media]
def directive
return unless tok(/@/)
@ -131,6 +131,10 @@ module Sass
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
end
def warn
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
end
def for
tok!(/\$/)
var = tok! IDENT

View file

@ -65,7 +65,8 @@ module Sass
NAME = /#{NMCHAR}+/
NUM = /[0-9]+|[0-9]*.[0-9]+/
STRING = /#{STRING1}|#{STRING2}/
URL = /((?:[!#%$&*-~]|#{NONASCII}|#{ESCAPE})*)/
URLCHAR = /[!#%&*-~]|#{NONASCII}|#{ESCAPE}/
URL = /(#{URLCHAR}*)/
W = /[ \t\r\n\f]*/
# This is more liberal than the spec's definition,

View file

@ -59,15 +59,16 @@ module Sass::Tree
else
content.gsub!(/\n( \*|\/\/)/, "\n ")
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
sep = silent ? "\n//" : "\n *"
if spaces >= 2
content
content.gsub(/\n /, sep)
else
content.gsub(/\n#{' ' * spaces}/, "\n ")
content.gsub(/\n#{' ' * spaces}/, sep)
end
end
content.gsub!(/^/, ' ' * tabs)
content.gsub!(/\A\/\*/, '//') if silent
content.gsub!(/^/, ' ' * tabs)
content.rstrip + "\n"
end

View file

@ -13,7 +13,7 @@ module Sass
protected
def to_src(tabs, opts, fmt)
"#{' ' * tabs}@debug #{@expr.to_sass}#{semi fmt}\n"
"#{' ' * tabs}@debug #{@expr.to_sass(opts)}#{semi fmt}\n"
end
# Prints the expression to STDERR.

View file

@ -62,6 +62,7 @@ module Sass
# @param environment [Sass::Environment] The lexical environment containing
# variable and mixin values
def perform!(environment)
environment.push_frame(:filename => @filename, :line => @line)
root = Sass::Files.tree_for(full_filename, @options)
@template = root.template
self.children = root.children
@ -70,6 +71,8 @@ module Sass
e.modify_backtrace(:filename => full_filename)
e.add_backtrace(:filename => @filename, :line => @line)
raise e
ensure
environment.pop_frame
end
private

View file

@ -65,13 +65,15 @@ module Sass::Tree
# @raise [Sass::SyntaxError] if an incorrect number of arguments was passed
# @see Sass::Tree
def perform!(environment)
original_env = environment
original_env.push_frame(:filename => filename, :line => line)
original_env.prepare_frame(:mixin => @name)
raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < @args.size
Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
but #{@args.size} #{@args.size == 1 ? 'was' : 'were'} passed.
END
environment = mixin.args.zip(@args).
inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
env.set_local_var(var.name,
@ -93,6 +95,8 @@ END
e.modify_backtrace(:mixin => @name, :line => @line)
e.add_backtrace(:line => @line)
raise e
ensure
original_env.pop_frame
end
end
end

View file

@ -0,0 +1,41 @@
module Sass
module Tree
# A dynamic node representing a Sass `@warn` statement.
#
# @see Sass::Tree
class WarnNode < Node
# @param expr [Script::Node] The expression to print
def initialize(expr)
@expr = expr
super()
end
protected
def to_src(tabs, opts, fmt)
"#{' ' * tabs}@warn #{@expr.to_sass(opts)}#{semi fmt}\n"
end
# Prints the expression to STDERR with a stylesheet trace.
#
# @param environment [Sass::Environment] The lexical environment containing
# variable and mixin values
def _perform(environment)
environment.push_frame(:filename => filename, :line => line)
res = @expr.perform(environment)
res = res.value if res.is_a?(Sass::Script::String)
msg = "WARNING: #{res}\n"
environment.stack.reverse.each_with_index do |entry, i|
msg << " #{i == 0 ? "on" : "from"} line #{entry[:line]}" <<
" of #{entry[:filename] || "an unknown file"}"
msg << ", in `#{entry[:mixin]}'" if entry[:mixin]
msg << "\n"
end
Haml::Util.haml_warn msg
[]
ensure
environment.pop_frame
end
end
end
end

View file

@ -251,9 +251,9 @@ SCSS
assert_renders <<SASS, <<SCSS
// foo
bar
baz
bang
// bar
// baz
// bang
foo bar
a: b
@ -286,6 +286,50 @@ foo bar
SASS
end
def test_nested_silent_comments
assert_renders <<SASS, <<SCSS
foo
bar: baz
// bip bop
// beep boop
bang: bizz
// bubble bubble
// toil trouble
SASS
foo {
bar: baz;
// bip bop
// beep boop
bang: bizz;
// bubble bubble
// toil trouble
}
SCSS
assert_sass_to_scss <<SCSS, <<SASS
foo {
bar: baz;
// bip bop
// beep boop
// bap blimp
bang: bizz;
// bubble bubble
// toil trouble
// gorp
}
SCSS
foo
bar: baz
// bip bop
beep boop
bap blimp
bang: bizz
// bubble bubble
toil trouble
gorp
SASS
end
def test_loud_comments
assert_renders <<SASS, <<SCSS
/* foo
@ -309,9 +353,9 @@ SCSS
assert_scss_to_sass <<SASS, <<SCSS
/* foo
bar
baz
bang
* bar
* baz
* bang
foo bar
a: b
@ -337,9 +381,9 @@ SCSS
assert_renders <<SASS, <<SCSS
/* foo
bar
baz
bang
* bar
* baz
* bang
foo bar
a: b
@ -354,12 +398,54 @@ foo bar {
SCSS
end
def test_nested_loud_comments
assert_renders <<SASS, <<SCSS
foo
bar: baz
/* bip bop
* beep boop
bang: bizz
/* bubble bubble
* toil trouble
SASS
foo {
bar: baz;
/* bip bop
* beep boop */
bang: bizz;
/* bubble bubble
* toil trouble */ }
SCSS
assert_sass_to_scss <<SCSS, <<SASS
foo {
bar: baz;
/* bip bop
* beep boop
* bap blimp */
bang: bizz;
/* bubble bubble
* toil trouble
* gorp */ }
SCSS
foo
bar: baz
/* bip bop
beep boop
bap blimp
bang: bizz
/* bubble bubble
toil trouble
gorp
SASS
end
def test_loud_comments_with_weird_indentation
assert_scss_to_sass <<SASS, <<SCSS
foo
/* foo
bar
baz
* bar
* baz
a: b
SASS
foo {
@ -387,8 +473,8 @@ SASS
def test_immediately_preceding_comments
assert_renders <<SASS, <<SCSS
/* Foo
Bar
Baz
* Bar
* Baz
.foo#bar
a: b
SASS
@ -401,8 +487,8 @@ SCSS
assert_renders <<SASS, <<SCSS
// Foo
Bar
Baz
// Bar
// Baz
=foo
a: b
SASS
@ -595,8 +681,8 @@ SCSS
end
def test_import_as_directive_in_sass
assert_sass_to_sass '@import "foo.css"'
assert_sass_to_sass '@import url(foo.css)'
assert_equal "@import foo.css\n", to_sass('@import "foo.css"')
assert_equal "@import foo.css\n", to_sass('@import url(foo.css)')
end
def test_import_as_directive_in_scss

View file

@ -105,20 +105,20 @@ elephant.rawr
rampages: excessively
/* actual multiline
comment
*comment
span.turkey
isdinner: true
.turducken
/* Sounds funny
doesn't it
* doesn't it
chimera: not_really
#overhere
bored: sorta
/* it's for a good
cause
* cause
better_than: thread_pools
#one_more

View file

@ -81,10 +81,10 @@ MSG
"a\n b: c\na\n d: e" => ["The line was indented 2 levels deeper than the previous line.", 4],
"a\n b: c\n a\n d: e" => ["The line was indented 3 levels deeper than the previous line.", 4],
"a\n \tb: c" => ["Indentation can't use both tabs and spaces.", 2],
"=a(" => 'Invalid CSS after "(": expected ")", was ""',
"=a(b)" => 'Invalid CSS after "(": expected ")", was "b)"',
"=a(,)" => 'Invalid CSS after "(": expected ")", was ",)"',
"=a(!)" => 'Invalid CSS after "(": expected ")", was "!)"',
"=a(" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ""',
"=a(b)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "b)"',
"=a(,)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was ",)"',
"=a($)" => 'Invalid CSS after "(": expected variable (e.g. $foo), was "$)"',
"=a($foo bar)" => 'Invalid CSS after "($foo ": expected ")", was "bar)"',
"=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
"a-\#{$b\n c: d" => ['Invalid CSS after "a-#{$b": expected "}", was ""', 1],
@ -107,6 +107,9 @@ MSG
'@if' => "Invalid if directive '@if': expected expression.",
'@while' => "Invalid while directive '@while': expected expression.",
'@debug' => "Invalid debug directive '@debug': expected expression.",
%Q{@debug "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath debug directives.",
'@warn' => "Invalid warn directive '@warn': expected expression.",
%Q{@warn "a message"\n "nested message"} => "Illegal nesting: Nothing may be nested beneath warn directives.",
"/* foo\n bar\n baz" => "Inconsistent indentation: previous line was indented by 4 spaces, but this line was indented by 2 spaces.",
# Regression tests
@ -937,7 +940,7 @@ SASS
def test_equals_warning_for_mixin_args
assert_warning(<<WARN) {assert_equal(<<CSS, render(<<SASS))}
DEPRECATION WARNING:
On line 1, character 6 of 'test_equals_warning_for_mixin_args_inline.sass'
On line 1, character 10 of 'test_equals_warning_for_mixin_args_inline.sass'
Setting mixin argument defaults with = has been deprecated and will be removed in version 3.2.
Use "$arg: 1px" instead.
@ -1301,6 +1304,18 @@ foo {
CSS
end
def test_loud_comments_with_starred_lines
assert_equal(<<CSS, render(<<SASS))
/* This is a comment that
* continues to the second line.
* And even to the third! */
CSS
/* This is a comment that
* continues to the second line.
* And even to the third!
SASS
end
def test_comment_indentation_at_beginning_of_doc
assert_equal <<CSS, render(<<SASS)
/* foo
@ -1709,6 +1724,58 @@ SASS
end
end
def test_warn_directive
expected_warning = <<EXPECTATION
WARNING: this is a warning
on line 4 of test_warn_directive_inline.sass
WARNING: this is a mixin warning
on line 2 of test_warn_directive_inline.sass, in `foo'
from line 7 of test_warn_directive_inline.sass
EXPECTATION
assert_warning expected_warning do
assert_equal <<CSS, render(<<SASS)
bar {
c: d; }
CSS
=foo
@warn "this is a mixin warning"
@warn "this is a warning"
bar
c: d
+foo
SASS
end
end
def test_warn_directive_when_quiet
assert_warning "" do
assert_equal <<CSS, render(<<SASS, :quiet => true)
CSS
@warn "this is a warning"
SASS
end
end
def test_warn_with_imports
expected_warning = <<WARN
WARNING: In the main file
on line 1 of #{File.dirname(__FILE__)}/templates/warn.sass
WARNING: Imported
on line 1 of #{File.dirname(__FILE__)}/templates/warn_imported.sass
from line 2 of #{File.dirname(__FILE__)}/templates/warn.sass
WARNING: In an imported mixin
on line 4 of #{File.dirname(__FILE__)}/templates/warn_imported.sass, in `emits-a-warning'
from line 3 of #{File.dirname(__FILE__)}/templates/warn.sass
WARN
assert_warning expected_warning do
renders_correctly "warn", :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"]
end
end
# Regression tests
def test_parens_in_mixins
@ -1834,6 +1901,15 @@ a
SASS
end
def test_mixin_no_arg_error
assert_raise(Sass::SyntaxError, 'Invalid CSS after "($bar,": expected variable name, was ")"') do
render(<<SASS)
=foo($bar,)
bip: bap
SASS
end
end
# Encodings
unless Haml::Util.ruby1_8?

View file

@ -505,6 +505,37 @@ The #options attribute is not set on this Sass::Script::String.
MSG
end
def test_type_of
assert_equal("string", evaluate("type-of(\"asdf\")"))
assert_equal("string", evaluate("type-of(asdf)"))
assert_equal("number", evaluate("type-of(1px)"))
assert_equal("bool", evaluate("type-of(true)"))
assert_equal("color", evaluate("type-of(#fff)"))
end
def test_unit
assert_equal(%Q{""}, evaluate("unit(100)"))
assert_equal(%Q{"px"}, evaluate("unit(100px)"))
assert_equal(%Q{"em*px"}, evaluate("unit(10px * 5em)"))
assert_equal(%Q{"em*px"}, evaluate("unit(5em * 10px)"))
assert_equal(%Q{"em*px/cm*rem"}, evaluate("unit(10px * 5em / 30cm / 1rem)"))
assert_error_message("#ff0000 is not a number for `unit'", "unit(#f00)")
end
def test_unitless
assert_equal(%Q{true}, evaluate("unitless(100)"))
assert_equal(%Q{false}, evaluate("unitless(100px)"))
assert_error_message("#ff0000 is not a number for `unitless'", "unitless(#f00)")
end
def test_comparable
assert_equal(%Q{true}, evaluate("comparable(2px, 1px)"))
assert_equal(%Q{true}, evaluate("comparable(10cm, 3mm)"))
assert_equal(%Q{false}, evaluate("comparable(100px, 3em)"))
assert_error_message("#ff0000 is not a number for `comparable'", "comparable(#f00, 1px)")
assert_error_message("#ff0000 is not a number for `comparable'", "comparable(1px, #f00)")
end
private
def evaluate(value)

View file

@ -14,7 +14,7 @@ class SassPluginTest < Test::Unit::TestCase
FileUtils.mkdir tempfile_loc
FileUtils.mkdir tempfile_loc(nil,"more_")
set_plugin_opts
Sass::Plugin.update_stylesheets
update_all_stylesheets!
reset_mtimes
end
@ -34,21 +34,21 @@ class SassPluginTest < Test::Unit::TestCase
def test_no_update
File.delete(tempfile_loc('basic'))
assert_needs_update 'basic'
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_stylesheet_updated 'basic'
end
def test_update_needed_when_modified
touch 'basic'
assert_needs_update 'basic'
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_stylesheet_updated 'basic'
end
def test_update_needed_when_dependency_modified
touch 'basic'
assert_needs_update 'import'
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_stylesheet_updated 'basic'
assert_stylesheet_updated 'import'
end
@ -56,7 +56,7 @@ class SassPluginTest < Test::Unit::TestCase
def test_update_needed_when_scss_dependency_modified
touch 'scss_importee'
assert_needs_update 'import'
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_stylesheet_updated 'scss_importee'
assert_stylesheet_updated 'import'
end
@ -64,14 +64,14 @@ class SassPluginTest < Test::Unit::TestCase
def test_scss_update_needed_when_dependency_modified
touch 'basic'
assert_needs_update 'scss_import'
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_stylesheet_updated 'basic'
assert_stylesheet_updated 'scss_import'
end
def test_full_exception_handling
File.delete(tempfile_loc('bork1'))
Sass::Plugin.update_stylesheets
update_all_stylesheets!
File.open(tempfile_loc('bork1')) do |file|
assert_equal(<<CSS.strip, file.read.split("\n")[0...6].join("\n"))
/*
@ -90,7 +90,7 @@ CSS
Sass::Plugin.options[:full_exception] = false
File.delete(tempfile_loc('bork1'))
assert_raise(Sass::SyntaxError) {Sass::Plugin.update_stylesheets}
assert_raise(Sass::SyntaxError) {update_all_stylesheets!}
ensure
Sass::Plugin.options[:full_exception] = old_full_exception
end
@ -100,7 +100,7 @@ CSS
template_loc => tempfile_loc,
template_loc(nil,'more_') => tempfile_loc(nil,'more_')
}
Sass::Plugin.update_stylesheets
update_all_stylesheets!
['more1', 'more_import'].each { |name| assert_renders_correctly(name, :prefix => 'more_') }
end
@ -111,7 +111,7 @@ CSS
template_loc => tempfile_loc,
template_loc(nil,'more_') => tempfile_loc(nil,'more_')
}
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_renders_correctly('more1_with_line_comments', 'more1', :prefix => 'more_')
end
@ -157,7 +157,7 @@ CSS
def test_updating_stylesheets_callback_with_individual_files
files = [[template_loc("basic"), tempfile_loc("basic")]]
assert_callback(:updating_stylesheets, files) {Sass::Plugin.update_stylesheets(files)}
assert_callback(:updating_stylesheets, files) {Haml::Util.silence_haml_warnings{Sass::Plugin.update_stylesheets(files)}}
end
def test_updating_stylesheets_callback_with_never_update
@ -248,7 +248,7 @@ CSS
touch 'basic', 'more_'
assert_needs_update "import"
Sass::Plugin.update_stylesheets
update_all_stylesheets!
assert_renders_correctly("import")
ensure
FileUtils.mv(template_loc("basic", "more_"), template_loc("basic"))
@ -299,7 +299,7 @@ CSS
if block_given?
yield
else
Sass::Plugin.update_stylesheets
update_all_stylesheets!
end
assert run, "Expected #{name} callback to be run with arguments:\n #{expected_args.inspect}"
@ -322,17 +322,17 @@ CSS
if block_given?
yield
else
Sass::Plugin.update_stylesheets
update_all_stylesheets!
end
end
def assert_callbacks(*args)
return Sass::Plugin.update_stylesheets if args.empty?
return update_all_stylesheets! if args.empty?
assert_callback(*args.pop) {assert_callbacks(*args)}
end
def assert_no_callbacks(*args)
return Sass::Plugin.update_stylesheets if args.empty?
return update_all_stylesheets! if args.empty?
assert_no_callback(*args.pop) {assert_no_callbacks(*args)}
end
@ -340,13 +340,19 @@ CSS
Sass::Plugin.instance_variable_set('@_sass_callbacks', {})
end
def update_all_stylesheets!
Haml::Util.silence_haml_warnings do
Sass::Plugin.update_stylesheets
end
end
def assert_needs_update(name)
assert(Sass::Plugin.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
assert(Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
"Expected #{template_loc(name)} to need an update.")
end
def assert_doesnt_need_update(name)
assert(!Sass::Plugin.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
assert(!Sass::Plugin::StalenessChecker.stylesheet_needs_update?(tempfile_loc(name), template_loc(name)),
"Expected #{template_loc(name)} not to need an update.")
end
@ -355,6 +361,7 @@ CSS
end
def reset_mtimes
Sass::Plugin::StalenessChecker.dependencies_cache = {}
atime = Time.now
mtime = Time.now - 5
Dir["{#{template_loc},#{tempfile_loc}}/**/*.{css,sass,scss}"].each {|f| File.utime(atime, mtime, f)}
@ -401,12 +408,6 @@ CSS
end
end
module Sass::Plugin
class << self
public :stylesheet_needs_update?
end
end
class Sass::Engine
alias_method :old_render, :render

View file

View file

View file

@ -143,6 +143,19 @@ SASS
assert_equal "Options defined!", resolve("assert_options(round(1.2))")
end
def test_dynamic_url
assert_equal "url(foo-bar)", resolve("url($foo)", {}, env('foo' => Sass::Script::String.new("foo-bar")))
assert_equal "url(foo-bar baz)", resolve("url($foo $bar)", {}, env('foo' => Sass::Script::String.new("foo-bar"), 'bar' => Sass::Script::String.new("baz")))
assert_equal "url(foo baz)", resolve("url(foo $bar)", {}, env('bar' => Sass::Script::String.new("baz")))
assert_equal "url(foo bar)", resolve("url(foo bar)")
end
def test_url_with_interpolation
assert_equal "url(http://sass-lang.com/images/foo-bar)", resolve("url(http://sass-lang.com/images/\#{foo-bar})")
assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve("url('http://sass-lang.com/images/\#{foo-bar}')")
assert_equal 'url("http://sass-lang.com/images/foo-bar")', resolve('url("http://sass-lang.com/images/#{foo-bar}")')
end
def test_hyphenated_variables
assert_equal("a-b", resolve("$a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
end

View file

@ -93,7 +93,7 @@ class ScssRxTest < Test::Unit::TestCase
assert_match URI, 'url("foo bar)")'
assert_match URI, "url('foo bar)')"
assert_match URI, 'url( "foo bar)" )'
assert_match URI, "url(!#\$%&**+,-./0123456789~)"
assert_match URI, "url(!#\\%&**+,-./0123456789~)"
end
def test_invalid_uri

View file

@ -113,6 +113,27 @@ SCSS
end
end
def test_warn_directive
expected_warning = <<EXPECTATION
WARNING: this is a warning
on line 2 of test_warn_directive_inline.scss
WARNING: this is a mixin
on line 1 of test_warn_directive_inline.scss, in `foo'
from line 3 of test_warn_directive_inline.scss
EXPECTATION
assert_warning expected_warning do
assert_equal <<CSS, render(<<SCSS)
bar {
c: d; }
CSS
@mixin foo { @warn "this is a mixin";}
@warn "this is a warning";
bar {c: d; @include foo;}
SCSS
end
end
def test_for_directive
assert_equal <<CSS, render(<<SCSS)
.foo {

View file

@ -3,7 +3,8 @@ $preconst: hello
=premixin
pre-mixin: here
@import importee.sass, scss_importee, "basic.sass", basic.css, ../results/complex.css, url(partial.sass)
@import importee.sass, scss_importee, "basic.sass", basic.css, ../results/complex.css
@import url(partial.sass)
nonimported
:myconst $preconst

View file

@ -0,0 +1,3 @@
@warn "In the main file"
@import warn_imported.sass
+emits-a-warning

View file

@ -0,0 +1,4 @@
@warn "Imported"
=emits-a-warning
@warn "In an imported mixin"