Merge branch 'yard' into yard-sassc

Conflicts:
	lib/sass/engine.rb
	lib/sass/environment.rb
	lib/sass/tree/variable_node.rb
This commit is contained in:
Nathan Weizenbaum 2009-04-29 15:33:14 -07:00
commit c236b9293c
13 changed files with 873 additions and 594 deletions

View File

@ -100,7 +100,8 @@ begin
files.exclude('TODO')
t.files = files.to_a
t.options << '-r' << 'README.md' << '-m' << 'maruku' << '--protected'
t.options << '-r' << 'README.md' << '-m' << 'markdown' << '--protected'
t.options += FileList.new('yard/*.rb').to_a.map {|f| ['-e', f]}.flatten
end
task :doc => :yardoc

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +1,98 @@
module Haml
# The module containing the default filters,
# as well as the base module,
# Haml::Filters::Base.
# The module containing the default Haml filters,
# as well as the base module, {Haml::Filters::Base}.
#
# @see Haml::Filters::Base
module Filters
# Returns a hash of defined filters.
# @return [Hash<String, Haml::Filters::Base>] a hash of filter names to classes
def self.defined
@defined ||= {}
end
# The base module for Haml filters.
# User-defined filters should be modules including this module.
# The name of the filter is taken by downcasing the module name.
# For instance, if the module is named `FooBar`, the filter will be `:foobar`.
#
# A user-defined filter should override either Base#render or Base #compile.
# Base#render is the most common.
# A user-defined filter should override either \{#render} or {\#compile}.
# \{#render} is the most common.
# It takes a string, the filter source,
# and returns another string,
# the result of the filter.
# For example:
# and returns another string, the result of the filter.
# For example, the following will define a filter named `:sass`:
#
# module Haml::Filters::Sass
# include Haml::Filters::Base
# module Haml::Filters::Sass
# include Haml::Filters::Base
#
# def render(text)
# ::Sass::Engine.new(text).render
# def render(text)
# ::Sass::Engine.new(text).render
# end
# end
# end
#
# For details on overriding #compile, see its documentation.
# For details on overriding \{#compile}, see its documentation.
#
# Note that filters overriding \{#render} automatically support `#{}`
# for interpolating Ruby code.
# Those overriding \{#compile} will need to add such support manually
# if it's desired.
module Base
def self.included(base) # :nodoc:
# This method is automatically called when {Base} is included in a module.
# It automatically defines a filter
# with the downcased name of that module.
# For example, if the module is named `FooBar`, the filter will be `:foobar`.
#
# @param base [Module, Class] The module that this is included in
def self.included(base)
Filters.defined[base.name.split("::").last.downcase] = base
base.extend(base)
end
# Takes a string, the source text that should be passed to the filter,
# and returns the string resulting from running the filter on <tt>text</tt>.
# Takes the source text that should be passed to the filter
# and returns the result of running the filter on that string.
#
# This should be overridden in most individual filter modules
# to render text with the given filter.
# If compile is overridden, however, render doesn't need to be.
# If \{#compile} is overridden, however, \{#render} doesn't need to be.
#
# @param text [String] The source text for the filter to process
# @return [String] The filtered result
# @raise [Haml::Error] if it's not overridden
def render(text)
raise Error.new("#{self.inspect}#render not defined!")
end
# Same as render, but takes the Haml options hash as well.
# It's only safe to rely on options made available in Haml::Engine#options_for_buffer.
# Same as \{#render}, but takes a {Haml::Engine} options hash as well.
# It's only safe to rely on options made available in {Haml::Engine#options\_for\_buffer}.
#
# @see #render
# @param text [String] The source text for the filter to process
# @return [String] The filtered result
# @raise [Haml::Error] if it or \{#render} isn't overridden
def render_with_options(text, options)
render(text)
end
def internal_compile(*args) # :nodoc:
# Same as \{#compile}, but requires the necessary files first.
# *This is used by {Haml::Engine} and is not intended to be overridden or used elsewhere.*
#
# @see #compile
def internal_compile(*args)
resolve_lazy_requires
compile(*args)
end
# compile should be overridden when a filter needs to have access
# to the Haml evaluation context.
# This should be overridden when a filter needs to have access to the Haml evaluation context.
# Rather than applying a filter to a string at compile-time,
# compile uses the Haml::Precompiler instance to compile the string to Ruby code
# \{#compile} uses the {Haml::Precompiler} instance to compile the string to Ruby code
# that will be executed in the context of the active Haml template.
#
# Warning: the Haml::Precompiler interface is neither well-documented
# Warning: the {Haml::Precompiler} interface is neither well-documented
# nor guaranteed to be stable.
# If you want to make use of it,
# you'll probably need to look at the source code
# If you want to make use of it, you'll probably need to look at the source code
# and should test your filter when upgrading to new Haml versions.
#
# @param precompiler [Haml::Precompiler] The precompiler instance
# @param text [String] The text of the filter
# @raise [Haml::Error] if none of \{#compile}, \{#render}, and \{#render_with_options} are overridden
def compile(precompiler, text)
resolve_lazy_requires
filter = self
@ -89,22 +116,22 @@ RUBY
end
end
# This becomes a class method of modules that include Base.
# This becomes a class method of modules that include {Base}.
# It allows the module to specify one or more Ruby files
# that Haml should try to require when compiling the filter.
#
# The first file specified is tried first,
# then the second, etc.
# The first file specified is tried first, then the second, etc.
# If none are found, the compilation throws an exception.
#
# For example:
#
# module Haml::Filters::Markdown
# lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
# module Haml::Filters::Markdown
# lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
#
# ...
# end
# ...
# end
#
# @param reqs [Array<String>] The requires to run
def lazy_require(*reqs)
@lazy_requires = reqs
end
@ -139,23 +166,29 @@ RUBY
end
end
# :stopdoc:
begin
require 'rubygems'
rescue LoadError; end
module Haml
module Filters
# Does not parse the filtered text.
# This is useful for large blocks of text without HTML tags,
# when you don't want lines starting with `.` or `-`
# to be parsed.
module Plain
include Base
# @see Base#render
def render(text); text; end
end
# Surrounds the filtered text with `<script>` and CDATA tags.
# Useful for including inline Javascript.
module Javascript
include Base
# @see Base#render_with_options
def render_with_options(text, options)
<<END
<script type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}>
@ -167,26 +200,37 @@ END
end
end
# Surrounds the filtered text with CDATA tags.
module Cdata
include Base
# @see Base#render
def render(text)
"<![CDATA[#{("\n" + text).rstrip.gsub("\n", "\n ")}\n]]>"
end
end
# Works the same as {Plain}, but HTML-escapes the text
# before placing it in the document.
module Escaped
include Base
# @see Base#render
def render(text)
Haml::Helpers.html_escape text
end
end
# Parses the filtered text with the normal Ruby interpreter.
# All output sent to `$stdout`, such as with `puts`,
# is output into the Haml document.
# Not available if the [`:suppress_eval`](../Haml.html#suppress-eval) option is set to true.
# The Ruby code is evaluated in the same context as the Haml template.
module Ruby
include Base
lazy_require 'stringio'
# @see Base#compile
def compile(precompiler, text)
return if precompiler.options[:suppress_eval]
precompiler.instance_eval do
@ -201,27 +245,40 @@ END
end
end
# Inserts the filtered text into the template with whitespace preserved.
# `preserve`d blocks of text aren't indented,
# and newlines are replaced with the HTML escape code for newlines,
# to preserve nice-looking output.
#
# @see Haml::Helpers#preserve
module Preserve
include Base
# @see Base#render
def render(text)
Haml::Helpers.preserve text
end
end
# Parses the filtered text with {Sass} to produce CSS output.
module Sass
include Base
lazy_require 'sass/plugin'
# @see Base#render
def render(text)
::Sass::Engine.new(text, ::Sass::Plugin.engine_options).render
end
end
# Parses the filtered text with ERB, like an RHTML template.
# Not available if the [`:suppress_eval`](../Haml.html#suppress-eval) option is set to true.
# Embedded Ruby code is evaluated in the same context as the Haml template.
module ERB
include Base
lazy_require 'erb'
# @see Base#compile
def compile(precompiler, text)
return if precompiler.options[:suppress_eval]
src = ::ERB.new(text).src.sub(/^#coding:.*?\n/, '').
@ -230,10 +287,13 @@ END
end
end
# Parses the filtered text with [Textile](http://www.textism.com/tools/textile).
# Only works if [RedCloth](http://redcloth.org) is installed.
module Textile
include Base
lazy_require 'redcloth'
# @see Base#render
def render(text)
::RedCloth.new(text).to_html(:textile)
end
@ -241,11 +301,16 @@ END
RedCloth = Textile
Filters.defined['redcloth'] = RedCloth
# Uses BlueCloth or RedCloth to provide only Markdown (not Textile) parsing
# Parses the filtered text with [Markdown](http://daringfireball.net/projects/markdown).
# Only works if [RDiscount](http://github.com/rtomayko/rdiscount),
# [RPeg-Markdown](http://github.com/rtomayko/rpeg-markdown),
# [Maruku](http://maruku.rubyforge.org),
# or [BlueCloth](www.deveiate.org/projects/BlueCloth) are installed.
module Markdown
include Base
lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
# @see Base#render
def render(text)
engine = case @required
when 'rdiscount'
@ -261,15 +326,16 @@ END
end
end
# Parses the filtered text with [Maruku](http://maruku.rubyforge.org),
# which has some non-standard extensions to Markdown.
module Maruku
include Base
lazy_require 'maruku'
# @see Base#render
def render(text)
::Maruku.new(text).to_html
end
end
end
end
# :startdoc:

View File

@ -1027,7 +1027,7 @@ require 'haml/version'
# Defaults to false in production mode, true otherwise.
# Only has meaning within Ruby on Rails or Merb.
#
# `:template_location`
# {#template-location} `:template_location`
# : A path to the root sass template directory for you application.
# If a hash, `:css_location` is ignored and this option designates
# both a mapping between input and output directories.
@ -1038,7 +1038,7 @@ require 'haml/version'
# This will be derived from the `:css_location` path list if not provided
# by appending a folder of "sass" to each corresponding css location.
#
# `:css_location`
# {#css-location} `:css_location`
# : The path where CSS output should be written to.
# This option is ignored when `:template_location` is a Hash.
# Defaults to `RAILS_ROOT + "/public/stylesheets"`

View File

@ -20,20 +20,55 @@ require 'sass/files'
require 'haml/shared'
module Sass
# :stopdoc:
Mixin = Struct.new(:name, :args, :environment, :tree)
# :startdoc:
# This is the class where all the parsing and processing of the Sass
# template is done. It can be directly used by the user by creating a
# new instance and calling <tt>render</tt> to render the template. For example:
# A Sass mixin.
#
# template = File.load('stylesheets/sassy.sass')
# sass_engine = Sass::Engine.new(template)
# output = sass_engine.render
# puts output
# `name`: [{String}]
# : The name of the mixin.
#
# `args`: [{Array}<({String}, {Script::Node})>]
# : The arguments for the mixin.
# Each element is a tuple containing the name of the argument
# and the parse tree for the default value of the argument.
#
# `environment`: [{Sass::Environment}]
# : The environment in which the mixin was defined.
# This is captured so that the mixin can have access
# to local variables defined in its scope.
#
# `tree`: [{Sass::Tree::Node}]
# : The parse tree for the mixin.
Mixin = Struct.new(:name, :args, :environment, :tree)
# This class handles the parsing and compilation of the Sass template.
# Example usage:
#
# template = File.load('stylesheets/sassy.sass')
# sass_engine = Sass::Engine.new(template)
# output = sass_engine.render
# puts output
class Engine
include Haml::Util
# A line of Sass code.
#
# `text`: [{String}]
# : The text in the line, without any whitespace at the beginning or end.
#
# `tabs`: [{Fixnum}]
# : The level of indentation of the line.
#
# `index`: [{Fixnum}]
# : The line number in the original document.
#
# `offset`: [{Fixnum}]
# : The number of bytes in on the line that the text begins.
# This ends up being the number of bytes of leading whitespace.
#
# `filename`: [{String}]
# : The name of the file in which this line appeared.
#
# `children`: [{Array}<{Line}>]
# : The lines nested below this one.
Line = Struct.new(:text, :tabs, :index, :offset, :filename, :children)
# The character that begins a CSS attribute.
@ -86,30 +121,28 @@ module Sass
:cache_location => './.sass-cache',
}.freeze
# Creates a new instace of Sass::Engine that will compile the given
# template string when <tt>render</tt> is called.
# See README.rdoc for available options.
#
#--
#
# TODO: Add current options to REFRENCE. Remember :filename!
#
# When adding options, remember to add information about them
# to README.rdoc!
#++
#
# @param template [String] The Sass template.
# @param options [Hash<Symbol, Object>] An options hash;
# see [the Sass options documentation](../Sass.html#sass_options)
def initialize(template, options={})
@options = DEFAULT_OPTIONS.merge(options)
@template = template
end
# Processes the template and returns the result as a string.
# Render the template to CSS.
#
# @return [String] The CSS
# @raise [Sass::SyntaxError] if there's an error in the document
def render
to_tree.render
end
alias_method :to_css, :render
# Parses the document into its parse tree.
#
# @return [Sass::Tree::Node] The root of the parse tree.
# @raise [Sass::SyntaxError] if there's an error in the document
def to_tree
root = Tree::Node.new
append_children(root, tree(tabulate(@template)).first, true)
@ -435,5 +468,71 @@ END
Tree::FileNode.new(filename)
end.flatten
end
<<<<<<< HEAD:lib/sass/engine.rb
=======
# Find the full filename of a Sass or CSS file to import.
# This follows Sass's import rules:
# if the filename given ends in `".sass"` or `".css"`,
# it will try to find that type of file;
# otherwise, it will try to find the corresponding Sass file
# and fall back on CSS if it's not available.
#
# Any Sass filename returned will correspond to
# an actual Sass file on the filesystem.
# CSS filenames, however, may not;
# they're expected to be put through directly to the stylesheet
# as CSS `@import` statements.
#
# @param filename [String] The filename to search for
# @param load_paths [Array<String>] The set of filesystem paths
# to search for Sass files.
# @return [String] The filename of the imported file.
# This is an absolute path if the file is a `".sass"` file.
# @raise [Sass::SyntaxError] if `filename` ends in ``".sass"``
# and no corresponding Sass file could be found.
def self.find_file_to_import(filename, load_paths)
was_sass = false
original_filename = filename
if filename[-5..-1] == ".sass"
filename = filename[0...-5]
was_sass = true
elsif filename[-4..-1] == ".css"
return filename
end
new_filename = find_full_path("#{filename}.sass", load_paths)
return new_filename if new_filename
return filename + '.css' unless was_sass
raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.", @line)
end
# Checks each of the paths in `load_paths` for a file named `filename`.
#
# This will also find files with the same name as `filename`
# but with an underscore prefixing the basename.
# For example, if filename is `"foo/bar/baz.sass"`,
# this will look for `"foo/bar/baz.sass"` **and** `"foo/bar/_baz.sass"`.
#
# @param filename [String] The filename to search for
# @param load_paths [Array<String>] The set of filesystem paths
# to search for Sass files
# @return [String] The absolute path of the matching file,
# or nil if no matching file is found
def self.find_full_path(filename, load_paths)
segments = filename.split(File::SEPARATOR)
segments.push "_#{segments.pop}"
partial_name = segments.join(File::SEPARATOR)
load_paths.each do |path|
[partial_name, filename].each do |name|
full_path = File.join(path, name)
return full_path if File.readable?(full_path)
end
end
nil
end
>>>>>>> yard:lib/sass/engine.rb
end
end

