railroad_diagrams 0.2.1 → 0.3.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 +4 -4
- data/.github/workflows/main.yml +5 -1
- data/CHANGELOG.md +4 -0
- data/README.md +2 -0
- data/lib/railroad_diagrams/alternating_sequence.rb +5 -5
- data/lib/railroad_diagrams/choice.rb +31 -28
- data/lib/railroad_diagrams/command.rb +3 -3
- data/lib/railroad_diagrams/comment.rb +1 -1
- data/lib/railroad_diagrams/diagram.rb +5 -4
- data/lib/railroad_diagrams/diagram_item.rb +1 -1
- data/lib/railroad_diagrams/group.rb +1 -1
- data/lib/railroad_diagrams/horizontal_choice.rb +14 -15
- data/lib/railroad_diagrams/multiple_choice.rb +1 -1
- data/lib/railroad_diagrams/one_or_more.rb +3 -3
- data/lib/railroad_diagrams/optional_sequence.rb +6 -5
- data/lib/railroad_diagrams/stack.rb +1 -0
- data/lib/railroad_diagrams/text_diagram.rb +96 -59
- data/lib/railroad_diagrams/version.rb +1 -1
- data/railroad_diagrams.gemspec +6 -6
- data/test.rb +2 -0
- 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: 3e08635bb1091eae639e0c6579810827a46b42dc7ab39856df0167c5a771b651
|
4
|
+
data.tar.gz: d0b8e359534ca8556b056e16323405bad2d187b35862f1198cc333e2909679ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0e40fcbe169c00fb808bbc479e59c9d2b52bb132f7d096ec228d82c8a594ef207fdb01fa4a7f68445937a0b6cda2f592c6c936215434ab1fb65ca86ea7a3300
|
7
|
+
data.tar.gz: 0355b014fc8baa9138dbabc81c2cdf338cabc7b0c8905563078b702b8765cabfd2475b281673eceffc5b6fd7684eb8e6f587c545621364a406b57509157aaa4a
|
data/.github/workflows/main.yml
CHANGED
@@ -26,4 +26,8 @@ jobs:
|
|
26
26
|
with:
|
27
27
|
ruby-version: ${{ matrix.ruby }}
|
28
28
|
bundler-cache: true
|
29
|
-
- run:
|
29
|
+
- run: |
|
30
|
+
exe/railroad_diagrams --format=svg
|
31
|
+
exe/railroad_diagrams --format=ascii
|
32
|
+
exe/railroad_diagrams --format=unicode
|
33
|
+
exe/railroad_diagrams --format=standalone
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -115,14 +115,14 @@ module RailroadDiagrams
|
|
115
115
|
|
116
116
|
first_td = @items[0].text_diagram
|
117
117
|
second_td = @items[1].text_diagram
|
118
|
-
max_width = TextDiagram.
|
119
|
-
left_width, right_width = TextDiagram.
|
118
|
+
max_width = TextDiagram.max_width(first_td, second_td)
|
119
|
+
left_width, right_width = TextDiagram.gaps(max_width, 0)
|
120
120
|
|
121
121
|
left_lines = []
|
122
122
|
right_lines = []
|
123
123
|
separator = []
|
124
124
|
|
125
|
-
left_size, right_size = TextDiagram.
|
125
|
+
left_size, right_size = TextDiagram.gaps(first_td.width, 0)
|
126
126
|
diagram_td = first_td.expand(left_width - left_size, right_width - right_size, 0, 0)
|
127
127
|
|
128
128
|
left_lines += [' ' * 2] * diagram_td.entry
|
@@ -142,7 +142,7 @@ module RailroadDiagrams
|
|
142
142
|
left_lines << (' ' * 2)
|
143
143
|
right_lines << (' ' * 2)
|
144
144
|
|
145
|
-
left_size, right_size = TextDiagram.
|
145
|
+
left_size, right_size = TextDiagram.gaps(second_td.width, 0)
|
146
146
|
second_td = second_td.expand(left_width - left_size, right_width - right_size, 0, 0)
|
147
147
|
diagram_td = diagram_td.append_below(second_td, separator, move_entry: true, move_exit: true)
|
148
148
|
|
@@ -155,7 +155,7 @@ module RailroadDiagrams
|
|
155
155
|
right_lines << (line + corner_bot_right)
|
156
156
|
|
157
157
|
mid_point = first_td.height + (separator.size / 2)
|
158
|
-
diagram_td = diagram_td.alter(
|
158
|
+
diagram_td = diagram_td.alter(new_entry: mid_point, new_exit: mid_point)
|
159
159
|
|
160
160
|
left_td = TextDiagram.new(mid_point, mid_point, left_lines)
|
161
161
|
right_td = TextDiagram.new(mid_point, mid_point, right_lines)
|
@@ -40,8 +40,6 @@ module RailroadDiagrams
|
|
40
40
|
@up += @items[0].up
|
41
41
|
|
42
42
|
@height = @items[default].height
|
43
|
-
@down = 0
|
44
|
-
|
45
43
|
(default + 1...@items.size).each do |i|
|
46
44
|
arcs =
|
47
45
|
if i == default + 1
|
@@ -67,7 +65,7 @@ module RailroadDiagrams
|
|
67
65
|
end
|
68
66
|
|
69
67
|
def to_s
|
70
|
-
items_str = @items.map(&:
|
68
|
+
items_str = @items.map(&:to_s).join(', ')
|
71
69
|
"Choice(#{@default}, #{items_str})"
|
72
70
|
end
|
73
71
|
|
@@ -150,57 +148,62 @@ module RailroadDiagrams
|
|
150
148
|
move_entry = false
|
151
149
|
move_exit = false
|
152
150
|
if i <= @default
|
153
|
-
#
|
154
|
-
|
155
|
-
[
|
156
|
-
|
151
|
+
# Item below the line: round off the entry/exit lines downwards.
|
152
|
+
left_lines[item_td.entry] = roundcorner_top_left
|
153
|
+
right_lines[item_td.exit] = roundcorner_top_right
|
154
|
+
if i.zero?
|
155
|
+
# First item and above the line: also remove ascenders above the item's entry and exit, suppress the separator above it.
|
156
|
+
has_separator = false
|
157
|
+
(0...item_td.entry).each { |j| left_lines[j] = ' ' }
|
158
|
+
(0...item_td.exit).each { |j| right_lines[j] = ' ' }
|
159
|
+
end
|
157
160
|
end
|
158
161
|
if i >= @default
|
159
162
|
# Item below the line: round off the entry/exit lines downwards.
|
160
163
|
left_lines[item_td.entry] = roundcorner_bot_left
|
161
|
-
right_lines[item_td.
|
162
|
-
if i
|
164
|
+
right_lines[item_td.exit] = roundcorner_bot_right
|
165
|
+
if i.zero?
|
163
166
|
# First item and below the line: also suppress the separator above it.
|
164
167
|
has_separator = false
|
165
168
|
end
|
166
169
|
if i == @items.size - 1
|
167
170
|
# Last item and below the line: also remove descenders below the item's entry and exit
|
168
|
-
|
169
|
-
|
171
|
+
(item_td.entry + 1...item_td.height).each { |j| left_lines[j] = ' ' }
|
172
|
+
(item_td.exit + 1...item_td.height).each { |j| right_lines[j] = ' ' }
|
170
173
|
end
|
171
174
|
end
|
172
175
|
if i == @default
|
173
176
|
# Item on the line: entry/exit are horizontal, and sets the outer entry/exit.
|
174
177
|
left_lines[item_td.entry] = cross
|
175
|
-
right_lines[item_td.
|
178
|
+
right_lines[item_td.exit] = cross
|
176
179
|
move_entry = true
|
177
180
|
move_exit = true
|
178
|
-
if i
|
181
|
+
if i.zero? && i == @items.size - 1
|
179
182
|
# Only item and on the line: set entry/exit for straight through.
|
180
183
|
left_lines[item_td.entry] = line
|
181
|
-
right_lines[item_td.
|
182
|
-
elsif i
|
184
|
+
right_lines[item_td.exit] = line
|
185
|
+
elsif i.zero?
|
183
186
|
# First item and on the line: set entry/exit for no ascenders.
|
184
|
-
left_lines[item_td.entry] =
|
185
|
-
|
187
|
+
left_lines[item_td.entry] = roundcorner_top_right
|
188
|
+
right_lines[item_td.exit] = roundcorner_top_left
|
186
189
|
elsif i == @items.size - 1
|
187
190
|
# Last item and on the line: set entry/exit for no descenders.
|
188
|
-
left_lines[item_td.entry] =
|
189
|
-
right_lines[item_td.
|
191
|
+
left_lines[item_td.entry] = roundcorner_bot_right
|
192
|
+
right_lines[item_td.exit] = roundcorner_bot_left
|
190
193
|
end
|
191
194
|
end
|
192
195
|
left_join_td = TextDiagram.new(item_td.entry, item_td.entry, left_lines)
|
193
196
|
right_join_td = TextDiagram.new(item_td.exit, item_td.exit, right_lines)
|
194
197
|
item_td = left_join_td.append_right(item_td, '').append_right(right_join_td, '')
|
195
|
-
separator =
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
198
|
+
separator =
|
199
|
+
if has_separator
|
200
|
+
[
|
201
|
+
line_vertical +
|
202
|
+
(' ' * (TextDiagram.max_width(diagram_td, item_td) - 2)) + line_vertical
|
203
|
+
]
|
204
|
+
else
|
205
|
+
[]
|
206
|
+
end
|
204
207
|
diagram_td = diagram_td.append_below(item_td, separator, move_entry: move_entry, move_exit: move_exit)
|
205
208
|
end
|
206
209
|
diagram_td
|
@@ -69,12 +69,12 @@ module RailroadDiagrams
|
|
69
69
|
|
70
70
|
case @format
|
71
71
|
when 'svg'
|
72
|
-
diagram.write_svg(
|
72
|
+
diagram.write_svg($stdout.method(:write))
|
73
73
|
when 'standalone'
|
74
|
-
diagram.write_standalone(
|
74
|
+
diagram.write_standalone($stdout.method(:write))
|
75
75
|
when 'ascii', 'unicode'
|
76
76
|
puts "\n<pre>"
|
77
|
-
diagram.write_text(
|
77
|
+
diagram.write_text($stdout.method(:write))
|
78
78
|
puts "\n</pre>"
|
79
79
|
end
|
80
80
|
|
@@ -74,9 +74,10 @@ module RailroadDiagrams
|
|
74
74
|
diagram_td = @items[0].text_diagram
|
75
75
|
@items[1..-1].each do |item|
|
76
76
|
item_td = item.text_diagram
|
77
|
-
item_td.expand(1, 1, 0, 0) if item.needs_space
|
78
|
-
diagram_td = diagram_td.append_right(separator)
|
77
|
+
item_td = item_td.expand(1, 1, 0, 0) if item.needs_space
|
78
|
+
diagram_td = diagram_td.append_right(item_td, separator)
|
79
79
|
end
|
80
|
+
diagram_td
|
80
81
|
end
|
81
82
|
|
82
83
|
def write_svg(write)
|
@@ -88,8 +89,8 @@ module RailroadDiagrams
|
|
88
89
|
def write_text(write)
|
89
90
|
output = text_diagram
|
90
91
|
output = "#{output.lines.join("\n")}\n"
|
91
|
-
output = output.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
|
92
|
-
write(output)
|
92
|
+
output = output.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
|
93
|
+
write.call(output)
|
93
94
|
end
|
94
95
|
|
95
96
|
def write_standalone(write, css = nil)
|
@@ -52,7 +52,7 @@ module RailroadDiagrams
|
|
52
52
|
).add(self)
|
53
53
|
|
54
54
|
@item.format(x, y, @width).add(self)
|
55
|
-
@label
|
55
|
+
@label&.format(x, y - (@box_up + @label.down + @label.height), @width)&.add(self)
|
56
56
|
|
57
57
|
self
|
58
58
|
end
|
@@ -20,7 +20,7 @@ module RailroadDiagrams
|
|
20
20
|
AR + # starting track
|
21
21
|
(AR * 2 * (@items.size - 1)) + # inbetween tracks
|
22
22
|
@items.sum { |x| x.width + (x.needs_space ? 20 : 0) } + # items
|
23
|
-
(last.height
|
23
|
+
(last.height.positive? ? AR : 0) + # needs space to curve up
|
24
24
|
AR # ending track
|
25
25
|
|
26
26
|
# Always exits at entrance height
|
@@ -156,31 +156,31 @@ module RailroadDiagrams
|
|
156
156
|
# diagram_entry: distance from top to lowest entry, aka distance from top to diagram entry, aka final diagram entry and exit.
|
157
157
|
diagram_entry = item_tds.map(&:entry).max
|
158
158
|
# soil_to_baseline: distance from top to lowest entry before rightmost item, aka distance from skip-over-items line to rightmost entry, aka SOIL height.
|
159
|
-
soil_to_baseline = item_tds[0
|
159
|
+
soil_to_baseline = item_tds[0...-1].map(&:entry).max
|
160
160
|
# top_to_soil: distance from top to skip-over-items line.
|
161
161
|
top_to_soil = diagram_entry - soil_to_baseline
|
162
162
|
# baseline_to_suil: distance from lowest entry or exit after leftmost item to bottom, aka distance from entry to skip-under-items line, aka SUIL height.
|
163
|
-
baseline_to_suil = item_tds[1..-1].map { |td| td.height - [td.entry, td.exit].min }.max
|
163
|
+
baseline_to_suil = item_tds[1..-1].map { |td| td.height - [td.entry, td.exit].min }.max - 1
|
164
164
|
|
165
165
|
# The diagram starts with a line from its entry up to skip-over-items line:
|
166
166
|
lines = Array.new(top_to_soil, ' ')
|
167
167
|
lines << (roundcorner_top_left + line)
|
168
|
-
lines += Array.new(soil_to_baseline, line_vertical
|
168
|
+
lines += Array.new(soil_to_baseline, "#{line_vertical} ")
|
169
169
|
lines << (roundcorner_bot_right + line)
|
170
170
|
|
171
171
|
diagram_td = TextDiagram.new(lines.size - 1, lines.size - 1, lines)
|
172
172
|
|
173
173
|
item_tds.each_with_index do |item_td, item_num|
|
174
|
-
if item_num
|
174
|
+
if item_num.positive?
|
175
175
|
# All items except the leftmost start with a line from the skip-over-items line down to their entry,
|
176
176
|
# with a joining-line across at the skip-under-items line:
|
177
|
-
lines =
|
177
|
+
lines = [' '] * top_to_soil
|
178
178
|
# All such items except the rightmost also have a continuation of the skip-over-items line:
|
179
179
|
line_to_next_item = item_num == item_tds.size - 1 ? ' ' : line
|
180
180
|
lines << (roundcorner_top_right + line_to_next_item)
|
181
|
-
lines +=
|
181
|
+
lines += ["#{line_vertical} "] * soil_to_baseline
|
182
182
|
lines << (roundcorner_bot_left + line)
|
183
|
-
lines +=
|
183
|
+
lines += [' '] * baseline_to_suil
|
184
184
|
lines << (line * 2)
|
185
185
|
|
186
186
|
entry_td = TextDiagram.new(diagram_td.exit, diagram_td.exit, lines)
|
@@ -201,7 +201,7 @@ module RailroadDiagrams
|
|
201
201
|
|
202
202
|
part_td = part_td.append_below(item_td, [], move_entry: true, move_exit: true)
|
203
203
|
|
204
|
-
if item_num
|
204
|
+
if item_num.positive?
|
205
205
|
# All items except the leftmost end with enough blank lines to pad down to the skip-under-items
|
206
206
|
# line, followed by a segment of the skip-under-items line:
|
207
207
|
lines = Array.new(baseline_to_suil - (item_td.height - item_td.entry) + 1, ' ' * item_td.width)
|
@@ -219,26 +219,25 @@ module RailroadDiagrams
|
|
219
219
|
lines << (line * 2)
|
220
220
|
lines += Array.new(diagram_td.exit - top_to_soil - 1, ' ')
|
221
221
|
lines << (line + roundcorner_top_right)
|
222
|
-
lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry),
|
223
|
-
line_from_prev_item = item_num
|
222
|
+
lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), " #{line_vertical}")
|
223
|
+
line_from_prev_item = item_num.positive? ? line : ' '
|
224
224
|
lines << (line_from_prev_item + roundcorner_bot_left)
|
225
225
|
|
226
226
|
entry = diagram_entry + 1 + (diagram_td.exit - diagram_td.entry)
|
227
227
|
exit_td = TextDiagram.new(entry, diagram_entry + 1, lines)
|
228
|
-
diagram_td = diagram_td.append_right(exit_td, '')
|
229
228
|
else
|
230
229
|
# The rightmost item has a line from the skip-under-items line and from its exit up to the diagram exit:
|
231
230
|
lines = []
|
232
231
|
line_from_exit = diagram_td.exit == diagram_td.entry ? line : ' '
|
233
232
|
lines << (line_from_exit + roundcorner_top_left)
|
234
|
-
lines += Array.new(diagram_td.exit - diagram_td.entry
|
233
|
+
lines += Array.new(diagram_td.exit - diagram_td.entry, " #{line_vertical}")
|
235
234
|
lines << (line + roundcorner_bot_right) if diagram_td.exit != diagram_td.entry
|
236
|
-
lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry),
|
235
|
+
lines += Array.new(baseline_to_suil - (diagram_td.exit - diagram_td.entry), " #{line_vertical}")
|
237
236
|
lines << (line + roundcorner_bot_right)
|
238
237
|
|
239
238
|
exit_td = TextDiagram.new(diagram_td.exit - diagram_td.entry, 0, lines)
|
240
|
-
diagram_td = diagram_td.append_right(exit_td, '')
|
241
239
|
end
|
240
|
+
diagram_td = diagram_td.append_right(exit_td, '')
|
242
241
|
end
|
243
242
|
|
244
243
|
diagram_td
|
@@ -124,7 +124,7 @@ module RailroadDiagrams
|
|
124
124
|
def text_diagram
|
125
125
|
multi_repeat = TextDiagram.get_parts(['multi_repeat']).first
|
126
126
|
any_all = TextDiagram.rect(@type == 'any' ? '1+' : 'all')
|
127
|
-
diagram_td = Choice.
|
127
|
+
diagram_td = Choice.new(0, Skip.new).text_diagram
|
128
128
|
repeat_td = TextDiagram.rect(multi_repeat)
|
129
129
|
diagram_td = any_all.append_right(diagram_td, '')
|
130
130
|
diagram_td.append_right(repeat_td, '')
|
@@ -55,7 +55,7 @@ module RailroadDiagrams
|
|
55
55
|
# Format the item and then format the repeat append it to the bottom, after a spacer.
|
56
56
|
item_td = @item.text_diagram
|
57
57
|
repeat_td = @rep.text_diagram
|
58
|
-
fir_width = TextDiagram.
|
58
|
+
fir_width = TextDiagram.max_width(item_td, repeat_td)
|
59
59
|
repeat_td = repeat_td.expand(0, fir_width - repeat_td.width, 0, 0)
|
60
60
|
item_td = item_td.expand(0, fir_width - item_td.width, 0, 0)
|
61
61
|
item_and_repeat_td = item_td.append_below(repeat_td, [])
|
@@ -63,7 +63,7 @@ module RailroadDiagrams
|
|
63
63
|
# Build the left side of the repeat line and append the combined item and repeat to its right.
|
64
64
|
left_lines = []
|
65
65
|
left_lines << (repeat_top_left + line)
|
66
|
-
left_lines += [repeat_left
|
66
|
+
left_lines += ["#{repeat_left} "] * ((item_td.height - item_td.entry) + repeat_td.entry - 1)
|
67
67
|
left_lines << (repeat_bot_left + line)
|
68
68
|
left_td = TextDiagram.new(0, 0, left_lines)
|
69
69
|
left_td = left_td.append_right(item_and_repeat_td, '')
|
@@ -71,7 +71,7 @@ module RailroadDiagrams
|
|
71
71
|
# Build the right side of the repeat line and append it to the combined left side, item, and repeat's right.
|
72
72
|
right_lines = []
|
73
73
|
right_lines << (line + repeat_top_right)
|
74
|
-
right_lines += [
|
74
|
+
right_lines += [" #{repeat_right}"] * ((item_td.height - item_td.exit) + repeat_td.exit - 1)
|
75
75
|
right_lines << (line + repeat_bot_right)
|
76
76
|
right_td = TextDiagram.new(0, 0, right_lines)
|
77
77
|
left_td.append_right(right_td, '')
|
@@ -136,7 +136,7 @@ module RailroadDiagrams
|
|
136
136
|
# diagramEntry: distance from top to lowest entry, aka distance from top to diagram entry, aka final diagram entry and exit.
|
137
137
|
diagram_entry = item_tds.map(&:entry).max
|
138
138
|
# SOILHeight: distance from top to lowest entry before rightmost item, aka distance from skip-over-items line to rightmost entry, aka SOIL height.
|
139
|
-
soil_height = item_tds
|
139
|
+
soil_height = item_tds.map(&:entry).max
|
140
140
|
# topToSOIL: distance from top to skip-over-items line.
|
141
141
|
top_to_soil = diagram_entry - soil_height
|
142
142
|
|
@@ -147,7 +147,7 @@ module RailroadDiagrams
|
|
147
147
|
lines += [roundcorner_bot_right + line]
|
148
148
|
diagram_td = TextDiagram.new(lines.size - 1, lines.size - 1, lines)
|
149
149
|
|
150
|
-
|
150
|
+
item_tds.each_with_index do |item_td, i|
|
151
151
|
if i.positive?
|
152
152
|
# All items except the leftmost start with a line from their entry down to their skip-under-item line,
|
153
153
|
# with a joining-line across at the skip-over-items line:
|
@@ -162,9 +162,10 @@ module RailroadDiagrams
|
|
162
162
|
|
163
163
|
# All items except the leftmost next have a line from skip-over-items line down to their entry,
|
164
164
|
# with joining-lines at their entry and at their skip-under-item line:
|
165
|
-
lines = ([' '] * top_to_soil) +
|
166
|
-
|
167
|
-
|
165
|
+
lines = ([' '] * top_to_soil) +
|
166
|
+
[line + roundcorner_top_right +
|
167
|
+
# All such items except the rightmost also have a continuation of the skip-over-items line:
|
168
|
+
(i < item_tds.size - 1 ? line : ' ')] +
|
168
169
|
([" #{line_vertical} "] * (diagram_td.exit - top_to_soil - 1)) +
|
169
170
|
[line + roundcorner_bot_left + line] +
|
170
171
|
([' ' * 3] * (item_td.height - item_td.entry - 1)) +
|
@@ -117,24 +117,43 @@ module RailroadDiagrams
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
-
def rect(item, dashed
|
120
|
+
def rect(item, dashed: false)
|
121
121
|
rectish('rect', item, dashed)
|
122
122
|
end
|
123
123
|
|
124
|
-
def round_rect(item, dashed
|
124
|
+
def round_rect(item, dashed: false)
|
125
125
|
rectish('roundrect', item, dashed)
|
126
126
|
end
|
127
127
|
|
128
|
+
def max_width(*args)
|
129
|
+
max_width = 0
|
130
|
+
args.each do |arg|
|
131
|
+
width =
|
132
|
+
case arg
|
133
|
+
when TextDiagram
|
134
|
+
arg.width
|
135
|
+
when Array
|
136
|
+
arg.map(&:length).max
|
137
|
+
when Numeric
|
138
|
+
arg.to_s.length
|
139
|
+
else
|
140
|
+
arg.length
|
141
|
+
end
|
142
|
+
max_width = width if width > max_width
|
143
|
+
end
|
144
|
+
max_width
|
145
|
+
end
|
146
|
+
|
128
147
|
def pad_l(string, width, pad)
|
129
148
|
gap = width - string.length
|
130
|
-
raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless gap % pad.length
|
149
|
+
raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless (gap % pad.length).zero?
|
131
150
|
|
132
151
|
(pad * (gap / pad.length)) + string
|
133
152
|
end
|
134
153
|
|
135
154
|
def pad_r(string, width, pad)
|
136
155
|
gap = width - string.length
|
137
|
-
raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless gap % pad.length
|
156
|
+
raise "Gap #{gap} must be a multiple of pad string '#{pad}'" unless (gap % pad.length).zero?
|
138
157
|
|
139
158
|
string + (pad * (gap / pad.length))
|
140
159
|
end
|
@@ -169,54 +188,57 @@ module RailroadDiagrams
|
|
169
188
|
|
170
189
|
def rectish(rect_type, data, dashed)
|
171
190
|
line_type = dashed ? '_dashed' : ''
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
lines
|
189
|
-
|
191
|
+
top_left, ctr_left, bot_left, top_right, ctr_right, bot_right, top_horiz, bot_horiz, line, cross =
|
192
|
+
get_parts([
|
193
|
+
"#{rect_type}_top_left",
|
194
|
+
"#{rect_type}_left#{line_type}",
|
195
|
+
"#{rect_type}_bot_left",
|
196
|
+
"#{rect_type}_top_right",
|
197
|
+
"#{rect_type}_right#{line_type}",
|
198
|
+
"#{rect_type}_bot_right",
|
199
|
+
"#{rect_type}_top#{line_type}",
|
200
|
+
"#{rect_type}_bot#{line_type}",
|
201
|
+
'line',
|
202
|
+
'cross'
|
203
|
+
])
|
204
|
+
|
205
|
+
item_td = data.is_a?(TextDiagram) ? data : new(0, 0, [data])
|
206
|
+
|
207
|
+
lines = [top_horiz * (item_td.width + 2)]
|
208
|
+
if data.is_a?(TextDiagram)
|
209
|
+
lines += item_td.expand(1, 1, 0, 0).lines
|
210
|
+
else
|
211
|
+
(0...item_td.lines.length).each do |i|
|
212
|
+
lines += [" #{item_td.lines[i]} "]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
lines += [(bot_horiz * (item_td.width + 2))]
|
190
216
|
|
191
217
|
entry = item_td.entry + 1
|
192
218
|
exit = item_td.exit + 1
|
193
219
|
|
194
|
-
|
195
|
-
lefts =
|
196
|
-
lefts[0] =
|
197
|
-
lefts[-1] =
|
198
|
-
lefts[entry] =
|
220
|
+
left_max_width = max_width(top_left, ctr_left, bot_left)
|
221
|
+
lefts = [pad_r(ctr_left, left_max_width, ' ')] * lines.length
|
222
|
+
lefts[0] = pad_r(top_left, left_max_width, top_horiz)
|
223
|
+
lefts[-1] = pad_r(bot_left, left_max_width, bot_horiz)
|
224
|
+
lefts[entry] = cross if data.is_a?(TextDiagram)
|
199
225
|
|
200
|
-
|
201
|
-
rights =
|
202
|
-
rights[0] =
|
203
|
-
rights[-1] =
|
204
|
-
rights[exit] =
|
226
|
+
right_max_width = max_width(top_right, ctr_right, bot_right)
|
227
|
+
rights = [pad_l(ctr_right, right_max_width, ' ')] * lines.length
|
228
|
+
rights[0] = pad_l(top_right, right_max_width, top_horiz)
|
229
|
+
rights[-1] = pad_l(bot_right, right_max_width, bot_horiz)
|
230
|
+
rights[exit] = cross if data.is_a?(TextDiagram)
|
205
231
|
|
206
|
-
|
207
|
-
lefts[i] + line + rights[i]
|
208
|
-
end
|
232
|
+
lines = enclose_lines(lines, lefts, rights)
|
209
233
|
|
210
|
-
lefts =
|
211
|
-
lefts[entry] =
|
212
|
-
rights =
|
213
|
-
rights[exit] =
|
234
|
+
lefts = [' '] * lines.length
|
235
|
+
lefts[entry] = line
|
236
|
+
rights = [' '] * lines.length
|
237
|
+
rights[exit] = line
|
214
238
|
|
215
|
-
|
216
|
-
lefts[i] + line + rights[i]
|
217
|
-
end
|
239
|
+
lines = enclose_lines(lines, lefts, rights)
|
218
240
|
|
219
|
-
new(entry, exit,
|
241
|
+
new(entry, exit, lines)
|
220
242
|
end
|
221
243
|
end
|
222
244
|
|
@@ -227,12 +249,17 @@ module RailroadDiagrams
|
|
227
249
|
@exit = exit
|
228
250
|
@lines = lines.dup
|
229
251
|
@height = lines.size
|
230
|
-
@width = lines.
|
252
|
+
@width = lines.any? ? lines[0].length : 0
|
253
|
+
|
254
|
+
raise "Entry is not within diagram vertically:\n#{dump(false)}" unless entry <= lines.length
|
255
|
+
raise "Exit is not within diagram vertically:\n#{dump(false)}" unless exit <= lines.length
|
231
256
|
|
232
|
-
|
257
|
+
lines.each do |line|
|
258
|
+
raise "Diagram data is not rectangular:\n#{dump(false)}" unless lines[0].length == line.length
|
259
|
+
end
|
233
260
|
end
|
234
261
|
|
235
|
-
def alter(new_entry
|
262
|
+
def alter(new_entry: nil, new_exit: nil, new_lines: nil)
|
236
263
|
self.class.new(
|
237
264
|
new_entry || @entry,
|
238
265
|
new_exit || @exit,
|
@@ -310,23 +337,33 @@ module RailroadDiagrams
|
|
310
337
|
)
|
311
338
|
end
|
312
339
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
@lines.each do |
|
320
|
-
|
340
|
+
def dump(show = true)
|
341
|
+
result = "height=#{@height}; len(lines)=#{@lines.length}"
|
342
|
+
|
343
|
+
result += "; entry outside diagram: entry=#{@ntry}" if @entry > @lines.length
|
344
|
+
result += "; exit outside diagram: exit=#{@exit}" if @exit > @lines.length
|
345
|
+
|
346
|
+
(0...[@lines.length, @entry + 1, @exit + 1].max).each do |y|
|
347
|
+
result += "\n[#{format('%03d', y)}]"
|
348
|
+
result += " '#{@lines[y]}' len=#{@lines[y].length}" if y < @lines.length
|
349
|
+
if y == @entry && y == @exit
|
350
|
+
result += ' <- entry, exit'
|
351
|
+
elsif y == @entry
|
352
|
+
result += ' <- entry'
|
353
|
+
elsif y == @exit
|
354
|
+
result += ' <- exit'
|
355
|
+
end
|
321
356
|
end
|
322
357
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
358
|
+
if show
|
359
|
+
puts result
|
360
|
+
else
|
361
|
+
result
|
362
|
+
end
|
328
363
|
end
|
329
364
|
|
365
|
+
private
|
366
|
+
|
330
367
|
def inspect
|
331
368
|
output = ["TextDiagram(entry=#{@entry}, exit=#{@exit}, height=#{@height})"]
|
332
369
|
@lines.each_with_index do |line, i|
|
data/railroad_diagrams.gemspec
CHANGED
@@ -10,14 +10,14 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = '🛤️ A tiny Ruby+SVG library for drawing railroad syntax diagrams like JSON.org.'
|
12
12
|
spec.description = 'Generate SVG railroad syntax diagrams, like on JSON.org.'
|
13
|
-
spec.homepage =
|
13
|
+
spec.homepage = 'https://github.com/ydah/railroad_diagrams'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new(
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
16
|
|
17
|
-
spec.metadata[
|
18
|
-
spec.metadata[
|
19
|
-
spec.metadata[
|
20
|
-
spec.metadata[
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/releases"
|
20
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
21
21
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
22
22
|
|
23
23
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
data/test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: railroad_diagrams
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yudai Takada
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: Generate SVG railroad syntax diagrams, like on JSON.org.
|
13
13
|
email:
|