tty-markdown 0.2.0 → 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 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