slimi 0.2.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce4329befc9d19fb756c5609a28a3169c4f5a5aa2698771df14541b88b792da1
4
- data.tar.gz: d99512e7191942f2fabb8667cb4de369dcad5d773c1e162cc039f2911b4b1dc9
3
+ metadata.gz: a503c590465e63f43b6d9ef8047700ff415bfea923df65be323f2d37f5d94b71
4
+ data.tar.gz: 4ad486738d38c81b670cb693afa31c9c7f8acb92ab05a844d47cf3df1d0219c8
5
5
  SHA512:
6
- metadata.gz: 6c1b46a4afdfc4ce2a8ce816898a17e6bd448e0e76a001efbebfe7fc498375a2c1c71aaa5f40cce11012f28e7e87f5c7eef06b70b6dbcd560a6343d5494ee71c
7
- data.tar.gz: 7554a443efa9d30952d3446b92c494dd6ecf1caeb78d77aea347eef1ab862e64c35e93d4ef76c1423728734eb3b23913e75497c3358b0df2543229e05b177c07
6
+ metadata.gz: '08dc1c84baa6a338564757e6c28143a77f1ecc03eb4ba7f72bf2af82ce673eb2c573debbbbf5eaf16673b53e07a49dd3493888875e6b9e5016557ab7f8373368'
7
+ data.tar.gz: dd3bb74308363284ff648f80d2dcc37fe9b7faa75df2b31210ca4567fa088debca11b5831bb8ec745800bd15e9886e5ff5bf6074bcb6aa673ca826421e10bc31
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ require:
2
+ - rubocop-rspec
3
+
1
4
  AllCops:
2
5
  NewCops: enable
3
6
  SuggestExtensions: false
@@ -12,5 +15,17 @@ Lint/InterpolationCheck:
12
15
  Metrics:
13
16
  Enabled: false
14
17
 
18
+ RSpec/AnyInstance:
19
+ Enabled: false
20
+
21
+ RSpec/ImplicitSubject:
22
+ Enabled: false
23
+
24
+ RSpec/MultipleExpectations:
25
+ Enabled: false
26
+
27
+ RSpec/NamedSubject:
28
+ Enabled: false
29
+
15
30
  Style/Documentation:
16
31
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.4.2 - 2021-12-27
6
+
7
+ ### Fixed
8
+
9
+ - Fix bug on quoted attribute parser.
10
+ - Remove trailing line-ending from source line of syntax error message.
11
+ - Support multi-line attributes.
12
+
13
+ ## 0.4.1 - 2021-12-26
14
+
15
+ ### Fixed
16
+
17
+ - Fix bug on parsing Ruby attribute value.
18
+ - Fix bug on empty line in text block.
19
+
20
+ ## 0.4.0 - 2021-12-25
21
+
22
+ ### Added
23
+
24
+ - Support :file option on parser for showing correct file path on syntax error.
25
+
26
+ ### Fixed
27
+
28
+ - Fix NameError on unknown line indicator.
29
+ - Fix bug that default parser options are not used.
30
+
31
+ ## 0.3.0 - 2021-12-24
32
+
33
+ ### Added
34
+
35
+ - Support Ruby attributes.
36
+
37
+ ### Fixed
38
+
39
+ - Fix bug about blank line handling.
40
+
5
41
  ## 0.2.0 - 2021-12-23
6
42
 
7
43
  ### Added
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  gem 'rake', '~> 13.0'
9
- gem 'rspec', '~> 3.0'
10
- gem 'rubocop', '~> 1.21'
9
+ gem 'rspec'
10
+ gem 'rubocop'
11
+ gem 'rubocop-rspec'
11
12
  gem 'slim'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- slimi (0.2.0)
4
+ slimi (0.4.2)
5
5
  temple
6
6
 
7
7
  GEM
@@ -40,6 +40,8 @@ GEM
40
40
  unicode-display_width (>= 1.4.0, < 3.0)
41
41
  rubocop-ast (1.15.0)
42
42
  parser (>= 3.0.1.1)
43
+ rubocop-rspec (2.6.0)
44
+ rubocop (~> 1.19)
43
45
  ruby-progressbar (1.11.0)
44
46
  slim (4.1.0)
45
47
  temple (>= 0.7.6, < 0.9)
@@ -53,8 +55,9 @@ PLATFORMS
53
55
 
54
56
  DEPENDENCIES
55
57
  rake (~> 13.0)
