# -*- coding: utf-8 -*- #
module Rouge
module Lexers
# A lexer for the Slim tempalte language
# @see http://slim-lang.org
class Slim < RegexLexer
include Indentation
desc 'The Slim template language'
tag 'slim'
filenames '*.slim'
# Ruby identifier characters
ruby_chars = /[\w\!\?\@\$]/
# Since you are allowed to wrap lines with a backslash, include \\\n in characters
dot = /(\\\n|.)/
def self.analyze_text(text)
return 1 if text.start_with? 'doctype'
return 1 if text =~ /(\*)(\{.+?\})/ # Contans a hash splat
end
def ruby
@ruby ||= Ruby.new(options)
end
def html
@html ||= HTML.new(options)
end
def filters
@filters ||= {
'ruby' => ruby,
'erb' => ERB.new(options),
'javascript' => Javascript.new(options),
'css' => CSS.new(options),
'coffee' => Coffeescript.new(options),
'markdown' => Markdown.new(options),
'scss' => Scss.new(options),
'sass' => Sass.new(options)
}
end
state :root do
rule /\s*\n/, Text
rule(/\s*/) { |m| token Text; indentation(m[0]) }
end
state :content do
mixin :css
rule /\/#{dot}*/, Comment, :indented_block
rule /(doctype)(\s+)(.*)/ do
groups Name::Namespace, Text::Whitespace, Text
pop!
end
# filters, shamelessly ripped from HAML
rule /(\w*):\s*\n/ do |m|
token Name::Decorator
pop!
starts_block :filter_block
filter_name = m[1].strip
@filter_lexer = self.filters[filter_name]
@filter_lexer.reset! unless @filter_lexer.nil?
puts " slim: filter #{filter_name.inspect} #{@filter_lexer.inspect}" if @debug
end
# Text
rule %r([\|'](?=\s)) do
token Punctuation
pop!
starts_block :plain_block
goto :plain_block
end
rule /-|==|=/, Punctuation, :ruby_line
# Dynamic tags
rule /(\*)(#{ruby_chars}+\(.*?\))/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
push :tag
end
rule /(\*)(#{ruby_chars}+)/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
push :tag
end
#rule /<\w+(?=.*>)/, Keyword::Constant, :tag # Maybe do this, look ahead and stuff
rule %r((</?[\w\s\=\'\"]+?/?>)) do |m| # Dirty html
delegate html, m[1]
pop!
end
# Ordinary slim tags
rule /\w+/, Name::Tag, :tag
end
state :tag do
mixin :css
mixin :indented_block
mixin :interpolation
# Whitespace control
rule /[<>]/, Punctuation
# Trim whitespace
rule /\s+?/, Text::Whitespace
# Splats, these two might be mergable?
rule /(\*)(#{ruby_chars}+)/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
end
rule /(\*)(\{#{dot}+?\})/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
end
# Attributes
rule /([\w\-]+)(\s*)(\=)/ do |m|
token Name::Attribute, m[1]
token Text::Whitespace, m[2]
token Punctuation, m[3]
push :html_attr
end
# Ruby value
rule /(\=)(#{dot}+)/ do |m|
token Punctuation, m[1]
#token Keyword::Constant, m[2]
delegate ruby, m[2]
end
rule /#{dot}+?/, Text
rule /\s*\n/, Text::Whitespace, :pop!
end
state :css do
rule(/\.\w+/) { token Name::Class; goto :tag }
rule(/#\w+/) { token Name::Function; goto :tag }
end
state :html_attr do
# Strings, double/single quoted
rule %r(\s*(['"])#{dot}*\1), Literal::String, :pop!
# Ruby stuff
rule(/(#{ruby_chars}+\(.*?\))/) { |m| delegate ruby, m[1]; pop! }
rule(/(#{ruby_chars}+)/) { |m| delegate ruby, m[1]; pop! }
rule /\s+/, Text::Whitespace
end
state :ruby_line do
# Need at top
mixin :indented_block
rule(/,\s*\n/) { delegate ruby }
rule /[ ]\|[ \t]*\n/, Str::Escape
rule(/.*?(?=(,$| \|)?[ \t]*$)/) { delegate ruby }
end
state :filter_block do
rule /([^#\n]|#[^{\n]|(\\\\)*\\#\{)+/ do
if @filter_lexer
delegate @filter_lexer
else
token Name::Decorator
end
end
mixin :interpolation
mixin :indented_block
end
state :plain_block do
mixin :interpolation
rule %r((</?[\w\s\=\'\"]+?/?>)) do |m| # Dirty html
delegate html, m[1]
end
#rule /([^#\n]|#[^{\n]|(\\\\)*\\#\{)+/ do
rule /#{dot}+?/, Text
mixin :indented_block
end
state :interpolation do
rule /#[{]/, Str::Interpol, :ruby_interp
end
state :ruby_interp do
rule /[}]/, Str::Interpol, :pop!
mixin :ruby_interp_inner
end
state :ruby_interp_inner do
rule(/[{]/) { delegate ruby; push :ruby_interp_inner }
rule(/[}]/) { delegate ruby; pop! }
rule(/[^{}]+/) { delegate ruby }
end
state :indented_block do
rule(/(?<!\\)\n/) { token Text; reset_stack }
end
end
end
end