tty-markdown 0.6.0 → 0.7.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.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kramdown/parser/kramdown"
4
+
5
+ module Kramdown
6
+ module Parser
7
+ class KramdownExt < Kramdown::Parser::Kramdown
8
+ def initialize(source, options)
9
+ super
10
+
11
+ { codeblock_fenced: :codeblock_fenced_ext }.each do |current, replacement|
12
+ @block_parsers[@block_parsers.index(current)] = replacement
13
+ end
14
+ end
15
+
16
+ FENCED_CODEBLOCK_START = /^[ ]{0,3}[~`]{3,}/.freeze
17
+ FENCED_CODEBLOCK_MATCH = /^[ ]{0,3}(([~`]){3,})\s*?((\S+?)(?:\?\S*)?)?\s*?\n(.*?)^[ ]{0,3}\1\2*\s*?\n/m.freeze
18
+
19
+ define_parser(:codeblock_fenced_ext, FENCED_CODEBLOCK_START, nil,
20
+ "parse_codeblock_fenced")
21
+ end # KramdownExt
22
+ end # Parser
23
+ end # TTY
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pastel'
4
- require 'rouge'
5
- require 'tty-color'
3
+ require "pastel"
4
+ require "rouge"
6
5
 
7
6
  module TTY
8
7
  module Markdown
@@ -32,33 +31,38 @@ module TTY
32
31
  #
33
32
  # @api private
34
33
  def guess_lang(code)
35
- lang = nil
36
34
  start_line = code.lines[0]
37
35
  if available_lexers.include?(start_line.strip.downcase)
38
- lang = start_line.strip.downcase
36
+ start_line.strip.downcase
39
37
  end
40
38
  end
41
39
  module_function :guess_lang
42
40
 
43
41
  # Highlight code snippet
44
42
  #
43
+ # @param [String] code
44
+ # @param [Integer] mode
45
+ # the color mode supported by the terminal
46
+ # @param [String] lang
47
+ # the code snippet language
48
+ # @param [Boolean] enabled
49
+ # whether or not coloring is enabled
50
+ # @param [Proc] color
51
+ # the fallback coloring
52
+ #
45
53
  # @api public
46
- def highlight(code, **options)
47
- lang = guess_lang(code)
48
- mode = options[:mode] || TTY::Color.mode
49
- lines = code.dup.lines
50
- if options[:fenced].nil?
51
- code = lines[1...-1].join + lines[-1].strip
52
- end
53
-
54
+ def highlight(code, mode: 256, lang: nil, enabled: nil,
55
+ color: ->(line) { line })
56
+ lang = guess_lang(code) if lang.nil?
54
57
  lexer = Rouge::Lexer.find_fancy(lang, code) || Rouge::Lexers::PlainText
55
58
 
56
- if mode >= 256
59
+ if enabled == false
60
+ code
61
+ elsif 256 <= mode
57
62
  formatter = Rouge::Formatters::Terminal256.new
58
63
  formatter.format(lexer.lex(code))
59
64
  else
60
- pastel = Pastel.new
61
- code.split("\n").map { |line| pastel.yellow(line) }.join("\n")
65
+ code.lines.map { |line| color.(line.chomp) }.join("\n")
62
66
  end
63
67
  end
64
68
  module_function :highlight
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  module Markdown
5
- VERSION = '0.6.0'
5
+ VERSION = "0.7.0"
6
6
  end # TTY
7
7
  end # Markdown
metadata CHANGED
@@ -1,113 +1,105 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-markdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-30 00:00:00.000000000 Z
11
+ date: 2020-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kramdown
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.16.2
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.16.2
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: pastel
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: 0.7.2
39
+ version: '0.8'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: 0.7.2
46
+ version: '0.8'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rouge
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '3.3'
53
+ version: '3.14'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '3.3'
60
+ version: '3.14'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: strings
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: 0.1.4
67
+ version: 0.2.0
62
68
  type: :runtime
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: 0.1.4
74
+ version: 0.2.0
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: tty-color
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
79
  - - "~>"
74
80
  - !ruby/object:Gem::Version
75
- version: '0.4'
81
+ version: '0.5'
76
82
  type: :runtime
77
83
  prerelease: false
78
84
  version_requirements: !ruby/object:Gem::Requirement
79
85
  requirements:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
- version: '0.4'
88
+ version: '0.5'
83
89
  - !ruby/object:Gem::Dependency
84
90
  name: tty-screen
85
91
  requirement: !ruby/object:Gem::Requirement
86
92
  requirements:
87
93
  - - "~>"
88
94
  - !ruby/object:Gem::Version
89
- version: '0.6'
95
+ version: '0.8'
90
96
  type: :runtime
91
97
  prerelease: false
