tty-markdown 0.6.0 → 0.7.1

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.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