slimi 0.2.0 → 0.4.2
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/.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
|