code-ruby 3.0.11 → 3.0.12
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/Gemfile.lock +1 -1
- data/VERSION +1 -1
- data/lib/code/format.rb +86 -5
- data/spec/code/format_spec.rb +64 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eef80c2ebc4ef6d318ee0af151704caca0f0a370c698dc727814836d6e5146db
|
|
4
|
+
data.tar.gz: 6ac8ae69e473aa937eb4794466e710ec40444c53f5d1a12149368e03de0bc4c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b99011ea5c71b86b7709949e2d4096f4708b2a4c985e1f30e704bac060c58c710922797c4c4ade47f91b31ad37a47153f66fd8e204783786d55da38234e0d79
|
|
7
|
+
data.tar.gz: 367d671e1fbb4aaacd278bd3e186a03a7304f7041f5381e502a54e44afe754048ed603eb83e6e21f2c8415916078b633fc352984ae6c5b4ab27f96c330394b19
|
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0.
|
|
1
|
+
3.0.12
|
data/lib/code/format.rb
CHANGED
|
@@ -57,13 +57,20 @@ class Code
|
|
|
57
57
|
"begin\n#{body}\n#{INDENT * indent}end"
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
def format_group(group, indent:)
|
|
61
|
+
formatted = format_code_inline(group, indent: indent)
|
|
62
|
+
return "(#{formatted})" unless formatted.include?("\n")
|
|
63
|
+
|
|
64
|
+
"(\n#{indent_lines(normalize_group_indentation(formatted, indent: indent), indent + 1)}\n#{INDENT * indent})"
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def format_statement(statement, indent:)
|
|
61
68
|
if statement.is_a?(Hash) && statement.key?(:nothing)
|
|
62
69
|
statement[:nothing].presence || "nothing"
|
|
63
70
|
elsif statement.is_a?(Hash) && statement.key?(:boolean)
|
|
64
71
|
statement[:boolean]
|
|
65
72
|
elsif statement.is_a?(Hash) && statement.key?(:group)
|
|
66
|
-
|
|
73
|
+
format_group(statement[:group], indent: indent)
|
|
67
74
|
elsif statement.is_a?(Hash) && statement.key?(:call)
|
|
68
75
|
format_call(statement[:call], indent: indent)
|
|
69
76
|
elsif statement.is_a?(Hash) && statement.key?(:number)
|
|
@@ -431,7 +438,7 @@ class Code
|
|
|
431
438
|
|
|
432
439
|
expression =
|
|
433
440
|
if compact_operator?(operator)
|
|
434
|
-
"#{expression}#{operator}#{right}"
|
|
441
|
+
"#{format_compact_receiver(expression, indent: indent)}#{operator}#{right}"
|
|
435
442
|
else
|
|
436
443
|
candidate = "#{expression} #{operator} #{right}"
|
|
437
444
|
if right.include?("\n")
|
|
@@ -449,8 +456,10 @@ class Code
|
|
|
449
456
|
right_lines =
|
|
450
457
|
if right.include?("\n")
|
|
451
458
|
right.lines(chomp: true).map(&:lstrip)
|
|
452
|
-
|
|
459
|
+
elsif %w[and or].include?(operator)
|
|
453
460
|
right.split(" #{operator} ")
|
|
461
|
+
else
|
|
462
|
+
[right]
|
|
454
463
|
end
|
|
455
464
|
continuation_lines =
|
|
456
465
|
right_lines.map do |line|
|
|
@@ -469,6 +478,34 @@ class Code
|
|
|
469
478
|
expression
|
|
470
479
|
end
|
|
471
480
|
|
|
481
|
+
def format_compact_receiver(expression, indent:)
|
|
482
|
+
return expression unless compact_receiver_needs_parentheses?(expression)
|
|
483
|
+
|
|
484
|
+
"(\n#{indent_lines(expression, indent + 1)}\n#{INDENT * indent})"
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def compact_receiver_needs_parentheses?(expression)
|
|
488
|
+
return false unless expression.include?("\n")
|
|
489
|
+
return false if expression.lstrip.start_with?("(")
|
|
490
|
+
|
|
491
|
+
continuation_lines =
|
|
492
|
+
expression
|
|
493
|
+
.lines(chomp: true)[1..]
|
|
494
|
+
.to_a
|
|
495
|
+
.reject { |line| line.strip.empty? || line.strip.match?(/\A[\)\]\}]+\z/) }
|
|
496
|
+
|
|
497
|
+
return false if continuation_lines.empty?
|
|
498
|
+
|
|
499
|
+
base_indent =
|
|
500
|
+
continuation_lines.map { |line| line[/\A */].to_s.length }.min
|
|
501
|
+
|
|
502
|
+
continuation_lines.any? do |line|
|
|
503
|
+
next false unless line[/\A */].to_s.length == base_indent
|
|
504
|
+
|
|
505
|
+
line.lstrip.match?(/\A(\+|-|\*|\/|%|<<|>>|\||\^|&|and\b|or\b)/)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
472
509
|
def extract_string_concatenation_parts(statement)
|
|
473
510
|
return nil unless statement.is_a?(Hash)
|
|
474
511
|
|
|
@@ -496,8 +533,14 @@ class Code
|
|
|
496
533
|
|
|
497
534
|
def format_right_operation(operation, indent:)
|
|
498
535
|
operator = operation[:operator].to_s
|
|
499
|
-
left =
|
|
536
|
+
left =
|
|
537
|
+
if %w[if unless].include?(operator)
|
|
538
|
+
format_modifier_left(operation[:left], indent: indent)
|
|
539
|
+
else
|
|
540
|
+
format_nested_statement(operation[:left], indent: indent)
|
|
541
|
+
end
|
|
500
542
|
right = format_nested_statement(operation[:right], indent: indent)
|
|
543
|
+
|
|
501
544
|
if right.include?("\n")
|
|
502
545
|
first_line, *rest = right.lines(chomp: true)
|
|
503
546
|
first_line = first_line.lstrip
|
|
@@ -512,6 +555,42 @@ class Code
|
|
|
512
555
|
"#{left} #{operator} #{right}"
|
|
513
556
|
end
|
|
514
557
|
|
|
558
|
+
def format_modifier_left(statement, indent:)
|
|
559
|
+
if statement.is_a?(Hash) && statement.key?(:right_operation)
|
|
560
|
+
nested = statement[:right_operation]
|
|
561
|
+
nested_operator = nested[:operator].to_s
|
|
562
|
+
if nested_operator == "="
|
|
563
|
+
left = format_nested_statement(nested[:left], indent: indent)
|
|
564
|
+
right = format_nested_statement(nested[:right], indent: indent)
|
|
565
|
+
return "#{left} #{nested_operator} #{group_multiline_expression(right, indent: indent)}"
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
format_nested_statement(statement, indent: indent)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def group_multiline_expression(expression, indent:)
|
|
573
|
+
return expression unless expression.include?("\n")
|
|
574
|
+
return expression if expression.lstrip.start_with?("(")
|
|
575
|
+
|
|
576
|
+
normalized = normalize_group_indentation(expression, indent: indent)
|
|
577
|
+
"(\n#{indent_lines(normalized, indent + 1)}\n#{INDENT * indent})"
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def normalize_group_indentation(expression, indent:)
|
|
581
|
+
prefix = INDENT * indent
|
|
582
|
+
|
|
583
|
+
expression
|
|
584
|
+
.lines(chomp: true)
|
|
585
|
+
.map
|
|
586
|
+
.with_index do |line, index|
|
|
587
|
+
next line if index.zero? || line.empty? || prefix.empty?
|
|
588
|
+
|
|
589
|
+
line.delete_prefix(prefix)
|
|
590
|
+
end
|
|
591
|
+
.join("\n")
|
|
592
|
+
end
|
|
593
|
+
|
|
515
594
|
def compact_operator?(operator)
|
|
516
595
|
%w[. :: &. .. ...].include?(operator)
|
|
517
596
|
end
|
|
@@ -519,9 +598,11 @@ class Code
|
|
|
519
598
|
def format_ternary(ternary, indent:)
|
|
520
599
|
left = format_nested_statement(ternary[:left], indent: indent)
|
|
521
600
|
middle = format_nested_statement(ternary[:middle], indent: indent)
|
|
601
|
+
middle = group_multiline_expression(middle, indent: indent)
|
|
522
602
|
return "#{left} ? #{middle}" unless ternary.key?(:right)
|
|
523
603
|
|
|
524
604
|
right = format_nested_statement(ternary[:right], indent: indent)
|
|
605
|
+
right = group_multiline_expression(right, indent: indent)
|
|
525
606
|
"#{left} ? #{middle} : #{right}"
|
|
526
607
|
end
|
|
527
608
|
|
|
@@ -654,7 +735,7 @@ class Code
|
|
|
654
735
|
|
|
655
736
|
def indent_lines(value, indent)
|
|
656
737
|
prefix = INDENT * indent
|
|
657
|
-
value.split("\n").map { |line| "#{prefix}#{line}" }.join("\n")
|
|
738
|
+
value.split("\n").map { |line| line.empty? ? "" : "#{prefix}#{line}" }.join("\n")
|
|
658
739
|
end
|
|
659
740
|
|
|
660
741
|
def statement_separator(inline:, indent: nil)
|
data/spec/code/format_spec.rb
CHANGED
|
@@ -58,16 +58,34 @@ RSpec.describe Code::Format do
|
|
|
58
58
|
],
|
|
59
59
|
[
|
|
60
60
|
"safe = post.present? and !post[:over_18] and post[:post_hint] == :image and post[:url].to_string.strip.presence and (post[:url].to_string.strip.ends_with?(\".jpg\") or post[:url].to_string.strip.ends_with?(\".jpeg\") or post[:url].to_string.strip.ends_with?(\".png\") or post[:url].to_string.strip.include?(\"i.redd.it\"))",
|
|
61
|
-
"safe = post.present?\n and !post[:over_18]\n and post[:post_hint] == :image\n and post[:url].to_string.strip.presence\n and (post[:url].to_string.strip.ends_with?(\".jpg\")\n
|
|
61
|
+
"safe = post.present?\n and !post[:over_18]\n and post[:post_hint] == :image\n and post[:url].to_string.strip.presence\n and (\n post[:url].to_string.strip.ends_with?(\".jpg\")\n or post[:url].to_string.strip.ends_with?(\".jpeg\")\n or post[:url].to_string.strip.ends_with?(\".png\")\n or post[:url].to_string.strip.include?(\"i.redd.it\")\n)"
|
|
62
62
|
],
|
|
63
63
|
[
|
|
64
64
|
"items.each { |item, index| proxied_image_url = if image_url proxy_url(image_url) else nothing end }",
|
|
65
65
|
"items.each { |item, index|\n proxied_image_url = if image_url\n proxy_url(image_url)\n else\n nothing\n end\n}"
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
"lines << \"vacances scolaires france {zone} (prochains {months_ahead} mois) :\".downcase",
|
|
69
|
+
"lines << (\n \"vacances scolaires france {zone} (prochains \"\n + \"{months_ahead} mois) :\"\n).downcase"
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"src = \"https://proxy.dorianmarie.com?\" + \"{{ url: src, disposition: :inline }.to_query}\" if src",
|
|
73
|
+
"src = (\n \"https://proxy.dorianmarie.com?\"\n + \"{{ url: src, disposition: :inline }.to_query}\"\n) if src"
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"inline_image = image ? \"https://proxy.dorianmarie.com?\" + \"{inline_params.to_query}\" : nothing",
|
|
77
|
+
"inline_image = image ? (\n \"https://proxy.dorianmarie.com?\"\n + \"{inline_params.to_query}\"\n) : nothing"
|
|
66
78
|
]
|
|
67
79
|
].each do |input, expected|
|
|
68
80
|
it "formats #{input.inspect}" do
|
|
69
81
|
expect(described_class.format(Code.parse(input))).to eq(expected)
|
|
70
82
|
end
|
|
83
|
+
|
|
84
|
+
it "formats #{input.inspect} idempotently" do
|
|
85
|
+
formatted = described_class.format(Code.parse(input))
|
|
86
|
+
|
|
87
|
+
expect(described_class.format(Code.parse(formatted))).to eq(formatted)
|
|
88
|
+
end
|
|
71
89
|
end
|
|
72
90
|
|
|
73
91
|
it "round-trips parse and evaluation semantics for formatted code" do
|
|
@@ -77,5 +95,50 @@ RSpec.describe Code::Format do
|
|
|
77
95
|
expect(Code.parse(formatted)).to be_present
|
|
78
96
|
expect(Code.evaluate(formatted)).to eq(Code.evaluate(input))
|
|
79
97
|
end
|
|
98
|
+
|
|
99
|
+
it "keeps grouped multiline receivers stable" do
|
|
100
|
+
input = <<~CODE.chomp
|
|
101
|
+
lines << (
|
|
102
|
+
"vacances scolaires france {zone} (prochains "
|
|
103
|
+
+ "{months_ahead} mois) :"
|
|
104
|
+
).downcase
|
|
105
|
+
CODE
|
|
106
|
+
|
|
107
|
+
expect(described_class.format(Code.parse(input))).to eq(input)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "does not split operators inside string interpolations when wrapping" do
|
|
111
|
+
input =
|
|
112
|
+
%q(body_text = "{title}\n\n{description}\n\ningrédients :\n" + ingredients.map { |ingredient| "- {ingredient}" }.join("\n") + "\n\ninstructions :\n" + instructions.map { |instruction, index| "{index + 1}. {instruction}" }.join("\n"))
|
|
113
|
+
|
|
114
|
+
formatted = described_class.format(Code.parse(input))
|
|
115
|
+
|
|
116
|
+
expect(formatted).to include(
|
|
117
|
+
'instructions.map { |instruction, index| "{index + 1}. {instruction}" }'
|
|
118
|
+
)
|
|
119
|
+
expect(formatted).not_to include(
|
|
120
|
+
"\"{index\n + 1}. {instruction}\""
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "does not emit whitespace-only blank lines" do
|
|
125
|
+
input = <<~CODE.chomp
|
|
126
|
+
body = Html.join(elements.map { |element| title = element.at_css(".x") value = element.at_css(".y") title }, Html.br)
|
|
127
|
+
CODE
|
|
128
|
+
|
|
129
|
+
formatted = described_class.format(Code.parse(input))
|
|
130
|
+
|
|
131
|
+
expect(formatted.lines).not_to include(match(/\A[ \t]+\n\z/))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "keeps lines within 80 characters" do
|
|
135
|
+
input = <<~CODE.chomp
|
|
136
|
+
src = "https://proxy.dorianmarie.com?" + "{{ url: src, disposition: :inline }.to_query}" if src
|
|
137
|
+
CODE
|
|
138
|
+
|
|
139
|
+
formatted = described_class.format(Code.parse(input))
|
|
140
|
+
|
|
141
|
+
expect(formatted.lines.map(&:chomp).map(&:length).max).to be <= 80
|
|
142
|
+
end
|
|
80
143
|
end
|
|
81
144
|
end
|