This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
# -*- 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
|
||||
Reference in New Issue
Block a user