View File

@ -1,8 +1,24 @@
module Sass
# The lexical environment for SassScript.
# This keeps track of variable and mixin definitions.
#
# A new environment is created for each level of Sass nesting.
# This allows variables to be lexically scoped.
# The new environment refers to the environment in the upper scope,
# so it has access to variables defined in enclosing scopes,
# but new variables are defined locally.
#
# Environment also keeps track of the {Engine} options
# so that they can be made available to {Sass::Functions}.
class Environment
# The enclosing environment,
# or nil if this is the global environment.
#
# @return [Environment]
attr_reader :parent
attr_writer :options
# @param parent [Environment] See \{#parent}
def initialize(parent = nil)
@vars = {}
@mixins = {}
@ -11,38 +27,53 @@ module Sass
set_var("important", Script::String.new("!important")) unless @parent
end
# The options hash.
# See [the Sass options documentation](../Sass.html#sass_options).
#
# @return [Hash<Symbol, Object>]
def options
@options || (parent && parent.options) || {}
end
def self.inherited_hash(name)
class_eval <<RUBY, __FILE__, __LINE__ + 1
def #{name}(name)
@#{name}s[name] || @parent && @parent.#{name}(name)
end
class << self
private
def set_#{name}(name, value)
@#{name}s[name] = value unless try_set_#{name}(name, value)
end
def try_set_#{name}(name, value)
if @#{name}s.include?(name)
@#{name}s[name] = value
true
elsif @parent
@parent.try_set_#{name}(name, value)
else
false
# Note: when updating this,
# update haml/yard/inherited_hash.rb as well.
def inherited_hash(name)
class_eval <<RUBY, __FILE__, __LINE__ + 1
def #{name}(name)
@#{name}s[name] || @parent && @parent.#{name}(name)
end
end
protected :try_set_#{name}
def set_local_#{name}(name, value)
@#{name}s[name] = value
end
def set_#{name}(name, value)
@#{name}s[name] = value unless try_set_#{name}(name, value)
end
def try_set_#{name}(name, value)
if @#{name}s.include?(name)
@#{name}s[name] = value
true
elsif @parent
@parent.try_set_#{name}(name, value)
else
false
end
end
protected :try_set_#{name}
def set_local_#{name}(name, value)
@#{name}s[name] = value
end
RUBY
end
end
# variable
# Script::Literal
inherited_hash :var
# mixin
# Engine::Mixin
inherited_hash :mixin
end
end

