tty-markdown 0.6.0 → 0.7.1

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.1"
6
6
  end # TTY
7
7
  end # Markdown
data/lib/tty/markdown.rb CHANGED
@@ -1,114 +1,236 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kramdown'
3
+ require "kramdown/document"
4
+ require "tty-color"
5
+ require "tty-screen"
4
6
 
5
- require_relative 'markdown/parser'
6
- require_relative 'markdown/version'
7
+ require_relative "markdown/converter"
8
+ require_relative "markdown/version"
9
+ require_relative "markdown/kramdown_ext"
7
10
 
8
11
  module TTY
12
+ # Responsible for converting Markdown to the terminal output
13
+ #
14
+ # @api public
9
15
  module Markdown
10
16
  SYMBOLS = {
11
- arrow: '»',
12
- bullet: '',
13
- bar: '',
14
- diamond: '',
15
- pipe: '',
16
- line: '',
17
- hellip: '',
18
- laquo: '«',
19
- laquo_space: '« ',
20
- raquo: '»',
21
- raquo_space: ' »',
22
- ndash: '-',
17
+ arrow: "»",
18
+ bullet: "",
19
+ bar: "",
20
+ diamond: "",
21
+ pipe: "",
22
+ line: "",
23
+ hellip: "",
24
+ laquo: "«",
25
+ laquo_space: "« ",
26
+ raquo: "»",
27
+ raquo_space: " »",
28
+ ndash: "-",
23
29
  mdash: "\u2014",
24
- lsquo: '',
25
- rsquo: '',
26
- ldquo: '',
27
- rdquo: '',
28
- top_left: '',
29
- top_right: '',
30
- top_center: '',
31
- mid_left: '',
32
- mid_right: '',
33
- mid_center: '',
34
- bottom_right: '',
35
- bottom_left: '',
36
- bottom_center: '',
30
+ lsquo: "",
31
+ rsquo: "",
32
+ ldquo: "",
33
+ rdquo: "",
34
+ top_left: "",
35
+ top_right: "",
36
+ top_center: "",
37
+ mid_left: "",
38
+ mid_right: "",
39
+ mid_center: "",
40
+ bottom_right: "",
41
+ bottom_left: "",
42
+ bottom_center: "",
43
+ paren_left: "(",
44
+ paren_right: ")",
45
+ bracket_left: "[",
46
+ bracket_right: "]",
47
+ hash: "#",
48
+ delete: "\u0336"
37
49
  }.freeze
38
50
 
