tty-markdown 0.6.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -2
- data/LICENSE.txt +1 -1
- data/README.md +176 -80
- data/lib/tty/markdown/converter.rb +819 -0
- data/lib/tty/markdown/kramdown_ext.rb +23 -0
- data/lib/tty/markdown/syntax_highlighter.rb +20 -16
- data/lib/tty/markdown/version.rb +1 -1
- data/lib/tty/markdown.rb +194 -72
- data/lib/tty-markdown.rb +1 -1
- metadata +41 -68
- data/Rakefile +0 -8
- data/assets/headers.png +0 -0
- data/assets/hr.png +0 -0
- data/assets/link.png +0 -0
- data/assets/list.png +0 -0
- data/assets/quote.png +0 -0
- data/assets/syntax_highlight.png +0 -0
- data/assets/table.png +0 -0
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/examples/man.rb +0 -6
- data/examples/marked.rb +0 -6
- data/lib/tty/markdown/parser.rb +0 -482
- data/spec/spec_helper.rb +0 -31
- data/spec/unit/parse/abbrev_spec.rb +0 -27
- data/spec/unit/parse/blockquote_spec.rb +0 -77
- data/spec/unit/parse/codeblock_spec.rb +0 -130
- data/spec/unit/parse/comment_spec.rb +0 -19
- data/spec/unit/parse/emphasis_spec.rb +0 -35
- data/spec/unit/parse/entity_spec.rb +0 -11
- data/spec/unit/parse/header_spec.rb +0 -35
- data/spec/unit/parse/hr_spec.rb +0 -25
- data/spec/unit/parse/link_spec.rb +0 -25
- data/spec/unit/parse/list_spec.rb +0 -103
- data/spec/unit/parse/math_spec.rb +0 -37
- data/spec/unit/parse/paragraph_spec.rb +0 -38
- data/spec/unit/parse/table_spec.rb +0 -164
- data/spec/unit/parse/typography_spec.rb +0 -20
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
data/examples/man.rb
DELETED
data/examples/marked.rb
DELETED
data/lib/tty/markdown/parser.rb
DELETED
@@ -1,482 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'kramdown/converter'
|
4
|
-
require 'pastel'
|
5
|
-
require 'strings'
|
6
|
-
require 'tty-screen'
|
7
|
-
|
8
|
-
require_relative 'syntax_highlighter'
|
9
|
-
|
10
|
-
module TTY
|
11
|
-
module Markdown
|
12
|
-
# Converts a Kramdown::Document tree to a terminal friendly output
|
13
|
-
class Parser < Kramdown::Converter::Base
|
14
|
-
|
15
|
-
def initialize(root, **options)
|
16
|
-
super
|
17
|
-
@stack = []
|
18
|
-
@current_indent = 0
|
19
|
-
@indent = options.fetch(:indent, 2)
|
20
|
-
@pastel = Pastel.new
|
21
|
-
@color_opts = { mode: options[:colors] }
|
22
|
-
@width = options.fetch(:width) { TTY::Screen.width }
|
23
|
-
@theme = options.fetch(:theme) { TTY::Markdown::THEME }
|
24
|
-
end
|
25
|
-
|
26
|
-
# Invoke an element conversion
|
27
|
-
#
|
28
|
-
# @api public
|
29
|
-
def convert(el, opts = { indent: 0, result: [] })
|
30
|
-
send("convert_#{el.type}", el, opts)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# Process children of this element
|
36
|
-
def inner(el, opts)
|
37
|
-
@stack << [el, opts]
|
38
|
-
el.children.each_with_index do |inner_el, i|
|
39
|
-
options = opts.dup
|
40
|
-
options[:parent] = el
|
41
|
-
options[:prev] = (i == 0 ? nil : el.children[i - 1])
|
42
|
-
options[:index] = i
|
43
|
-
convert(inner_el, options)
|
44
|
-
end
|
45
|
-
@stack.pop
|
46
|
-
end
|
47
|
-
|
48
|
-
def convert_root(el, opts)
|
49
|
-
inner(el, opts)
|
50
|
-
opts[:result]
|
51
|
-
end
|
52
|
-
|
53
|
-
def convert_header(el, opts)
|
54
|
-
level = el.options[:level]
|
55
|
-
@current_indent = (level - 1) * @indent # Header determines indentation
|
56
|
-
indent = ' ' * (level - 1) * @indent
|
57
|
-
styles = Array(@theme[:header]).dup
|
58
|
-
styles << :underline if level == 1
|
59
|
-
opts[:result] << indent + @pastel.lookup(*styles)
|
60
|
-
inner(el, opts)
|
61
|
-
opts[:result] << @pastel.lookup(:reset) + "\n"
|
62
|
-
end
|
63
|
-
|
64
|
-
def convert_p(el, opts)
|
65
|
-
result_before = @stack.last[1][:result].dup
|
66
|
-
indent = ' ' * @current_indent
|
67
|
-
|
68
|
-
if opts[:parent].type != :blockquote
|
69
|
-
opts[:result] << indent
|
70
|
-
end
|
71
|
-
|
72
|
-
opts[:indent] = @current_indent
|
73
|
-
opts[:strip] = false
|
74
|
-
|
75
|
-
case opts[:parent].type
|
76
|
-
when :li
|
77
|
-
bullet = TTY::Markdown.symbols[:bullet]
|
78
|
-
index = @stack.last[1][:index] + 1
|
79
|
-
symbol = opts[:ordered] ? "#{index}." : bullet
|
80
|
-
styles = Array(@theme[:list])
|
81
|
-
opts[:result] << @pastel.decorate(symbol, *styles) + ' '
|
82
|
-
opts[:indent] += @indent
|
83
|
-
opts[:strip] = true
|
84
|
-
when :blockquote
|
85
|
-
opts[:indent] = 0
|
86
|
-
end
|
87
|
-
|
88
|
-
inner(el, opts)
|
89
|
-
|
90
|
-
if opts[:parent].type == :blockquote
|
91
|
-
format_blockquote(result_before, opts[:result])
|
92
|
-
end
|
93
|
-
|
94
|
-
unless opts[:result].last.end_with?("\n")
|
95
|
-
opts[:result] << "\n"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# Format current element by inserting prefix for each
|
100
|
-
# quoted line within the allowed screen size.
|
101
|
-
#
|
102
|
-
# @param [Array[String]] result_before
|
103
|
-
# @param [Array[String]] result
|
104
|
-
#
|
105
|
-
# @return [nil]
|
106
|
-
#
|
107
|
-
# @api private
|
108
|
-
def format_blockquote(result_before, result)
|
109
|
-
indent = ' ' * @current_indent
|
110
|
-
start_index = result_before.size
|
111
|
-
max_index = result.size - 1
|
112
|
-
bar_symbol = TTY::Markdown.symbols[:bar]
|
113
|
-
styles = Array(@theme[:quote])
|
114
|
-
prefix = "#{indent}#{@pastel.decorate(bar_symbol, *styles)} "
|
115
|
-
|
116
|
-
result.map!.with_index do |str, i|
|
117
|
-
if i == start_index
|
118
|
-
str.insert(0, prefix)
|
119
|
-
end
|
120
|
-
|
121
|
-
# only modify blockquote element
|
122
|
-
if i >= start_index && str.to_s.include?("\n") # multiline string found
|
123
|
-
str.lines.map! do |line|
|
124
|
-
if (line != str.lines.last || i < max_index)
|
125
|
-
line.insert(-1, line.end_with?("\n") ? prefix : "\n" + prefix)
|
126
|
-
else
|
127
|
-
line
|
128
|
-
end
|
129
|
-
end.join
|
130
|
-
else
|
131
|
-
str
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def convert_text(el, opts)
|
137
|
-
text = Strings.wrap(el.value, @width)
|
138
|
-
text = text.chomp if opts[:strip]
|
139
|
-
indent = ' ' * opts[:indent]
|
140
|
-
text = text.gsub(/\n/, "\n#{indent}")
|
141
|
-
opts[:result] << text
|
142
|
-
end
|
143
|
-
|
144
|
-
def convert_strong(el, opts)
|
145
|
-
styles = Array(@theme[:strong])
|
146
|
-
opts[:result] << @pastel.lookup(*styles)
|
147
|
-
inner(el, opts)
|
148
|
-
opts[:result] << @pastel.lookup(:reset)
|
149
|
-
end
|
150
|
-
|
151
|
-
def convert_em(el, opts)
|
152
|
-
styles = Array(@theme[:em])
|
153
|
-
opts[:result] << @pastel.lookup(*styles)
|
154
|
-
inner(el, opts)
|
155
|
-
opts[:result] << @pastel.lookup(:reset)
|
156
|
-
end
|
157
|
-
|
158
|
-
def convert_blank(el, opts)
|
159
|
-
opts[:result] << "\n"
|
160
|
-
end
|
161
|
-
|
162
|
-
def convert_smart_quote(el, opts)
|
163
|
-
opts[:result] << TTY::Markdown.symbols[el.value]
|
164
|
-
end
|
165
|
-
|
166
|
-
def convert_codespan(el, opts)
|
167
|
-
raw_code = Strings.wrap(el.value, @width)
|
168
|
-
highlighted = SyntaxHighliter.highlight(raw_code, @color_opts.merge(opts))
|
169
|
-
code = highlighted.split("\n").map.with_index do |line, i|
|
170
|
-
if i.zero? # first line
|
171
|
-
line
|
172
|
-
else
|
173
|
-
line.insert(0, ' ' * @current_indent)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
opts[:result] << code.join("\n")
|
177
|
-
end
|
178
|
-
|
179
|
-
def convert_codeblock(el, opts)
|
180
|
-
opts[:fenced] = false
|
181
|
-
convert_codespan(el, opts)
|
182
|
-
end
|
183
|
-
|
184
|
-
def convert_blockquote(el, opts)
|
185
|
-
inner(el, opts)
|
186
|
-
end
|
187
|
-
|
188
|
-
def convert_ul(el, opts)
|
189
|
-
@current_indent += @indent unless opts[:parent].type == :root
|
190
|
-
inner(el, opts)
|
191
|
-
@current_indent -= @indent unless opts[:parent].type == :root
|
192
|
-
end
|
193
|
-
alias convert_ol convert_ul
|
194
|
-
|
195
|
-
def convert_li(el, opts)
|
196
|
-
if opts[:parent].type == :ol
|
197
|
-
opts[:ordered] = true
|
198
|
-
end
|
199
|
-
inner(el, opts)
|
200
|
-
end
|
201
|
-
|
202
|
-
def convert_table(el, opts)
|
203
|
-
opts[:alignment] = el.options[:alignment]
|
204
|
-
|
205
|
-
result = opts[:result]
|
206
|
-
opts[:result] = []
|
207
|
-
data = []
|
208
|
-
|
209
|
-
el.children.each do |container|
|
210
|
-
container.children.each do |row|
|
211
|
-
data_row = []
|
212
|
-
data << data_row
|
213
|
-
row.children.each do |cell|
|
214
|
-
opts[:result] = []
|
215
|
-
cell_data = inner(cell, opts)
|
216
|
-
data_row << cell_data[1][:result]
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
opts[:result] = result
|
222
|
-
opts[:table_data] = data
|
223
|
-
|
224
|
-
inner(el, opts)
|
225
|
-
end
|
226
|
-
|
227
|
-
def convert_thead(el, opts)
|
228
|
-
indent = ' ' * @current_indent
|
229
|
-
table_data = opts[:table_data]
|
230
|
-
|
231
|
-
opts[:result] << indent
|
232
|
-
opts[:result] << border(table_data, :top)
|
233
|
-
opts[:result] << "\n"
|
234
|
-
inner(el, opts)
|
235
|
-
end
|
236
|
-
|
237
|
-
# Render horizontal border line
|
238
|
-
#
|
239
|
-
# @param [Array[Array[String]]] table_data
|
240
|
-
# table rows and cells
|
241
|
-
# @param [Symbol] location
|
242
|
-
# location out of :top, :mid, :bottom
|
243
|
-
#
|
244
|
-
# @return [String]
|
245
|
-
#
|
246
|
-
# @api private
|
247
|
-
def border(table_data, location)
|
248
|
-
symbols = TTY::Markdown.symbols
|
249
|
-
result = []
|
250
|
-
result << symbols[:"#{location}_left"]
|
251
|
-
distribute_widths(max_widths(table_data)).each.with_index do |width, i|
|
252
|
-
result << symbols[:"#{location}_center"] if i != 0
|
253
|
-
result << (symbols[:line] * (width + 2))
|
254
|
-
end
|
255
|
-
result << symbols[:"#{location}_right"]
|
256
|
-
styles = Array(@theme[:table])
|
257
|
-
@pastel.decorate(result.join, *styles)
|
258
|
-
end
|
259
|
-
|
260
|
-
def convert_tbody(el, opts)
|
261
|
-
indent = ' ' * @current_indent
|
262
|
-
table_data = opts[:table_data]
|
263
|
-
|
264
|
-
opts[:result] << indent
|
265
|
-
if opts[:prev] && opts[:prev].type == :thead
|
266
|
-
opts[:result] << border(table_data, :mid)
|
267
|
-
else
|
268
|
-
opts[:result] << border(table_data, :top)
|
269
|
-
end
|
270
|
-
opts[:result] << "\n"
|
271
|
-
|
272
|
-
inner(el, opts)
|
273
|
-
|
274
|
-
opts[:result] << indent
|
275
|
-
opts[:result] << border(table_data, :bottom)
|
276
|
-
opts[:result] << "\n"
|
277
|
-
end
|
278
|
-
|
279
|
-
def convert_tfoot(el, opts)
|
280
|
-
inner(el, opts)
|
281
|
-
end
|
282
|
-
|
283
|
-
def convert_tr(el, opts)
|
284
|
-
indent = ' ' * @current_indent
|
285
|
-
table_data = opts[:table_data]
|
286
|
-
|
287
|
-
if opts[:prev] && opts[:prev].type == :tr
|
288
|
-
opts[:result] << indent
|
289
|
-
opts[:result] << border(table_data, :mid)
|
290
|
-
opts[:result] << "\n"
|
291
|
-
end
|
292
|
-
|
293
|
-
opts[:cells] = []
|
294
|
-
|
295
|
-
inner(el, opts)
|
296
|
-
|
297
|
-
columns = table_data.first.count
|
298
|
-
|
299
|
-
row = opts[:cells].each_with_index.reduce([]) do |acc, (cell, i)|
|
300
|
-
if cell.size > 1 # multiline
|
301
|
-
cell.each_with_index do |c, j| # zip columns
|
302
|
-
acc[j] = [] if acc[j].nil?
|
303
|
-
acc[j] << c.chomp
|
304
|
-
acc[j] << "\n" if i == (columns - 1)
|
305
|
-
end
|
306
|
-
else
|
307
|
-
acc << cell
|
308
|
-
acc << "\n" if i == (columns - 1)
|
309
|
-
end
|
310
|
-
acc
|
311
|
-
end.join
|
312
|
-
|
313
|
-
opts[:result] << row
|
314
|
-
end
|
315
|
-
|
316
|
-
def convert_td(el, opts)
|
317
|
-
indent = ' ' * @current_indent
|
318
|
-
pipe = TTY::Markdown.symbols[:pipe]
|
319
|
-
styles = Array(@theme[:table])
|
320
|
-
table_data = opts[:table_data]
|
321
|
-
result = opts[:cells]
|
322
|
-
suffix = " #{@pastel.decorate(pipe, *styles)} "
|
323
|
-
opts[:result] = []
|
324
|
-
|
325
|
-
inner(el, opts)
|
326
|
-
|
327
|
-
row, column = *find_row_column(table_data, opts[:result])
|
328
|
-
cell_widths = distribute_widths(max_widths(table_data))
|
329
|
-
cell_width = cell_widths[column]
|
330
|
-
cell_height = max_height(table_data, row, cell_widths)
|
331
|
-
alignment = opts[:alignment][column]
|
332
|
-
align_opts = alignment == :default ? {} : { direction: alignment }
|
333
|
-
|
334
|
-
wrapped = Strings.wrap(opts[:result].join, cell_width)
|
335
|
-
aligned = Strings.align(wrapped, cell_width, align_opts)
|
336
|
-
padded = if aligned.lines.size < cell_height
|
337
|
-
Strings.pad(aligned, [0, 0, cell_height - aligned.lines.size, 0])
|
338
|
-
else
|
339
|
-
aligned.dup
|
340
|
-
end
|
341
|
-
|
342
|
-
result << padded.lines.map do |line|
|
343
|
-
# add pipe to first column
|
344
|
-
(column.zero? ? indent + @pastel.decorate("#{pipe} ", *styles) : '') +
|
345
|
-
(line.end_with?("\n") ? line.insert(-2, suffix) : line << suffix)
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
# Find row and column indexes
|
350
|
-
#
|
351
|
-
# @return [Array[Integer, Integer]]
|
352
|
-
#
|
353
|
-
# @api private
|
354
|
-
def find_row_column(table_data, cell)
|
355
|
-
table_data.each_with_index do |row, row_no|
|
356
|
-
row.size.times do |col|
|
357
|
-
return [row_no, col] if row[col] == cell
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
# Calculate maximum cell width for a given column
|
363
|
-
#
|
364
|
-
# @return [Integer]
|
365
|
-
#
|
366
|
-
# @api private
|
367
|
-
def max_width(table_data, col)
|
368
|
-
table_data.map do |row|
|
369
|
-
Strings.sanitize(row[col].join).lines.map(&:length).max
|
370
|
-
end.max
|
371
|
-
end
|
372
|
-
|
373
|
-
# Calculate maximum cell height for a given row
|
374
|
-
#
|
375
|
-
# @return [Integer]
|
376
|
-
#
|
377
|
-
# @api private
|
378
|
-
def max_height(table_data, row, cell_widths)
|
379
|
-
table_data[row].map.with_index do |col, i|
|
380
|
-
Strings.wrap(col.join, cell_widths[i]).lines.size
|
381
|
-
end.max
|
382
|
-
end
|
383
|
-
|
384
|
-
def max_widths(table_data)
|
385
|
-
table_data.first.each_with_index.reduce([]) do |acc, (*, col)|
|
386
|
-
acc << max_width(table_data, col)
|
387
|
-
acc
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
def distribute_widths(widths)
|
392
|
-
indent = ' ' * @current_indent
|
393
|
-
total_width = widths.reduce(&:+)
|
394
|
-
screen_width = @width - (indent.length + 1) * 2 - (widths.size + 1)
|
395
|
-
return widths if total_width <= screen_width
|
396
|
-
|
397
|
-
extra_width = total_width - screen_width
|
398
|
-
|
399
|
-
widths.map do |w|
|
400
|
-
ratio = w / total_width.to_f
|
401
|
-
w - (extra_width * ratio).floor
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
def convert_hr(el, opts)
|
406
|
-
indent = ' ' * @current_indent
|
407
|
-
symbols = TTY::Markdown.symbols
|
408
|
-
width = @width - (indent.length + 1) * 2
|
409
|
-
styles = Array(@theme[:hr])
|
410
|
-
line = symbols[:diamond] + symbols[:line] * width + symbols[:diamond]
|
411
|
-
|
412
|
-
opts[:result] << indent
|
413
|
-
opts[:result] << @pastel.decorate(line, *styles)
|
414
|
-
opts[:result] << "\n"
|
415
|
-
end
|
416
|
-
|
417
|
-
def convert_a(el, opts)
|
418
|
-
symbols = TTY::Markdown.symbols
|
419
|
-
styles = Array(@theme[:link])
|
420
|
-
if el.children.size == 1 && el.children[0].type == :text
|
421
|
-
opts[:result] << @pastel.decorate(el.attr['href'], *styles)
|
422
|
-
else
|
423
|
-
if el.attr['title']
|
424
|
-
opts[:result] << el.attr['title']
|
425
|
-
else
|
426
|
-
inner(el, opts)
|
427
|
-
end
|
428
|
-
opts[:result] << " #{symbols[:arrow]} "
|
429
|
-
opts[:result] << @pastel.decorate(el.attr['href'], *styles)
|
430
|
-
opts[:result] << "\n"
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
def convert_math(el, opts)
|
435
|
-
if opts[:prev] && opts[:prev].type == :blank
|
436
|
-
indent = ' ' * @current_indent
|
437
|
-
opts[:result] << indent
|
438
|
-
end
|
439
|
-
convert_codespan(el, opts)
|
440
|
-
opts[:result] << "\n"
|
441
|
-
end
|
442
|
-
|
443
|
-
def convert_abbreviation(el, opts)
|
444
|
-
opts[:result] << el.value
|
445
|
-
end
|
446
|
-
|
447
|
-
def convert_typographic_sym(el, opts)
|
448
|
-
opts[:result] << TTY::Markdown.symbols[el.value]
|
449
|
-
end
|
450
|
-
|
451
|
-
def convert_entity(el, opts)
|
452
|
-
opts[:result] << unicode_char(el.value.code_point)
|
453
|
-
end
|
454
|
-
|
455
|
-
# Convert codepoint to UTF-8 representation
|
456
|
-
def unicode_char(codepoint)
|
457
|
-
[codepoint].pack('U*')
|
458
|
-
end
|
459
|
-
|
460
|
-
def convert_footnote(*)
|
461
|
-
warning("Footnotes are not supported")
|
462
|
-
end
|
463
|
-
|
464
|
-
def convert_raw(*)
|
465
|
-
warning("Raw content is not supported")
|
466
|
-
end
|
467
|
-
|
468
|
-
def convert_img(*)
|
469
|
-
warning("Images are not supported")
|
470
|
-
end
|
471
|
-
|
472
|
-
def convert_html_element(*)
|
473
|
-
warning("HTML elements are not supported")
|
474
|
-
end
|
475
|
-
|
476
|
-
def convert_xml_comment(el, opts)
|
477
|
-
opts[:result] << el.value << "\n"
|
478
|
-
end
|
479
|
-
alias convert_comment convert_xml_comment
|
480
|
-
end # Parser
|
481
|
-
end # Markdown
|
482
|
-
end # TTY
|
data/spec/spec_helper.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
if ENV['COVERAGE'] || ENV['TRAVIS']
|
4
|
-
require 'simplecov'
|
5
|
-
require 'coveralls'
|
6
|
-
|
7
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
-
SimpleCov::Formatter::HTMLFormatter,
|
9
|
-
Coveralls::SimpleCov::Formatter
|
10
|
-
]
|
11
|
-
|
12
|
-
SimpleCov.start do
|
13
|
-
command_name 'spec'
|
14
|
-
add_filter 'spec'
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
require "bundler/setup"
|
19
|
-
require "tty/markdown"
|
20
|
-
|
21
|
-
RSpec.configure do |config|
|
22
|
-
# Enable flags like --only-failures and --next-failure
|
23
|
-
config.example_status_persistence_file_path = ".rspec_status"
|
24
|
-
|
25
|
-
# Disable RSpec exposing methods globally on `Module` and `main`
|
26
|
-
config.disable_monkey_patching!
|
27
|
-
|
28
|
-
config.expect_with :rspec do |c|
|
29
|
-
c.syntax = :expect
|
30
|
-
end
|
31
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Markdown, 'abbrev' do
|
4
|
-
it "abbreviates markdown" do
|
5
|
-
markdown =<<-TEXT
|
6
|
-
*[HTML]: Hyper Text Markup Language
|
7
|
-
test HTML
|
8
|
-
TEXT
|
9
|
-
parsed = TTY::Markdown.parse(markdown)
|
10
|
-
expect(parsed).to eq([
|
11
|
-
"test HTML\n"
|
12
|
-
].join("\n"))
|
13
|
-
end
|
14
|
-
|
15
|
-
it "indents abbreviations correctly" do
|
16
|
-
markdown =<<-TEXT
|
17
|
-
### header
|
18
|
-
*[HTML]: Hyper Text Markup Language
|
19
|
-
test HTML
|
20
|
-
TEXT
|
21
|
-
parsed = TTY::Markdown.parse(markdown)
|
22
|
-
expect(parsed).to eq([
|
23
|
-
" \e[36;1mheader\e[0m",
|
24
|
-
" test HTML\n"
|
25
|
-
].join("\n"))
|
26
|
-
end
|
27
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe TTY::Markdown, 'blockquote' do
|
4
|
-
let(:bar) { TTY::Markdown.symbols[:bar] }
|
5
|
-
let(:apos) { TTY::Markdown.symbols[:rsquo] }
|
6
|
-
|
7
|
-
it "converts single blockquote" do
|
8
|
-
markdown =<<-TEXT
|
9
|
-
> Oh, you can *put* **Markdown** into a blockquote.
|
10
|
-
TEXT
|
11
|
-
parsed = TTY::Markdown.parse(markdown)
|
12
|
-
expect(parsed).to eq([
|
13
|
-
"\e[33m#{bar}\e[0m Oh, you can \e[33mput\e[0m \e[33;1mMarkdown\e[0m into a blockquote.\n"
|
14
|
-
].join)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "indents blockquote within header" do
|
18
|
-
markdown =<<-TEXT
|
19
|
-
### Quote
|
20
|
-
> Oh, you can *put* **Markdown** into a blockquote.
|
21
|
-
TEXT
|
22
|
-
parsed = TTY::Markdown.parse(markdown)
|
23
|
-
expect(parsed).to eq([
|
24
|
-
" \e[36;1mQuote\e[0m",
|
25
|
-
" \e[33m#{bar}\e[0m Oh, you can \e[33mput\e[0m \e[33;1mMarkdown\e[0m into a blockquote.\n"
|
26
|
-
].join("\n"))
|
27
|
-
end
|
28
|
-
|
29
|
-
it "converts multiple blockquotes without header" do
|
30
|
-
markdown =<<-TEXT
|
31
|
-
> one
|
32
|
-
> two
|
33
|
-
> three
|
34
|
-
TEXT
|
35
|
-
parsed = TTY::Markdown.parse(markdown)
|
36
|
-
expected_output =
|
37
|
-
"\e[33m#{bar}\e[0m one\n" +
|
38
|
-
"\e[33m#{bar}\e[0m two\n" +
|
39
|
-
"\e[33m#{bar}\e[0m three\n"
|
40
|
-
|
41
|
-
expect(parsed).to eq(expected_output)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "converts multiple blockquote" do
|
45
|
-
markdown =<<-TEXT
|
46
|
-
### Quote
|
47
|
-
> Blockquotes are very handy in email to emulate reply text.
|
48
|
-
> This line is part of the same quote.
|
49
|
-
> *Oh*, you can put **Markdown** into a blockquote.
|
50
|
-
TEXT
|
51
|
-
parsed = TTY::Markdown.parse(markdown)
|
52
|
-
expect(parsed).to eq([
|
53
|
-
" \e[36;1mQuote\e[0m\n",
|
54
|
-
" \e[33m#{bar}\e[0m Blockquotes are very handy in email to emulate reply text.\n",
|
55
|
-
" \e[33m#{bar}\e[0m This line is part of the same quote.\n",
|
56
|
-
" \e[33m#{bar}\e[0m \e[33mOh\e[0m, you can put \e[33;1mMarkdown\e[0m into a blockquote.\n"
|
57
|
-
].join)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "converts blockquote into lines" do
|
61
|
-
markdown =<<-TEXT
|
62
|
-
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
|
63
|
-
> Last line to ensure all is fine.
|
64
|
-
TEXT
|
65
|
-
|
66
|
-
parsed = TTY::Markdown.parse(markdown, width: 50)
|
67
|
-
expected_output =
|
68
|
-
"\e[33m#{bar}\e[0m This is a very long line that will still be \n" +
|
69
|
-
"\e[33m#{bar}\e[0m quoted properly when it wraps. Oh boy let\n" +
|
70
|
-
"\e[33m#{bar}\e[0m #{apos}s keep writing to make sure this is long enough \n" +
|
71
|
-
"\e[33m#{bar}\e[0m to actually wrap for everyone. Oh, you can \n" +
|
72
|
-
"\e[33m#{bar}\e[0m \e[33mput\e[0m \e[33;1mMarkdown\e[0m into a blockquote.\n" +
|
73
|
-
"\e[33m#{bar}\e[0m Last line to ensure all is fine.\n"
|
74
|
-
|
75
|
-
expect(parsed).to eq(expected_output)
|
76
|
-
end
|
77
|
-
end
|