tty-markdown 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44486f20a5fb0b3db0de9d760bf88e33a713aa56
4
- data.tar.gz: 9c55823506373b0a6d95c38fa1fab6a4b38d2462
3
+ metadata.gz: 78fdb3db2ddf8fcb253d3f2b7fd48ce85f34db5b
4
+ data.tar.gz: b4176a98f2e6238a0b9d1d3eb32585610750a4b7
5
5
  SHA512:
6
- metadata.gz: 6a49d8dc74703dd156ea8646beb251c51dfc0efd0f27ccee0c29b04648c8d7aa671730d607154a3d1fb9ab0c3bf988662565a39f346fcdb95a2b3331a8c73eda
7
- data.tar.gz: cab7e9b37d1a281ad5c9419e26881fa56f1420070d141ace550468be373885d637177927d362c537cf91c31f760704f1370a544625e420d8b07f152d357dc3bf
6
+ metadata.gz: c4ddf759c61068a151a2e07ad973fa0268ea671be2006ce1ae0fbd5d4aca6f823d0d682bd005af1473d75dd88b61fa69ac9ab5cc23576773b79b546b5651c232
7
+ data.tar.gz: 1fa053194d9f4ad41366a4b592888184668f4ea4be628d9cf57e79a296f60b90baaacbac4a8b5df3ed94b29f3baae29268103e7dc3b8328b5a5ecc7cd8547ee4
@@ -10,6 +10,7 @@ rvm:
10
10
  - 2.2.8
11
11
  - 2.3.6
12
12
  - 2.4.3
13
+ - 2.5.0
13
14
  - ruby-head
14
15
  - jruby-9000
15
16
  - jruby-head
@@ -1,5 +1,18 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.3.0] - 2018-03-17
4
+
5
+ ### Added
6
+ * Add :width option to allow setting maximum display width
7
+ * Add :colors options for specifying rendering colors capabilities
8
+ * Add ability to parse multiline table content
9
+
10
+ ### Changed
11
+ * Change color scheme to replace table and links blue with yellow
12
+
13
+ ## Fixed
14
+ * Fix issue with multiline blockquote elements raising NoMethodError
15
+
3
16
  ## [v0.2.0] - 2018-01-29
4
17
 
5
18
  ### Added
@@ -19,5 +32,6 @@
19
32
 
20
33
  * Initial implementation and release
21
34
 
35
+ [v0.3.0]: https://github.com/piotrmurach/tty-markdown/compare/v0.2.0...v0.3.0
22
36
  [v0.2.0]: https://github.com/piotrmurach/tty-markdown/compare/v0.1.0...v0.2.0
23
37
  [v0.1.0]: https://github.com/piotrmurach/tty-markdown/compare/v0.1.0
data/README.md CHANGED
@@ -39,13 +39,17 @@ Or install it yourself as:
39
39
  ## Contents
40
40
 