56
- rspec (~> 3.0)
57
- rubocop (~> 1.21)
58
+ rspec
59
+ rubocop
60
+ rubocop-rspec
58
61
  slim
59
62
  slimi!
60
63
 
data/lib/slimi/errors.rb CHANGED
@@ -23,7 +23,7 @@ module Slimi
23
23
  def to_s
24
24
  <<~TEXT
25
25
  #{error_type} at #{@file_path}:#{@line_number}:#{@column}
26
- #{@line}
26
+ #{@line.rstrip}
27
27
  #{' ' * (@column - 1)}^
28
28
  TEXT
29
29
  end
@@ -36,12 +36,21 @@ module Slimi
36
36
  end
37
37
  end
38
38
 
39
+ class AttributeClosingDelimiterNotFoundError < SlimSyntaxError
40
+ end
41
+
42
+ class InvalidEmptyAttributeError < SlimSyntaxError
43
+ end
44
+
39
45
  class LineEndingNotFoundError < SlimSyntaxError
40
46
  end
41
47
 
42
48
  class MalformedIndentationError < SlimSyntaxError
43
49
  end
44
50
 
51
+ class RubyAttributeClosingDelimiterNotFoundError < SlimSyntaxError
52
+ end
53
+
45
54
  class UnexpectedEosError < SlimSyntaxError
46
55
  end
47
56
 
@@ -51,7 +60,7 @@ module Slimi
51
60
  class UnexpectedTextAfterClosedTagError < SlimSyntaxError
52
61
  end
53
62
 
54
- class UnknownLineIndicator < SlimSyntaxError
63
+ class UnknownLineIndicatorError < SlimSyntaxError
55
64
  end
56
65
  end
57
66
  end
data/lib/slimi/parser.rb CHANGED
@@ -6,22 +6,30 @@ require 'temple'
6
6
  module Slimi
7
7
  class Parser < ::Temple::Parser
8
8
  define_options(
9
+ :file,
9
10
  attr_list_delims: {
10
11
  '(' => ')',
11
12
  '[' => ']',
12
13
  '{' => '}'
13
14
  },
15
+ code_attr_delims: {
16
+ '(' => ')',
17
+ '[' => ']',
18
+ '{' => '}'
19
+ },
14
20
  shortcut: {
15
21
  '#' => { attr: 'id' },
16
22
  '.' => { attr: 'class' }
17
23
  }
18
24
  )
19
25
 
20
- def initialize(options = {})
26
+ def initialize(_options = {})
21
27
  super
28
+ @file_path = options[:file] || '(__TEMPLATE__)'
22
29
  factory = Factory.new(
23
30
  attribute_delimiters: options[:attr_list_delims] || {},
24
31
  default_tag: options[:default_tag] || 'div',
32
+ ruby_attribute_delimiters: options[:code_attr_delims] || {},
25
33
  shortcut: options[:shortcut] || {}
26
34
  )
27
35
  @attribute_delimiters = factory.attribute_delimiters
@@ -32,6 +40,9 @@ module Slimi
32
40
  @quoted_attribute_regexp = factory.quoted_attribute_regexp
33
41
  @tag_name_regexp = factory.tag_name_regexp
34
42
  @attribute_name_regexp = factory.attribute_name_regexp
43
+ @ruby_attribute_regexp = factory.ruby_attribute_regexp
44
+ @ruby_attribute_delimiter_regexp = factory.ruby_attribute_delimiter_regexp
45
+ @ruby_attribute_delimiters = factory.ruby_attribute_delimiters
35
46
  @embedded_template_regexp = factory.embedded_template_regexp
36
47
  end
37
48
 
@@ -46,10 +57,11 @@ module Slimi
46
57
  private
47
58
 
48
59
  def parse_block
60
+ return if parse_blank_line
61
+
49
62
  parse_indent
50
63
 
51
- parse_line_ending ||
52
- parse_html_comment ||
64
+ parse_html_comment ||
53
65
  parse_html_conditional_comment ||
54
66
  parse_slim_comment_block ||
55
67
  parse_verbatim_text_block ||
@@ -59,7 +71,18 @@ module Slimi
59
71
  parse_embedded_template ||
60
72
  parse_doctype ||
61
73
  parse_tag ||
62
- raise(Errors::UnknownLineIndicatorError)
74
+ syntax_error!(Errors::UnknownLineIndicatorError)
75
+ end
76
+
77
+ # Parse blank line.
78
+ # @return [Boolean] True if it could parse a blank line.
79
+ def parse_blank_line
80
+ if @scanner.skip(/[ \t]*$/)
81
+ parse_line_ending
82
+ true
83
+ else
84
+ false
85
+ end
63
86
  end
