hamlit 2.9.5 → 2.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 818a7688a58be17941aea6ddd21986d58186048882d0672b9f8a586d19a00972
4
- data.tar.gz: 22c7eaef6d2fb67fe939c8d861780d4d56affa6df0d478ede2b0beb7da6bf79f
3
+ metadata.gz: d8a587e57cbdc02c90d18591926efb8bf4fd990d60c31969501418a1eed0d2d9
4
+ data.tar.gz: 7f97e39c33bad82a1cc5b4056b8ac0765a2939f2a9c0a6fecfd156daefa98fa3
5
5
  SHA512:
6
- metadata.gz: 56fa71f1352812b9f98e2042f4f59fb54cb35183ee3ee9c29fd9302293446522bbd5c03e0cd9859fb0d757ec854692e544a4a51e169bb9478da68b251f1059a2
7
- data.tar.gz: 564b8282a5a1db7b476d664792d157112fbe5f4d28be48b0d31f89542c084fda0c17f86a2930306c3ed9b599b04e43073e81929488d49b0101a78ed2c336bceb
6
+ metadata.gz: dba41938cf6697aeef29f404178de0ccf46cb438402510e989af63593e5ea853627f3f0594fd0359e4b10c68b3c53a7ae64b9c3e2d2f717707e5996006b49721
7
+ data.tar.gz: 61ce345a747e0fffee535ecab7caee9b0320858f0243b405643115b97cef012039ed9d72766f5bbd4f1b0cde8fa2ffca7b7cea74a8b9ce2a875106c680f5274d
data/.gitignore CHANGED
@@ -11,6 +11,7 @@
11
11
  .ruby-version
12
12
  *.bundle
13
13
  *.so
14
+ *.su
14
15
  *.o
15
16
  *.a
16
17
  *.swp
@@ -9,11 +9,13 @@ matrix:
9
9
  include:
10
10
  - rvm: 2.3.8
11
11
  env: TASK=test
12
- - rvm: 2.4.5
12
+ - rvm: 2.4.9
13
13
  env: TASK=test
14
- - rvm: 2.5.3
14
+ - rvm: 2.5.7
15
15
  env: TASK=test
16
- - rvm: 2.6.0
16
+ - rvm: 2.6.5
17
+ env: TASK=test
18
+ - rvm: 2.7.0
17
19
  env: TASK=test
18
20
  - rvm: ruby-head
19
21
  env: TASK=test
@@ -21,19 +23,19 @@ matrix:
21
23
  env: TASK=test
22
24
  - rvm: truffleruby
23
25
  env: TASK=test
24
- - rvm: 2.6.0
26
+ - rvm: 2.7.0
25
27
  env: TASK=bench TEMPLATE=benchmark/boolean_attribute.haml,benchmark/class_attribute.haml,benchmark/id_attribute.haml,benchmark/data_attribute.haml,benchmark/common_attribute.haml
26
- - rvm: 2.6.0
28
+ - rvm: 2.7.0
27
29
  env: TASK=bench TEMPLATE=benchmark/dynamic_attributes/boolean_attribute.haml,benchmark/dynamic_attributes/class_attribute.haml,benchmark/dynamic_attributes/id_attribute.haml,benchmark/dynamic_attributes/data_attribute.haml,benchmark/dynamic_attributes/common_attribute.haml
28
- - rvm: 2.6.0
30
+ - rvm: 2.7.0
29
31
  env: TASK=bench SLIM_BENCH=1
30
- - rvm: 2.6.0
32
+ - rvm: 2.7.0
31
33
  env: TASK=bench TEMPLATE=benchmark/etc/attribute_builder.haml
32
- - rvm: 2.6.0
34
+ - rvm: 2.7.0
33
35
  env: TASK=bench TEMPLATE=benchmark/etc/static_analyzer.haml
34
- - rvm: 2.6.0
36
+ - rvm: 2.7.0
35
37
  env: TASK=bench TEMPLATE=benchmark/etc/string_interpolation.haml
36
- - rvm: 2.6.0
38
+ - rvm: 2.7.0
37
39
  env: TASK=bench TEMPLATE=test/haml/templates/standard.haml COMPILE=1
38
40
  allow_failures:
39
41
  - rvm: ruby-head