92
98
  version_requirements: !ruby/object:Gem::Requirement
93
99
  requirements:
94
100
  - - "~>"
95
101
  - !ruby/object:Gem::Version
96
- version: '0.6'
97
- - !ruby/object:Gem::Dependency
98
- name: bundler
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '1.16'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '1.16'
102
+ version: '0.8'
111
103
  - !ruby/object:Gem::Dependency
112
104
  name: rake
113
105
  requirement: !ruby/object:Gem::Requirement
@@ -137,53 +129,34 @@ dependencies:
137
129
  - !ruby/object:Gem::Version
138
130
  version: '3.0'
139
131
  description: Convert a markdown text or document into a terminal friendly output.
140
- email: []
132
+ email:
133
+ - piotr@piotrmurach.com
141
134
  executables: []
142
135
  extensions: []
143
- extra_rdoc_files: []
136
+ extra_rdoc_files:
137
+ - README.md
138
+ - CHANGELOG.md
139
+ - LICENSE.txt
144
140
  files:
145
141
  - CHANGELOG.md
146
142
  - LICENSE.txt
147
143
  - README.md
148
- - Rakefile
149
- - assets/headers.png
150
- - assets/hr.png
151
- - assets/link.png
152
- - assets/list.png
153
- - assets/quote.png
154
- - assets/syntax_highlight.png
155
- - assets/table.png
156
- - bin/console
157
- - bin/setup
158
- - examples/man.rb
159
- - examples/marked.rb
160
144
  - lib/tty-markdown.rb
161
145
  - lib/tty/markdown.rb
162
- - lib/tty/markdown/parser.rb
146
+ - lib/tty/markdown/converter.rb
147
+ - lib/tty/markdown/kramdown_ext.rb
163
148
  - lib/tty/markdown/syntax_highlighter.rb
164
149
  - lib/tty/markdown/version.rb
165
- - spec/spec_helper.rb
166
- - spec/unit/parse/abbrev_spec.rb
167
- - spec/unit/parse/blockquote_spec.rb
168
- - spec/unit/parse/codeblock_spec.rb
169
- - spec/unit/parse/comment_spec.rb
170
- - spec/unit/parse/emphasis_spec.rb
171
- - spec/unit/parse/entity_spec.rb
172
- - spec/unit/parse/header_spec.rb
173
- - spec/unit/parse/hr_spec.rb
174
- - spec/unit/parse/link_spec.rb
175
- - spec/unit/parse/list_spec.rb
176
- - spec/unit/parse/math_spec.rb
177
- - spec/unit/parse/paragraph_spec.rb
178
- - spec/unit/parse/table_spec.rb
179
- - spec/unit/parse/typography_spec.rb
180
- - tasks/console.rake
181
- - tasks/coverage.rake
182
- - tasks/spec.rake
183
- homepage: https://piotrmurach.github.io/tty
150
+ homepage: https://ttytoolkit.org
184
151
  licenses:
185
152
  - MIT
186
- metadata: {}
153
+ metadata:
154
+ allowed_push_host: https://rubygems.org
155
+ bug_tracker_uri: https://github.com/piotrmurach/tty-markdown/issues
156
+ changelog_uri: https://github.com/piotrmurach/tty-markdown/blob/master/CHANGELOG.md
157
+ documentation_uri: https://www.rubydoc.info/gems/tty-markdown
158
+ homepage_uri: https://ttytoolkit.org
159
+ source_code_uri: https://github.com/piotrmurach/tty-markdown
187
160
  post_install_message:
188
161
  rdoc_options: []
189
162
  require_paths:
@@ -199,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
199
172
  - !ruby/object:Gem::Version
200
173
  version: '0'
201
174
  requirements: []
202
- rubygems_version: 3.0.3
175
+ rubygems_version: 3.1.2
203
176
  signing_key:
204
177
  specification_version: 4
205
178
  summary: Convert a markdown text or document into a terminal friendly output.
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require "bundler/gem_tasks"
2
-
3
- FileList['tasks/**/*.rake'].each(&method(:import))
4
-
5
- desc 'Run all specs'
6
- task ci: %w[ spec ]
7
-
8
- task default: :spec
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "tty/markdown"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
@@ -1,6 +0,0 @@
1
- require_relative '../lib/tty-markdown'
2
-
3
- path = File.join(__dir__, 'man.md')
4
- out = TTY::Markdown.parse_file(path, colors: 256)
5
-
6
- puts out
@@ -1,6 +0,0 @@
1
- require_relative '../lib/tty-markdown'
2
-
3
- path = File.join(__dir__, 'example.md')
4
- out = TTY::Markdown.parse_file(path, colors: 256, width: 80)
5
-
6
- puts out
@@ -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