41
41
  * [1. Usage](#1-usage)
42
- * [1.1 List](#11-list)
43
- * [1.2 Header](#12-header)
44
- * [1.3 Quote](#13-quote)
45
- * [1.4 Codeblock](#14-codeblock)
46
- * [1.5 Table](#15-table)
42
+ * [1.1 Header](#11-header)
43
+ * [1.2 List](#12-list)
44
+ * [1.3 Link](#13-link)
45
+ * [1.4 Blockquote](#14-blockquote)
46
+ * [1.5 Code and Syntax Highlighting](#15-code-and-syntax-highlighting)
47
+ * [1.6 Table](#16-table)
48
+ * [1.7 Horizontal Rule](#17-horizontal-rule)
47
49
  * [2. Options](#2-options)
48
- * [2.1 :theme](#21-theme)
50
+ * [2.1 :colors](#21-colors)
51
+ * [2.2 :theme](#22-theme)
52
+ * [2.3 :width](#23-width)
49
53
 
50
54
  ## 1. Usage
51
55
 
@@ -64,7 +68,30 @@ parsed = TTY::Markdown.parse('example.md')
64
68
  puts parsed
65
69
  ```
66
70
 
67
- ### 1.1 List
71
+ ### 1.1 Header
72
+
73
+ ```markdown
74
+ TTY::Markdown
75
+ =============
76
+
77
+ **tty-markdown** converts markdown document into a terminal friendly output.
78
+
79
+ ## Examples
80
+
81
+ ### Nested list items
82
+ ```
83
+
84
+ and transforming it:
85
+
86
+ ```ruby
87
+ parsed = TTY::Markdown.parse(markdown_string)
88
+ ```
89
+
90
+ `puts parsed` will output:
91
+
92
+ ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/headers.png)
93
+
94
+ ### 1.2 List
68
95
 
69
96
  Both numbered and unordered lists are supported. Given a markdown:
70
97
 
@@ -86,30 +113,19 @@ parsed = TTY::Markdown.parse(markdown_string)
86
113
 
87
114
  ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/list.png)
88
115
 
89
- ### 1.2 Header
90
-
91
- ```markdown
92
- TTY::Markdown
93
- =============
94
-
95
- **tty-markdown** converts markdown document into a terminal friendly output.
96
-
97
- ## Examples
98
-
99
- ### Nested list items
100
- ```
116
+ ### 1.3 Link
101
117
 
102
- and transforming it:
118
+ A mardown link:
103
119
 
104
- ```ruby
105
- parsed = TTY::Markdown.parse(markdown_string)
120
+ ```markdown
121
+ [I'm an inline-style link](https://www.google.com)
106
122
  ```
107
123
 
108
- `puts parsed` will output:
124
+ will be rendered with actual link content next to it:
109
125
 
110
- ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/headers.png)
126
+ ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/link.png)
111
127
 
112
- ### 1.3 Quote
128
+ ### 1.4 Blockquote
113
129
 
114
130
  Given a markdown quote:
115
131
 
@@ -129,7 +145,7 @@ parsed = TTY::Markdown.parse(markdown_string)
129
145
 
130
146
  ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/quote.png)
131
147
 
132
- ### 1.4 Codeblock
148
+ ### 1.5 Code and Syntax Highlighting
133
149
 
134
150
  The parser can highlight syntax of many programming languages. Given the markdown codeblock with language specification:
135
151
 
@@ -153,7 +169,7 @@ parsed = TTY::Markdown.parse(code_snippet)
153
169
 
154
170
  ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/syntax_highlight.png)
155
171
 
156
- ### 1.5 Table
172
+ ### 1.6 Table
157
173
 
158
174
  You can transform tables which understand the markdown alignment.
159
175
 
@@ -177,21 +193,49 @@ parsed = TTY::Markdown.parse(markdown_string)
177
193
 
178
194
  ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/table.png)
179
195
 
196
+ ### 1.7 Horizontal Rule
197
+
198
+ You can specify a horizontal rule in markdown:
199
+
200
+ ```markdown
201
+ ***
202
+ ```
203
+
204
+ and then transform it:
205
+
206
+ ```ruby
207
+ parsed = TTY::Markdown.parse(markdown_string)
208
+ ```
209
+
210
+ `puts parsed` will output:
211
+
212
+ ![Code highlight](https://cdn.rawgit.com/piotrmurach/tty-markdown/master/assets/hr.png)
213
+
180
214
  ## 2. Options
181
215
 
182
- ### 2.1 `:theme`
216
+ ### 2.1 `:colors`
217
+
218
+ By default the `256` colors scheme is used to render code block elements. You can change that by specifying desired number of colors:
219
+
220
+ ```ruby
221
+ TTY::Markdown.pasre(markdown_string, colors: 16)
222
+ ```
223
+
224
+ This feauture may be handy when working in terminals with limited color support. By default, **TTY::Markdown** detects your terminal color mode and adjusts output automatically.
225
+
226
+ ### 2.2 `:theme`
183
227
 
184
228
  A hash of styles that allows to customize specific elements of the markdown text. By default the following styles are used:
185
229
 
186
230
  ```ruby
187
231
  THEME = {
188
- em: :italic,
232
+ em: :yellow,
189
233
  header: [:cyan, :bold],
190
234
  hr: :yellow,
191
- link: [:blue, :underline],
235
+ link: [:yellow, :underline],
192
236
  list: :yellow,
193
237
  strong: [:yellow, :bold],
194
- table: :blue,
238
+ table: :yellow,
195
239
  quote: :yellow,
196
240
  }
197
241
  ```
@@ -202,6 +246,16 @@ In order to provide new styles use `:theme` key:
202
246
  TTY::Markdown.parse(markdown_string, theme: { ... })
203
247
  ```
204
248
 
249
+ ### 2.3 `:width`
250
+
251
+ You can easily control the maximum width of the output by using the `:width` key:
252
+
253
+ ```ruby
254
+ TTY::Markdown.parse(markdown_string, width: 80)
255
+ ```
256
+
257
+ By default the terminal screen width is used.
258
+
205
259
  ## Development
206
260
 
207
261
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -19,3 +19,5 @@ environment:
19
19
  - ruby_version: "23-x64"
20
20
  - ruby_version: "24"
21
21
  - ruby_version: "24-x64"
22
+ - ruby_version: "25"
23
+ - ruby_version: "25-x64"
Binary file
Binary file
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  require_relative '../lib/tty-markdown'
2
2
 
3
3
  path = File.join(__dir__, 'example.md')
4
- out = TTY::Markdown.parse_file(path, colors: 256)
4
+ out = TTY::Markdown.parse_file(path, colors: 256, width: 80)
5
5
 
6
6
  puts out
@@ -66,13 +66,13 @@ module TTY
66
66
  }.freeze
67
67
 
68
68
  THEME = {
69
- em: :italic,
69
+ em: :yellow,
70
70
  header: [:cyan, :bold],
71
71
  hr: :yellow,
72
- link: [:blue, :underline],
72
+ link: [:yellow, :underline],
73
73
  list: :yellow,
74
74
  strong: [:yellow, :bold],
75
- table: :blue,
75
+ table: :yellow,
76
76
  quote: :yellow,
77
77
  }.freeze
78
78
 
@@ -18,7 +18,7 @@ module TTY
18
18
  @current_indent = 0
19
19
  @indent = options.fetch(:indent, 2)
20
20
  @pastel = Pastel.new
21
- @color_opts = {mode: options[:colors]}
21
+ @color_opts = { mode: options[:colors] }
22
22
  @width = options.fetch(:width) { TTY::Screen.width }
23
23
  @theme = options.fetch(:theme) { TTY::Markdown::THEME }
24
24
  end
@@ -26,7 +26,7 @@ module TTY
26
26
  # Invoke an element conversion
27
27
  #
28
28
  # @api public
29
- def convert(el, opts = {indent: 0, result: []})
29
+ def convert(el, opts = { indent: 0, result: [] })
30
30
  send("convert_#{el.type}", el, opts)
31
31
  end
32
32
 
@@ -64,6 +64,7 @@ module TTY
64
64
  def convert_p(el, opts)
65
65
  result_before = @stack.last[1][:result].dup
66
66
  indent = ' ' * @current_indent
67
+
67
68
  if opts[:parent].type != :blockquote
68
69
  opts[:result] << indent
69
70
  end
@@ -88,12 +89,21 @@ module TTY
88
89
  end
89
90
  end
90
91
 
92
+ # Format current element by inserting prefix for each
93
+ # quoted line within the allowed screen size.
94
+ #
95
+ # @param [Array[String]] result_before
96
+ # @param [Array[String]] result
97
+ #
98
+ # @return [nil]
99
+ #
100
+ # @api private
91
101
  def format_blockquote(result_before, result)
92
102
  indent = ' ' * @current_indent
93
103
  start_index = result_before.size
94
104
  max_index = result.size - 1
95
105
  bar_symbol = TTY::Markdown.symbols[:bar]
96
- styles = Array(@theme[:quote])
106
+ styles = Array(@theme[:quote])
97
107
  prefix = "#{indent}#{@pastel.decorate(bar_symbol, *styles)} "
98
108
 
99
109
  result.map!.with_index do |str, i|
@@ -101,14 +111,15 @@ module TTY
101
111
  str.insert(0, prefix)
102
112
  end
103
113
 
104
- if i >= start_index && str.include?("\n")
114
+ # only modify blockquote element
115
+ if i >= start_index && str.to_s.include?("\n") # multiline string found
105
116
  str.lines.map! do |line|
106
- if line != str.lines.last || i < max_index
107
- line.insert(-1, prefix)
117
+ if (line != str.lines.last || i < max_index)
118
+ line.insert(-1, line.end_with?("\n") ? prefix : "\n" + prefix)
108
119
  else
109
120
  line
110
121
  end
111
- end
122
+ end.join
112
123
  else
113
124
  str
114
125
  end
@@ -117,19 +128,19 @@ module TTY
117
128
 
118
129
  def convert_text(el, opts)
119
130
  text = el.value
120
- opts[:result] << text
131
+ opts[:result] << Strings.wrap(text, @width)
121
132
  end
122
133
 
123
134
  def convert_strong(el, opts)
124
135
  styles = Array(@theme[:strong])
125
- opts[:result] << @pastel.lookup(*styles)
136
+ opts[:result] << @pastel.lookup(*styles)
126
137
  inner(el, opts)
127
138
  opts[:result] << @pastel.lookup(:reset)
128
139
  end
129
140
 
130
141
  def convert_em(el, opts)
131
142
  styles = Array(@theme[:em])
132
- opts[:result] << @pastel.lookup(*styles)
143
+ opts[:result] << @pastel.lookup(*styles)
133
144
  inner(el, opts)
134
145
  opts[:result] << @pastel.lookup(:reset)
135
146
  end
@@ -143,10 +154,10 @@ module TTY
143
154
  end
144
155
 
145
156
  def convert_codespan(el, opts)
146
- raw_code = el.value
157
+ raw_code = Strings.wrap(el.value, @width)
147
158
  highlighted = SyntaxHighliter.highlight(raw_code, @color_opts.merge(opts))
148
159
  code = highlighted.split("\n").map.with_index do |line, i|
149
- if i == 0 # first line
160
+ if i.zero? # first line
150
161
  line
151
162
  else
152
163
  line.insert(0, ' ' * @current_indent)
@@ -227,7 +238,7 @@ module TTY
227
238
  symbols = TTY::Markdown.symbols
228
239
  result = []
229
240
  result << symbols[:"#{location}_left"]
230
- max_widths(table_data).each.with_index do |width, i|
241
+ distribute_widths(max_widths(table_data)).each.with_index do |width, i|
231
242
  result << symbols[:"#{location}_center"] if i != 0
232
243
  result << (symbols[:line] * (width + 2))
233
244
  end
@@ -256,13 +267,11 @@ module TTY
256
267
  end
257
268
 
258
269
  def convert_tfoot(el, opts)
259
- innert(el, opts)
270
+ inner(el, opts)
260
271
  end
261
272
 
262
273
  def convert_tr(el, opts)
263
274
  indent = ' ' * @current_indent
264
- pipe = TTY::Markdown.symbols[:pipe]
265
- styles = Array(@theme[:table])
266
275
  table_data = opts[:table_data]
267
276
 
268
277
  if opts[:prev] && opts[:prev].type == :tr
@@ -271,39 +280,95 @@ module TTY
271
280
  opts[:result] << "\n"
272
281
  end
273
282
 
274
- opts[:result] << indent + @pastel.decorate("#{pipe} ", *styles)
283
+ opts[:cells] = []
284
+
275
285
  inner(el, opts)
276
- opts[:result] << "\n"
286
+
287
+ columns = table_data.first.count
288
+
289
+ row = opts[:cells].each_with_index.reduce([]) do |acc, (cell, i)|
290
+ if cell.size > 1 # multiline
291
+ cell.each_with_index do |c, j| # zip columns
292
+ acc[j] = [] if acc[j].nil?
293
+ acc[j] << c.chomp
294
+ acc[j] << "\n" if i == (columns - 1)
295
+ end
296
+ else
297
+ acc << cell
298
+ acc << "\n" if i == (columns - 1)
299
+ end
300
+ acc
301
+ end.join
302
+
303
+ opts[:result] << row
277
304
  end
278
305
 
279
306
  def convert_td(el, opts)
280
- pipe = TTY::Markdown.symbols[:pipe]
281
- styles = Array(@theme[:table])
307
+ indent = ' ' * @current_indent
308
+ pipe = TTY::Markdown.symbols[:pipe]
309
+ styles = Array(@theme[:table])
282
310
  table_data = opts[:table_data]
283
- result = opts[:result]
311
+ result = opts[:cells]
312
+ suffix = " #{@pastel.decorate(pipe, *styles)} "
284
313
  opts[:result] = []
285
314
 
286
315
  inner(el, opts)
287
316
 
288
- column = find_column(table_data, opts[:result])
289
- width = max_width(table_data, column)
290
- alignment = opts[:alignment][column]
291
- align_opts = alignment == :default ? {} : {direction: alignment}
292
-
293
- result << Strings.align(opts[:result].join, width, align_opts) <<
294
- " #{@pastel.decorate(pipe, *styles)} "
317
+ row, column = *find_row_column(table_data, opts[:result])
318
+ cell_widths = distribute_widths(max_widths(table_data))
319
+ cell_width = cell_widths[column]
320
+ cell_height = max_height(table_data, row, cell_widths)
321
+ alignment = opts[:alignment][column]
322
+ align_opts = alignment == :default ? {} : { direction: alignment }
323
+
324
+ wrapped = Strings.wrap(opts[:result].join, cell_width)
325
+ aligned = Strings.align(wrapped, cell_width, align_opts)
326
+ padded = if aligned.lines.size < cell_height
327
+ Strings.pad(aligned, [0, 0, cell_height - aligned.lines.size, 0])
328
+ else
329
+ aligned.dup
330
+ end
331
+
332
+ result << padded.lines.map do |line|
333
+ # add pipe to first column
334
+ (column.zero? ? indent + @pastel.decorate("#{pipe} ", *styles) : '') +
335
+ (line.end_with?("\n") ? line.insert(-2, suffix) : line << suffix)
336
+ end
295
337
  end
296
338
 
297
- def find_column(table_data, cell)
298
- table_data.each do |row|
339
+ # Find row and column indexes
340
+ #
341
+ # @return [Array[Integer, Integer]]
342
+ #
343
+ # @api private
344
+ def find_row_column(table_data, cell)
345
+ table_data.each_with_index do |row, row_no|
299
346
  row.size.times do |col|
300
- return col if row[col] == cell
347
+ return [row_no, col] if row[col] == cell
301
348
  end
302
349
  end
303
350
  end
304
351
 
352
+ # Calculate maximum cell width for a given column
353
+ #
354
+ # @return [Integer]
355
+ #
356
+ # @api private
305
357
  def max_width(table_data, col)
306
- table_data.map { |row| Strings.sanitize(row[col].join).length }.max
358
+ table_data.map do |row|
359
+ Strings.sanitize(row[col].join).lines.map(&:length).max
360
+ end.max
361
+ end
362
+
363
+ # Calculate maximum cell height for a given row
364
+ #
365
+ # @return [Integer]
366
+ #
367
+ # @api private
368
+ def max_height(table_data, row, cell_widths)
369
+ table_data[row].map.with_index do |col, i|
370
+ Strings.wrap(col.join, cell_widths[i]).lines.size
371
+ end.max
307
372
  end
308
373
 
309
374
  def max_widths(table_data)
@@ -313,10 +378,24 @@ module TTY
313
378
  end
314
379
  end
315
380
 
381
+ def distribute_widths(widths)
382
+ indent = ' ' * @current_indent
383
+ total_width = widths.reduce(&:+)
384
+ screen_width = @width - (indent.length + 1) * 2 - (widths.size + 1)
385
+ return widths if total_width <= screen_width
386
+
387
+ extra_width = total_width - screen_width
388
+
389
+ widths.map do |w|
390
+ ratio = w / total_width.to_f
391
+ w - (extra_width * ratio).floor
392
+ end
393
+ end
394
+
316
395
  def convert_hr(el, opts)
317
396
  indent = ' ' * @current_indent
318
397
  symbols = TTY::Markdown.symbols
319
- width = @width - (indent.length+1) * 2
398
+ width = @width - (indent.length + 1) * 2
320
399
  styles = Array(@theme[:hr])
321
400
  line = symbols[:diamond] + symbols[:line] * width + symbols[:diamond]
322
401
 
@@ -1,5 +1,5 @@
1
1
  module TTY
2
2
  module Markdown
3
- VERSION = '0.2.0'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end # TTY
5
5
  end # Markdown
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-markdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-29 00:00:00.000000000 Z
11
+ date: 2018-03-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kramdown
@@ -153,6 +153,8 @@ files:
153
153
  - Rakefile
154
154
  - appveyor.yml
155
155
  - assets/headers.png
156
+ - assets/hr.png
157
+ - assets/link.png
156
158
  - assets/list.png
157
159
  - assets/quote.png
158
160
  - assets/syntax_highlight.png