Excise Sass from within files.
This commit is contained in:
parent
dca79538bc
commit
fd9a367bff
|
@ -3,7 +3,6 @@
|
|||
/doc
|
||||
/pkg
|
||||
/test/rails
|
||||
/.sass-cache
|
||||
/.haml
|
||||
/site
|
||||
*.rbc
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
--markup markdown
|
||||
--markup-provider maruku
|
||||
--default-return ""
|
||||
--title "Haml/Sass Documentation"
|
||||
--title "Haml Documentation"
|
||||
--query 'object.type != :classvariable'
|
||||
--query 'object.type != :constant || @api && @api.text == "public"'
|
||||
--hide-void-return
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
Contributions are welcomed. Please see the following sites for guidelines:
|
||||
|
||||
http://haml-lang.com/development.html#contributing
|
||||
http://sass-lang.com/development.html#contributing
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
|
||||
Copyright (c) 2006-2009 Hampton Catlin and Nathan Weizenbaum
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
185
README.md
185
README.md
|
@ -1,72 +1,40 @@
|
|||
# Haml and Sass
|
||||
# Haml
|
||||
|
||||
Haml and Sass are templating engines
|
||||
for the two most common types of documents on the web:
|
||||
HTML and CSS, respectively.
|
||||
They are designed to make it both easier and more pleasant
|
||||
to code HTML and CSS documents,
|
||||
Haml is a templating engine for HTML.
|
||||
It's are designed to make it both easier and more pleasant
|
||||
to write HTML documents,
|
||||
by eliminating redundancy,
|
||||
reflecting the underlying structure that the document represents,
|
||||
and providing elegant, easily understandable, and powerful syntax.
|
||||
|
||||
## Using
|
||||
|
||||
Haml and Sass can be used from the command line
|
||||
Haml can be used from the command line
|
||||
or as part of a Ruby web framework.
|
||||
The first step is to install the gem:
|
||||
|
||||
gem install haml
|
||||
|
||||
After you convert some HTML to Haml or some CSS to Sass,
|
||||
you can run
|
||||
After you convert some HTML to Haml, you can run
|
||||
|
||||
haml document.haml
|
||||
sass style.scss
|
||||
|
||||
to compile them.
|
||||
For more information on these commands, check out
|
||||
|
||||
haml --help
|
||||
sass --help
|
||||
|
||||
To install Haml and Sass in Rails 2,
|
||||
To install Haml in Rails 2,
|
||||
just add `config.gem "haml"` to `config/environment.rb`.
|
||||
In Rails 3, add `gem "haml"` to your Gemfile instead.
|
||||
and both Haml and Sass will be installed.
|
||||
Views with the `.html.haml` extension will automatically use Haml.
|
||||
Sass is a little more complicated;
|
||||
`.sass` files should be placed in `public/stylesheets/sass`,
|
||||
where they'll be automatically compiled
|
||||
to corresponding CSS files in `public/stylesheets` when needed
|
||||
(the Sass template directory is customizable...
|
||||
see [the Sass reference](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#template_location-option) for details).
|
||||
|
||||
For Merb, `.html.haml` views will work without any further modification.
|
||||
To enable Sass, you also need to add a dependency.
|
||||
To do so, just add
|
||||
|
||||
dependency "merb-haml"
|
||||
|
||||
to `config/dependencies.rb` (or `config/init.rb` in a flat/very flat Merb application).
|
||||
Then it'll work just like it does in Rails.
|
||||
|
||||
Sass can also be used with any Rack-enabled web framework.
|
||||
To do so, just add
|
||||
|
||||
require 'sass/plugin/rack'
|
||||
use Sass::Plugin::Rack
|
||||
|
||||
to `config.ru`.
|
||||
Then any Sass files in `public/stylesheets/sass`
|
||||
will be compiled CSS files in `public/stylesheets` on every request.
|
||||
|
||||
To use Haml and Sass programatically,
|
||||
To use Haml programatically,
|
||||
check out the [YARD documentation](http://haml-lang.com/docs/yardoc/).
|
||||
|
||||
## Formatting
|
||||
|
||||
### Haml
|
||||
|
||||
The most basic element of Haml
|
||||
is a shorthand for creating HTML:
|
||||
|
||||
|
@ -133,141 +101,16 @@ Haml provides far more tools than those presented here.
|
|||
Check out the [reference documentation](http://beta.haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html)
|
||||
for full details.
|
||||
|
||||
#### Indentation
|
||||
### Indentation
|
||||
|
||||
Haml's indentation can be made up of one or more tabs or spaces.
|
||||
However, indentation must be consistent within a given document.
|
||||
Hard tabs and spaces can't be mixed,
|
||||
and the same number of tabs or spaces must be used throughout.
|
||||
|
||||
### Sass
|
||||
|
||||
Sass is an extension of CSS
|
||||
that adds power and elegance to the basic language.
|
||||
It allows you to use [variables][vars], [nested rules][nested],
|
||||
[mixins][mixins], [inline imports][imports],
|
||||
and more, all with a fully CSS-compatible syntax.
|
||||
Sass helps keep large stylesheets well-organized,
|
||||
and get small stylesheets up and running quickly,
|
||||
particularly with the help of
|
||||
[the Compass style library](http://compass-style.org).
|
||||
|
||||
[vars]: http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#variables_
|
||||
[nested]: http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#nested_rules_
|
||||
[mixins]: http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#mixins
|
||||
[imports]: http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#import
|
||||
|
||||
Sass has two syntaxes.
|
||||
The one presented here, known as "SCSS" (for "Sassy CSS"),
|
||||
is fully CSS-compatible.
|
||||
The other (older) syntax, known as the indented syntax or just "Sass",
|
||||
is whitespace-sensitive and indentation-based.
|
||||
For more information, see the [reference documentation][syntax].
|
||||
|
||||
[syntax]: http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#syntax
|
||||
|
||||
To run the following examples and see the CSS they produce,
|
||||
put them in a file called `test.scss` and run `sass test.scss`.
|
||||
|
||||
#### Nesting
|
||||
|
||||
Sass avoids repetition by nesting selectors within one another.
|
||||
The same thing works for properties.
|
||||
|
||||
table.hl {
|
||||
margin: 2em 0;
|
||||
td.ln { text-align: right; }
|
||||
}
|
||||
|
||||
li {
|
||||
font: {
|
||||
family: serif;
|
||||
weight: bold;
|
||||
size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
#### Variables
|
||||
|
||||
Use the same color all over the place?
|
||||
Need to do some math with height and width and text size?
|
||||
Sass supports variables, math operations, and many useful functions.
|
||||
|
||||
$blue: #3bbfce;
|
||||
$margin: 16px;
|
||||
|
||||
.content_navigation {
|
||||
border-color: $blue;
|
||||
color: darken($blue, 10%);
|
||||
}
|
||||
|
||||
.border {
|
||||
padding: $margin / 2;
|
||||
margin: $margin / 2;
|
||||
border-color: $blue;
|
||||
}
|
||||
|
||||
#### Mixins
|
||||
|
||||
Even more powerful than variables,
|
||||
mixins allow you to re-use whole chunks of CSS,
|
||||
properties or selectors.
|
||||
You can even give them arguments.
|
||||
|
||||
@mixin table-scaffolding {
|
||||
th {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
td, th { padding: 2px; }
|
||||
}
|
||||
|
||||
@mixin left($dist) {
|
||||
float: left;
|
||||
margin-left: $dist;
|
||||
}
|
||||
|
||||
#data {
|
||||
@include left(10px);
|
||||
@include table-scaffolding;
|
||||
}
|
||||
|
||||
A comprehensive list of features is available
|
||||
in the [Sass reference](http://beta.sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html).
|
||||
|
||||
## Executables
|
||||
|
||||
The Haml gem includes several executables that are useful
|
||||
for dealing with Haml and Sass from the command line.
|
||||
|
||||
### `haml`
|
||||
|
||||
The `haml` executable transforms a source Haml file into HTML.
|
||||
See `haml --help` for further information and options.
|
||||
|
||||
### `sass`
|
||||
|
||||
The `sass` executable transforms a source Sass file into CSS.
|
||||
See `sass --help` for further information and options.
|
||||
|
||||
### `html2haml`
|
||||
|
||||
The `html2haml` executable attempts to transform HTML,
|
||||
optionally with ERB markup, into Haml code.
|
||||
Since HTML is so variable, this transformation is not always perfect;
|
||||
it's a good idea to have a human check the output of this tool.
|
||||
See `html2haml --help` for further information and options.
|
||||
|
||||
### `sass-convert`
|
||||
|
||||
The `sass-convert` executable converts between CSS, Sass, and SCSS.
|
||||
When converting from CSS to Sass or SCSS,
|
||||
nesting is applied where appropriate.
|
||||
See `sass-convert --help` for further information and options.
|
||||
|
||||
## Authors
|
||||
|
||||
Haml and Sass were created by [Hampton Catlin](http://hamptoncatlin.com)
|
||||
Haml was created by [Hampton Catlin](http://hamptoncatlin.com)
|
||||
(hcatlin) and he is the author of the original implementation. However, Hampton
|
||||
doesn't even know his way around the code anymore and now occasionally consults
|
||||
on the language issues. Hampton lives in Jacksonville, Florida and is the lead
|
||||
|
@ -281,14 +124,6 @@ getting Hampton coffee (a fitting task for a boy-genius). Nathan lives in
|
|||
Seattle, Washington and while not being a student at the University of
|
||||
Washington or working at an internship, he consults for Unspace Interactive.
|
||||
|
||||
[Chris Eppstein](http://acts-as-architect.blogspot.com) is a core contributor to
|
||||
Sass and the creator of Compass, the first Sass-based framework. Chris focuses
|
||||
on making Sass more powerful, easy to use, and on ways to speed its adoption
|
||||
through the web development community. Chris lives in San Jose, California with
|
||||
his wife and daughter. He is the Software Architect for
|
||||
[Caring.com](http://caring.com), a website devoted to the 34 Million caregivers
|
||||
whose parents are sick or elderly, that uses Haml and Sass.
|
||||
|
||||
If you use this software, you must pay Hampton a compliment. And
|
||||
buy Nathan some jelly beans. Maybe pet a kitten. Yeah. Pet that kitty.
|
||||
|
||||
|
|
108
Rakefile
108
Rakefile
|
@ -77,7 +77,7 @@ task :install => [:package] do
|
|||
end
|
||||
|
||||
desc "Release a new Haml package to Rubyforge."
|
||||
task :release => [:check_release, :release_elpa, :package] do
|
||||
task :release => [:check_release, :package] do
|
||||
name = File.read(scope("VERSION_NAME")).strip
|
||||
version = File.read(scope("VERSION")).strip
|
||||
sh %{rubyforge add_release haml haml "#{name} (v#{version})" pkg/haml-#{version}.gem}
|
||||
|
@ -85,46 +85,6 @@ task :release => [:check_release, :release_elpa, :package] do
|
|||
sh %{gem push pkg/haml-#{version}.gem}
|
||||
end
|
||||
|
||||
# Releases haml-mode.el and sass-mode.el to ELPA.
|
||||
task :release_elpa do
|
||||
require 'tlsmail'
|
||||
require 'time'
|
||||
require scope('lib/haml')
|
||||
|
||||
next if Haml.version[:prerelease]
|
||||
version = Haml.version[:number]
|
||||
|
||||
haml_unchanged = mode_unchanged?(:haml, version)
|
||||
sass_unchanged = mode_unchanged?(:sass, version)
|
||||
next if haml_unchanged && sass_unchanged
|
||||
raise "haml-mode.el and sass-mode.el are out of sync." if (!!haml_unchanged) ^ (!!sass_unchanged)
|
||||
|
||||
if sass_unchanged && File.read(scope("extra/sass-mode.el")).
|
||||
include?(";; Package-Requires: ((haml-mode #{sass_unchanged.inspect}))")
|
||||
raise "sass-mode.el doesn't require the same version of haml-mode."
|
||||
end
|
||||
|
||||
from = `git config user.email`.strip
|
||||
raise "Don't know how to send emails except via Gmail" unless from =~ /@gmail.com$/
|
||||
|
||||
to = "elpa@tromey.com"
|
||||
Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE)
|
||||
Net::SMTP.start('smtp.gmail.com', 587, 'gmail.com', from, read_password("GMail Password"), :login) do |smtp|
|
||||
smtp.send_message(<<CONTENT, from, to)
|
||||
From: Nathan Weizenbaum <#{from}>
|
||||
To: #{to}
|
||||
Subject: Submitting haml-mode and sass-mode #{version}
|
||||
Date: #{Time.now.rfc2822}
|
||||
|
||||
haml-mode and sass-mode #{version} are packaged and ready to be included in ELPA.
|
||||
They can be downloaded from:
|
||||
|
||||
http://github.com/nex3/haml/raw/#{Haml.version[:rev]}/extra/haml-mode.el
|
||||
http://github.com/nex3/haml/raw/#{Haml.version[:rev]}/extra/sass-mode.el
|
||||
CONTENT
|
||||
end
|
||||
end
|
||||
|
||||
# Ensures that the version have been updated for a new release.
|
||||
task :check_release do
|
||||
version = File.read(scope("VERSION")).strip
|
||||
|
@ -155,20 +115,6 @@ def changed_since?(rev, *files)
|
|||
return !$?.success?
|
||||
end
|
||||
|
||||
# Returns whether or not the given Emacs mode file (haml or sass)
|
||||
# has changed since the given version.
|
||||
#
|
||||
# @param mode [String, Symbol] The name of the mode
|
||||
# @param version [String] The version number
|
||||
# @return [String, nil] The version number if the version has changed
|
||||
def mode_unchanged?(mode, version)
|
||||
mode_version = File.read(scope("extra/#{mode}-mode.el")).scan(/^;; Version: (.*)$/).first.first
|
||||
return false if mode_version == version
|
||||
return mode_version unless changed_since?(mode_version, "extra/#{mode}-mode.el")
|
||||
raise "#{mode}-mode.el version is #{version.inspect}, but it has changed as of #{version.inspect}"
|
||||
return false
|
||||
end
|
||||
|
||||
task :submodules do
|
||||
if File.exist?(File.dirname(__FILE__) + "/.git")
|
||||
sh %{git submodule sync}
|
||||
|
@ -240,7 +186,7 @@ begin
|
|||
|
||||
namespace :doc do
|
||||
task :sass do
|
||||
require scope('lib/sass')
|
||||
require 'sass'
|
||||
Dir[scope("yard/default/**/*.sass")].each do |sass|
|
||||
File.open(sass.gsub(/sass$/, 'css'), 'w') do |f|
|
||||
f.write(Sass::Engine.new(File.read(sass)).render)
|
||||
|
@ -267,9 +213,6 @@ OPTS
|
|||
list.exclude('lib/haml/railtie.rb')
|
||||
list.exclude('lib/haml/helpers/action_view_mods.rb')
|
||||
list.exclude('lib/haml/helpers/xss_mods.rb')
|
||||
list.exclude('lib/sass/plugin/merb.rb')
|
||||
list.exclude('lib/sass/plugin/rails.rb')
|
||||
list.exclude('lib/sass/less.rb')
|
||||
end.to_a
|
||||
t.options << '--incremental' if Rake.application.top_level_tasks.include?('redoc')
|
||||
t.options += FileList.new(scope('yard/*.rb')).to_a.map {|f| ['-e', f]}.flatten
|
||||
|
@ -298,20 +241,19 @@ rescue LoadError
|
|||
end
|
||||
|
||||
task :pages do
|
||||
puts "#{'=' * 50} Running rake pages"
|
||||
ensure_git_cleanup do
|
||||
puts "#{'=' * 50} Running rake pages PROJ=#{ENV["PROJ"].inspect}"
|
||||
raise 'No ENV["PROJ"]!' unless proj = ENV["PROJ"]
|
||||
sh %{git checkout #{proj}-pages}
|
||||
sh %{git reset --hard origin/#{proj}-pages}
|
||||
sh %{git checkout haml-pages}
|
||||
sh %{git reset --hard origin/haml-pages}
|
||||
|
||||
Dir.chdir("/var/www/#{proj}-pages") do
|
||||
Dir.chdir("/var/www/haml-pages") do
|
||||
sh %{git fetch origin}
|
||||
|
||||
sh %{git checkout stable}
|
||||
sh %{git reset --hard origin/stable}
|
||||
|
||||
sh %{git checkout #{proj}-pages}
|
||||
sh %{git reset --hard origin/#{proj}-pages}
|
||||
sh %{git checkout haml-pages}
|
||||
sh %{git reset --hard origin/haml-pages}
|
||||
sh %{rake build --trace}
|
||||
sh %{mkdir -p tmp}
|
||||
sh %{touch tmp/restart.txt}
|
||||
|
@ -341,31 +283,21 @@ begin
|
|||
|
||||
desc <<END
|
||||
Run a profile of haml.
|
||||
ENGINE=str sets the engine to be profiled. Defaults to Haml.
|
||||
TIMES=n sets the number of runs. Defaults to 1000.
|
||||
FILE=str sets the file to profile.
|
||||
Defaults to 'standard' for Haml and 'complex' for Sass.
|
||||
FILE=str sets the file to profile. Defaults to 'standard'
|
||||
OUTPUT=str sets the ruby-prof output format.
|
||||
Can be Flat, CallInfo, or Graph. Defaults to Flat. Defaults to Flat.
|
||||
END
|
||||
task :profile do
|
||||
engine = (ENV['ENGINE'] || 'haml').downcase
|
||||
times = (ENV['TIMES'] || '1000').to_i
|
||||
file = ENV['FILE']
|
||||
|
||||
if engine == 'sass'
|
||||
require 'lib/sass'
|
||||
require 'lib/haml'
|
||||
|
||||
file = File.read(scope("test/sass/templates/#{file || 'complex'}.sass"))
|
||||
result = RubyProf.profile { times.times { Sass::Engine.new(file).render } }
|
||||
else
|
||||
require 'lib/haml'
|
||||
|
||||
file = File.read(scope("test/haml/templates/#{file || 'standard'}.haml"))
|
||||
obj = Object.new
|
||||
Haml::Engine.new(file).def_method(obj, :render)
|
||||
result = RubyProf.profile { times.times { obj.render } }
|
||||
end
|
||||
file = File.read(scope("test/haml/templates/#{file || 'standard'}.haml"))
|
||||
obj = Object.new
|
||||
Haml::Engine.new(file).def_method(obj, :render)
|
||||
result = RubyProf.profile { times.times { obj.render } }
|
||||
|
||||
RubyProf.const_get("#{(ENV['OUTPUT'] || 'Flat').capitalize}Printer").new(result).print
|
||||
end
|
||||
|
@ -434,7 +366,7 @@ end
|
|||
|
||||
task :handle_update do
|
||||
email_on_error do
|
||||
unless ENV["REF"] =~ %r{^refs/heads/(master|stable|(?:haml|sass)-pages)$}
|
||||
unless ENV["REF"] =~ %r{^refs/heads/(master|stable|haml-pages)$}
|
||||
puts "#{'=' * 20} Ignoring rake handle_update REF=#{ENV["REF"].inspect}"
|
||||
next
|
||||
end
|
||||
|
@ -451,13 +383,11 @@ task :handle_update do
|
|||
sh %{git checkout master}
|
||||
sh %{git reset --hard origin/master}
|
||||
|
||||
if branch == "master"
|
||||
case branch
|
||||
when "master"
|
||||
sh %{rake release_edge --trace}
|
||||
elsif branch == "stable"
|
||||
sh %{rake pages --trace PROJ=haml}
|
||||
sh %{rake pages --trace PROJ=sass}
|
||||
elsif branch =~ /^(haml|sass)-pages$/
|
||||
sh %{rake pages --trace PROJ=#{$1}}
|
||||
when "stable", "haml-pages"
|
||||
sh %{rake pages --trace}
|
||||
end
|
||||
|
||||
puts 'Done running handle_update'
|
||||
|
|
31
TODO
31
TODO
|
@ -1,4 +1,4 @@
|
|||
y# -*- mode: org -*-
|
||||
# -*- mode: org -*-
|
||||
#+STARTUP: nofold
|
||||
|
||||
* Documentation
|
||||
|
@ -10,9 +10,6 @@ y# -*- mode: org -*-
|
|||
* Code
|
||||
Keep track of error offsets everywhere
|
||||
Use this to show error location in messages
|
||||
Just clean up SassScript syntax errors in general
|
||||
Lexer errors in particular are icky
|
||||
See in particular error changes made in c07b5c8
|
||||
** Haml
|
||||
Support finer-grained HTML-escaping in filters
|
||||
Speed
|
||||
|
@ -25,29 +22,3 @@ y# -*- mode: org -*-
|
|||
Don't quote attributes that don't require it
|
||||
http://www.w3.org/TR/REC-html40/intro/sgmltut.html#h-3.2.2
|
||||
http://www.w3.org/TR/html5/syntax.html#attributes
|
||||
** Sass
|
||||
Benchmark the effects of storing the raw template in sassc
|
||||
If it's expensive, overload RootNode dumping/loading to dup and set @template to nil
|
||||
Then fall back on reading from actual file
|
||||
Make Rack middleware the default for Rails and Merb versions that support it
|
||||
CSS superset
|
||||
Classes are mixins
|
||||
Can refer to specific property values? Syntax?
|
||||
Pull in Compass watcher stuff
|
||||
Internationalization
|
||||
Particularly word constituents in Regexps
|
||||
Optimization
|
||||
http://csstidy.sourceforge.net/
|
||||
http://developer.yahoo.com/yui/compressor/
|
||||
Also comma-folding identical rules where possible
|
||||
Multiple levels
|
||||
0: No optimization
|
||||
1: Nothing that changes doc structure
|
||||
No comma-folding
|
||||
2: Anything that keeps functionality identical to O2 (default)
|
||||
3: Assume order of rules doesn't matter
|
||||
Comma-fold even if there are intervening rules that might interfere
|
||||
CSS3
|
||||
Add (optional) support for http://www.w3.org/TR/css3-values/#calc
|
||||
Cross-unit arithmetic should compile into this
|
||||
Should we use "mod" in Sass for consistency?
|
||||
|
|
|
@ -152,36 +152,9 @@ to
|
|||
|
||||
For other plugins, a little searching will probably turn up a way to fix them as well.
|
||||
|
||||
## Sass
|
||||
|
||||
### Can I use a variable from my controller in my Sass file?
|
||||
{#q-ruby-code}
|
||||
|
||||
No. Sass files aren't views.
|
||||
They're compiled once into static CSS files,
|
||||
then left along until they're changed and need to be compiled again.
|
||||
Not only don't you want to be running a full request cycle
|
||||
every time someone requests a stylesheet,
|
||||
but it's not a great idea to put much logic in there anyway
|
||||
due to how browsers handle them.
|
||||
|
||||
If you really need some sort of dynamic CSS,
|
||||
you can define your own {Sass::Script::Functions Sass functions} using Ruby
|
||||
that can access the database or other configuration.
|
||||
*Be aware when doing this that Sass files are by default only compiled once
|
||||
and then served statically.*
|
||||
|
||||
If you really, really need to compile Sass on each request,
|
||||
first make sure you have adequate caching set up.
|
||||
Then you can use {Sass::Engine} to render the code,
|
||||
using the {file:SASS_REFERENCE.md#custom-option `:custom` option}
|
||||
to pass in data that {Sass::Script::Functions::EvaluationContext#options can be accessed}
|
||||
from your Sass functions.
|
||||
|
||||
## You still haven't answered my question!
|
||||
|
||||
Sorry! Try looking at the [Haml](http://haml-lang.com/docs/yardoc/HAML_REFERENCE.md.html)
|
||||
or [Sass](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html) references,
|
||||
Sorry! Try looking at the [Haml](http://haml-lang.com/docs/yardoc/HAML_REFERENCE.md.html) reference,
|
||||
If you can't find an answer there,
|
||||
feel free to ask in `#haml` on irc.freenode.net
|
||||
or send an email to the [mailing list](http://groups.google.com/group/haml?hl=en).
|
||||
|
|
|
@ -1,753 +0,0 @@
|
|||
;;; haml-mode.el --- Major mode for editing Haml files
|
||||
|
||||
;; Copyright (c) 2007, 2008 Nathan Weizenbaum
|
||||
|
||||
;; Author: Nathan Weizenbaum
|
||||
;; URL: http://github.com/nex3/haml/tree/master
|
||||
;; Version: 3.0.14
|
||||
;; Created: 2007-03-08
|
||||
;; By: Nathan Weizenbaum
|
||||
;; Keywords: markup, language, html
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Because Haml's indentation schema is similar
|
||||
;; to that of YAML and Python, many indentation-related
|
||||
;; functions are similar to those in yaml-mode and python-mode.
|
||||
|
||||
;; To install, save this on your load path and add the following to
|
||||
;; your .emacs file:
|
||||
;;
|
||||
;; (require 'haml-mode)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'ruby-mode)
|
||||
|
||||
;; Additional (optional) libraries for fontification
|
||||
(require 'css-mode nil t)
|
||||
(require 'textile-mode nil t)
|
||||
(require 'markdown-mode nil t)
|
||||
(require 'javascript-mode "javascript" t)
|
||||
(require 'js nil t)
|
||||
|
||||
|
||||
;; User definable variables
|
||||
|
||||
(defgroup haml nil
|
||||
"Support for the Haml template language."
|
||||
:group 'languages
|
||||
:prefix "haml-")
|
||||
|
||||
(defcustom haml-mode-hook nil
|
||||
"Hook run when entering Haml mode."
|
||||
:type 'hook
|
||||
:group 'haml)
|
||||
|
||||
(defcustom haml-indent-offset 2
|
||||
"Amount of offset per level of indentation."
|
||||
:type 'integer
|
||||
:group 'haml)
|
||||
|
||||
(defcustom haml-backspace-backdents-nesting t
|
||||
"Non-nil to have `haml-electric-backspace' re-indent blocks of code.
|
||||
This means that all code nested beneath the backspaced line is
|
||||
re-indented along with the line itself."
|
||||
:type 'boolean
|
||||
:group 'haml)
|
||||
|
||||
(defvar haml-indent-function 'haml-indent-p
|
||||
"A function for checking if nesting is allowed.
|
||||
This function should look at the current line and return t
|
||||
if the next line could be nested within this line.
|
||||
|
||||
The function can also return a positive integer to indicate
|
||||
a specific level to which the current line could be indented.")
|
||||
|
||||
(defconst haml-tag-beg-re
|
||||
"^[ \t]*\\(?:[%\\.#][a-z0-9_:\\-]*\\)+\\(?:(.*)\\|{.*}\\|\\[.*\\]\\)*"
|
||||
"A regexp matching the beginning of a Haml tag, through (), {}, and [].")
|
||||
|
||||
(defvar haml-block-openers
|
||||
`(,(concat haml-tag-beg-re "[><]*[ \t]*$")
|
||||
"^[ \t]*[&!]?[-=~].*do[ \t]*\\(|.*|[ \t]*\\)?$"
|
||||
,(concat "^[ \t]*[&!]?[-=~][ \t]*\\("
|
||||
(regexp-opt '("if" "unless" "while" "until" "else"
|
||||
"begin" "elsif" "rescue" "ensure" "when"))
|
||||
"\\)")
|
||||
"^[ \t]*/\\(\\[.*\\]\\)?[ \t]*$"
|
||||
"^[ \t]*-#"
|
||||
"^[ \t]*:")
|
||||
"A list of regexps that match lines of Haml that open blocks.
|
||||
That is, a Haml line that can have text nested beneath it should
|
||||
be matched by a regexp in this list.")
|
||||
|
||||
;; Font lock
|
||||
|
||||
(defun haml-nested-regexp (re)
|
||||
"Create a regexp to match a block starting with RE.
|
||||
The line containing RE is matched, as well as all lines indented beneath it."
|
||||
(concat "^\\([ \t]*\\)" re "\\(\n\\(?:\\(?:\\1 .*\\| *\\)\n\\)*\\(?:\\1 .*\\| *\\)?\\)?"))
|
||||
|
||||
(defconst haml-font-lock-keywords
|
||||
`((,(haml-nested-regexp "\\(?:-#\\|/\\).*") 0 font-lock-comment-face)
|
||||
(,(haml-nested-regexp ":\\w+") 0 font-lock-string-face)
|
||||
(haml-highlight-ruby-filter-block 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-css-filter-block 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-textile-filter-block 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-markdown-filter-block 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-js-filter-block 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-interpolation 1 font-lock-variable-name-face prepend)
|
||||
(haml-highlight-ruby-tag 1 font-lock-preprocessor-face)
|
||||
(haml-highlight-ruby-script 1 font-lock-preprocessor-face)
|
||||
("^!!!.*" 0 font-lock-constant-face)
|
||||
("| *$" 0 font-lock-string-face)))
|
||||
|
||||
(defconst haml-filter-re "^[ \t]*:\\w+")
|
||||
(defconst haml-comment-re "^[ \t]*\\(?:-\\#\\|/\\)")
|
||||
|
||||
(defun haml-fontify-region (beg end keywords syntax-table syntactic-keywords)
|
||||
"Fontify a region between BEG and END using another mode's fontification.
|
||||
|
||||
KEYWORDS, SYNTAX-TABLE, and SYNTACTIC-KEYWORDS are the values of that mode's
|
||||
`font-lock-keywords', `font-lock-syntax-table',
|
||||
and `font-lock-syntactic-keywords', respectively."
|
||||
(save-excursion
|
||||
(save-match-data
|
||||
(let ((font-lock-keywords keywords)
|
||||
(font-lock-syntax-table syntax-table)
|
||||
(font-lock-syntactic-keywords syntactic-keywords)
|
||||
(font-lock-multiline 'undecided)
|
||||
font-lock-keywords-only
|
||||
font-lock-extend-region-functions
|
||||
font-lock-keywords-case-fold-search)
|
||||
;; font-lock-fontify-region apparently isn't inclusive,
|
||||
;; so we have to move the beginning back one char
|
||||
(font-lock-fontify-region (- beg 1) end)))))
|
||||
|
||||
(defun haml-fontify-region-as-ruby (beg end)
|
||||
"Use Ruby's font-lock variables to fontify the region between BEG and END."
|
||||
(haml-fontify-region beg end ruby-font-lock-keywords
|
||||
ruby-font-lock-syntax-table
|
||||
ruby-font-lock-syntactic-keywords))
|
||||
|
||||
(defun haml-handle-filter (filter-name limit fn)
|
||||
"If a FILTER-NAME filter is found within LIMIT, run FN on that filter.
|
||||
|
||||
FN is passed a pair of points representing the beginning and end
|
||||
of the filtered text."
|
||||
(when (re-search-forward (haml-nested-regexp (concat ":" filter-name)) limit t)
|
||||
(funcall fn (+ 2 (match-beginning 2)) (match-end 2))))
|
||||
|
||||
(defun haml-fontify-filter-region (filter-name limit &rest fontify-region-args)
|
||||
"If a FILTER-NAME filter is found within LIMIT, fontify it.
|
||||
|
||||
The fontification is done by passing FONTIFY-REGION-ARGS to
|
||||
`haml-fontify-region'."
|
||||
(haml-handle-filter filter-name limit
|
||||
(lambda (beg end)
|
||||
(apply 'haml-fontify-region
|
||||
(append (list beg end)
|
||||
fontify-region-args)))))
|
||||
|
||||
(defun haml-highlight-ruby-filter-block (limit)
|
||||
"If a :ruby filter is found within LIMIT, highlight it."
|
||||
(haml-handle-filter "ruby" limit 'haml-fontify-region-as-ruby))
|
||||
|
||||
(defun haml-highlight-css-filter-block (limit)
|
||||
"If a :css filter is found within LIMIT, highlight it.
|
||||
|
||||
This requires that `css-mode' is available.
|
||||
`css-mode' is included with Emacs 23."
|
||||
(if (boundp 'css-font-lock-keywords)
|
||||
(haml-fontify-filter-region "css" limit css-font-lock-keywords nil nil)))
|
||||
|
||||
(defun haml-highlight-js-filter-block (limit)
|
||||
"If a :javascript filter is found within LIMIT, highlight it.
|
||||
|
||||
This requires that Karl Landström's javascript mode be available, either as the
|
||||
\"js.el\" bundled with Emacs 23, or as \"javascript.el\" found in ELPA and
|
||||
elsewhere."
|
||||
(let ((keywords (or (and (featurep 'js) js--font-lock-keywords-3)
|
||||
(and (featurep 'javascript-mode) js-font-lock-keywords-3)))
|
||||
(syntax-table (or (and (featurep 'js) js-mode-syntax-table)
|
||||
(and (featurep 'javascript-mode) javascript-mode-syntax-table))))
|
||||
(when keywords
|
||||
(haml-fontify-filter-region "javascript" limit keywords syntax-table nil))))
|
||||
|
||||
(defun haml-highlight-textile-filter-block (limit)
|
||||
"If a :textile filter is found within LIMIT, highlight it.
|
||||
|
||||
This requires that `textile-mode' be available.
|
||||
|
||||
Note that the results are not perfect, since `textile-mode' expects
|
||||
certain constructs such as \"h1.\" to be at the beginning of a line,
|
||||
and indented Haml filters always have leading whitespace."
|
||||
(if (boundp 'textile-font-lock-keywords)
|
||||
(haml-fontify-filter-region "textile" limit textile-font-lock-keywords nil nil)))
|
||||
|
||||
(defun haml-highlight-markdown-filter-block (limit)
|
||||
"If a :markdown filter is found within LIMIT, highlight it.
|
||||
|
||||
This requires that `markdown-mode' be available."
|
||||
(if (boundp 'markdown-mode-font-lock-keywords)
|
||||
(haml-fontify-filter-region "markdown" limit
|
||||
markdown-mode-font-lock-keywords
|
||||
markdown-mode-syntax-table
|
||||
nil)))
|
||||
|
||||
(defun haml-highlight-ruby-script (limit)
|
||||
"Highlight a Ruby script expression (-, =, or ~).
|
||||
LIMIT works as it does in `re-search-forward'."
|
||||
(when (re-search-forward "^[ \t]*\\(-\\|[&!]?[=~]\\) \\(.*\\)$" limit t)
|
||||
(haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))))
|
||||
|
||||
(defun haml-highlight-ruby-tag (limit)
|
||||
"Highlight Ruby code within a Haml tag.
|
||||
LIMIT works as it does in `re-search-forward'.
|
||||
|
||||
This highlights the tag attributes and object refs of the tag,
|
||||
as well as the script expression (-, =, or ~) following the tag.
|
||||
|
||||
For example, this will highlight all of the following:
|
||||
%p{:foo => 'bar'}
|
||||
%p[@bar]
|
||||
%p= 'baz'
|
||||
%p{:foo => 'bar'}[@bar]= 'baz'"
|
||||
(when (re-search-forward "^[ \t]*[%.#]" limit t)
|
||||
(forward-char -1)
|
||||
|
||||
;; Highlight tag, classes, and ids
|
||||
(while (haml-move "\\([.#%]\\)[a-z0-9_:\\-]*")
|
||||
(put-text-property (match-beginning 0) (match-end 0) 'face
|
||||
(case (char-after (match-beginning 1))
|
||||
(?% font-lock-function-name-face)
|
||||
(?# font-lock-keyword-face)
|
||||
(?. font-lock-type-face))))
|
||||
|
||||
(block loop
|
||||
(while t
|
||||
(let ((eol (save-excursion (end-of-line) (point))))
|
||||
(case (char-after)
|
||||
;; Highlight obj refs
|
||||
(?\[
|
||||
(let ((beg (point)))
|
||||
(haml-limited-forward-sexp eol)
|
||||
(haml-fontify-region-as-ruby beg (point))))
|
||||
;; Highlight new attr hashes
|
||||
(?\(
|
||||
(forward-char 1)
|
||||
(while
|
||||
(and (haml-parse-new-attr-hash
|
||||
(lambda (type beg end)
|
||||
(case type
|
||||
(name (put-text-property beg end 'face font-lock-constant-face))
|
||||
(value (haml-fontify-region-as-ruby beg end)))))
|
||||
(not (eobp)))
|
||||
(forward-line 1)
|
||||
(beginning-of-line)))
|
||||
;; Highlight old attr hashes
|
||||
(?\{
|
||||
(let ((beg (point)))
|
||||
(haml-limited-forward-sexp eol)
|
||||
|
||||
;; Check for multiline
|
||||
(while (and (eolp) (eq (char-before) ?,) (not (eobp)))
|
||||
(forward-line)
|
||||
(let ((eol (save-excursion (end-of-line) (point))))
|
||||
;; If no sexps are closed,
|
||||
;; we're still continuing a multiline hash
|
||||
(if (>= (car (parse-partial-sexp (point) eol)) 0)
|
||||
(end-of-line)
|
||||
;; If sexps have been closed,
|
||||
;; set the point at the end of the total sexp
|
||||
(goto-char beg)
|
||||
(haml-limited-forward-sexp eol))))
|
||||
|
||||
(haml-fontify-region-as-ruby (+ 1 beg) (point))))
|
||||
(t (return-from loop))))))
|
||||
|
||||
;; Move past end chars
|
||||
(when (looking-at "[<>&!]+") (goto-char (match-end 0)))
|
||||
;; Highlight script
|
||||
(if (looking-at "\\([=~]\\) \\(.*\\)$")
|
||||
(haml-fontify-region-as-ruby (match-beginning 2) (match-end 2))
|
||||
;; Give font-lock something to highlight
|
||||
(forward-char -1)
|
||||
(looking-at "\\(\\)"))
|
||||
t))
|
||||
|
||||
(defun haml-move (re)
|
||||
"Try matching and moving to the end of regular expression RE.
|
||||
Returns non-nil if the expression was sucessfully matched."
|
||||
(when (looking-at re)
|
||||
(goto-char (match-end 0))
|
||||
t))
|
||||
|
||||
(defun haml-highlight-interpolation (limit)
|
||||
"Highlight Ruby interpolation (#{foo}).
|
||||
LIMIT works as it does in `re-search-forward'."
|
||||
(when (re-search-forward "\\(#{\\)" limit t)
|
||||
(save-match-data
|
||||
(forward-char -1)
|
||||
(let ((beg (point)))
|
||||
(haml-limited-forward-sexp limit)
|
||||
(haml-fontify-region-as-ruby (+ 1 beg) (point)))
|
||||
|
||||
(when (eq (char-before) ?})
|
||||
(put-text-property (- (point) 1) (point)
|
||||
'face font-lock-variable-name-face))
|
||||
t)))
|
||||
|
||||
(defun haml-limited-forward-sexp (limit &optional arg)
|
||||
"Move forward using `forward-sexp' or to LIMIT, whichever comes first.
|
||||
With ARG, do it that many times."
|
||||
(let (forward-sexp-function)
|
||||
(condition-case err
|
||||
(save-restriction
|
||||
(narrow-to-region (point) limit)
|
||||
(forward-sexp arg))
|
||||
(scan-error
|
||||
(unless (equal (nth 1 err) "Unbalanced parentheses")
|
||||
(signal 'scan-error (cdr err)))
|
||||
(goto-char limit)))))
|
||||
|
||||
(defun* haml-extend-region-filters-comments ()
|
||||
"Extend the font-lock region to encompass filters and comments."
|
||||
(let ((old-beg font-lock-beg)
|
||||
(old-end font-lock-end))
|
||||
(save-excursion
|
||||
(goto-char font-lock-beg)
|
||||
(beginning-of-line)
|
||||
(unless (or (looking-at haml-filter-re)
|
||||
(looking-at haml-comment-re))
|
||||
(return-from haml-extend-region-filters-comments))
|
||||
(setq font-lock-beg (point))
|
||||
(haml-forward-sexp)
|
||||
(beginning-of-line)
|
||||
(setq font-lock-end (max font-lock-end (point))))
|
||||
(or (/= old-beg font-lock-beg)
|
||||
(/= old-end font-lock-end))))
|
||||
|
||||
(defun* haml-extend-region-multiline-hashes ()
|
||||
"Extend the font-lock region to encompass multiline attribute hashes."
|
||||
(let ((old-beg font-lock-beg)
|
||||
(old-end font-lock-end))
|
||||
(save-excursion
|
||||
(goto-char font-lock-beg)
|
||||
(let ((attr-props (haml-parse-multiline-attr-hash))
|
||||
multiline-end)
|
||||
(when attr-props
|
||||
(setq font-lock-beg (cdr (assq 'point attr-props)))
|
||||
|
||||
(end-of-line)
|
||||
;; Move through multiline attrs
|
||||
(when (eq (char-before) ?,)
|
||||
(save-excursion
|
||||
(while (progn (end-of-line)
|
||||
(and (eq (char-before) ?,) (not (eobp))))
|
||||
(forward-line))
|
||||
|
||||
(forward-line -1)
|
||||
(end-of-line)
|
||||
(setq multiline-end (point))))
|
||||
|
||||
(goto-char (+ (cdr (assq 'point attr-props))
|
||||
(cdr (assq 'hash-indent attr-props))
|
||||
-1))
|
||||
(haml-limited-forward-sexp
|
||||
(or multiline-end
|
||||
(save-excursion (end-of-line) (point))))
|
||||
(setq font-lock-end (max font-lock-end (point))))))
|
||||
(or (/= old-beg font-lock-beg)
|
||||
(/= old-end font-lock-end))))
|
||||
|
||||
|
||||
;; Mode setup
|
||||
|
||||
(defvar haml-mode-syntax-table
|
||||
(let ((table (make-syntax-table)))
|
||||
(modify-syntax-entry ?: "." table)
|
||||
(modify-syntax-entry ?_ "w" table)
|
||||
table)
|
||||
"Syntax table in use in `haml-mode' buffers.")
|
||||
|
||||
(defvar haml-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map [backspace] 'haml-electric-backspace)
|
||||
(define-key map "\C-?" 'haml-electric-backspace)
|
||||
(define-key map "\C-c\C-f" 'haml-forward-sexp)
|
||||
(define-key map "\C-c\C-b" 'haml-backward-sexp)
|
||||
(define-key map "\C-c\C-u" 'haml-up-list)
|
||||
(define-key map "\C-c\C-d" 'haml-down-list)
|
||||
(define-key map "\C-c\C-k" 'haml-kill-line-and-indent)
|
||||
(define-key map "\C-c\C-r" 'haml-output-region)
|
||||
(define-key map "\C-c\C-l" 'haml-output-buffer)
|
||||
map))
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode haml-mode fundamental-mode "Haml"
|
||||
"Major mode for editing Haml files.
|
||||
|
||||
\\{haml-mode-map}"
|
||||
(set-syntax-table haml-mode-syntax-table)
|
||||
(add-to-list 'font-lock-extend-region-functions 'haml-extend-region-filters-comments)
|
||||
(add-to-list 'font-lock-extend-region-functions 'haml-extend-region-multiline-hashes)
|
||||
(set (make-local-variable 'font-lock-multiline) t)
|
||||
(set (make-local-variable 'indent-line-function) 'haml-indent-line)
|
||||
(set (make-local-variable 'indent-region-function) 'haml-indent-region)
|
||||
(set (make-local-variable 'parse-sexp-lookup-properties) t)
|
||||
(setq comment-start "-#")
|
||||
(setq font-lock-defaults '((haml-font-lock-keywords) t t)))
|
||||
|
||||
;; Useful functions
|
||||
|
||||
(defun haml-comment-block ()
|
||||
"Comment the current block of Haml code."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(let ((indent (current-indentation)))
|
||||
(back-to-indentation)
|
||||
(insert "-#")
|
||||
(newline)
|
||||
(indent-to indent)
|
||||
(beginning-of-line)
|
||||
(haml-mark-sexp)
|
||||
(haml-reindent-region-by haml-indent-offset))))
|
||||
|
||||
(defun haml-uncomment-block ()
|
||||
"Uncomment the current block of Haml code."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(while (not (looking-at haml-comment-re))
|
||||
(haml-up-list)
|
||||
(beginning-of-line))
|
||||
(haml-mark-sexp)
|
||||
(kill-line 1)
|
||||
(haml-reindent-region-by (- haml-indent-offset))))
|
||||
|
||||
(defun haml-replace-region (start end)
|
||||
"Replace the current block of Haml code with the HTML equivalent.
|
||||
Called from a program, START and END specify the region to indent."
|
||||
(interactive "r")
|
||||
(save-excursion
|
||||
(goto-char end)
|
||||
(setq end (point-marker))
|
||||
(goto-char start)
|
||||
(let ((ci (current-indentation)))
|
||||
(while (re-search-forward "^ +" end t)
|
||||
(replace-match (make-string (- (current-indentation) ci) ? ))))
|
||||
(shell-command-on-region start end "haml" "haml-output" t)))
|
||||
|
||||
(defun haml-output-region (start end)
|
||||
"Displays the HTML output for the current block of Haml code.
|
||||
Called from a program, START and END specify the region to indent."
|
||||
(interactive "r")
|
||||
(kill-new (buffer-substring start end))
|
||||
(with-temp-buffer
|
||||
(yank)
|
||||
(haml-indent-region (point-min) (point-max))
|
||||
(shell-command-on-region (point-min) (point-max) "haml" "haml-output")))
|
||||
|
||||
(defun haml-output-buffer ()
|
||||
"Displays the HTML output for entire buffer."
|
||||
(interactive)
|
||||
(haml-output-region (point-min) (point-max)))
|
||||
|
||||
;; Navigation
|
||||
|
||||
(defun haml-forward-through-whitespace (&optional backward)
|
||||
"Move the point forward through any whitespace.
|
||||
The point will move forward at least one line, until it reaches
|
||||
either the end of the buffer or a line with no whitespace.
|
||||
|
||||
If BACKWARD is non-nil, move the point backward instead."
|
||||
(let ((arg (if backward -1 1))
|
||||
(endp (if backward 'bobp 'eobp)))
|
||||
(loop do (forward-line arg)
|
||||
while (and (not (funcall endp))
|
||||
(looking-at "^[ \t]*$")))))
|
||||
|
||||
(defun haml-at-indent-p ()
|
||||
"Return non-nil if the point is before any text on the line."
|
||||
(let ((opoint (point)))
|
||||
(save-excursion
|
||||
(back-to-indentation)
|
||||
(>= (point) opoint))))
|
||||
|
||||
(defun haml-forward-sexp (&optional arg)
|
||||
"Move forward across one nested expression.
|
||||
With ARG, do it that many times. Negative arg -N means move
|
||||
backward across N balanced expressions.
|
||||
|
||||
A sexp in Haml is defined as a line of Haml code as well as any
|
||||
lines nested beneath it."
|
||||
(interactive "p")
|
||||
(or arg (setq arg 1))
|
||||
(if (and (< arg 0) (not (haml-at-indent-p)))
|
||||
(back-to-indentation)
|
||||
(while (/= arg 0)
|
||||
(let ((indent (current-indentation)))
|
||||
(loop do (haml-forward-through-whitespace (< arg 0))
|
||||
while (and (not (eobp))
|
||||
(not (bobp))
|
||||
(> (current-indentation) indent)))
|
||||
(back-to-indentation)
|
||||
(setq arg (+ arg (if (> arg 0) -1 1)))))))
|
||||
|
||||
(defun haml-backward-sexp (&optional arg)
|
||||
"Move backward across one nested expression.
|
||||
With ARG, do it that many times. Negative arg -N means move
|
||||
forward across N balanced expressions.
|
||||
|
||||
A sexp in Haml is defined as a line of Haml code as well as any
|
||||
lines nested beneath it."
|
||||
(interactive "p")
|
||||
(haml-forward-sexp (if arg (- arg) -1)))
|
||||
|
||||
(defun haml-up-list (&optional arg)
|
||||
"Move out of one level of nesting.
|
||||
With ARG, do this that many times."
|
||||
(interactive "p")
|
||||
(or arg (setq arg 1))
|
||||
(while (> arg 0)
|
||||
(let ((indent (current-indentation)))
|
||||
(loop do (haml-forward-through-whitespace t)
|
||||
while (and (not (bobp))
|
||||
(>= (current-indentation) indent)))
|
||||
(setq arg (- arg 1))))
|
||||
(back-to-indentation))
|
||||
|
||||
(defun haml-down-list (&optional arg)
|
||||
"Move down one level of nesting.
|
||||
With ARG, do this that many times."
|
||||
(interactive "p")
|
||||
(or arg (setq arg 1))
|
||||
(while (> arg 0)
|
||||
(let ((indent (current-indentation)))
|
||||
(haml-forward-through-whitespace)
|
||||
(when (<= (current-indentation) indent)
|
||||
(haml-forward-through-whitespace t)
|
||||
(back-to-indentation)
|
||||
(error "Nothing is nested beneath this line"))
|
||||
(setq arg (- arg 1))))
|
||||
(back-to-indentation))
|
||||
|
||||
(defun haml-mark-sexp ()
|
||||
"Mark the next Haml block."
|
||||
(let ((forward-sexp-function 'haml-forward-sexp))
|
||||
(mark-sexp)))
|
||||
|
||||
(defun haml-mark-sexp-but-not-next-line ()
|
||||
"Mark the next Haml block, but not the next line.
|
||||
Put the mark at the end of the last line of the sexp rather than
|
||||
the first non-whitespace character of the next line."
|
||||
(haml-mark-sexp)
|
||||
(set-mark
|
||||
(save-excursion
|
||||
(goto-char (mark))
|
||||
(forward-line -1)
|
||||
(end-of-line)
|
||||
(point))))
|
||||
|
||||
;; Indentation and electric keys
|
||||
|
||||
(defun* haml-indent-p ()
|
||||
"Returns t if the current line can have lines nested beneath it."
|
||||
(let ((attr-props (haml-parse-multiline-attr-hash)))
|
||||
(when attr-props
|
||||
(return-from haml-indent-p
|
||||
(if (haml-unclosed-attr-hash-p) (cdr (assq 'hash-indent attr-props))
|
||||
(list (+ (cdr (assq 'indent attr-props)) haml-indent-offset) nil)))))
|
||||
(loop for opener in haml-block-openers
|
||||
if (looking-at opener) return t
|
||||
finally return nil))
|
||||
|
||||
(defun* haml-parse-multiline-attr-hash ()
|
||||
"Parses a multiline attribute hash, and returns
|
||||
an alist with the following keys:
|
||||
|
||||
INDENT is the indentation of the line beginning the hash.
|
||||
|
||||
HASH-INDENT is the indentation of the first character
|
||||
within the attribute hash.
|
||||
|
||||
POINT is the character position at the beginning of the line
|
||||
beginning the hash."
|
||||
(save-excursion
|
||||
(while t
|
||||
(beginning-of-line)
|
||||
(if (looking-at (concat haml-tag-beg-re "\\([{(]\\)"))
|
||||
(progn
|
||||
(goto-char (- (match-end 0) 1))
|
||||
(haml-limited-forward-sexp (save-excursion (end-of-line) (point)))
|
||||
(return-from haml-parse-multiline-attr-hash
|
||||
(when (or (string-equal (match-string 1) "(") (eq (char-before) ?,))
|
||||
`((indent . ,(current-indentation))
|
||||
(hash-indent . ,(- (match-end 0) (match-beginning 0)))
|
||||
(point . ,(match-beginning 0))))))
|
||||
(when (bobp) (return-from haml-parse-multiline-attr-hash))
|
||||
(forward-line -1)
|
||||
(unless (haml-unclosed-attr-hash-p)
|
||||
(return-from haml-parse-multiline-attr-hash))))))
|
||||
|
||||
(defun* haml-unclosed-attr-hash-p ()
|
||||
"Return t if this line has an unclosed attribute hash, new or old."
|
||||
(save-excursion
|
||||
(end-of-line)
|
||||
(when (eq (char-before) ?,) (return-from haml-unclosed-attr-hash-p t))
|
||||
(re-search-backward "(\\|^")
|
||||
(haml-move "(")
|
||||
(haml-parse-new-attr-hash)))
|
||||
|
||||
(defun* haml-parse-new-attr-hash (&optional (fn (lambda (type beg end) ())))
|
||||
"Parse a new-style attribute hash on this line, and returns
|
||||
t if it's not finished on the current line.
|
||||
|
||||
FN should take three parameters: TYPE, BEG, and END.
|
||||
TYPE is the type of text parsed ('name or 'value)
|
||||
and BEG and END delimit that text in the buffer."
|
||||
(let ((eol (save-excursion (end-of-line) (point))))
|
||||
(while (not (haml-move ")"))
|
||||
(haml-move "[ \t]*")
|
||||
(unless (haml-move "[a-z0-9_:\\-]+")
|
||||
(return-from haml-parse-new-attr-hash (haml-move "[ \t]*$")))
|
||||
(funcall fn 'name (match-beginning 0) (match-end 0))
|
||||
(haml-move "[ \t]*")
|
||||
(when (haml-move "=")
|
||||
(haml-move "[ \t]*")
|
||||
(unless (looking-at "[\"'@a-z]") (return-from haml-parse-new-attr-hash))
|
||||
(let ((beg (point)))
|
||||
(haml-limited-forward-sexp eol)
|
||||
(funcall fn 'value beg (point)))
|
||||
(haml-move "[ \t]*")))
|
||||
nil))
|
||||
|
||||
(defun haml-compute-indentation ()
|
||||
"Calculate the maximum sensible indentation for the current line."
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(if (bobp) (list 0 nil)
|
||||
(haml-forward-through-whitespace t)
|
||||
(let ((indent (funcall haml-indent-function)))
|
||||
(cond
|
||||
((consp indent) indent)
|
||||
((integerp indent) (list indent t))
|
||||
(indent (list (+ (current-indentation) haml-indent-offset) nil))
|
||||
(t (list (current-indentation) nil)))))))
|
||||
|
||||
(defun haml-indent-region (start end)
|
||||
"Indent each nonblank line in the region.
|
||||
This is done by indenting the first line based on
|
||||
`haml-compute-indentation' and preserving the relative
|
||||
indentation of the rest of the region. START and END specify the
|
||||
region to indent.
|
||||
|
||||
If this command is used multiple times in a row, it will cycle
|
||||
between possible indentations."
|
||||
(save-excursion
|
||||
(goto-char end)
|
||||
(setq end (point-marker))
|
||||
(goto-char start)
|
||||
(let (this-line-column current-column
|
||||
(next-line-column
|
||||
(if (and (equal last-command this-command) (/= (current-indentation) 0))
|
||||
(* (/ (- (current-indentation) 1) haml-indent-offset) haml-indent-offset)
|
||||
(car (haml-compute-indentation)))))
|
||||
(while (< (point) end)
|
||||
(setq this-line-column next-line-column
|
||||
current-column (current-indentation))
|
||||
;; Delete whitespace chars at beginning of line
|
||||
(delete-horizontal-space)
|
||||
(unless (eolp)
|
||||
(setq next-line-column (save-excursion
|
||||
(loop do (forward-line 1)
|
||||
while (and (not (eobp)) (looking-at "^[ \t]*$")))
|
||||
(+ this-line-column
|
||||
(- (current-indentation) current-column))))
|
||||
;; Don't indent an empty line
|
||||
(unless (eolp) (indent-to this-line-column)))
|
||||
(forward-line 1)))
|
||||
(move-marker end nil)))
|
||||
|
||||
(defun haml-indent-line ()
|
||||
"Indent the current line.
|
||||
The first time this command is used, the line will be indented to the
|
||||
maximum sensible indentation. Each immediately subsequent usage will
|
||||
back-dent the line by `haml-indent-offset' spaces. On reaching column
|
||||
0, it will cycle back to the maximum sensible indentation."
|
||||
(interactive "*")
|
||||
(let ((ci (current-indentation))
|
||||
(cc (current-column)))
|
||||
(destructuring-bind (need strict) (haml-compute-indentation)
|
||||
(save-excursion
|
||||
(beginning-of-line)
|
||||
(delete-horizontal-space)
|
||||
(if (and (not strict) (equal last-command this-command) (/= ci 0))
|
||||
(indent-to (* (/ (- ci 1) haml-indent-offset) haml-indent-offset))
|
||||
(indent-to need))))
|
||||
(when (< (current-column) (current-indentation))
|
||||
(forward-to-indentation 0))))
|
||||
|
||||
(defun haml-reindent-region-by (n)
|
||||
"Add N spaces to the beginning of each line in the region.
|
||||
If N is negative, will remove the spaces instead. Assumes all
|
||||
lines in the region have indentation >= that of the first line."
|
||||
(let* ((ci (current-indentation))
|
||||
(indent-rx
|
||||
(concat "^"
|
||||
(if indent-tabs-mode
|
||||
(concat (make-string (/ ci tab-width) ?\t)
|
||||
(make-string (mod ci tab-width) ?\t))
|
||||
(make-string ci ?\s)))))
|
||||
(save-excursion
|
||||
(while (re-search-forward indent-rx (mark) t)
|
||||
(let ((ci (current-indentation)))
|
||||
(delete-horizontal-space)
|
||||
(beginning-of-line)
|
||||
(indent-to (max 0 (+ ci n))))))))
|
||||
|
||||
(defun haml-electric-backspace (arg)
|
||||
"Delete characters or back-dent the current line.
|
||||
If invoked following only whitespace on a line, will back-dent
|
||||
the line and all nested lines to the immediately previous
|
||||
multiple of `haml-indent-offset' spaces. With ARG, do it that
|
||||
many times.
|
||||
|
||||
Set `haml-backspace-backdents-nesting' to nil to just back-dent
|
||||
the current line."
|
||||
(interactive "*p")
|
||||
(if (or (/= (current-indentation) (current-column))
|
||||
(bolp)
|
||||
(looking-at "^[ \t]+$"))
|
||||
(backward-delete-char arg)
|
||||
(save-excursion
|
||||
(let ((ci (current-column)))
|
||||
(beginning-of-line)
|
||||
(if haml-backspace-backdents-nesting
|
||||
(haml-mark-sexp-but-not-next-line)
|
||||
(set-mark (save-excursion (end-of-line) (point))))
|
||||
(haml-reindent-region-by (* (- arg) haml-indent-offset))
|
||||
(pop-mark)))
|
||||
(back-to-indentation)))
|
||||
|
||||
(defun haml-kill-line-and-indent ()
|
||||
"Kill the current line, and re-indent all lines nested beneath it."
|
||||
(interactive)
|
||||
(beginning-of-line)
|
||||
(haml-mark-sexp-but-not-next-line)
|
||||
(kill-line 1)
|
||||
(haml-reindent-region-by (* -1 haml-indent-offset)))
|
||||
|
||||
(defun haml-indent-string ()
|
||||
"Return the indentation string for `haml-indent-offset'."
|
||||
(mapconcat 'identity (make-list haml-indent-offset " ") ""))
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.haml$" . haml-mode))
|
||||
|
||||
;; Setup/Activation
|
||||
(provide 'haml-mode)
|
||||
;;; haml-mode.el ends here
|
|
@ -7,9 +7,9 @@ require 'rubygems'
|
|||
HAML_GEMSPEC = Gem::Specification.new do |spec|
|
||||
spec.rubyforge_project = 'haml'
|
||||
spec.name = File.exist?(File.dirname(__FILE__) + '/EDGE_GEM_VERSION') ? 'haml-edge' : 'haml'
|
||||
spec.summary = "An elegant, structured XHTML/XML templating engine.\nComes with Sass, a similar CSS templating engine."
|
||||
spec.summary = "An elegant, structured XHTML/XML templating engine."
|
||||
spec.version = File.read(File.dirname(__FILE__) + '/VERSION').strip
|
||||
spec.authors = ['Nathan Weizenbaum', 'Chris Eppstein', 'Hampton Catlin']
|
||||
spec.authors = ['Nathan Weizenbaum', 'Hampton Catlin']
|
||||
spec.email = 'haml@googlegroups.com'
|
||||
spec.description = <<-END
|
||||
Haml (HTML Abstraction Markup Language) is a layer on top of XHTML or XML
|
||||
|
@ -25,7 +25,7 @@ HAML_GEMSPEC = Gem::Specification.new do |spec|
|
|||
spec.add_development_dependency 'maruku', '>= 0.5.9'
|
||||
|
||||
readmes = Dir['*'].reject{ |x| x =~ /(^|[^.a-z])[a-z]+/ || x == "TODO" }
|
||||
spec.executables = ['haml', 'html2haml', 'sass', 'css2sass', 'sass-convert']
|
||||
spec.executables = ['haml', 'html2haml']
|
||||
spec.files = Dir['rails/init.rb', 'lib/**/*', 'vendor/**/*',
|
||||
'bin/*', 'test/**/*', 'extra/**/*', 'Rakefile', 'init.rb',
|
||||
'.yardopts'] + readmes
|
||||
|
|
2
init.rb
2
init.rb
|
@ -13,6 +13,6 @@ rescue LoadError
|
|||
end
|
||||
end
|
||||
|
||||
# Load Haml and Sass.
|
||||
# Load Haml.
|
||||
# Haml may be undefined if we're running gems:install.
|
||||
Haml.init_rails(binding) if defined?(Haml)
|
||||
|
|
|
@ -33,12 +33,10 @@ module Haml
|
|||
# it's just passed in in case it needs to be used in the future
|
||||
def self.init_rails(binding)
|
||||
# No &method here for Rails 2.1 compatibility
|
||||
%w[haml/template sass sass/plugin].each {|f| require f}
|
||||
%w[haml/template].each {|f| require f}
|
||||
end
|
||||
end
|
||||
|
||||
require 'haml/util'
|
||||
unless $0 =~ /sass(-convert)?$/
|
||||
require 'haml/engine'
|
||||
require 'haml/railtie'
|
||||
end
|
||||
require 'haml/engine'
|
||||
require 'haml/railtie'
|
||||
|
|
455
lib/haml/exec.rb
455
lib/haml/exec.rb
|
@ -2,7 +2,7 @@ require 'optparse'
|
|||
require 'fileutils'
|
||||
|
||||
module Haml
|
||||
# This module handles the various Haml executables (`haml`, `sass`, `sass-convert`, etc).
|
||||
# This module handles the various Haml executables (`haml` and `haml-convert`).
|
||||
module Exec
|
||||
# An abstract class that encapsulates the executable code for all three executables.
|
||||
class Generic
|
||||
|
@ -91,7 +91,7 @@ module Haml
|
|||
end
|
||||
|
||||
opts.on_tail("-v", "--version", "Print version") do
|
||||
puts("Haml/Sass #{::Haml.version[:string]}")
|
||||
puts("Haml #{::Haml.version[:string]}")
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
@ -168,234 +168,6 @@ MESSAGE
|
|||
end
|
||||
end
|
||||
|
||||
# The `sass` executable.
|
||||
class Sass < Generic
|
||||
# @param args [Array<String>] The command-line arguments
|
||||
def initialize(args)
|
||||
super
|
||||
@options[:for_engine] = {
|
||||
:load_paths => ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR)
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Tells optparse how to parse the arguments.
|
||||
#
|
||||
# @param opts [OptionParser]
|
||||
def set_opts(opts)
|
||||
super
|
||||
|
||||
opts.banner = <<END
|
||||
Usage: sass [options] [INPUT] [OUTPUT]
|
||||
|
||||
Description:
|
||||
Converts SCSS or Sass files to CSS.
|
||||
|
||||
Options:
|
||||
END
|
||||
|
||||
opts.on('--scss',
|
||||
'Use the CSS-superset SCSS syntax.') do
|
||||
@options[:for_engine][:syntax] = :scss
|
||||
end
|
||||
opts.on('--watch', 'Watch files or directories for changes.',
|
||||
'The location of the generated CSS can be set using a colon:',
|
||||
' sass --watch input.sass:output.css',
|
||||
' sass --watch input-dir:output-dir') do
|
||||
@options[:watch] = true
|
||||
end
|
||||
opts.on('--update', 'Compile files or directories to CSS.',
|
||||
'Locations are set like --watch.') do
|
||||
@options[:update] = true
|
||||
end
|
||||
opts.on('--stop-on-error', 'If a file fails to compile, exit immediately.',
|
||||
'Only meaningful for --watch and --update.') do
|
||||
@options[:stop_on_error] = true
|
||||
end
|
||||
opts.on('-c', '--check', "Just check syntax, don't evaluate.") do
|
||||
require 'stringio'
|
||||
@options[:check_syntax] = true
|
||||
@options[:output] = StringIO.new
|
||||
end
|
||||
opts.on('-t', '--style NAME',
|
||||
'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
|
||||
end
|
||||
opts.on('-l', '--line-numbers', '--line-comments',
|
||||
'Emit comments in the generated CSS indicating the corresponding sass line.') do
|
||||
@options[:for_engine][:line_numbers] = true
|
||||
end
|
||||
opts.on('-i', '--interactive',
|
||||
'Run an interactive SassScript shell.') do
|
||||
@options[:interactive] = true
|
||||
end
|
||||
opts.on('-I', '--load-path PATH', 'Add a sass import path.') do |path|
|
||||
@options[:for_engine][:load_paths] << path
|
||||
end
|
||||
opts.on('-r', '--require LIB', 'Require a Ruby library before running Sass.') do |lib|
|
||||
require lib
|
||||
end
|
||||
opts.on('--cache-location PATH', 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc|
|
||||
@options[:for_engine][:cache_location] = loc
|
||||
end
|
||||
opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
|
||||
@options[:for_engine][:cache] = false
|
||||
end
|
||||
|
||||
unless ::Haml::Util.ruby1_8?
|
||||
opts.on('-E encoding', 'Specify the default encoding for Sass files.') do |encoding|
|
||||
Encoding.default_external = encoding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Processes the options set by the command-line arguments,
|
||||
# and runs the Sass compiler appropriately.
|
||||
def process_result
|
||||
require 'sass'
|
||||
|
||||
if !@options[:update] && !@options[:watch] &&
|
||||
@args.first && colon_path?(@args.first)
|
||||
if @args.size == 1
|
||||
@args = split_colon_path(@args.first)
|
||||
else
|
||||
@options[:update] = true
|
||||
end
|
||||
end
|
||||
|
||||
return interactive if @options[:interactive]
|
||||
return watch_or_update if @options[:watch] || @options[:update]
|
||||
super
|
||||
@options[:for_engine][:filename] = @options[:filename]
|
||||
|
||||
begin
|
||||
input = @options[:input]
|
||||
output = @options[:output]
|
||||
|
||||
@options[:syntax] ||= :scss if input.is_a?(File) && input.path =~ /\.scss$/
|
||||
tree =
|
||||
if input.is_a?(File) && !@options[:check_syntax]
|
||||
::Sass::Files.tree_for(input.path, @options[:for_engine])
|
||||
else
|
||||
# We don't need to do any special handling of @options[:check_syntax] here,
|
||||
# because the Sass syntax checking happens alongside evaluation
|
||||
# and evaluation doesn't actually evaluate any code anyway.
|
||||
::Sass::Engine.new(input.read(), @options[:for_engine]).to_tree
|
||||
end
|
||||
|
||||
input.close() if input.is_a?(File)
|
||||
|
||||
output.write(tree.render)
|
||||
output.close() if output.is_a? File
|
||||
rescue ::Sass::SyntaxError => e
|
||||
raise e if @options[:trace]
|
||||
raise e.sass_backtrace_str("standard input")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def interactive
|
||||
require 'sass/repl'
|
||||
::Sass::Repl.new(@options).run
|
||||
end
|
||||
|
||||
def watch_or_update
|
||||
require 'sass/plugin'
|
||||
::Sass::Plugin.options.merge! @options[:for_engine]
|
||||
::Sass::Plugin.options[:unix_newlines] = @options[:unix_newlines]
|
||||
|
||||
raise <<MSG if @args.empty?
|
||||
What files should I watch? Did you mean something like:
|
||||
sass --watch input.sass:output.css
|
||||
sass --watch input-dir:output-dir
|
||||
MSG
|
||||
|
||||
if !colon_path?(@args[0]) && probably_dest_dir?(@args[1])
|
||||
flag = @options[:update] ? "--update" : "--watch"
|
||||
err =
|
||||
if !File.exist?(@args[1])
|
||||
"doesn't exist"
|
||||
elsif @args[1] =~ /\.css$/
|
||||
"is a CSS file"
|
||||
end
|
||||
raise <<MSG if err
|
||||
File #{@args[1]} #{err}.
|
||||
Did you mean: sass #{flag} #{@args[0]}:#{@args[1]}
|
||||
MSG
|
||||
end
|
||||
|
||||
dirs, files = @args.map {|name| split_colon_path(name)}.
|
||||
partition {|i, _| File.directory? i}
|
||||
files.map! {|from, to| [from, to || from.gsub(/\..*?$/, '.css')]}
|
||||
dirs.map! {|from, to| [from, to || from]}
|
||||
::Sass::Plugin.options[:template_location] = dirs
|
||||
|
||||
::Sass::Plugin.on_updating_stylesheet do |_, css|
|
||||
if File.exists? css
|
||||
puts_action :overwrite, :yellow, css
|
||||
else
|
||||
puts_action :create, :green, css
|
||||
end
|
||||
end
|
||||
|
||||
had_error = false
|
||||
::Sass::Plugin.on_creating_directory {|dirname| puts_action :directory, :green, dirname}
|
||||
::Sass::Plugin.on_deleting_css {|filename| puts_action :delete, :yellow, filename}
|
||||
::Sass::Plugin.on_compilation_error do |error, _, _|
|
||||
raise error unless error.is_a?(::Sass::SyntaxError) && !@options[:stop_on_error]
|
||||
had_error = true
|
||||
puts_action :error, :red, "#{error.sass_filename} (Line #{error.sass_line}: #{error.message})"
|
||||
end
|
||||
|
||||
if @options[:update]
|
||||
::Sass::Plugin.update_stylesheets(files)
|
||||
exit 1 if had_error
|
||||
return
|
||||
end
|
||||
|
||||
puts ">>> Sass is watching for changes. Press Ctrl-C to stop."
|
||||
|
||||
::Sass::Plugin.on_template_modified {|template| puts ">>> Change detected to: #{template}"}
|
||||
::Sass::Plugin.on_template_created {|template| puts ">>> New template detected: #{template}"}
|
||||
::Sass::Plugin.on_template_deleted {|template| puts ">>> Deleted template detected: #{template}"}
|
||||
|
||||
::Sass::Plugin.watch(files)
|
||||
end
|
||||
|
||||
def colon_path?(path)
|
||||
!split_colon_path(path)[1].nil?
|
||||
end
|
||||
|
||||
def split_colon_path(path)
|
||||
one, two = path.split(':', 2)
|
||||
if one && two && ::Haml::Util.windows? &&
|
||||
one =~ /\A[A-Za-z]\Z/ && two =~ /\A[\/\\]/
|
||||
# If we're on Windows and we were passed a drive letter path,
|
||||
# don't split on that colon.
|
||||
one2, two = two.split(':', 2)
|
||||
one = one + ':' + one2
|
||||
end
|
||||
return one, two
|
||||
end
|
||||
|
||||
# Whether path is likely to be meant as the destination
|
||||
# in a source:dest pair.
|
||||
def probably_dest_dir?(path)
|
||||
return false unless path
|
||||
return false if colon_path?(path)
|
||||
return Dir.glob(File.join(path, "*.s[ca]ss")).empty?
|
||||
end
|
||||
end
|
||||
|
||||
# The `haml` executable.
|
||||
class Haml < Generic
|
||||
# @param args [Array<String>] The command-line arguments
|
||||
|
@ -574,228 +346,5 @@ END
|
|||
handle_load_error(err)
|
||||
end
|
||||
end
|
||||
|
||||
# The `sass-convert` executable.
|
||||
class SassConvert < Generic
|
||||
# @param args [Array<String>] The command-line arguments
|
||||
def initialize(args)
|
||||
super
|
||||
require 'sass'
|
||||
@options[:for_tree] = {}
|
||||
@options[:for_engine] = {:cache => false, :read_cache => true}
|
||||
end
|
||||
|
||||
# Tells optparse how to parse the arguments.
|
||||
#
|
||||
# @param opts [OptionParser]
|
||||
def set_opts(opts)
|
||||
opts.banner = <<END
|
||||
Usage: sass-convert [options] [INPUT] [OUTPUT]
|
||||
|
||||
Description:
|
||||
Converts between CSS, Sass, and SCSS files.
|
||||
E.g. converts from SCSS to Sass,
|
||||
or converts from CSS to SCSS (adding appropriate nesting).
|
||||
|
||||
Options:
|
||||
END
|
||||
|
||||
opts.on('-F', '--from FORMAT',
|
||||
'The format to convert from. Can be css, scss, sass, less, or sass2.',
|
||||
'sass2 is the same as sass, but updates more old syntax to new.',
|
||||
'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, :less, :sass2].include?(@options[:from])
|
||||
raise "Unknown format for sass-convert --from: #{name}"
|
||||
end
|
||||
try_less_note if @options[:from] == :less
|
||||
end
|
||||
|
||||
opts.on('-T', '--to FORMAT',
|
||||
'The format to convert to. Can be scss or sass.',
|
||||
'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',
|
||||
'Convert all the files in a directory. Requires --from and --to.') do
|
||||
@options[:recursive] = true
|
||||
end
|
||||
|
||||
opts.on('-i', '--in-place',
|
||||
'Convert a file to its own syntax.',
|
||||
'This can be used to update some deprecated syntax.') do
|
||||
@options[:in_place] = true
|
||||
end
|
||||
|
||||
opts.on('--dasherize', 'Convert underscores to dashes') do
|
||||
@options[:for_tree][:dasherize] = true
|
||||
end
|
||||
|
||||
opts.on('--old', 'Output the old-style ":prop val" property syntax.',
|
||||
'Only meaningful when generating Sass.') do
|
||||
@options[:for_tree][:old] = true
|
||||
end
|
||||
|
||||
opts.on('-C', '--no-cache', "Don't cache to sassc files.") do
|
||||
@options[:for_engine][:read_cache] = false
|
||||
end
|
||||
|
||||
unless ::Haml::Util.ruby1_8?
|
||||
opts.on('-E encoding', 'Specify the default encoding for Sass and CSS files.') do |encoding|
|
||||
Encoding.default_external = encoding
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
# Processes the options set by the command-line arguments,
|
||||
# and runs the CSS compiler appropriately.
|
||||
def process_result
|
||||
require 'sass'
|
||||
|
||||
if @options[:recursive]
|
||||
process_directory
|
||||
return
|
||||
end
|
||||
|
||||
super
|
||||
input = @options[:input]
|
||||
raise "Error: '#{input.path}' is a directory (did you mean to use --recursive?)" if File.directory?(input)
|
||||
output = @options[:output]
|
||||
output = input if @options[:in_place]
|
||||
process_file(input, output)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_directory
|
||||
unless input = @options[:input] = @args.shift
|
||||
raise "Error: directory required when using --recursive."
|
||||
end
|
||||
|
||||
output = @options[:output] = @args.shift
|
||||
raise "Error: --from required when using --recursive." unless @options[:from]
|
||||
raise "Error: --to required when using --recursive." unless @options[:to]
|
||||
raise "Error: '#{@options[:input]}' is not a directory" unless File.directory?(@options[:input])
|
||||
if @options[:output] && File.exists?(@options[:output]) && !File.directory?(@options[:output])
|
||||
raise "Error: '#{@options[:output]}' is not a directory"
|
||||
end
|
||||
@options[:output] ||= @options[:input]
|
||||
|
||||
from = @options[:from]
|
||||
from = :sass if from == :sass2
|
||||
if @options[:to] == @options[:from] && !@options[:in_place]
|
||||
fmt = @options[:from]
|
||||
raise "Error: converting from #{fmt} to #{fmt} without --in-place"
|
||||
end
|
||||
|
||||
ext = @options[:from]
|
||||
ext = :sass if ext == :sass2
|
||||
Dir.glob("#{@options[:input]}/**/*.#{ext}") do |f|
|
||||
output =
|
||||
if @options[:in_place]
|
||||
f
|
||||
elsif @options[:output]
|
||||
output_name = f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
|
||||
output_name[0...@options[:input].size] = @options[:output]
|
||||
output_name
|
||||
else
|
||||
f.gsub(/\.(c|sa|sc|le)ss$/, ".#{@options[:to]}")
|
||||
end
|
||||
|
||||
unless File.directory?(File.dirname(output))
|
||||
puts_action :directory, :green, File.dirname(output)
|
||||
FileUtils.mkdir_p(File.dirname(output))
|
||||
end
|
||||
puts_action :convert, :green, f
|
||||
if File.exists?(output)
|
||||
puts_action :overwrite, :yellow, output
|
||||
else
|
||||
puts_action :create, :green, output
|
||||
end
|
||||
|
||||
input = open_file(f)
|
||||
output = @options[:in_place] ? input : open_file(output, "w")
|
||||
process_file(input, output)
|
||||
end
|
||||
end
|
||||
|
||||
def process_file(input, output)
|
||||
if input.is_a?(File)
|
||||
@options[:from] ||=
|
||||
case input.path
|
||||
when /\.scss$/; :scss
|
||||
when /\.sass$/; :sass
|
||||
when /\.less$/; :less
|
||||
when /\.css$/; :css
|
||||
end
|
||||
elsif @options[:in_place]
|
||||
raise "Error: the --in-place option requires a filename."
|
||||
end
|
||||
|
||||
if output.is_a?(File)
|
||||
@options[:to] ||=
|
||||
case output.path
|
||||
when /\.scss$/; :scss
|
||||
when /\.sass$/; :sass
|
||||
end
|
||||
end
|
||||
|
||||
if @options[:from] == :sass2
|
||||
@options[:from] = :sass
|
||||
@options[:for_engine][:sass2] = true
|
||||
end
|
||||
|
||||
@options[:from] ||= :css
|
||||
@options[:to] ||= :sass
|
||||
@options[:for_engine][:syntax] = @options[:from]
|
||||
|
||||
out =
|
||||
::Haml::Util.silence_haml_warnings do
|
||||
if @options[:from] == :css
|
||||
require 'sass/css'
|
||||
::Sass::CSS.new(input.read, @options[:for_tree]).render(@options[:to])
|
||||
elsif @options[:from] == :less
|
||||
require 'sass/less'
|
||||
try_less_note
|
||||
input = input.read if input.is_a?(IO) && !input.is_a?(File) # Less is dumb
|
||||
Less::Engine.new(input).to_tree.to_sass_tree.send("to_#{@options[:to]}", @options[:for_tree])
|
||||
else
|
||||
if input.is_a?(File)
|
||||
::Sass::Files.tree_for(input.path, @options[:for_engine])
|
||||
else
|
||||
::Sass::Engine.new(input.read, @options[:for_engine]).to_tree
|
||||
end.send("to_#{@options[:to]}", @options[:for_tree])
|
||||
end
|
||||
end
|
||||
|
||||
output = File.open(input.path, 'w') if @options[:in_place]
|
||||
output.write(out)
|
||||
rescue ::Sass::SyntaxError => e
|
||||
raise e if @options[:trace]
|
||||
file = " of #{e.sass_filename}" if e.sass_filename
|
||||
raise "Error on line #{e.sass_line}#{file}: #{e.message}\n Use --trace for backtrace"
|
||||
rescue LoadError => err
|
||||
handle_load_error(err)
|
||||
end
|
||||
|
||||
@@less_note_printed = false
|
||||
def try_less_note
|
||||
return if @@less_note_printed
|
||||
@@less_note_printed = true
|
||||
warn <<NOTE
|
||||
* NOTE: Sass and Less are different languages, and they work differently.
|
||||
* I'll do my best to translate, but some features -- especially mixins --
|
||||
* should be checked by hand.
|
||||
NOTE
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module Haml
|
|||
#
|
||||
# <div class="entry show">My Div</div>
|
||||
#
|
||||
# Then, in a stylesheet (shown here as {Sass}),
|
||||
# Then, in a stylesheet (shown here as [Sass](http://sass-lang.com)),
|
||||
# you could refer to this specific action:
|
||||
#
|
||||
# .entry.show
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
if Haml::Util.ap_geq_3? && !Haml::Util.ap_geq?("3.0.0.beta4")
|
||||
raise <<ERROR
|
||||
Haml and Sass no longer support Rails 3 versions before beta 4.
|
||||
Haml no longer supports Rails 3 versions before beta 4.
|
||||
Please upgrade to Rails 3.0.0.beta4 or later.
|
||||
ERROR
|
||||
end
|
||||
|
@ -8,12 +8,7 @@ end
|
|||
# Rails 3.0.0.beta.2+
|
||||
if defined?(ActiveSupport) && Haml::Util.has?(:public_method, ActiveSupport, :on_load)
|
||||
require 'haml/template/options'
|
||||
require 'sass/plugin/configuration'
|
||||
ActiveSupport.on_load(:before_initialize) do
|
||||
require 'sass'
|
||||
require 'sass/plugin'
|
||||
|
||||
# Haml requires AV, but Sass doesn't
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
Haml.init_rails(binding)
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@ require 'strscan'
|
|||
require 'rbconfig'
|
||||
|
||||
require 'haml/root'
|
||||
require 'haml/util/subset_map'
|
||||
|
||||
module Haml
|
||||
# A module containing various useful functions.
|
||||
|
@ -433,7 +432,7 @@ MSG
|
|||
# Like {\#check\_encoding}, but also checks for a Ruby-style `-# coding:` comment
|
||||
# at the beginning of the template and uses that encoding if it exists.
|
||||
#
|
||||
# The Sass encoding rules are simple.
|
||||
# The Haml encoding rules are simple.
|
||||
# If a `-# coding:` comment exists,
|
||||
# we assume that that's the original encoding of the document.
|
||||
# Otherwise, we use whatever encoding Ruby has.
|
||||
|
@ -462,51 +461,6 @@ MSG
|
|||
return check_encoding(str, &block)
|
||||
end
|
||||
|
||||
# Like {\#check\_encoding}, but also checks for a `@charset` declaration
|
||||
# at the beginning of the file and uses that encoding if it exists.
|
||||
#
|
||||
# The Sass encoding rules are simple.
|
||||
# If a `@charset` declaration exists,
|
||||
# we assume that that's the original encoding of the document.
|
||||
# Otherwise, we use whatever encoding Ruby has.
|
||||
# Then we convert that to UTF-8 to process internally.
|
||||
# The UTF-8 end result is what's returned by this method.
|
||||
#
|
||||
# @param str [String] The string of which to check the encoding
|
||||
# @yield [msg] A block in which an encoding error can be raised.
|
||||
# Only yields if there is an encoding error
|
||||
# @yieldparam msg [String] The error message to be raised
|
||||
# @return [(String, Encoding)] The original string encoded as UTF-8,
|
||||
# and the source encoding of the string (or `nil` under Ruby 1.8)
|
||||
# @raise [Encoding::UndefinedConversionError] if the source encoding
|
||||
# cannot be converted to UTF-8
|
||||
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
|
||||
def check_sass_encoding(str, &block)
|
||||
return check_encoding(str, &block), nil if ruby1_8?
|
||||
# We allow any printable ASCII characters but double quotes in the charset decl
|
||||
bin = str.dup.force_encoding("BINARY")
|
||||
encoding = Haml::Util::ENCODINGS_TO_CHECK.find do |enc|
|
||||
bin =~ Haml::Util::CHARSET_REGEXPS[enc]
|
||||
end
|
||||
charset, bom = $1, $2
|
||||
if charset
|
||||
charset = charset.force_encoding(encoding).encode("UTF-8")
|
||||
if endianness = encoding[/[BL]E$/]
|
||||
begin
|
||||
Encoding.find(charset + endianness)
|
||||
charset << endianness
|
||||
rescue ArgumentError # Encoding charset + endianness doesn't exist
|
||||
end
|
||||
end
|
||||
str.force_encoding(charset)
|
||||
elsif bom
|
||||
str.force_encoding(encoding)
|
||||
end
|
||||
|
||||
str = check_encoding(str, &block)
|
||||
return str.encode("UTF-8"), str.encoding
|
||||
end
|
||||
|
||||
unless ruby1_8?
|
||||
# @private
|
||||
def _enc(string, encoding)
|
||||
|
|
|
@ -5,15 +5,14 @@ times = (ARGV.first || 1000).to_i
|
|||
if times == 0 # Invalid parameter
|
||||
puts <<END
|
||||
ruby #$0 [times=1000]
|
||||
Benchmark Haml against various other templating languages and Sass
|
||||
on its own.
|
||||
Benchmark Haml against various other templating languages.
|
||||
END
|
||||
exit 1
|
||||
end
|
||||
|
||||
require File.dirname(__FILE__) + '/../lib/haml'
|
||||
require File.dirname(__FILE__) + '/linked_rails'
|
||||
%w[sass rubygems erb erubis markaby active_support action_controller
|
||||
%w[rubygems erb erubis markaby active_support action_controller
|
||||
action_view action_pack haml/template rbench].each {|dep| require(dep)}
|
||||
|
||||
def view
|
||||
|
@ -91,9 +90,3 @@ RBench.run(times) do
|
|||
haml_ugly { render @base, 'haml/templates/action_view_ugly' }
|
||||
end
|
||||
end
|
||||
|
||||
RBench.run(times) do
|
||||
sass_template = File.read("#{File.dirname(__FILE__)}/sass/templates/complex.sass")
|
||||
|
||||
report("Sass") { Sass::Engine.new(sass_template).render }
|
||||
end
|
||||
|
|
|
@ -1101,18 +1101,18 @@ END
|
|||
end
|
||||
|
||||
def test_css_filter
|
||||
assert_equal(<<CSS, render(<<SASS))
|
||||
assert_equal(<<HTML, render(<<HAML))
|
||||
<style type='text/css'>
|
||||
/*<![CDATA[*/
|
||||
#foo {
|
||||
bar: baz; }
|
||||
/*]]>*/
|
||||
</style>
|
||||
CSS
|
||||
HTML
|
||||
:css
|
||||
#foo {
|
||||
bar: baz; }
|
||||
SASS
|
||||
HAML
|
||||
end
|
||||
|
||||
def test_local_assigns_dont_modify_class
|
||||
|
|
|
@ -5,43 +5,12 @@ require 'test/unit'
|
|||
require 'fileutils'
|
||||
$:.unshift lib_dir unless $:.include?(lib_dir)
|
||||
require 'haml'
|
||||
require 'sass'
|
||||
|
||||
require 'haml/template'
|
||||
Haml::Template.options[:ugly] = false
|
||||
Haml::Template.options[:format] = :xhtml
|
||||
|
||||
Sass::RAILS_LOADED = true unless defined?(Sass::RAILS_LOADED)
|
||||
|
||||
module Sass::Script::Functions
|
||||
module UserFunctions; end
|
||||
include UserFunctions
|
||||
|
||||
def option(name)
|
||||
Sass::Script::String.new(@options[name.value.to_sym].to_s)
|
||||
end
|
||||
end
|
||||
|
||||
class Test::Unit::TestCase
|
||||
def munge_filename(opts = {})
|
||||
return if opts.has_key?(:filename)
|
||||
opts[:filename] = filename_for_test(opts[:syntax] || :sass)
|
||||
end
|
||||
|
||||
def filename_for_test(syntax = :sass)
|
||||
test_name = caller.
|
||||
map {|c| Haml::Util.caller_info(c)[2]}.
|
||||
compact.
|
||||
map {|c| c.sub(/^(block|rescue) in /, '')}.
|
||||
find {|c| c =~ /^test_/}
|
||||
"#{test_name}_inline.#{syntax}"
|
||||
end
|
||||
|
||||
def clean_up_sassc
|
||||
path = File.dirname(__FILE__) + "/../.sass-cache"
|
||||
FileUtils.rm_r(path) if File.exist?(path)
|
||||
end
|
||||
|
||||
def assert_warning(message)
|
||||
the_real_stderr, $stderr = $stderr, StringIO.new
|
||||
yield
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
class InheritedHashHandler < YARD::Handlers::Ruby::Legacy::Base
|
||||
handles /\Ainherited_hash(\s|\()/
|
||||
|
||||
def process
|
||||
hash_name = tokval(statement.tokens[2])
|
||||
name = statement.comments.first.strip
|
||||
type = statement.comments[1].strip
|
||||
|
||||
o = register(MethodObject.new(namespace, hash_name, scope))
|
||||
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"]
|
||||
|
||||
o = register(MethodObject.new(namespace, "set_#{hash_name}", scope))
|
||||
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"]
|
||||
|
||||
o = register(MethodObject.new(namespace, "set_local_#{hash_name}", scope))
|
||||
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
|
Loading…
Reference in New Issue