View File

@ -1,9 +1,13 @@
require 'sass/engine'
module Sass
# This module contains methods to aid in using Sass
# as a stylesheet-rendering plugin for various systems.
# Currently Rails/ActionController and Merb are supported out of the box.
# This module handles the compilation of Sass files.
# It provides global options and checks whether CSS files
# need to be updated.
#
# This module is used as the primary interface with Sass
# when it's used as a plugin for various frameworks.
# Currently Rails and Merb are supported out of the box.
module Plugin
extend self
@ -15,37 +19,42 @@ module Sass
}
@checked_for_updates = false
# Whether or not Sass has *ever* checked if the stylesheets need updates
# Whether or not Sass has **ever** checked if the stylesheets need to be updated
# (in this Ruby instance).
def checked_for_updates
@checked_for_updates
end
#
# @return [Boolean]
attr_reader :checked_for_updates
# Gets various options for Sass. See README.rdoc for details.
#--
# TODO: *DOCUMENT OPTIONS*
#++
def options
@options
end
# An options hash.
# See [the Sass options documentation](../Sass.html#sass_options).
#
# @return [Hash<Symbol, Object>]
attr_reader :options
# Sets various options for Sass.
# Sets the options hash.
# See [the Sass options documentation](../Sass.html#sass_options).
#
# @param value [Hash<Symbol, Object>] The options hash
def options=(value)
@options.merge!(value)
end
# Get the options ready to be passed to the Sass::Engine
# Non-destructively modifies \{#options} so that default values are properly set.
#
# @param additional_options [Hash<Symbol, Object>] An options hash with which to merge \{#options}
# @return [Hash<Symbol, Object>] The modified options hash
def engine_options(additional_options = {})
opts = options.dup.merge(additional_options)
opts[:load_paths] = load_paths(opts)
opts
end
# Checks each stylesheet in <tt>options[:css_location]</tt>
# to see if it needs updating,
# and updates it using the corresponding template
# from <tt>options[:templates]</tt>
# if it does.
# Updates out-of-date stylesheets.
#
# Checks each Sass file in [`:template_location`](../Sass.html#template-location)
# to see if it's been modified more recently than the corresponding CSS file
# in [`:css_location`](../Sass.html#css-location).
# If it has, it updates the CSS file.
def update_stylesheets
return if options[:never_update]

