# -*- coding: utf-8 -*- #
# frozen_string_literal: true
module Rouge
module Lexers
# A lexer for the Slim tempalte language
# @see http://slim-lang.org
class Slim < RegexLexer
include Indentation
title "Slim"
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 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
start { ruby.reset!; html.reset! }
state :root do
rule %r/\s*\n/, Text
rule(/\s*/) { |m| token Text; indentation(m[0]) }
end
state :content do
mixin :css
rule %r/\/#{dot}*/, Comment, :indented_block
rule %r/(doctype)(\s+)(.*)/ do
groups Name::Namespace, Text::Whitespace, Text
pop!
end
# filters, shamelessly ripped from HAML
rule %r/(\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 %r/-|==|=/, Punctuation, :ruby_line
# Dynamic tags
rule %r/(\*)(#{ruby_chars}+\(.*?\))/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
push :tag
end
rule %r/(\*)(#{ruby_chars}+)/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
push :tag
end
#rule %r/<\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 %r/\w+/, Name::Tag, :tag
end
state :tag do
mixin :css
mixin :indented_block
mixin :interpolation
# Whitespace control
rule %r/[<>]/, Punctuation
# Trim whitespace
rule %r/\s+?/, Text::Whitespace
# Splats, these two might be mergable?
rule %r/(\*)(#{ruby_chars}+)/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
end
rule %r/(\*)(\{#{dot}+?\})/ do |m|
token Punctuation, m[1]
delegate ruby, m[2]
end
# Attributes
rule %r/([\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 %r/(\=)(#{dot}+)/ do |m|
token Punctuation, m[1]
#token Keyword::Constant, m[2]
delegate ruby, m[2]
end
# HTML Entities
rule(/&\S*?;/, Name::Entity)
rule %r/#{dot}+?/, Text
rule %r/\s*\n/, Text::Whitespace, :pop!
end
state :css do
rule(/\.[\w-]*/) { token Name::Class; goto :tag }
rule(/#[a-zA-Z][\w:-]*/) { token Name::Function; goto :tag }
end
state :html_attr do
# Strings, double/single quoted
rule(/\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 %r/\s+/, Text::Whitespace
end
state :ruby_line do
# Need at top
mixin :indented_block
rule(/[,\\]\s*\n/) { delegate ruby }
rule %r/[ ]\|[ \t]*\n/, Str::Escape
rule(/.*?(?=([,\\]$| \|)?[ \t]*$)/) { delegate ruby }
end
state :filter_block do
rule %r/([^#\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
# HTML Entities
rule(/&\S*?;/, Name::Entity)
#rule %r/([^#\n]|#[^{\n]|(\\\\)*\\#\{)+/ do
rule %r/#{dot}+?/, Text
mixin :indented_block
end
state :interpolation do
rule %r/#[{]/, Str::Interpol, :ruby_interp
end
state :ruby_interp do
rule %r/[}]/, 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