64
87
 
65
88
  def parse_indent
@@ -177,8 +200,6 @@ module Slimi
177
200
  marker = @scanner[1]
178
201
  attribute_value = @scanner[2]
179
202
  attribute_names = @attribute_shortcuts[marker]
180
- raise 'Illegal shortcut' unless attribute_names
181
-
182
203
  attribute_names.map do |attribute_name|
183
204
  result << [:html, :attr, attribute_name.to_s, [:static, attribute_value]]
184
205
  end
@@ -199,18 +220,24 @@ module Slimi
199
220
  value = +''
200
221
  count = 0
201
222
  loop do
202
- if @scanner.match?(/#{quote}/) && count.zero?
203
- end_ = @scanner.charpos
204
- @scanner.pos += @scanner.matched_size
205
- break
206
- end
207
-
208
- if @scanner.skip(/\{/)
223
+ if @scanner.match?(/#{quote}/)
224
+ if count.zero?
225
+ end_ = @scanner.charpos
226
+ @scanner.pos += @scanner.matched_size
227
+ break
228
+ else
229
+ @scanner.pos += @scanner.matched_size
230
+ value << @scanner.matched
231
+ end
232
+ elsif @scanner.skip(/\{/)
209
233
  count += 1
234
+ value << @scanner.matched
210
235
  elsif @scanner.skip(/\}/)
211
236
  count -= 1
237
+ value << @scanner.matched
238
+ else
239
+ value << @scanner.scan(/[^{}#{quote}]*/)
212
240
  end
213
- value << @scanner.scan(/[^{}#{quote}]*/)
214
241
  end
215
242
  [:slimi, :interpolate, begin_, end_, value]
216
243
  end
@@ -232,12 +259,19 @@ module Slimi
232
259
  attribute_delimiter_closing_part_regexp = /[ \t]*#{attribute_delimiter_closing_regexp}/
233
260
  end
234
261
 
262
+ # TODO: Support splat attributes.
235
263
  loop do
236
264
  if @scanner.skip(@quoted_attribute_regexp)
237
265
  attribute_name = @scanner[1]
238
266
  escape = @scanner[2].empty?
239
267
  quote = @scanner[3]
240
268
  attributes << [:html, :attr, attribute_name, [:escape, escape, parse_quoted_attribute_value(quote)]]
269
+ elsif @scanner.skip(@ruby_attribute_regexp)
270
+ attribute_name = @scanner[1]
271
+ escape = @scanner[2].empty?
272
+ attribute_value = parse_ruby_attribute_value(attribute_delimiter_closing)
273
+ syntax_error!(Errors::InvalidEmptyAttributeError) if attribute_value.empty?
274
+ attributes << [:html, :attr, attribute_name, [:slim, :attrvalue, escape, attribute_value]]
241
275
  elsif !attribute_delimiter_closing_part_regexp
242
276
  break
243
277
  elsif @scanner.skip(boolean_attribute_regexp)
@@ -245,13 +279,56 @@ module Slimi
245
279
  elsif @scanner.skip(attribute_delimiter_closing_part_regexp) # rubocop:disable Lint/DuplicateBranch
246
280
  break
247
281
  else
248
- raise ::NotImplementedError
282
+ @scanner.skip(/[ \t]+/)
283
+ expect_line_ending
284
+
285
+ syntax_error!(Errors::AttributeClosingDelimiterNotFoundError) if @scanner.eos?
249
286
  end
250
287
  end
251
288
 
252
289
  attributes
253
290
  end
254
291
 
292
+ # Parse Ruby attribute value part.
293
+ # e.g. div class=foo
294
+ # ^^^
295
+ # `- Ruby attribute value
296
+ # @param [String] attribute_delimiter_closing
297
+ # @return [String]
298
+ def parse_ruby_attribute_value(attribute_delimiter_closing)
299
+ ending_regexp = /\s/
300
+ ending_regexp = ::Regexp.union(ending_regexp, attribute_delimiter_closing) if attribute_delimiter_closing
301
+ count = 0
302
+ attribute_value = +''
303
+ opening_delimiter = nil
304
+ closing_delimiter = nil
305
+ loop do
306
+ break if count.zero? && @scanner.match?(ending_regexp)
307
+
308
+ if @scanner.skip(/([,\\])\r?\n/)
309
+ attribute_value << @scanner[1] << "\n"
310
+ else
311
+ if count.positive?
312
+ if opening_delimiter && @scanner.match?(/#{::Regexp.escape(opening_delimiter)}/)
313
+ count += 1
314
+ elsif closing_delimiter && @scanner.match?(/#{::Regexp.escape(closing_delimiter)}/)
315
+ count -= 1
316
+ end
317
+ elsif @scanner.match?(@ruby_attribute_delimiter_regexp)
318
+ count = 1
319
+ opening_delimiter = @scanner.matched
320
+ closing_delimiter = @ruby_attribute_delimiters[opening_delimiter]
321
+ end
322
+ if (character = @scanner.scan(/./))
323
+ attribute_value << character
324
+ end
325
+ end
326
+ end
327
+ syntax_error!(Errors::RubyAttributeClosingDelimiterNotFoundError) if count != 0
328
+
329
+ attribute_value
330
+ end
331
+
255
332
  # @return [Boolean]
256
333
  def parse_html_comment
257
334
  if @scanner.skip(%r{/!})
@@ -435,9 +512,13 @@ module Slimi
435
512
  interpolate = parse_interpolate_line
436
513
  result << interpolate if interpolate
437
514
 
438
- loop do
439
- break unless @scanner.match?(/\r?\n[ \t]*/)
515
+ until @scanner.eos?
516
+ if @scanner.skip(/\r?\n[ \t]*(?=\r?\n)/)
517
+ result << [:newline]
518
+ next
519
+ end
440
520
 
521
+ @scanner.match?(/\r?\n[ \t]*/)
441
522
  indent = indent_from_last_match
442
523
  break if indent <= @indents.last
443
524
 
@@ -488,7 +569,7 @@ module Slimi
488
569
  range = Range.new(index: @scanner.charpos, source: @scanner.string)
489
570
  raise syntax_error_class.new(
490
571
  column: range.column,
491
- file_path: '(__TEMPLATE__)',
572
+ file_path: @file_path,
492
573
  line: range.line,
493
574
  line_number: range.line_number
494
575
  )
@@ -512,12 +593,17 @@ module Slimi
512
593
  # @return [Hash]
513
594
  attr_reader :attribute_delimiters
514
595
 
596
+ # @return [Hash]
597
+ attr_reader :ruby_attribute_delimiters
598
+
515
599
  # @param [Hash] attribute_delimiters
516
600
  # @param [String] default_tag
601
+ # @param [Hash] ruby_attribute_delimiters
517
602
  # @param [Hash] shortcut
518
- def initialize(attribute_delimiters:, default_tag:, shortcut:)
603
+ def initialize(attribute_delimiters:, default_tag:, ruby_attribute_delimiters:, shortcut:)
519
604
  @attribute_delimiters = attribute_delimiters
520
605
  @default_tag = default_tag
606
+ @ruby_attribute_delimiters = ruby_attribute_delimiters
521
607
  @shortcut = shortcut
522
608
  end
523
609
 
@@ -573,6 +659,11 @@ module Slimi
573
659
  %r{(#{markers_regexp}+)((?:\p{Word}|-|/\d+|:(\w|-)+)*)}
574
660
  end
575
661
 
662
+ # @return [Regexp]
663
+ def ruby_attribute_regexp
664
+ /#{attribute_name_regexp}[ \t]*=(=?)[ \t]*/
665
+ end
666
+
576
667
  # @return [Regexp]
577
668
  def embedded_template_regexp
578
669
  /(#{::Regexp.union(EMBEDDED_TEMPLAE_ENGINE_NAMES)})(?:[ \t]*(?:(.*)))?:([ \t]*)/
@@ -583,6 +674,11 @@ module Slimi
583
674
  /#{attribute_name_regexp}[ \t]*=(=?)[ \t]*("|')/
584
675
  end
585
676
 
677
+ # @return [Regexp]
678
+ def ruby_attribute_delimiter_regexp
679
+ ::Regexp.union(@ruby_attribute_delimiters.keys)
680
+ end
681
+
586
682
  # @return [Regexp] Pattern that matches to tag header part.
587
683
  def tag_name_regexp
588
684
  markers = tag_shortcuts.keys.sort_by { |marker| -marker.size }
data/lib/slimi/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Slimi
4
- VERSION = '0.2.0'
4
+ VERSION = '0.4.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slimi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2021-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: temple