View File

@ -25,7 +25,7 @@ unless defined?(Sass::MERB_LOADED)
if version[0] > 0 || version[1] >= 9
class Merb::Rack::Application # :nodoc:
class Merb::Rack::Application
def call_with_sass(env)
if !Sass::Plugin.checked_for_updates ||
Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
@ -40,7 +40,7 @@ unless defined?(Sass::MERB_LOADED)
else
class MerbHandler # :nodoc:
class MerbHandler
def process_with_sass(request, response)
if !Sass::Plugin.checked_for_updates ||
Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]

View File

@ -7,7 +7,6 @@ unless defined?(Sass::RAILS_LOADED)
:always_check => RAILS_ENV != "production",
:full_exception => RAILS_ENV != "production")
# :stopdoc:
module ActionController
class Base
alias_method :sass_old_process, :process
@ -21,5 +20,4 @@ unless defined?(Sass::RAILS_LOADED)
end
end
end
# :startdoc:
end

View File

@ -8,16 +8,16 @@ module Sass
class Lexer
# A struct containing information about an individual token.
#
# `type`: {Symbol}
# `type`: [{Symbol}]
# : The type of token.
#
# `value`: {Object}
# `value`: [{Object}]
# : The Ruby object corresponding to the value of the token.
#
# `line`: {Fixnum}
# `line`: [{Fixnum}]
# : The line of the source file on which the token appears.
#
# `offset`: {Fixnum}
# `offset`: [{Fixnum}]
# : The number of bytes into the line the SassScript token appeared.
Token = Struct.new(:type, :value, :line, :offset)

