haml 7.4.0 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/haml/attribute_compiler.rb +13 -2
- data/lib/haml/attribute_parser.rb +8 -6
- data/lib/haml/compiler/children_compiler.rb +1 -3
- data/lib/haml/compiler/script_compiler.rb +6 -9
- data/lib/haml/helpers.rb +1 -1
- data/lib/haml/parser.rb +6 -0
- data/lib/haml/preserver.rb +52 -0
- data/lib/haml/rails_helpers.rb +4 -12
- data/lib/haml/template.rb +1 -1
- data/lib/haml/temple_line_counter.rb +15 -1
- data/lib/haml/util.rb +0 -1
- data/lib/haml/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6436b78efc92438b9bd1a948dd0672bc8d3798c654fa2846d310386ef0552084
|
|
4
|
+
data.tar.gz: b8c2657b36c209e49c0e58c95470c45cc8213e30b1eb17458e0550db37bd616e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0530e52beb2fd093dfeb7d55c107b92a84f91e854bd38ed2e919349fcb820043cd3dd6e328ba4b2b24aa1b64ee8fcf2faf4a36dae1da60cb1b0a2b6abef7f531
|
|
7
|
+
data.tar.gz: b5a94fe5203318f1eaefa08b86e418090b8d0ef48cb94afa219512d87b116f568833cdf42e8f24a0a5e540d47b4b98dfb8a04f9e372bfe99d912d01dc41c2c24
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Haml Changelog
|
|
2
2
|
|
|
3
|
+
## 7.5.0
|
|
4
|
+
|
|
5
|
+
* Compile a multi-line attribute hash statically https://github.com/haml/haml/pull/1222
|
|
6
|
+
* Errors from multi-line attributes will be reported on the tag's line instead of the expression that raised the error.
|
|
7
|
+
|
|
8
|
+
## 7.4.1
|
|
9
|
+
|
|
10
|
+
* Build the preserve regex once per tag list instead of per call https://github.com/haml/haml/pull/1220
|
|
11
|
+
|
|
3
12
|
## 7.4.0
|
|
4
13
|
|
|
5
14
|
* `Haml::BOOLEAN_ATTRIBUTES` is changed from Array to Set https://github.com/haml/haml/pull/1216
|
|
@@ -3,6 +3,7 @@ require 'set'
|
|
|
3
3
|
require 'haml/attribute_builder'
|
|
4
4
|
require 'haml/attribute_parser'
|
|
5
5
|
require 'haml/ruby_expression'
|
|
6
|
+
require 'haml/temple_line_counter'
|
|
6
7
|
|
|
7
8
|
module Haml
|
|
8
9
|
# The set of boolean attributes. You may add custom attributes to this constant.
|
|
@@ -24,12 +25,15 @@ module Haml
|
|
|
24
25
|
def compile(node)
|
|
25
26
|
hashes = []
|
|
26
27
|
return runtime_compile(node) if node.value[:object_ref] != :nil
|
|
27
|
-
|
|
28
|
+
dynamic_attributes = node.value[:dynamic_attributes]
|
|
29
|
+
[dynamic_attributes.new, dynamic_attributes.old].compact.each do |attribute_str|
|
|
28
30
|
hash = AttributeParser.parse(attribute_str)
|
|
29
31
|
return runtime_compile(node) if hash.nil? || hash.any? { |_key, value| value.empty? }
|
|
30
32
|
hashes << hash
|
|
31
33
|
end
|
|
32
|
-
static_compile(node.value[:attributes], hashes)
|
|
34
|
+
temple = static_compile(node.value[:attributes], hashes)
|
|
35
|
+
restore_newlines!(temple, dynamic_attributes)
|
|
36
|
+
temple
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
private
|
|
@@ -67,6 +71,13 @@ module Haml
|
|
|
67
71
|
temple
|
|
68
72
|
end
|
|
69
73
|
|
|
74
|
+
# ChildrenCompiler counts on the tag spanning as many lines as its attribute source does.
|
|
75
|
+
def restore_newlines!(temple, dynamic_attributes)
|
|
76
|
+
missing = dynamic_attributes.newline_count
|
|
77
|
+
missing -= TempleLineCounter.count_lines(temple) if missing > 0
|
|
78
|
+
missing.times { temple << [:newline] }
|
|
79
|
+
end
|
|
80
|
+
|
|
70
81
|
def compile_id!(temple, key, values)
|
|
71
82
|
build_code = attribute_builder(:id, values)
|
|
72
83
|
if values.all? { |type, exp| type == :static || Temple::StaticAnalyzer.static?(exp) }
|
|
@@ -15,15 +15,11 @@ module Haml
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# @return [Hash,nil] - keys and values are the attribute source as written, or nil if
|
|
18
|
-
# the text is not a Hash literal whose keys are all static.
|
|
18
|
+
# the text is not a Hash literal whose keys are all static, or if it holds a heredoc.
|
|
19
19
|
def parse(text)
|
|
20
20
|
exp = wrap_bracket(text)
|
|
21
|
-
# A multi-line hash is left to the runtime, which keeps the [:newline] bookkeeping of
|
|
22
|
-
# the compiled code correct. Compiling it statically is a separate optimization.
|
|
23
|
-
return if exp.include?("\n")
|
|
24
|
-
|
|
25
21
|
node = hash_node(exp)
|
|
26
|
-
return if node.nil?
|
|
22
|
+
return if node.nil? || contains_heredoc?(exp, node)
|
|
27
23
|
|
|
28
24
|
hash = {}
|
|
29
25
|
node.elements.each do |element|
|
|
@@ -58,6 +54,12 @@ module Haml
|
|
|
58
54
|
node if node.is_a?(Prism::HashNode)
|
|
59
55
|
end
|
|
60
56
|
|
|
57
|
+
# A heredoc's body lies outside its node, so the value's slice would not be the value.
|
|
58
|
+
# Its opener always spells `<<`, which spares the tree walk for nearly every hash.
|
|
59
|
+
def contains_heredoc?(exp, node)
|
|
60
|
+
exp.include?('<<') && !node.breadth_first_search { |n| n.respond_to?(:heredoc?) && n.heredoc? }.nil?
|
|
61
|
+
end
|
|
62
|
+
|
|
61
63
|
# The key as written between its delimiters, not unescaped: an escape has to reach the
|
|
62
64
|
# attribute name as the source spelled it, like the `\0` of `{ "a\0b" => 1 }`.
|
|
63
65
|
def static_key(key)
|
|
@@ -40,9 +40,7 @@ module Haml
|
|
|
40
40
|
when :script, :silent_script
|
|
41
41
|
@lineno += 1
|
|
42
42
|
when :tag
|
|
43
|
-
|
|
44
|
-
@lineno += attribute_hash.count("\n")
|
|
45
|
-
end
|
|
43
|
+
@lineno += node.value[:dynamic_attributes].newline_count
|
|
46
44
|
@lineno += 1 if node.children.empty? && node.value[:parse]
|
|
47
45
|
end
|
|
48
46
|
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'temple/static_analyzer'
|
|
3
|
+
require 'haml/helpers'
|
|
4
|
+
require 'haml/preserver'
|
|
3
5
|
require 'haml/ruby_expression'
|
|
4
6
|
require 'haml/string_splitter'
|
|
5
7
|
|
|
6
8
|
module Haml
|
|
7
9
|
class Compiler
|
|
8
10
|
class ScriptCompiler
|
|
9
|
-
def self.find_and_preserve(input, tags)
|
|
10
|
-
|
|
11
|
-
re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
|
|
12
|
-
input.to_s.gsub(re) do |s|
|
|
13
|
-
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
|
14
|
-
"<#{$1}#{$2}>#{Haml::Helpers.preserve($3)}</#{$1}>"
|
|
15
|
-
end
|
|
11
|
+
def self.find_and_preserve(input, tags = ::Haml::Preserver::DEFAULT_TAGS)
|
|
12
|
+
::Haml::Preserver.find_and_preserve(input, tags) { |content| ::Haml::Helpers.preserve(content) }
|
|
16
13
|
end
|
|
17
14
|
|
|
18
15
|
def initialize(identity, options)
|
|
@@ -69,7 +66,7 @@ module Haml
|
|
|
69
66
|
if node.value[:escape_html]
|
|
70
67
|
str = Haml::Util.escape_html(str)
|
|
71
68
|
elsif node.value[:preserve]
|
|
72
|
-
str = ScriptCompiler.find_and_preserve(str
|
|
69
|
+
str = ScriptCompiler.find_and_preserve(str)
|
|
73
70
|
end
|
|
74
71
|
[:multi, [:static, str], [:newline]]
|
|
75
72
|
end
|
|
@@ -104,7 +101,7 @@ module Haml
|
|
|
104
101
|
end
|
|
105
102
|
|
|
106
103
|
def find_and_preserve(code)
|
|
107
|
-
%Q[::Haml::Compiler::ScriptCompiler.find_and_preserve(#{code}
|
|
104
|
+
%Q[::Haml::Compiler::ScriptCompiler.find_and_preserve(#{code})]
|
|
108
105
|
end
|
|
109
106
|
|
|
110
107
|
def escape_html(temple)
|
data/lib/haml/helpers.rb
CHANGED
data/lib/haml/parser.rb
CHANGED
|
@@ -247,6 +247,12 @@ module Haml
|
|
|
247
247
|
[new, stripped_old].compact.join(', ')
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
+
# The lines a tag spans beyond its first because of these attributes, which is what
|
|
251
|
+
# the compiled tag has to span too for the line numbers after it to hold.
|
|
252
|
+
def newline_count
|
|
253
|
+
(new ? new.count("\n") : 0) + (old ? old.count("\n") : 0)
|
|
254
|
+
end
|
|
255
|
+
|
|
250
256
|
private
|
|
251
257
|
|
|
252
258
|
# For `%foo{ { foo: 1 }, bar: 2 }`, :old is "{ { foo: 1 }, bar: 2 }" and this method returns " { foo: 1 }, bar: 2 " for last argument.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Haml
|
|
3
|
+
# find_and_preserve, shared by compiled templates and RailsHelpers.
|
|
4
|
+
#
|
|
5
|
+
# @api private
|
|
6
|
+
module Preserver
|
|
7
|
+
DEFAULT_TAGS = %w[textarea pre code].freeze
|
|
8
|
+
|
|
9
|
+
def self.build_regex(tags)
|
|
10
|
+
# An empty entry would add an alternative matching `<>`.
|
|
11
|
+
pattern = tags.reject(&:empty?).map { |tag| Regexp.escape(tag) }.join('|')
|
|
12
|
+
/<(#{pattern})([^>]*)>(.*?)(<\/\1>)/im
|
|
13
|
+
end
|
|
14
|
+
private_class_method :build_regex
|
|
15
|
+
|
|
16
|
+
DEFAULT_REGEX = build_regex(DEFAULT_TAGS)
|
|
17
|
+
private_constant :DEFAULT_REGEX
|
|
18
|
+
|
|
19
|
+
# Past this many lists the merge below turns quadratic, so the cache starts over.
|
|
20
|
+
MAX_REGEXES = 64
|
|
21
|
+
private_constant :MAX_REGEXES
|
|
22
|
+
|
|
23
|
+
INITIAL_REGEXES = { DEFAULT_TAGS => DEFAULT_REGEX }.freeze
|
|
24
|
+
private_constant :INITIAL_REGEXES
|
|
25
|
+
|
|
26
|
+
@regexes = INITIAL_REGEXES
|
|
27
|
+
|
|
28
|
+
# The cache is replaced, never mutated, so a racing writer at worst rebuilds a regex.
|
|
29
|
+
def self.regex(tags)
|
|
30
|
+
return DEFAULT_REGEX if tags.equal?(DEFAULT_TAGS)
|
|
31
|
+
|
|
32
|
+
cache = @regexes
|
|
33
|
+
cache[tags] || begin
|
|
34
|
+
regex = build_regex(tags)
|
|
35
|
+
# Copied so a caller mutating its own list, or a String in it, cannot strand the entry.
|
|
36
|
+
key = tags.map { |tag| tag.dup.freeze }.freeze
|
|
37
|
+
cache = INITIAL_REGEXES if cache.size >= MAX_REGEXES
|
|
38
|
+
@regexes = cache.merge(key => regex).freeze
|
|
39
|
+
regex
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.find_and_preserve(input, tags)
|
|
44
|
+
re = regex(tags)
|
|
45
|
+
input.to_s.gsub(re) do |s|
|
|
46
|
+
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
|
47
|
+
name, attributes, content = $1, $2, $3
|
|
48
|
+
"<#{name}#{attributes}>#{yield(content)}</#{name}>"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/haml/rails_helpers.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
# frozen_string_literal:
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
require 'haml/helpers'
|
|
3
|
+
require 'haml/preserver'
|
|
3
4
|
|
|
4
5
|
# There are only helpers that depend on ActionView internals.
|
|
5
6
|
module Haml
|
|
@@ -7,21 +8,12 @@ module Haml
|
|
|
7
8
|
include Helpers
|
|
8
9
|
extend self
|
|
9
10
|
|
|
10
|
-
DEFAULT_PRESERVE_TAGS =
|
|
11
|
+
DEFAULT_PRESERVE_TAGS = Preserver::DEFAULT_TAGS
|
|
11
12
|
|
|
12
13
|
def find_and_preserve(input = nil, tags = DEFAULT_PRESERVE_TAGS, &block)
|
|
13
14
|
return find_and_preserve(capture_haml(&block), input || tags) if block
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
s << '|' unless s.empty?
|
|
17
|
-
s << Regexp.escape(t)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
|
|
21
|
-
input.to_s.gsub(re) do |s|
|
|
22
|
-
s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
|
|
23
|
-
"<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
|
|
24
|
-
end
|
|
16
|
+
Preserver.find_and_preserve(input, tags) { |content| preserve(content) }
|
|
25
17
|
end
|
|
26
18
|
|
|
27
19
|
def preserve(input = nil, &block)
|
data/lib/haml/template.rb
CHANGED
|
@@ -19,13 +19,27 @@ module Haml
|
|
|
19
19
|
arg.count("\n") + cases.map do |cond, e|
|
|
20
20
|
(cond == :else ? 0 : cond.count("\n")) + count_lines(e)
|
|
21
21
|
end.reduce(:+)
|
|
22
|
-
when :escape
|
|
22
|
+
when :escape, :fescape
|
|
23
23
|
count_lines(args[1])
|
|
24
|
+
when :html
|
|
25
|
+
count_html_lines(args)
|
|
24
26
|
when :newline
|
|
25
27
|
1
|
|
26
28
|
else
|
|
27
29
|
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
|
|
28
30
|
end
|
|
29
31
|
end
|
|
32
|
+
|
|
33
|
+
def self.count_html_lines(args)
|
|
34
|
+
case args[0]
|
|
35
|
+
when :attrs
|
|
36
|
+
args.drop(1).sum { |a| count_lines(a) }
|
|
37
|
+
when :attr
|
|
38
|
+
count_lines(args[2])
|
|
39
|
+
else
|
|
40
|
+
raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression 'html #{args[0]}' is given!")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
private_class_method :count_html_lines
|
|
30
44
|
end
|
|
31
45
|
end
|
data/lib/haml/util.rb
CHANGED
data/lib/haml/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: haml
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.
|
|
4
|
+
version: 7.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Natalie Weizenbaum
|
|
@@ -307,6 +307,7 @@ files:
|
|
|
307
307
|
- lib/haml/identity.rb
|
|
308
308
|
- lib/haml/object_ref.rb
|
|
309
309
|
- lib/haml/parser.rb
|
|
310
|
+
- lib/haml/preserver.rb
|
|
310
311
|
- lib/haml/rails_helpers.rb
|
|
311
312
|
- lib/haml/rails_template.rb
|
|
312
313
|
- lib/haml/railtie.rb
|