tty-markdown 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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