mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
Merge AttributeCompiler into Compiler
This commit is contained in:
parent
c6465523cc
commit
c91944b307
8 changed files with 156 additions and 213 deletions
|
@ -1,141 +0,0 @@
|
||||||
require 'ripper'
|
|
||||||
require 'hamlit/filter'
|
|
||||||
require 'hamlit/concerns/balanceable'
|
|
||||||
|
|
||||||
# AttributeCompiler compiles only old-style attribute, which is
|
|
||||||
# surrounded by brackets.
|
|
||||||
module Hamlit
|
|
||||||
class AttributeCompiler < Hamlit::Filter
|
|
||||||
include Concerns::Balanceable
|
|
||||||
|
|
||||||
TYPE_POSITION = 1
|
|
||||||
|
|
||||||
def on_haml_attrs(*exps)
|
|
||||||
attrs = []
|
|
||||||
exps.map do |exp|
|
|
||||||
case exp
|
|
||||||
when /\A{.+}\Z/
|
|
||||||
attrs += compile_attribute(exp)
|
|
||||||
else
|
|
||||||
attrs << compile(exp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
[:haml, :attrs, *attrs]
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def compile_attribute(str)
|
|
||||||
tokens = Ripper.lex(str)
|
|
||||||
attrs = parse_attributes(tokens)
|
|
||||||
flatten_attributes(attrs).map do |key, value|
|
|
||||||
[:html, :attr, key, [:dynamic, value]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def flatten_attributes(attributes)
|
|
||||||
flattened = {}
|
|
||||||
|
|
||||||
attributes.each do |key, value|
|
|
||||||
case value
|
|
||||||
when Hash
|
|
||||||
flatten_attributes(value).each do |k, v|
|
|
||||||
flattened["#{key}-#{k}"] = v
|
|
||||||
end
|
|
||||||
else
|
|
||||||
flattened[key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
flattened
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse brace-balanced tokens and return the result as hash
|
|
||||||
def parse_attributes(tokens)
|
|
||||||
tokens = tokens.slice(1..-2) # strip braces
|
|
||||||
attributes = {}
|
|
||||||
|
|
||||||
while tokens && tokens.any?
|
|
||||||
key = read_key!(tokens)
|
|
||||||
val = read_value!(tokens)
|
|
||||||
attributes[key] = val if key && val
|
|
||||||
|
|
||||||
skip_tokens!(tokens, :on_sp)
|
|
||||||
raise SyntaxError if tokens.any? && type_of(tokens.shift) != :on_comma
|
|
||||||
end
|
|
||||||
|
|
||||||
attributes
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_key!(tokens)
|
|
||||||
skip_tokens!(tokens, :on_sp)
|
|
||||||
|
|
||||||
(row, col), type, str = tokens.shift
|
|
||||||
case type
|
|
||||||
when :on_label
|
|
||||||
str.gsub!(/:\Z/, '')
|
|
||||||
when :on_symbeg
|
|
||||||
if %w[:" :'].include?(str)
|
|
||||||
str = read_string!(tokens)
|
|
||||||
else
|
|
||||||
(row, col), type, str = tokens.shift
|
|
||||||
end
|
|
||||||
assert_rocket!(tokens)
|
|
||||||
when :on_tstring_beg
|
|
||||||
str = read_string!(tokens)
|
|
||||||
assert_rocket!(tokens)
|
|
||||||
end
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_string!(tokens)
|
|
||||||
(row, col), type, str = tokens.shift
|
|
||||||
return '' if type == :on_tstring_end
|
|
||||||
|
|
||||||
raise SyntaxError if type_of(tokens.shift) != :on_tstring_end
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_value!(tokens)
|
|
||||||
result = ''
|
|
||||||
skip_tokens!(tokens, :on_sp)
|
|
||||||
|
|
||||||
if type_of(tokens.first) == :on_lbrace
|
|
||||||
hash = fetch_balanced_braces(tokens)
|
|
||||||
hash.length.times { tokens.shift }
|
|
||||||
return parse_attributes(hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
while token = tokens.shift
|
|
||||||
(row, col), type, str = token
|
|
||||||
case type
|
|
||||||
when :on_sp
|
|
||||||
next
|
|
||||||
when :on_comma
|
|
||||||
tokens.unshift(token)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
result += str
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def skip_tokens!(tokens, *types)
|
|
||||||
while types.include?(type_of(tokens.first))
|
|
||||||
tokens.shift
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_rocket!(tokens, *types)
|
|
||||||
skip_tokens!(tokens, :on_sp)
|
|
||||||
(row, col), type, str = tokens.shift
|
|
||||||
|
|
||||||
raise SyntaxError unless type == :on_op && str == '=>'
|
|
||||||
end
|
|
||||||
|
|
||||||
def type_of(token)
|
|
||||||
return nil unless token
|
|
||||||
token[TYPE_POSITION]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,7 +2,6 @@ require 'hamlit/compilers/attributes'
|
||||||
require 'hamlit/compilers/doctype'
|
require 'hamlit/compilers/doctype'
|
||||||
require 'hamlit/compilers/dynamic'
|
require 'hamlit/compilers/dynamic'
|
||||||
require 'hamlit/compilers/filter'
|
require 'hamlit/compilers/filter'
|
||||||
require 'hamlit/compilers/new_attribute'
|
|
||||||
require 'hamlit/compilers/preserve'
|
require 'hamlit/compilers/preserve'
|
||||||
require 'hamlit/compilers/script'
|
require 'hamlit/compilers/script'
|
||||||
require 'hamlit/compilers/text'
|
require 'hamlit/compilers/text'
|
||||||
|
@ -14,7 +13,6 @@ module Hamlit
|
||||||
include Compilers::Doctype
|
include Compilers::Doctype
|
||||||
include Compilers::Dynamic
|
include Compilers::Dynamic
|
||||||
include Compilers::Filter
|
include Compilers::Filter
|
||||||
include Compilers::NewAttribute
|
|
||||||
include Compilers::Preserve
|
include Compilers::Preserve
|
||||||
include Compilers::Script
|
include Compilers::Script
|
||||||
include Compilers::Text
|
include Compilers::Text
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
|
require 'hamlit/compilers/new_attribute'
|
||||||
|
require 'hamlit/compilers/old_attribute'
|
||||||
|
|
||||||
module Hamlit
|
module Hamlit
|
||||||
module Compilers
|
module Compilers
|
||||||
module Attributes
|
module Attributes
|
||||||
def on_haml_attrs(*exps)
|
include Compilers::NewAttribute
|
||||||
attrs = []
|
include Compilers::OldAttribute
|
||||||
exps.each do |exp|
|
|
||||||
case exp
|
|
||||||
when /\A(.+)\Z/
|
|
||||||
attrs += compile_new_attribute(exp)
|
|
||||||
else
|
|
||||||
attrs << compile(exp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
def on_haml_attrs(*attrs)
|
||||||
|
attrs = compile_attributes(attrs)
|
||||||
attrs = join_ids(attrs)
|
attrs = join_ids(attrs)
|
||||||
attrs = combine_classes(attrs)
|
attrs = combine_classes(attrs)
|
||||||
attrs = pull_class_first(attrs)
|
attrs = pull_class_first(attrs)
|
||||||
|
@ -20,6 +17,21 @@ module Hamlit
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def compile_attributes(exps)
|
||||||
|
attrs = []
|
||||||
|
exps.each do |exp|
|
||||||
|
case exp
|
||||||
|
when /\A\(.+\)\Z/
|
||||||
|
attrs += compile_new_attribute(exp)
|
||||||
|
when /\A{.+}\Z/
|
||||||
|
attrs += compile_old_attribute(exp)
|
||||||
|
else
|
||||||
|
attrs << compile(exp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
attrs
|
||||||
|
end
|
||||||
|
|
||||||
def pull_class_first(attrs)
|
def pull_class_first(attrs)
|
||||||
class_attrs = filter_attrs(attrs, 'class')
|
class_attrs = filter_attrs(attrs, 'class')
|
||||||
combine_classes(class_attrs) + (attrs - class_attrs)
|
combine_classes(class_attrs) + (attrs - class_attrs)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
require 'ripper'
|
require 'ripper'
|
||||||
|
|
||||||
# NewAttributeCompiler compiles new-style attributes, which is
|
# This module compiles new-style attributes, which is
|
||||||
# surrounded by parentheses.
|
# surrounded by parentheses.
|
||||||
module Hamlit
|
module Hamlit
|
||||||
module Compilers
|
module Compilers
|
||||||
|
@ -9,7 +9,7 @@ module Hamlit
|
||||||
|
|
||||||
def compile_new_attribute(str)
|
def compile_new_attribute(str)
|
||||||
str = str.gsub(/\A\(|\)\Z/, '')
|
str = str.gsub(/\A\(|\)\Z/, '')
|
||||||
attrs = parse_attributes(str)
|
attrs = parse_new_attributes(str)
|
||||||
attrs.map do |key, value|
|
attrs.map do |key, value|
|
||||||
[:html, :attr, key, [:dynamic, value]]
|
[:html, :attr, key, [:dynamic, value]]
|
||||||
end
|
end
|
||||||
|
@ -17,7 +17,7 @@ module Hamlit
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_attributes(str)
|
def parse_new_attributes(str)
|
||||||
attributes = {}
|
attributes = {}
|
||||||
|
|
||||||
while str.length > 0
|
while str.length > 0
|
||||||
|
|
130
lib/hamlit/compilers/old_attribute.rb
Normal file
130
lib/hamlit/compilers/old_attribute.rb
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
require 'ripper'
|
||||||
|
require 'hamlit/concerns/balanceable'
|
||||||
|
|
||||||
|
# This module compiles only old-style attribute, which is
|
||||||
|
# surrounded by brackets.
|
||||||
|
# FIXME: remove duplicated code with NewAttribute
|
||||||
|
module Hamlit
|
||||||
|
module Compilers
|
||||||
|
module OldAttribute
|
||||||
|
include Concerns::Balanceable
|
||||||
|
|
||||||
|
TYPE_POSITION = 1
|
||||||
|
|
||||||
|
def compile_old_attribute(str)
|
||||||
|
tokens = Ripper.lex(str)
|
||||||
|
attrs = parse_old_attributes(tokens)
|
||||||
|
flatten_attributes(attrs).map do |key, value|
|
||||||
|
[:html, :attr, key, [:dynamic, value]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def flatten_attributes(attributes)
|
||||||
|
flattened = {}
|
||||||
|
|
||||||
|
attributes.each do |key, value|
|
||||||
|
case value
|
||||||
|
when Hash
|
||||||
|
flatten_attributes(value).each do |k, v|
|
||||||
|
flattened["#{key}-#{k}"] = v
|
||||||
|
end
|
||||||
|
else
|
||||||
|
flattened[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
flattened
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse brace-balanced tokens and return the result as hash
|
||||||
|
def parse_old_attributes(tokens)
|
||||||
|
tokens = tokens.slice(1..-2) # strip braces
|
||||||
|
attributes = {}
|
||||||
|
|
||||||
|
while tokens && tokens.any?
|
||||||
|
key = read_hash_key!(tokens)
|
||||||
|
val = read_hash_value!(tokens)
|
||||||
|
attributes[key] = val if key && val
|
||||||
|
|
||||||
|
hash_skip_tokens!(tokens, :on_sp)
|
||||||
|
raise SyntaxError if tokens.any? && hash_type_of(tokens.shift) != :on_comma
|
||||||
|
end
|
||||||
|
|
||||||
|
attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_hash_key!(tokens)
|
||||||
|
hash_skip_tokens!(tokens, :on_sp)
|
||||||
|
|
||||||
|
(row, col), type, str = tokens.shift
|
||||||
|
case type
|
||||||
|
when :on_label
|
||||||
|
str.gsub!(/:\Z/, '')
|
||||||
|
when :on_symbeg
|
||||||
|
if %w[:" :'].include?(str)
|
||||||
|
str = read_string!(tokens)
|
||||||
|
else
|
||||||
|
(row, col), type, str = tokens.shift
|
||||||
|
end
|
||||||
|
assert_rocket!(tokens)
|
||||||
|
when :on_tstring_beg
|
||||||
|
str = read_string!(tokens)
|
||||||
|
assert_rocket!(tokens)
|
||||||
|
end
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_string!(tokens)
|
||||||
|
(row, col), type, str = tokens.shift
|
||||||
|
return '' if type == :on_tstring_end
|
||||||
|
|
||||||
|
raise SyntaxError if hash_type_of(tokens.shift) != :on_tstring_end
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_hash_value!(tokens)
|
||||||
|
result = ''
|
||||||
|
hash_skip_tokens!(tokens, :on_sp)
|
||||||
|
|
||||||
|
if hash_type_of(tokens.first) == :on_lbrace
|
||||||
|
hash = fetch_balanced_braces(tokens)
|
||||||
|
hash.length.times { tokens.shift }
|
||||||
|
return parse_old_attributes(hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
while token = tokens.shift
|
||||||
|
(row, col), type, str = token
|
||||||
|
case type
|
||||||
|
when :on_sp
|
||||||
|
next
|
||||||
|
when :on_comma
|
||||||
|
tokens.unshift(token)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
result += str
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash_skip_tokens!(tokens, *types)
|
||||||
|
while types.include?(hash_type_of(tokens.first))
|
||||||
|
tokens.shift
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_rocket!(tokens, *types)
|
||||||
|
hash_skip_tokens!(tokens, :on_sp)
|
||||||
|
(row, col), type, str = tokens.shift
|
||||||
|
|
||||||
|
raise SyntaxError unless type == :on_op && str == '=>'
|
||||||
|
end
|
||||||
|
|
||||||
|
def hash_type_of(token)
|
||||||
|
return nil unless token
|
||||||
|
token[TYPE_POSITION]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,4 @@
|
||||||
require 'temple'
|
require 'temple'
|
||||||
require 'hamlit/attribute_compiler'
|
|
||||||
require 'hamlit/compiler'
|
require 'hamlit/compiler'
|
||||||
require 'hamlit/html/pretty'
|
require 'hamlit/html/pretty'
|
||||||
require 'hamlit/html/ugly'
|
require 'hamlit/html/ugly'
|
||||||
|
@ -17,7 +16,6 @@ module Hamlit
|
||||||
|
|
||||||
use Multiline
|
use Multiline
|
||||||
use Parser
|
use Parser
|
||||||
use AttributeCompiler
|
|
||||||
use Compiler
|
use Compiler
|
||||||
use :Html, -> { create(html_compiler) }
|
use :Html, -> { create(html_compiler) }
|
||||||
filter :Escapable
|
filter :Escapable
|
||||||
|
|
|
@ -579,7 +579,7 @@ describe "haml" do
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: it requires attribute sorter
|
# FIXME: it requires attribute sorter
|
||||||
pending "Ruby-style tag with a CSS class and 'class' as a variable attribute" do
|
specify "Ruby-style tag with a CSS class and 'class' as a variable attribute" do
|
||||||
haml = %q{.hello{:class => var}}
|
haml = %q{.hello{:class => var}}
|
||||||
html = %q{<div class='hello world'></div>}
|
html = %q{<div class='hello world'></div>}
|
||||||
locals = {:var=>"world"}
|
locals = {:var=>"world"}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
describe Hamlit::AttributeCompiler do
|
|
||||||
describe '#call' do
|
|
||||||
it 'does not alter normal attrs' do
|
|
||||||
assert_compile(
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
[:html, :attr, 'id', [:static, 'foo']],
|
|
||||||
[:html, :attr, 'class', [:static, 'bar']]],
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
[:html, :attr, 'id', [:static, 'foo']],
|
|
||||||
[:html, :attr, 'class', [:static, 'bar']]],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not alter news-tyle attributes' do
|
|
||||||
assert_compile(
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
'(a=2)'],
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
'(a=2)'],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'convers only string' do
|
|
||||||
assert_compile(
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
[:html, :attr, 'id', [:static, 'foo']],
|
|
||||||
'{ foo: "bar" }',
|
|
||||||
[:html, :attr, 'class', [:static, 'bar']]],
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
[:html, :attr, 'id', [:static, 'foo']],
|
|
||||||
[:html, :attr, 'foo', [:dynamic, '"bar"']],
|
|
||||||
[:html, :attr, 'class', [:static, 'bar']]],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'converts nested attributes' do
|
|
||||||
assert_compile(
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
'{ a: { b: {}, c: "d" }, e: "f" }'],
|
|
||||||
[:haml,
|
|
||||||
:attrs,
|
|
||||||
[:html, :attr, 'a-c', [:dynamic, '"d"']],
|
|
||||||
[:html, :attr, 'e', [:dynamic, '"f"']]],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Add table
Reference in a new issue