1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

Implement multiline attributes

Close #981
This commit is contained in:
Takashi Kokubun 2020-10-19 20:17:19 -07:00 committed by Hampton Catlin
parent 7d15d4cda1
commit 000b6657b5
3 changed files with 49 additions and 6 deletions

View file

@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'ripper'
require 'strscan'
module Haml
@ -90,6 +91,9 @@ module Haml
ID_KEY = 'id'.freeze
CLASS_KEY = 'class'.freeze
# Used for scanning old attributes, substituting the first '{'
METHOD_CALL_PREFIX = 'a('
def initialize(options)
@options = Options.wrap(options)
# Record the indent levels of "if" statements to validate the subsequent
@ -651,13 +655,18 @@ module Haml
# @return [String] rest
# @return [Integer] last_line
def parse_old_attributes(text)
text = text.dup
last_line = @line.index + 1
begin
attributes_hash, rest = balance(text, ?{, ?})
# Old attributes often look like a valid Hash literal, but it sometimes allow code like
# `{ hash, foo: bar }`, which is compiled to `_hamlout.attributes({}, nil, hash, foo: bar)`.
#
# To scan such code correctly, this scans `a( hash, foo: bar }` instead, stops when there is
# 1 more :on_embexpr_end (the last '}') than :on_embexpr_beg, and resurrects '{' afterwards.
balanced, rest = balance_tokens(text.sub(?{, METHOD_CALL_PREFIX), :on_embexpr_beg, :on_embexpr_end, count: 1)
attributes_hash = balanced.sub(METHOD_CALL_PREFIX, ?{)
rescue SyntaxError => e
if text.strip[-1] == ?, && e.message == Error.message(:unbalanced_brackets)
if e.message == Error.message(:unbalanced_brackets) && !@template.empty?
text << "\n#{@next_line.text}"
last_line += 1
next_line
@ -811,6 +820,25 @@ module Haml
Haml::Util.balance(*args) or raise(SyntaxError.new(Error.message(:unbalanced_brackets)))
end
# Unlike #balance, this balances Ripper tokens to balance something like `{ a: "}" }` correctly.
def balance_tokens(buf, start, finish, count: 0)
text = ''.dup
Ripper.lex(buf).each do |_, token, str|
text << str
case token
when start
count += 1
when finish
count -= 1
end
if count == 0
return text, buf.sub(text, '')
end
end
raise SyntaxError.new(Error.message(:unbalanced_brackets))
end
def block_opened?
@next_line.tabs > @line.tabs
end

View file

@ -109,4 +109,19 @@ HAML
%a{h1, :aria => h2}
HAML
end
def test_multiline_attributes
assert_equal(<<HTML, render(<<HAML))
<div class='haml' data-content='/:|}' data-haml-info-url='https://haml.info' id='info'>Haml</div>
HTML
.haml#info{
"data": {
"content": "/:|}",
"haml-info": {
"url": "https://haml.info",
}
}
} Haml
HAML
end
end

View file

@ -56,9 +56,9 @@ class ExceptionTest < TestBase
"%p{'foo' => 'bar' 'bar' => 'baz'}" => :compile,
"%p{:foo => }" => :compile,
"%p{=> 'bar'}" => :compile,
"%p{'foo => 'bar'}" => :compile,
"%p{:foo => 'bar}" => :compile,
"%p{:foo => 'bar\"}" => :compile,
"%p{'foo => 'bar'}" => error(:unbalanced_brackets),
"%p{:foo => 'bar}" => error(:unbalanced_brackets),
"%p{:foo => 'bar\"}" => error(:unbalanced_brackets),
# Regression tests
"foo\n\n\n bar" => [error(:illegal_nesting_plain), 4],
"%p/\n\n bar" => [error(:illegal_nesting_self_closing), 3],