mirror of
https://github.com/haml/haml.git
synced 2022-11-09 12:33:31 -05:00
parent
7d15d4cda1
commit
000b6657b5
3 changed files with 49 additions and 6 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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],
|
||||
|
|
Loading…
Add table
Reference in a new issue