View File

@ -12,7 +12,7 @@ module Sass::Script
class Number < Literal
# The Ruby value of the number.
#
# @returns [Numeric]
# @return [Numeric]
attr_reader :value
# A list of units in the numerator of the number.

View File

@ -1,6 +1,12 @@
module Sass
module Tree
# A dynamic node representing a variable definition.
#
# @see Sass::Tree
class VariableNode < Node
# @param name [String] The name of the variable
# @param expr [Script::Node] The parse tree for the initial variable value
# @param guarded [Boolean] Whether this is a guarded variable assignment (`||=`)
def initialize(name, expr, guarded)
@name = name
@expr = expr
@ -10,6 +16,10 @@ module Sass
protected
# Loads the new variable value into the environment.
#
# @param environment [Sass::Environment] The lexical environment containing
# variable and mixin values
def _perform(environment)
if @guarded && environment.var(@name).nil?
environment.set_var(@name, @expr.perform(environment))

44
yard/inherited_hash.rb Normal file
View File

@ -0,0 +1,44 @@
class InheritedHashHandler < YARD::Handlers::Base
handles /\Ainherited_hash(\s|\()/
def process
hash_name = tokval(statement.tokens[2])
name = statement.comments.first.strip
type = statement.comments[1].strip
register(MethodObject.new(namespace, hash_name, scope)) do |o|
o.docstring = [
"Gets a #{name} from this {Environment} or one of its \\{#parent}s.",
"@param name [String] The name of the #{name}",
"@return [#{type}] The #{name} value",
]
o.signature = true
o.parameters = ["name"]
end
register(MethodObject.new(namespace, "set_#{hash_name}", scope)) do |o|
o.docstring = [
"Sets a #{name} in this {Environment} or one of its \\{#parent}s.",
"If the #{name} is already defined in some environment,",
"that one is set; otherwise, a new one is created in this environment.",
"@param name [String] The name of the #{name}",
"@param value [#{type}] The value of the #{name}",
"@return [#{type}] `value`",
]
o.signature = true
o.parameters = ["name", "value"]
end
register(MethodObject.new(namespace, "set_local_#{hash_name}", scope)) do |o|
o.docstring = [
"Sets a #{name} in this {Environment}.",
"Ignores any parent environments.",
"@param name [String] The name of the #{name}",
"@param value [#{type}] The value of the #{name}",
"@return [#{type}] `value`",
]
o.signature = true
o.parameters = ["name", "value"]
end
end
end