@@ -4,6 +4,45 @@ All notable changes to this project will be documented in this file. This
4
4
  project adheres to [Semantic Versioning](http://semver.org/). This change log is based upon
5
5
  [keep-a-changelog](https://github.com/olivierlacan/keep-a-changelog).
6
6
 
7
+ ## [2.12.0](https://github.com/k0kubun/hamlit/compare/v2.11.1...v2.12.0) - 2020-09-30
8
+
9
+ ### Changed
10
+
11
+ - Class names are no longer ordered alphabetically
12
+ *Thanks to @aliismayilov*
13
+ - This is compatible with [Haml 5.2](https://github.com/haml/haml/blob/v5.2.0/CHANGELOG.md#52)
14
+
15
+ ## [2.11.1](https://github.com/k0kubun/hamlit/compare/v2.11.0...v2.11.1) - 2020-08-25
16
+
17
+ ### Fixed
18
+
19
+ - Fix a line number on an error after filters like preserve, plain, and ruby.
20
+ *Thanks to @rgisiger*
21
+
22
+ ## [2.11.0](https://github.com/k0kubun/hamlit/compare/v2.10.1...v2.11.0) - 2019-12-12
23
+
24
+ ### Added
25
+
26
+ - Support Haml's _revealed_ conditional comment feature on `/![if !IE]` [#153](https://github.com/k0kubun/hamlit/issues/153).
27
+ *Thanks to @esb*
28
+
29
+ ## [2.10.1](https://github.com/k0kubun/hamlit/compare/v2.10.0...v2.10.1) - 2019-11-28
30
+
31
+ ### Added
32
+
33
+ - Register `Hamlit::Template` to Tilt as :hamlit as well, in addition to :haml
34
+
35
+ ## [2.10.0](https://github.com/k0kubun/hamlit/compare/v2.9.5...v2.10.0) - 2019-09-15
36
+
37
+ ### Added
38
+
39
+ - Optimize template rendering by string interpolation [#146](https://github.com/k0kubun/hamlit/issues/146)
40
+ - Exploiting pre-allocation of string interpolation introduced in Ruby 2.5 [ruby/ruby#1626](https://github.com/ruby/ruby/pull/1626)
41
+
42
+ ### Changed
43
+
44
+ - Require temple.gem >= 0.8.2
45
+
7
46
  ## [2.9.5](https://github.com/k0kubun/hamlit/compare/v2.9.4...v2.9.5) - 2019-09-08
8
47
 
9
48
  ### Added
data/Gemfile CHANGED
@@ -8,23 +8,17 @@ end
8
8
  # Specify your gem's dependencies in hamlit.gemspec
9
9
  gemspec
10
10
 
11
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.2.2')
12
- gem 'rack', '< 2'
13
- end
14
-
15
11
  gem 'benchmark-ips', '2.3.0'
16
12
  gem 'maxitest'
13
+ gem 'pry'
17
14
 
18
15
  if /java/ === RUBY_PLATFORM # JRuby
19
16
  gem 'pandoc-ruby'
20
17
  else
21
- gem 'pry-byebug'
22
18
  gem 'redcarpet'
23
19
 
24
20
  if RUBY_PLATFORM !~ /mswin|mingw/ && RUBY_ENGINE != 'truffleruby'
25
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0') # Travis cannot compile ruby.h with C++
26
- gem 'faml'
27
- end
21
+ gem 'faml'
28
22
  gem 'stackprof'
29
23
  end
30
24
  end
@@ -57,6 +57,7 @@ for full features in original implementation.
57
57
  - [x] :plain
58
58
  - [x] :preserve
59
59
  - [x] :ruby
60
+ - `haml_io` API is not supported. Use [hamlit-haml\_io.gem](https://github.com/hamlit/hamlit-haml_io) if you need.
60
61
  - [x] :sass
61
62
  - [x] :scss
62
63
  - [ ] :textile
@@ -121,10 +122,10 @@ and merging multiple ids results in concatenation by "\_".
121
122
  %div{ id: false }
122
123
 
123
124
  # Output
124
- <div id='foo_bar'></span>
125
- <div id='foo_bar'></span>
126
- <div id='foo_bar'></span>
127
- <div id=''></span>
125
+ <div id='foo_bar'></div>
126
+ <div id='foo_bar'></div>
127
+ <div id='foo_bar'></div>
128
+ <div id=''></div>
128
129
  ```
129
130
 
130
131
  ### class attribute
@@ -224,6 +225,14 @@ Hamlit::RailsTemplate.set_options attr_quote: '"'
224
225
  set :haml, { attr_quote: '"' }
225
226
  ```
226
227
 
228
+ ## Ruby module
229
+
230
+ `Hamlit::Template` is a module registered to `Tilt`. You can use it like:
231
+
232
+ ```rb
233
+ Hamlit::Template.new { "%strong Yay for HAML!" }.render
234
+ ```
235
+
227
236
  ## Creating a custom filter
228
237
 
229
238
  Currently it doesn't have filter registering interface compatible with Haml.
@@ -0,0 +1,25 @@
1
+ # Original: https://github.com/amatsuda/string_template/blob/master/benchmark.rb
2
+ require 'benchmark_driver'
3
+
4
+ Benchmark.driver(repeat_count: 8) do |x|
5
+ x.prelude %{
6
+ require 'rails'
7
+ require 'action_view'
8
+ require 'string_template'
9
+ StringTemplate::Railtie.run_initializers
10
+ require 'hamlit'
11
+ Hamlit::Railtie.run_initializers
12
+ Hamlit::RailsTemplate.set_options(escape_html: false, generator: Temple::Generators::ArrayBuffer)
13
+ require 'action_view/base'
14
+
15
+ (view = Class.new(ActionView::Base).new(ActionView::LookupContext.new(''))).instance_variable_set(:@world, 'world!')
16
+
17
+ # compile template
18
+ hello = 'benchmark/dynamic_merger/hello'
19
+ view.render(template: hello, handlers: 'string')
20
+ view.render(template: hello, handlers: 'haml')
21
+ }
22
+ x.report 'string', %{ view.render(template: hello, handlers: 'string') }
23
+ x.report 'hamlit', %{ view.render(template: hello, handlers: 'haml') }
24
+ x.loop_count 100_000
25
+ end
@@ -0,0 +1,50 @@
1
+ hello, #{ @world }
2
+ hello, #{ @world }
3
+ hello, #{ @world }
4
+ hello, #{ @world }
5
+ hello, #{ @world }
6
+ hello, #{ @world }
7
+ hello, #{ @world }
8
+ hello, #{ @world }
9
+ hello, #{ @world }
10
+ hello, #{ @world }
11
+ hello, #{ @world }
12
+ hello, #{ @world }
13
+ hello, #{ @world }
14
+ hello, #{ @world }
15
+ hello, #{ @world }
16
+ hello, #{ @world }
17
+ hello, #{ @world }
18
+ hello, #{ @world }
19
+ hello, #{ @world }
20
+ hello, #{ @world }
21
+ hello, #{ @world }
22
+ hello, #{ @world }
23
+ hello, #{ @world }
24
+ hello, #{ @world }
25
+ hello, #{ @world }
26
+ hello, #{ @world }
27
+ hello, #{ @world }
28
+ hello, #{ @world }
29
+ hello, #{ @world }
30
+ hello, #{ @world }
31
+ hello, #{ @world }
32
+ hello, #{ @world }
33
+ hello, #{ @world }
34
+ hello, #{ @world }
35
+ hello, #{ @world }
36
+ hello, #{ @world }
37
+ hello, #{ @world }
38
+ hello, #{ @world }
39
+ hello, #{ @world }
40
+ hello, #{ @world }
41
+ hello, #{ @world }
42
+ hello, #{ @world }
43
+ hello, #{ @world }
44
+ hello, #{ @world }
45
+ hello, #{ @world }
46
+ hello, #{ @world }
47
+ hello, #{ @world }
48
+ hello, #{ @world }
49
+ hello, #{ @world }
50
+ hello, #{ @world }
@@ -0,0 +1,50 @@
1
+ hello, #{ @world }
2
+ hello, #{ @world }
3
+ hello, #{ @world }
4
+ hello, #{ @world }
5
+ hello, #{ @world }
6
+ hello, #{ @world }
7
+ hello, #{ @world }
8
+ hello, #{ @world }
9
+ hello, #{ @world }
10
+ hello, #{ @world }
11
+ hello, #{ @world }
12
+ hello, #{ @world }
13
+ hello, #{ @world }
14
+ hello, #{ @world }
15
+ hello, #{ @world }
16
+ hello, #{ @world }
17
+ hello, #{ @world }
18
+ hello, #{ @world }
19
+ hello, #{ @world }
20
+ hello, #{ @world }
21
+ hello, #{ @world }
22
+ hello, #{ @world }
23
+ hello, #{ @world }
24
+ hello, #{ @world }
25
+ hello, #{ @world }
26
+ hello, #{ @world }
27
+ hello, #{ @world }
28
+ hello, #{ @world }
29
+ hello, #{ @world }
30
+ hello, #{ @world }
31
+ hello, #{ @world }
32
+ hello, #{ @world }
33
+ hello, #{ @world }
34
+ hello, #{ @world }
35
+ hello, #{ @world }
36
+ hello, #{ @world }
37
+ hello, #{ @world }
38
+ hello, #{ @world }
39
+ hello, #{ @world }
40
+ hello, #{ @world }
41
+ hello, #{ @world }
42
+ hello, #{ @world }
43
+ hello, #{ @world }
44
+ hello, #{ @world }
45
+ hello, #{ @world }
46
+ hello, #{ @world }
47
+ hello, #{ @world }
48
+ hello, #{ @world }
49
+ hello, #{ @world }
50
+ hello, #{ @world }
data/bin/bench CHANGED
@@ -25,7 +25,7 @@ class Bench < Thor
25
25
  haml = File.read(file)
26
26
 
27
27
  Benchmark.ips do |x|
28
- x.report("haml v#{Haml::VERSION}") { Haml::Engine.new(haml, escape_html: true, escape_attrs: true, ugly: true).precompiled }
28
+ x.report("haml v#{Haml::VERSION}") { Haml::Engine.new(haml, escape_html: true, escape_attrs: true).precompiled }
29
29
  x.report("faml v#{Faml::VERSION}") { Faml::Engine.new.call(haml) }
30
30
  x.report("hamlit v#{Hamlit::VERSION}") { Hamlit::Engine.new.call(haml) }
31
31
  x.compare!
@@ -43,7 +43,7 @@ class Bench < Thor
43
43
  object.instance_eval(File.read(ruby_file))
44
44
  end
45
45
 
46
- Haml::Engine.new(haml, escape_html: true, escape_attrs: true, ugly: true).def_method(object, :haml)
46
+ Haml::Engine.new(haml, escape_html: true, escape_attrs: true).def_method(object, :haml)
47
47
  object.instance_eval "def faml; #{Faml::Engine.new.call(haml)}; end"
48
48
  object.instance_eval "def hamlit; #{Hamlit::Engine.new.call(haml)}; end"
49
49
 
@@ -59,7 +59,7 @@ class Bench < Thor
59
59
  def code(file)
60
60
  haml = File.read(file)
61
61
  puts "#{?= * 49}\n Haml Source: #{file}\n#{?= * 49}"
62
- puts Haml::Engine.new(haml, escape_html: true, escape_attrs: true, ugly: true).precompiled
62
+ puts Haml::Engine.new(haml, escape_html: true, escape_attrs: true).precompiled
63
63
  puts "\n#{?= * 49}\n Faml Source: #{file}\n#{?= * 49}"
64
64
  puts Faml::Engine.new.call(haml)
65
65
  puts "\n#{?= * 49}\n Hamlit Source: #{file}\n#{?= * 49}"
@@ -152,7 +152,6 @@ hamlit_build_multi_class(VALUE escape_attrs, VALUE values)
152
152
  }
153
153
  }
154
154
 
155
- rb_ary_sort_bang(buf);
156
155
  rb_funcall(buf, id_uniq_bang, 0);
157
156
 
158
157
  return escape_attribute(escape_attrs, rb_ary_join(buf, str_space()));
@@ -26,10 +26,11 @@ Gem::Specification.new do |spec|
26
26
  spec.required_ruby_version = '>= 2.1.0'
27
27
  end
28
28
 
29
- spec.add_dependency 'temple', '>= 0.8.0'
29
+ spec.add_dependency 'temple', '>= 0.8.2'
30
30
  spec.add_dependency 'thor'
31
31
  spec.add_dependency 'tilt'
32
32
 
33
+ spec.add_development_dependency 'benchmark_driver'
33
34
  spec.add_development_dependency 'bundler'
34
35
  spec.add_development_dependency 'coffee-script'
35
36
  spec.add_development_dependency 'erubi'
@@ -41,5 +42,6 @@ Gem::Specification.new do |spec|
41
42
  spec.add_development_dependency 'rake-compiler'
42
43
  spec.add_development_dependency 'sass'
43
44
  spec.add_development_dependency 'slim'
45
+ spec.add_development_dependency 'string_template'
44
46
  spec.add_development_dependency 'unindent'
45
47
  end
@@ -47,7 +47,7 @@ module Hamlit::AttributeBuilder
47
47
  when value.is_a?(String)
48
48
  # noop
49
49
  when value.is_a?(Array)
50
- value = value.flatten.select { |v| v }.map(&:to_s).sort.uniq.join(' ')
50
+ value = value.flatten.select { |v| v }.map(&:to_s).uniq.join(' ')
51
51
  when value
52
52
  value = value.to_s
53
53
  else
@@ -67,7 +67,7 @@ module Hamlit::AttributeBuilder
67
67
  classes << value.to_s
68
68
  end
69
69
  end
70
- escape_html(escape_attrs, classes.map(&:to_s).sort.uniq.join(' '))
70
+ escape_html(escape_attrs, classes.map(&:to_s).uniq.join(' '))
71
71
  end
72
72
 
73
73
  def build_data(escape_attrs, quote, *hashes)
@@ -17,7 +17,7 @@ module Hamlit
17
17
  if node.value[:object_ref] != :nil || !Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
18
18
  return runtime_compile(node)
19
19
  end
20
- node.value[:attributes_hashes].each do |attribute_str|
20
+ [node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_str|
21
21
  hash = AttributeParser.parse(attribute_str)
22
22
  return runtime_compile(node) unless hash
23
23
  hashes << hash
@@ -28,11 +28,11 @@ module Hamlit
28
28
  private
29
29
 
30
30
  def runtime_compile(node)
31
- attrs = node.value[:attributes_hashes]
31
+ attrs = []
32
32
  attrs.unshift(node.value[:attributes].inspect) if node.value[:attributes] != {}
33
33
 
34
34
  args = [@escape_attrs.inspect, "#{@quote.inspect}.freeze", @format.inspect].push(node.value[:object_ref]) + attrs
35
- [:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')})"]]
35
+ [:html, :attrs, [:dynamic, "::Hamlit::AttributeBuilder.build(#{args.join(', ')}, #{node.value[:dynamic_attributes].to_literal})"]]
36
36
  end
37
37
 
38
38
  def static_compile(static_hash, dynamic_hashes)
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require 'hamlit/temple_line_counter'
3
+
2
4
  module Hamlit
3
5
  class Compiler
4
6
  class ChildrenCompiler
@@ -14,7 +16,7 @@ module Hamlit
14
16
  node.children.each do |n|
15
17
  rstrip_whitespace!(temple) if nuke_prev_whitespace?(n)
16
18
  insert_newlines!(temple, n)
17
- temple << yield(n)
19
+ temple << moving_lineno(n) { block.call(n) }
18
20
  temple << :whitespace if insert_whitespace?(n)
19
21
  end
20
22
  rstrip_whitespace!(temple) if nuke_inner_whitespace?(node)
@@ -27,19 +29,31 @@ module Hamlit
27
29
  (node.line - @lineno).times do
28
30
  temple << [:newline]
29
31
  end
32
+
30
33
  @lineno = node.line
34
+ end
31
35
 
36
+ def moving_lineno(node, &block)
37
+ # before: As they may have children, we need to increment lineno before compilation.
32
38
  case node.type
33
39
  when :script, :silent_script
34
40
  @lineno += 1
35
- when :filter
36
- @lineno += (node.value[:text] || '').split("\n").size
37
41
  when :tag
38
- node.value[:attributes_hashes].each do |attribute_hash|
42
+ [node.value[:dynamic_attributes].new, node.value[:dynamic_attributes].old].compact.each do |attribute_hash|
39
43
  @lineno += attribute_hash.count("\n")
40
44
  end
41
45
  @lineno += 1 if node.children.empty? && node.value[:parse]
42
46
  end
47
+
48
+ temple = block.call # compile
49
+
50
+ # after: filter may not have children, and for some dynamic filters we can't predict the number of lines.
51
+ case node.type
52
+ when :filter
53
+ @lineno += TempleLineCounter.count_lines(temple)
54
+ end
55
+
56
+ temple
43
57
  end
44
58
 
45
59
  def confirm_whitespace(temple)
@@ -25,11 +25,13 @@ module Hamlit
25
25
  condition = $1
26
26
  end
27
27
 
28
- if node.children.empty?
29
- [:html, :condcomment, condition, [:static, " #{node.value[:text]} "]]
30
- else
31
- [:html, :condcomment, condition, yield(node)]
32
- end
28
+ content =
29
+ if node.children.empty?
30
+ [:static, " #{node.value[:text]} "]
31
+ else
32
+ yield(node)
33
+ end
34
+ [:html, :condcomment, condition, content, node.value[:revealed]]
33
35
  end
34
36
  end
35
37
  end
@@ -55,10 +55,6 @@ module Hamlit
55
55
 
56
56
  # We should handle interpolation here to escape only interpolated values.
57
57
  def compile_interpolated_plain(node)
58
- unless Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
59
- return [:multi, [:escape, node.value[:escape_interpolation], [:dynamic, "%Q[#{node.value[:value]}]"]], [:newline]]
60
- end
61
-
62
58
  temple = [:multi]
63
59
  StringSplitter.compile(node.value[:value]).each do |type, value|
64
60
  case type
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ # Compile [:multi, [:static, 'foo'], [:dynamic, 'bar']] to [:dynamic, '"foo#{bar}"']
4
+ class DynamicMerger < Temple::Filter
5
+ def on_multi(*exps)
6
+ exps = exps.dup
7
+ result = [:multi]
8
+ buffer = []
9
+
10
+ until exps.empty?
11
+ type, arg = exps.first
12
+ if type == :dynamic && arg.count("\n") == 0
13
+ buffer << exps.shift
14
+ elsif type == :static && exps.size > (count = arg.count("\n")) &&
15
+ exps[1, count].all? { |e| e == [:newline] }
16
+ (1 + count).times { buffer << exps.shift }
17
+ elsif type == :newline && exps.size > (count = count_newline(exps)) &&
18
+ exps[count].first == :static && count == exps[count].last.count("\n")
19
+ (count + 1).times { buffer << exps.shift }
20
+ else
21
+ result.concat(merge_dynamic(buffer))
22
+ buffer = []
23
+ result << compile(exps.shift)
24
+ end
25
+ end
26
+ result.concat(merge_dynamic(buffer))
27
+
28
+ result.size == 2 ? result[1] : result
29
+ end
30
+
31
+ private
32
+
33
+ def merge_dynamic(exps)
34
+ # Merge exps only when they have both :static and :dynamic
35
+ unless exps.any? { |type,| type == :static } && exps.any? { |type,| type == :dynamic }
36
+ return exps
37
+ end
38
+
39
+ strlit_body = String.new
40
+ exps.each do |type, arg|
41
+ case type
42
+ when :static
43
+ strlit_body << arg.dump.sub!(/\A"/, '').sub!(/"\z/, '').gsub('\n', "\n")
44
+ when :dynamic
45
+ strlit_body << "\#{#{arg}}"
46
+ when :newline
47
+ # newline is added by `gsub('\n', "\n")`
48
+ else
49
+ raise "unexpected type #{type.inspect} is given to #merge_dynamic"
50
+ end
51
+ end
52
+ [[:dynamic, "%Q\0#{strlit_body}\0"]]
53
+ end
54
+
55
+ def count_newline(exps)
56
+ count = 0
57
+ exps.each do |exp|
58
+ if exp == [:newline]
59
+ count += 1
60
+ else
61
+ return count
62
+ end
63
+ end
64
+ return count
65
+ end
66
+ end
67
+ end
@@ -2,10 +2,10 @@
2
2
  require 'temple'
3
3
  require 'hamlit/parser'
4
4
  require 'hamlit/compiler'
5
+ require 'hamlit/html'
5
6
  require 'hamlit/escapable'
6
7
  require 'hamlit/force_escapable'
7
- require 'hamlit/html'
8
- require 'hamlit/string_splitter'
8
+ require 'hamlit/dynamic_merger'
9
9
 
10
10
  module Hamlit
11
11
  class Engine < Temple::Engine
@@ -25,15 +25,14 @@ module Hamlit
25
25
  use Parser
26
26
  use Compiler
27
27
  use HTML
28
- if Ripper.respond_to?(:lex) # No Ripper.lex in truffleruby
29
- use StringSplitter
30
- filter :StaticAnalyzer
31
- end
28
+ filter :StringSplitter
29
+ filter :StaticAnalyzer
32
30
  use Escapable
33
31
  use ForceEscapable
34
32
  filter :ControlFlow
35
33
  filter :MultiFlattener
36
34
  filter :StaticMerger
35
+ use DynamicMerger
37
36
  use :Generator, -> { options[:generator] }
38
37
  end
39
38
  end
@@ -14,10 +14,6 @@ module Hamlit
14
14
 
15
15
  def compile_plain(text)
16
16
  string_literal = ::Hamlit::HamlUtil.unescape_interpolation(text)
17
- unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
18
- return [[:escape, false, [:dynamic, string_literal]]]
19
- end
20
-
21
17
  StringSplitter.compile(string_literal).map do |temple|
22
18
  type, str = temple
23
19
  case type
@@ -10,5 +10,13 @@ module Hamlit
10
10
  end
11
11
  super(opts)
12
12
  end
13
+
14
+ # This dispatcher supports Haml's "revealed" conditional comment.
15
+ def on_html_condcomment(condition, content, revealed = false)
16
+ on_html_comment [:multi,
17
+ [:static, "[#{condition}]>#{'<!-->' if revealed}"],
18
+ content,
19
+ [:static, "#{'<!--' if revealed}<![endif]"]]
20
+ end
13
21
  end
14
22
  end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hamlit
4
+ module HamlAttributeBuilder
5
+ # https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
6
+ INVALID_ATTRIBUTE_NAME_REGEX = /[ \0"'>\/=]/
7
+
8
+ class << self
9
+ def build_attributes(is_html, attr_wrapper, escape_attrs, hyphenate_data_attrs, attributes = {})
10
+ # @TODO this is an absolutely ridiculous amount of arguments. At least
11
+ # some of this needs to be moved into an instance method.
12
+ join_char = hyphenate_data_attrs ? '-' : '_'
13
+
14
+ attributes.each do |key, value|
15
+ if value.is_a?(Hash)
16
+ data_attributes = attributes.delete(key)
17
+ data_attributes = flatten_data_attributes(data_attributes, '', join_char)
18
+ data_attributes = build_data_keys(data_attributes, hyphenate_data_attrs, key)
19
+ verify_attribute_names!(data_attributes.keys)
20
+ attributes = data_attributes.merge(attributes)
21
+ end
22
+ end
23
+
24
+ result = attributes.collect do |attr, value|
25
+ next if value.nil?
26
+
27
+ value = filter_and_join(value, ' ') if attr == 'class'
28
+ value = filter_and_join(value, '_') if attr == 'id'
29
+
30
+ if value == true
31
+ next " #{attr}" if is_html
32
+ next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}"
33
+ elsif value == false
34
+ next
35
+ end
36
+
37
+ value =
38
+ if escape_attrs == :once
39
+ Hamlit::HamlHelpers.escape_once_without_haml_xss(value.to_s)
40
+ elsif escape_attrs
41
+ Hamlit::HamlHelpers.html_escape_without_haml_xss(value.to_s)
42
+ else
43
+ value.to_s
44
+ end
45
+ " #{attr}=#{attr_wrapper}#{value}#{attr_wrapper}"
46
+ end
47
+ result.compact!
48
+ result.sort!
49
+ result.join
50
+ end
51
+
52
+ # @return [String, nil]
53
+ def filter_and_join(value, separator)
54
+ return '' if (value.respond_to?(:empty?) && value.empty?)
55
+
56
+ if value.is_a?(Array)
57
+ value = value.flatten
58
+ value.map! {|item| item ? item.to_s : nil}
59
+ value.compact!
60
+ value = value.join(separator)
61
+ else
62
+ value = value ? value.to_s : nil
63
+ end
64
+ !value.nil? && !value.empty? && value
65
+ end
66
+
67
+ # Merges two attribute hashes.
68
+ # This is the same as `to.merge!(from)`,
69
+ # except that it merges id, class, and data attributes.
70
+ #
71
+ # ids are concatenated with `"_"`,
72
+ # and classes are concatenated with `" "`.
73
+ # data hashes are simply merged.
74
+ #
75
+ # Destructively modifies `to`.
76
+ #
77
+ # @param to [{String => String,Hash}] The attribute hash to merge into
78
+ # @param from [{String => Object}] The attribute hash to merge from
79
+ # @return [{String => String,Hash}] `to`, after being merged
80
+ def merge_attributes!(to, from)
81
+ from.keys.each do |key|
82
+ to[key] = merge_value(key, to[key], from[key])
83
+ end
84
+ to
85
+ end
86
+
87
+ # Merge multiple values to one attribute value. No destructive operation.
88
+ #
89
+ # @param key [String]
90
+ # @param values [Array<Object>]
91
+ # @return [String,Hash]
92
+ def merge_values(key, *values)
93
+ values.inject(nil) do |to, from|
94
+ merge_value(key, to, from)
95
+ end
96
+ end
97
+
98
+ def verify_attribute_names!(attribute_names)
99
+ attribute_names.each do |attribute_name|
100
+ if attribute_name =~ INVALID_ATTRIBUTE_NAME_REGEX
101
+ raise InvalidAttributeNameError.new("Invalid attribute name '#{attribute_name}' was rendered")
102
+ end
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ # Merge a couple of values to one attribute value. No destructive operation.
109
+ #
110
+ # @param to [String,Hash,nil]
111
+ # @param from [Object]
112
+ # @return [String,Hash]
113
+ def merge_value(key, to, from)
114
+ if from.kind_of?(Hash) || to.kind_of?(Hash)
115
+ from = { nil => from } if !from.is_a?(Hash)
116
+ to = { nil => to } if !to.is_a?(Hash)
117
+ to.merge(from)
118
+ elsif key == 'id'
119
+ merged_id = filter_and_join(from, '_')
120
+ if to && merged_id
121
+ merged_id = "#{to}_#{merged_id}"
122
+ elsif to || merged_id
123
+ merged_id ||= to
124
+ end
125
+ merged_id
126
+ elsif key == 'class'
127
+ merged_class = filter_and_join(from, ' ')
128
+ if to && merged_class
129
+ merged_class = (to.split(' ') | merged_class.split(' ')).join(' ')
130
+ elsif to || merged_class
131
+ merged_class ||= to
132
+ end
133
+ merged_class
134
+ else
135
+ from
136
+ end
137
+ end
138
+
139
+ def build_data_keys(data_hash, hyphenate, attr_name="data")
140
+ Hash[data_hash.map do |name, value|
141
+ if name == nil
142
+ [attr_name, value]
143
+ elsif hyphenate
144
+ ["#{attr_name}-#{name.to_s.tr('_', '-')}", value]
145
+ else
146
+ ["#{attr_name}-#{name}", value]
147
+ end
148
+ end]
149
+ end
150
+
151
+ def flatten_data_attributes(data, key, join_char, seen = [])
152
+ return {key => data} unless data.is_a?(Hash)
153
+
154
+ return {key => nil} if seen.include? data.object_id
155
+ seen << data.object_id
156
+
157
+ data.sort {|x, y| x[0].to_s <=> y[0].to_s}.inject({}) do |hash, (k, v)|
158
+ joined = key == '' ? k : [key, k].join(join_char)
159
+ hash.merge! flatten_data_attributes(v, joined, join_char, seen)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -617,6 +617,9 @@ MESSAGE
617
617
  text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
618
618
  end
619
619
 
620
+ # Always escape text regardless of html_safe?
621
+ alias_method :html_escape_without_haml_xss, :html_escape
622
+
620
623
  HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
621
624
 
622
625
  # Escapes HTML entities in `text`, but without escaping an ampersand
@@ -629,6 +632,9 @@ MESSAGE
629
632
  text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
630
633
  end
631
634
 
635
+ # Always escape text once regardless of html_safe?
636
+ alias_method :escape_once_without_haml_xss, :escape_once
637
+
632
638
  # Returns whether or not the current template is a Haml template.
633
639
  #
634
640
  # This function, unlike other {Haml::Helpers} functions,
@@ -1,6 +1,7 @@
1
1
  require 'strscan'
2
2
  require 'hamlit/parser/haml_util'
3
3
  require 'hamlit/parser/haml_error'
4
+ require 'hamlit/parser/haml_attribute_builder'
4
5
 
5
6
  module Hamlit
6
7
  class HamlParser
@@ -206,6 +207,31 @@ module Hamlit
206
207
  end
207
208
  end
208
209
 
210
+ # @param [String] new - Hash literal including dynamic values.
211
+ # @param [String] old - Hash literal including dynamic values or Ruby literal of multiple Hashes which MUST be interpreted as method's last arguments.
212
+ DynamicAttributes = Struct.new(:new, :old) do
213
+ undef :old=
214
+ def old=(value)
215
+ unless value =~ /\A{.*}\z/m
216
+ raise ArgumentError.new('Old attributes must start with "{" and end with "}"')
217
+ end
218
+ self[:old] = value
219
+ end
220
+
221
+ # This will be a literal for Haml::Buffer#attributes's last argument, `attributes_hashes`.
222
+ def to_literal
223
+ [new, stripped_old].compact.join(', ')
224
+ end
225
+
226
+ private
227
+
228
+ # For `%foo{ { foo: 1 }, bar: 2 }`, :old is "{ { foo: 1 }, bar: 2 }" and this method returns " { foo: 1 }, bar: 2 " for last argument.
229
+ def stripped_old
230
+ return nil if old.nil?
231
+ old.sub!(/\A{/, '').sub!(/}\z/m, '')
232
+ end
233
+ end
234
+
209
235
  # Processes and deals with lowering indentation.
210
236
  def process_indent(line)
211
237
  return unless line.tabs <= @template_tabs && @template_tabs > 0
@@ -403,22 +429,20 @@ module Hamlit
403
429
  end
404
430
 
405
431
  attributes = ::Hamlit::HamlParser.parse_class_and_id(attributes)
406
- attributes_list = []
432
+ dynamic_attributes = DynamicAttributes.new
407
433
 
408
434
  if attributes_hashes[:new]
409
435
  static_attributes, attributes_hash = attributes_hashes[:new]
410
- ::Hamlit::HamlBuffer.merge_attrs(attributes, static_attributes) if static_attributes
411
- attributes_list << attributes_hash
436
+ HamlAttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
437
+ dynamic_attributes.new = attributes_hash
412
438
  end
413
439
 
414
440
  if attributes_hashes[:old]
415
441
  static_attributes = parse_static_hash(attributes_hashes[:old])
416
- ::Hamlit::HamlBuffer.merge_attrs(attributes, static_attributes) if static_attributes
417
- attributes_list << attributes_hashes[:old] unless static_attributes || @options.suppress_eval
442
+ HamlAttributeBuilder.merge_attributes!(attributes, static_attributes) if static_attributes
443
+ dynamic_attributes.old = attributes_hashes[:old] unless static_attributes || @options.suppress_eval
418
444
  end
419
445
 
420
- attributes_list.compact!
421
-
422
446
  raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:illegal_nesting_self_closing), @next_line.index) if block_opened? && self_closing
423
447
  raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:no_ruby_code, action), last_line - 1) if parse && value.empty?
424
448
  raise ::Hamlit::HamlSyntaxError.new(::Hamlit::HamlError.message(:self_closing_content), last_line - 1) if self_closing && !value.empty?
@@ -433,7 +457,7 @@ module Hamlit
433
457
  line = handle_ruby_multiline(line) if parse
434
458
 
435
459
  ParseNode.new(:tag, line.index + 1, :name => tag_name, :attributes => attributes,
436
- :attributes_hashes => attributes_list, :self_closing => self_closing,
460
+ :dynamic_attributes => dynamic_attributes, :self_closing => self_closing,
437
461
  :nuke_inner_whitespace => nuke_inner_whitespace,
438
462
  :nuke_outer_whitespace => nuke_outer_whitespace, :object_ref => object_ref,
439
463
  :escape_html => escape_html, :preserve_tag => preserve_tag,
@@ -641,7 +665,6 @@ module Hamlit
641
665
  raise e
642
666
  end
643
667
 
644
- attributes_hash = attributes_hash[1...-1] if attributes_hash
645
668
  return attributes_hash, rest, last_line
646
669
  end
647
670
 
@@ -6,12 +6,15 @@ module Hamlit
6
6
  # to work with Rails' XSS protection methods.
7
7
  module XssMods
8
8
  def self.included(base)
9
- %w[html_escape find_and_preserve preserve list_of surround
10
- precede succeed capture_haml haml_concat haml_internal_concat haml_indent
11
- escape_once].each do |name|
9
+ %w[find_and_preserve preserve list_of surround
10
+ precede succeed capture_haml haml_concat haml_internal_concat haml_indent].each do |name|
12
11
  base.send(:alias_method, "#{name}_without_haml_xss", name)
13
12
  base.send(:alias_method, name, "#{name}_with_haml_xss")
14
13
  end
14
+ # Those two always have _without_haml_xss
15
+ %w[html_escape escape_once].each do |name|
16
+ base.send(:alias_method, name, "#{name}_with_haml_xss")
17
+ end
15
18
  end
16
19
 
17
20
  # Don't escape text that's already safe,
@@ -2,87 +2,18 @@ require 'ripper'
2
2
  require 'hamlit/ruby_expression'
3
3
 
4
4
  module Hamlit
5
- class StringSplitter < Temple::Filter
6
- class << self
7
- # `code` param must be valid string literal
8
- def compile(code)
9
- [].tap do |exps|
10
- tokens = Ripper.lex(code.strip)
11
- tokens.pop while tokens.last && %i[on_comment on_sp].include?(tokens.last[1])
12
-
13
- if tokens.size < 2
14
- raise Hamlit::InternalError.new("Expected token size >= 2 but got: #{tokens.size}")
15
- end
16
- compile_tokens!(exps, tokens)
17
- end
18
- end
19
-
20
- private
21
-
22
- def strip_quotes!(tokens)
23
- _, type, beg_str = tokens.shift
24
- if type != :on_tstring_beg
25
- raise Hamlit::InternalError.new("Expected :on_tstring_beg but got: #{type}")
26
- end
27
-
28
- _, type, end_str = tokens.pop
29
- if type != :on_tstring_end
30
- raise Hamlit::InternalError.new("Expected :on_tstring_end but got: #{type}")
31
- end
32
-
33
- [beg_str, end_str]
34
- end
35
-
36
- def compile_tokens!(exps, tokens)
37
- beg_str, end_str = strip_quotes!(tokens)
38
-
39
- until tokens.empty?
40
- _, type, str = tokens.shift
41
-
42
- case type
43
- when :on_tstring_content
44
- exps << [:static, eval("#{beg_str}#{str}#{end_str}")]
45
- when :on_embexpr_beg
46
- embedded = shift_balanced_embexpr(tokens)
47
- exps << [:dynamic, embedded] unless embedded.empty?
48
- end
49
- end
50
- end
51
-
52
- def shift_balanced_embexpr(tokens)
53
- String.new.tap do |embedded|
54
- embexpr_open = 1
55
-
56
- until tokens.empty?
57
- _, type, str = tokens.shift
58
- case type
59
- when :on_embexpr_beg
60
- embexpr_open += 1
61
- when :on_embexpr_end
62
- embexpr_open -= 1
63
- break if embexpr_open == 0
64
- end
65
-
66
- embedded << str
67
- end
68
- end
5
+ module StringSplitter
6
+ # `code` param must be valid string literal
7
+ def self.compile(code)
8
+ unless Ripper.respond_to?(:lex) # truffleruby doesn't have Ripper.lex
9
+ return [[:dynamic, code]]
69
10
  end
70
- end
71
-
72
- def on_dynamic(code)
73
- return [:dynamic, code] unless RubyExpression.string_literal?(code)
74
- return [:dynamic, code] if code.include?("\n")
75
11
 
76
- temple = [:multi]
77
- StringSplitter.compile(code).each do |type, content|
78
- case type
79
- when :static
80
- temple << [:static, content]
81
- when :dynamic
82
- temple << on_dynamic(content)
83
- end
12
+ begin
13
+ Temple::Filters::StringSplitter.compile(code)
14
+ rescue Temple::FilterError => e
15
+ raise Hamlit::InternalError.new(e.message)
84
16
  end
85
- temple
86
17
  end
87
18
  end
88
19
  end
@@ -14,7 +14,7 @@ end
14
14
  module Hamlit
15
15
  Template = Temple::Templates::Tilt.create(
16
16
  Hamlit::Engine,
17
- register_as: :haml,
17
+ register_as: [:haml, :hamlit],
18
18
  )
19
19
 
20
20
  module TemplateExtension
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Hamlit
3
+ # A module to count lines of expected code. This would be faster than actual code generation
4
+ # and counting newlines in it.
5
+ module TempleLineCounter
6
+ class UnexpectedExpression < StandardError; end
7
+
8
+ def self.count_lines(exp)
9
+ type, *args = exp
10
+ case type
11
+ when :multi
12
+ args.map { |a| count_lines(a) }.reduce(:+) || 0
13
+ when :dynamic, :code
14
+ args.first.count("\n")
15
+ when :static
16
+ 0 # It has not real newline "\n" but escaped "\\n".
17
+ when :case
18
+ arg, *cases = args
19
+ arg.count("\n") + cases.map do |cond, e|
20
+ (cond == :else ? 0 : cond.count("\n")) + count_lines(e)
21
+ end.reduce(:+)
22
+ when :escape
23
+ count_lines(args[1])
24
+ when :newline
25
+ 1
26
+ else
27
+ raise UnexpectedExpression.new("[HAML BUG] Unexpected Temple expression '#{type}' is given!")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Hamlit
3
- VERSION = '2.9.5'
3
+ VERSION = '2.12.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hamlit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.5
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-08 00:00:00.000000000 Z
11
+ date: 2020-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: temple
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.8.0
19
+ version: 0.8.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.8.0
26
+ version: 0.8.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: thor
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: benchmark_driver
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,20 @@ dependencies:
206
220
  - - ">="
207
221
  - !ruby/object:Gem::Version
208
222
  version: '0'
223
+ - !ruby/object:Gem::Dependency
224
+ name: string_template
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
209
237
  - !ruby/object:Gem::Dependency
210
238
  name: unindent
211
239
  requirement: !ruby/object:Gem::Requirement
@@ -247,6 +275,9 @@ files:
247
275
  - benchmark/dynamic_attributes/data_attribute.haml
248
276
  - benchmark/dynamic_attributes/id_attribute.haml
249
277
  - benchmark/dynamic_boolean_attribute.haml
278
+ - benchmark/dynamic_merger/benchmark.rb
279
+ - benchmark/dynamic_merger/hello.haml
280
+ - benchmark/dynamic_merger/hello.string
250
281
  - benchmark/etc/attribute_builder.haml
251
282
  - benchmark/etc/real_sample.haml
252
283
  - benchmark/etc/real_sample.rb
@@ -290,6 +321,7 @@ files:
290
321
  - lib/hamlit/compiler/script_compiler.rb
291
322
  - lib/hamlit/compiler/silent_script_compiler.rb
292
323
  - lib/hamlit/compiler/tag_compiler.rb
324
+ - lib/hamlit/dynamic_merger.rb
293
325
  - lib/hamlit/engine.rb
294
326
  - lib/hamlit/error.rb
295
327
  - lib/hamlit/escapable.rb
@@ -311,7 +343,6 @@ files:
311
343
  - lib/hamlit/filters/text_base.rb
312
344
  - lib/hamlit/filters/tilt_base.rb
313
345
  - lib/hamlit/force_escapable.rb
314
- - lib/hamlit/hamlit.su
315
346
  - lib/hamlit/helpers.rb
316
347
  - lib/hamlit/html.rb
317
348
  - lib/hamlit/identity.rb
@@ -319,6 +350,7 @@ files:
319
350
  - lib/hamlit/parser.rb
320
351
  - lib/hamlit/parser/MIT-LICENSE
321
352
  - lib/hamlit/parser/README.md
353
+ - lib/hamlit/parser/haml_attribute_builder.rb
322
354
  - lib/hamlit/parser/haml_buffer.rb
323
355
  - lib/hamlit/parser/haml_compiler.rb
324
356
  - lib/hamlit/parser/haml_error.rb
@@ -333,6 +365,7 @@ files:
333
365
  - lib/hamlit/ruby_expression.rb
334
366
  - lib/hamlit/string_splitter.rb
335
367
  - lib/hamlit/template.rb
368
+ - lib/hamlit/temple_line_counter.rb
336
369
  - lib/hamlit/utils.rb
337
370
  - lib/hamlit/version.rb
338
371
  homepage: https://github.com/k0kubun/hamlit
@@ -354,7 +387,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
354
387
  - !ruby/object:Gem::Version
355
388
  version: '0'
356
389
  requirements: []
357
- rubygems_version: 3.0.3
390
+ rubygems_version: 3.1.2
358
391
  signing_key:
359
392
  specification_version: 4
360
393
  summary: High Performance Haml Implementation
Binary file