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 +4 -4
- data/.rubocop.yml +15 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +6 -3
- data/lib/slimi/errors.rb +11 -2
- data/lib/slimi/parser.rb +115 -19
- data/lib/slimi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a503c590465e63f43b6d9ef8047700ff415bfea923df65be323f2d37f5d94b71
|
4
|
+
data.tar.gz: 4ad486738d38c81b670cb693afa31c9c7f8acb92ab05a844d47cf3df1d0219c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
slimi (0.2
|
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
|
57
|
-
rubocop
|
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
|
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(
|
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
|
-
|
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
|
-
|
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}/)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
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
|
-
|
439
|
-
|
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:
|
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
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
|
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-
|
11
|
+
date: 2021-12-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: temple
|