39
- WIN_SYMBOLS = {
40
- arrow: '->',
41
- bullet: '*',
42
- diamond: '*',
43
- bar: '',
44
- pipe: '|',
45
- line: '─',
46
- hellip: '...',
47
- laquo: '<<',
48
- laquo_space: '<< ',
49
- raquo: '>>',
50
- raquo_space: ' >>',
51
- ndash: '-',
51
+ ASCII_SYMBOLS = {
52
+ arrow: "->",
53
+ bullet: "*",
54
+ diamond: "*",
55
+ bar: "",
56
+ pipe: "|",
57
+ line: "-",
58
+ hellip: "...",
59
+ laquo: "<<",
60
+ laquo_space: "<< ",
61
+ raquo: ">>",
62
+ raquo_space: " >>",
63
+ ndash: "-",
52
64
  mdash: "--",
53
- lsquo: ''',
54
- rsquo: ''',
55
- ldquo: '"',
56
- rdquo: '"',
57
- top_left: '+',
58
- top_right: '+',
59
- top_center: '+',
60
- mid_left: '+',
61
- mid_right: '+',
62
- mid_center: '+',
63
- bottom_right: '+',
64
- bottom_left: '+',
65
- bottom_center: '+'
65
+ lsquo: "\"",
66
+ rsquo: "\"",
67
+ ldquo: "\"",
68
+ rdquo: "\"",
69
+ top_left: "+",
70
+ top_right: "+",
71
+ top_center: "+",
72
+ mid_left: "+",
73
+ mid_right: "+",
74
+ mid_center: "+",
75
+ bottom_right: "+",
76
+ bottom_left: "+",
77
+ bottom_center: "+",
78
+ paren_left: "(",
79
+ paren_right: ")",
80
+ bracket_left: "[",
81
+ bracket_right: "]",
82
+ hash: "#",
83
+ delete: "\u0336"
66
84
  }.freeze
67
85
 
68
86
  THEME = {
69
87
  em: :yellow,
70
- header: [:cyan, :bold],
88
+ header: %i[cyan bold],
71
89
  hr: :yellow,
72
- link: [:yellow, :underline],
90
+ link: %i[yellow underline],
73
91
  list: :yellow,
74
- strong: [:yellow, :bold],
92
+ strong: %i[yellow bold],
75
93
  table: :yellow,
76
94
  quote: :yellow,
95
+ image: :bright_black,
96
+ note: :yellow,
97
+ comment: :bright_black
77
98
  }.freeze
78
99
 
79
100
  # Parse a markdown string
80
101
  #
81
- # @param [Hash] options
82
- # @option options [String] :colors
83
- # a number of colors supported
84
- # @option options [String] :width
102
+ # @example
103
+ # TTY::Markdown.parse("# Header")
85
104
  #
86
105
  # @param [String] source
87
106
  # the source with markdown
107
+ # @param [String, Symbol] color
108
+ # the output coloring support out of always, auto or never
109
+ # @param [Integer] indent
110
+ # the converted output indent
111
+ # @param [Integer] mode
112
+ # the number of supported colors
113
+ # @param [Hash, String, Symbol, nil] symbols
114
+ # the converted output symbols
115
+ # @param [Hash{Symbol => Array, String, Symbol}, nil] theme
116
+ # the converted output color theme
117
+ # @param [Integer] width
118
+ # the width at which to wrap content
119
+ # @param [Hash] doc_opts
120
+ # the markdown document parser options
121
+ #
122
+ # @return [String]
123
+ # the converted terminal output
88
124
  #
89
125
  # @api public
90
- def parse(source, **options)
91
- doc = Kramdown::Document.new(source, options)
92
- Parser.convert(doc.root, doc.options).join
126
+ def parse(source,
127
+ color: :auto,
128
+ indent: 2,
129
+ mode: TTY::Color.mode,
130
+ symbols: {},
131
+ theme: {},
132
+ width: TTY::Screen.width,
133
+ **doc_opts)
134
+ converter_options = {
135
+ enabled: color_enabled(color),
136
+ indent: indent,
137
+ input: "KramdownExt",
138
+ mode: mode,
139
+ symbols: build_symbols(symbols),
140
+ theme: build_theme(theme),
141
+ width: width
142
+ }
143
+ doc = Kramdown::Document.new(source, converter_options.merge(doc_opts))
144
+ Converter.convert(doc.root, doc.options).join
93
145
  end
94
146
  module_function :parse
95
147
 
96
- # Pase a markdown document
148
+ # Parse a markdown document
149
+ #
150
+ # @example
151
+ # TTY::Markdown.parse_file("example.md")
152
+ #
153
+ # @param [String] path
154
+ # the file path
155
+ # @param [Hash] options
156
+ # the conversion options
157
+ #
158
+ # @return [String]
159
+ # the converted terminal output
97
160
  #
98
161
  # @api public
99
162
  def parse_file(path, **options)
100
- parse(::File.read(path), options)
163
+ parse(::File.read(path), **options)
101
164
  end
102
165
  module_function :parse_file
103
166
 
104
- def symbols
105
- @symbols ||= windows? ? WIN_SYMBOLS : SYMBOLS
167
+ # Convert color option to Pastel option
168
+ #
169
+ # @param [String, Symbol] color
170
+ # the color option to convert
171
+ #
172
+ # @return [Boolean, nil]
173
+ #
174
+ # @api private
175
+ def color_enabled(color)
176
+ case color.to_s
177
+ when "always" then true
178
+ when "never" then false
179
+ end
180
+ end
181
+ module_function :color_enabled
182
+ private_class_method :color_enabled
183
+
184
+ # Build symbols hash from the provided symbols option
185
+ #
186
+ # @param [Hash, String, Symbol, nil] symbols
187
+ # the converted output symbols
188
+ #
189
+ # @return [Hash{Symbol => String}]
190
+ #
191
+ # @api private
192
+ def build_symbols(symbols)
193
+ case symbols
194
+ when String, Symbol
195
+ select_symbols(symbols)
196
+ when Hash
197
+ base_symbols = select_symbols(symbols[:base])
198
+ base_symbols.merge(symbols[:override].to_h)
199
+ else
200
+ SYMBOLS
201
+ end
106
202
  end
107
- module_function :symbols
203
+ module_function :build_symbols
204
+ private_class_method :build_symbols
108
205
 
109
- def windows?
110
- ::File::ALT_SEPARATOR == "\\"
206
+ # Select between ASCII or Unicode symbols
207
+ #
208
+ # @param [String, Symbol, nil] name
209
+ # the symbols name
210
+ #
211
+ # @return [Hash{Symbol => String}]
212
+ #
213
+ # @api private
214
+ def select_symbols(name)
215
+ name.to_s == "ascii" ? ASCII_SYMBOLS : SYMBOLS
216
+ end
217
+ module_function :select_symbols
218
+ private_class_method :select_symbols
219
+
220
+ # Build theme hash from the provided theme option
221
+ #
222
+ # @param [Hash{Symbol => Array, String, Symbol}, nil] theme
223
+ # the converted output theme
224
+ #
225
+ # @return [Hash{Symbol => Array<Symbol>}]
226
+ #
227
+ # @api private
228
+ def build_theme(theme)
229
+ THEME.merge(theme.to_h) do |*, new_style|
230
+ Array(new_style).map(&:to_sym)
231
+ end
111
232
  end
112
- module_function :windows?
233
+ module_function :build_theme
234
+ private_class_method :build_theme
113
235
  end # Markdown
114
236
  end # TTY
data/lib/tty-markdown.rb CHANGED
@@ -1 +1 @@
1
- require_relative 'tty/markdown'
1
+ require_relative "tty/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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
- autorequire:
9
- bindir: exe
8
+ autorequire:
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-30 00:00:00.000000000 Z
11
+ date: 2022-12-21 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,54 +129,35 @@ 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: {}
187
- post_install_message:
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
160
+ post_install_message:
188
161
  rdoc_options: []
189
162
  require_paths:
190
163
  - lib
@@ -199,8 +172,8 @@ 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
203
- signing_key:
175
+ rubygems_version: 3.1.2
176
+ signing_key:
204
177
  specification_version: 4
205
178
  summary: Convert a markdown text or document into a terminal friendly output.
206
179
  test_files: []
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
data/assets/headers.png DELETED
Binary file
data/assets/hr.png DELETED
Binary file
data/assets/link.png DELETED
Binary file
data/assets/list.png DELETED
Binary file
data/assets/quote.png DELETED
Binary file
Binary file
data/assets/table.png DELETED
Binary file
data/bin/console DELETED
@@ -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