mdless 1.0.37 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/mdless +1 -1
- data/lib/mdless/converter.rb +142 -743
- data/lib/mdless/tables.rb +37 -43
- data/lib/mdless/theme.rb +4 -1
- data/lib/mdless/version.rb +1 -1
- data/lib/mdless.rb +3 -0
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c645677f1a192348600bf54180afe501b76888d14bad121c4577090ca81718b2
|
4
|
+
data.tar.gz: 7e82688bd079cede12e529a813c628297070b91b47d68276e54b368b7155bc25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 830a9aea162d1481ceca6a5dd9cfed3e1a46821a4f1ceaae0d77a843d19a9d0515338025120bc8cfd6bd55fec449009782be872f6558d01d55155bfea9699804
|
7
|
+
data.tar.gz: ae994c34ece07c3fa05cad41c0e28c866e642dd268a6d175e8746bbd1c8e4d492ca1eef4b943ba506b4769079958296f0ac885e3a10eadc37d595164618db349
|
data/bin/mdless
CHANGED
data/lib/mdless/converter.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'yaml'
|
3
|
-
require 'fileutils'
|
4
3
|
|
5
4
|
module CLIMarkdown
|
6
5
|
class Converter
|
@@ -14,23 +13,26 @@ module CLIMarkdown
|
|
14
13
|
end
|
15
14
|
|
16
15
|
def initialize(args)
|
17
|
-
@log = Logger.new(
|
16
|
+
@log = Logger.new($stderr)
|
18
17
|
@log.level = Logger::ERROR
|
19
18
|
|
20
19
|
@options = {}
|
20
|
+
config = File.expand_path('~/.config/mdless/config.yml')
|
21
|
+
@options = YAML.load(IO.read(config)) if File.exist?(config)
|
22
|
+
|
21
23
|
optparse = OptionParser.new do |opts|
|
22
24
|
opts.banner = "#{version} by Brett Terpstra\n\n> Usage: #{CLIMarkdown::EXECUTABLE_NAME} [options] [path]\n\n"
|
23
25
|
|
24
|
-
@options[:color]
|
26
|
+
@options[:color] ||= true
|
25
27
|
opts.on('-c', '--[no-]color', 'Colorize output (default on)') do |c|
|
26
28
|
@options[:color] = c
|
27
29
|
end
|
28
30
|
|
29
|
-
opts.on('-d', '--debug LEVEL', 'Level of debug messages to output') do |level|
|
30
|
-
if level.to_i
|
31
|
+
opts.on('-d', '--debug LEVEL', 'Level of debug messages to output (1-4, 4 to see all messages)') do |level|
|
32
|
+
if level.to_i.positive? && level.to_i < 5
|
31
33
|
@log.level = 5 - level.to_i
|
32
34
|
else
|
33
|
-
|
35
|
+
puts 'Error: Debug level out of range (1-4)'
|
34
36
|
Process.exit 1
|
35
37
|
end
|
36
38
|
end
|
@@ -40,18 +42,19 @@ module CLIMarkdown
|
|
40
42
|
exit
|
41
43
|
end
|
42
44
|
|
43
|
-
@options[:local_images]
|
44
|
-
@options[:remote_images]
|
45
|
-
opts.on('-i', '--images=TYPE',
|
46
|
-
|
47
|
-
|
48
|
-
else
|
45
|
+
@options[:local_images] ||= false
|
46
|
+
@options[:remote_images] ||= false
|
47
|
+
opts.on('-i', '--images=TYPE',
|
48
|
+
'Include [local|remote (both)] images in output (requires chafa or imgcat, default NONE).') do |type|
|
49
|
+
if exec_available('imgcat') || exec_available('chafa')
|
49
50
|
if type =~ /^(r|b|a)/i
|
50
51
|
@options[:local_images] = true
|
51
52
|
@options[:remote_images] = true
|
52
53
|
elsif type =~ /^l/i
|
53
54
|
@options[:local_images] = true
|
54
55
|
end
|
56
|
+
else
|
57
|
+
@log.warn('images turned on but imgcat/chafa not found')
|
55
58
|
end
|
56
59
|
end
|
57
60
|
opts.on('-I', '--all-images', 'Include local and remote images in output (requires imgcat or chafa)') do
|
@@ -63,19 +66,23 @@ module CLIMarkdown
|
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
66
|
-
@options[:
|
67
|
-
opts.on('--
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
@options[:syntax_higlight] ||= false
|
70
|
+
opts.on('--syntax', 'Syntax highlight code blocks') do |p|
|
71
|
+
@options[:syntax_higlight] = p
|
72
|
+
end
|
73
|
+
|
74
|
+
@options[:links] ||= :inline
|
75
|
+
opts.on('--links=FORMAT',
|
76
|
+
'Link style ([inline, reference], default inline) [NOT CURRENTLY IMPLEMENTED]') do |format|
|
77
|
+
@options[:links] = :reference if format =~ /^r/i
|
71
78
|
end
|
72
79
|
|
73
|
-
@options[:list]
|
74
|
-
opts.on('-l', '--list', 'List headers in document and exit'
|
80
|
+
@options[:list] ||= false
|
81
|
+
opts.on('-l', '--list', 'List headers in document and exit') do
|
75
82
|
@options[:list] = true
|
76
83
|
end
|
77
84
|
|
78
|
-
@options[:pager]
|
85
|
+
@options[:pager] ||= true
|
79
86
|
opts.on('-p', '--[no-]pager', 'Formatted output to pager (default on)') do |p|
|
80
87
|
@options[:pager] = p
|
81
88
|
end
|
@@ -84,12 +91,13 @@ module CLIMarkdown
|
|
84
91
|
@options[:pager] = false
|
85
92
|
end
|
86
93
|
|
87
|
-
@options[:section]
|
88
|
-
opts.on('-s', '--section=NUMBER[,NUMBER]',
|
94
|
+
@options[:section] ||= nil
|
95
|
+
opts.on('-s', '--section=NUMBER[,NUMBER]',
|
96
|
+
'Output only a headline-based section of the input (numeric from --list)') do |section|
|
89
97
|
@options[:section] = section.split(/ *, */).map(&:strip).map(&:to_i)
|
90
98
|
end
|
91
99
|
|
92
|
-
@options[:theme]
|
100
|
+
@options[:theme] ||= 'default'
|
93
101
|
opts.on('-t', '--theme=THEME_NAME', 'Specify an alternate color theme to load') do |theme|
|
94
102
|
@options[:theme] = theme
|
95
103
|
end
|
@@ -103,6 +111,12 @@ module CLIMarkdown
|
|
103
111
|
opts.on('-w', '--width=COLUMNS', 'Column width to format for (default: terminal width)') do |columns|
|
104
112
|
@options[:width] = columns.to_i
|
105
113
|
end
|
114
|
+
|
115
|
+
@options[:inline_footnotes] ||= false
|
116
|
+
opts.on('--[no-]inline_footnotes',
|
117
|
+
'Display footnotes immediately after the paragraph that references them') do |p|
|
118
|
+
@options[:inline_footnotes] = p
|
119
|
+
end
|
106
120
|
end
|
107
121
|
|
108
122
|
begin
|
@@ -112,8 +126,19 @@ module CLIMarkdown
|
|
112
126
|
exit 1
|
113
127
|
end
|
114
128
|
|
129
|
+
unless File.exist?(config)
|
130
|
+
FileUtils.mkdir_p(File.dirname(config))
|
131
|
+
File.open(config, 'w') do |f|
|
132
|
+
opts = @options.dup
|
133
|
+
opts.delete(:list)
|
134
|
+
opts.delete(:section)
|
135
|
+
f.puts YAML.dump(opts)
|
136
|
+
warn "Config file saved to #{config}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
115
140
|
@theme = load_theme(@options[:theme])
|
116
|
-
@cols = @options[:width]
|
141
|
+
@cols = @options[:width] - 2
|
117
142
|
@output = ''
|
118
143
|
@headers = []
|
119
144
|
@setheaders = []
|
@@ -122,11 +147,32 @@ module CLIMarkdown
|
|
122
147
|
@ref_links = {}
|
123
148
|
@footnotes = {}
|
124
149
|
|
150
|
+
renderer = Redcarpet::Render::Console.new
|
151
|
+
renderer.theme = @theme
|
152
|
+
renderer.cols = @cols
|
153
|
+
renderer.log = @log
|
154
|
+
renderer.options = @options
|
155
|
+
|
156
|
+
markdown = Redcarpet::Markdown.new(renderer,
|
157
|
+
autolink: true,
|
158
|
+
fenced_code_blocks: true,
|
159
|
+
footnotes: true,
|
160
|
+
hard_wrap: false,
|
161
|
+
highlight: true,
|
162
|
+
lax_spacing: true,
|
163
|
+
quote: false,
|
164
|
+
space_after_headers: false,
|
165
|
+
strikethrough: true,
|
166
|
+
superscript: true,
|
167
|
+
tables: true,
|
168
|
+
underline: false)
|
169
|
+
|
125
170
|
if !args.empty?
|
126
171
|
files = args.delete_if { |f| !File.exist?(f) }
|
127
172
|
files.each do |file|
|
128
173
|
@log.info(%(Processing "#{file}"))
|
129
174
|
@file = file
|
175
|
+
renderer.file = @file
|
130
176
|
begin
|
131
177
|
input = IO.read(file).force_encoding('utf-8')
|
132
178
|
rescue StandardError
|
@@ -137,7 +183,7 @@ module CLIMarkdown
|
|
137
183
|
puts list_headers(input)
|
138
184
|
Process.exit 0
|
139
185
|
else
|
140
|
-
|
186
|
+
@output = markdown.render(input)
|
141
187
|
end
|
142
188
|
end
|
143
189
|
printout
|
@@ -153,14 +199,13 @@ module CLIMarkdown
|
|
153
199
|
puts list_headers(input)
|
154
200
|
Process.exit 0
|
155
201
|
else
|
156
|
-
|
202
|
+
@output = markdown.render(input)
|
157
203
|
end
|
158
204
|
printout
|
159
205
|
else
|
160
206
|
warn 'No input'
|
161
207
|
Process.exit 1
|
162
208
|
end
|
163
|
-
|
164
209
|
end
|
165
210
|
|
166
211
|
def color(key)
|
@@ -182,7 +227,7 @@ module CLIMarkdown
|
|
182
227
|
end
|
183
228
|
if val.is_a? String
|
184
229
|
val = "x #{val}"
|
185
|
-
res = val.split(/ /).map
|
230
|
+
res = val.split(/ /).map(&:to_sym)
|
186
231
|
c(res)
|
187
232
|
else
|
188
233
|
c([:reset])
|
@@ -194,7 +239,7 @@ module CLIMarkdown
|
|
194
239
|
@headers = []
|
195
240
|
headers = input.scan(/^((?!#!)(\#{1,6})\s*([^#]+?)(?: #+)?\s*|(\S.+)\n([=-]+))$/i)
|
196
241
|
|
197
|
-
headers.each
|
242
|
+
headers.each do |h|
|
198
243
|
hlevel = 6
|
199
244
|
title = nil
|
200
245
|
if h[4] =~ /=+/
|
@@ -208,209 +253,92 @@ module CLIMarkdown
|
|
208
253
|
title = h[2]
|
209
254
|
end
|
210
255
|
@headers << [
|
211
|
-
'#'*hlevel,
|
256
|
+
'#' * hlevel,
|
212
257
|
title,
|
213
258
|
h[0]
|
214
259
|
]
|
215
|
-
|
260
|
+
end
|
216
261
|
end
|
217
262
|
|
218
263
|
@headers
|
219
264
|
end
|
220
265
|
|
221
|
-
|
222
266
|
def list_headers(input)
|
223
267
|
h_adjust = highest_header(input) - 1
|
224
|
-
input.gsub!(/^(#+)/) do
|
225
|
-
|
226
|
-
new_level =
|
227
|
-
|
228
|
-
"#" * new_level
|
229
|
-
else
|
230
|
-
''
|
231
|
-
end
|
268
|
+
input.gsub!(/^(#+)/) do
|
269
|
+
m = Regexp.last_match
|
270
|
+
new_level = m[1].length - h_adjust
|
271
|
+
new_level.positive? ? '#' * new_level : ''
|
232
272
|
end
|
233
273
|
|
234
274
|
@headers = get_headers(input)
|
235
275
|
last_level = 0
|
236
276
|
headers_out = []
|
237
|
-
@headers.each_with_index do |h,idx|
|
238
|
-
|
277
|
+
@headers.each_with_index do |h, idx|
|
239
278
|
level = h[0].length - 1
|
240
279
|
title = h[1]
|
241
280
|
|
242
|
-
if level - 1 > last_level
|
243
|
-
|
244
|
-
end
|
281
|
+
level = last_level + 1 if level - 1 > last_level
|
282
|
+
|
245
283
|
last_level = level
|
246
284
|
|
247
285
|
subdoc = case level
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
headers_out.push ('
|
286
|
+
when 0
|
287
|
+
''
|
288
|
+
when 1
|
289
|
+
'- '
|
290
|
+
when 2
|
291
|
+
'+ '
|
292
|
+
when 3
|
293
|
+
'* '
|
294
|
+
else
|
295
|
+
' '
|
296
|
+
end
|
297
|
+
headers_out.push format('%<d>d: %<s>s',
|
298
|
+
d: idx + 1,
|
299
|
+
s: "#{c(%i[x black])}#{'.' * level}#{c(%i[x yellow])}#{subdoc}#{title.strip}#{xc}")
|
260
300
|
end
|
261
301
|
|
262
|
-
|
302
|
+
headers_out.join("\n")
|
263
303
|
end
|
264
304
|
|
265
305
|
def highest_header(input)
|
266
306
|
@headers = get_headers(input)
|
267
307
|
top = 6
|
268
|
-
@headers.each {|h|
|
269
|
-
top = h[0].length if h[0].length < top
|
270
|
-
}
|
308
|
+
@headers.each { |h| top = h[0].length if h[0].length < top }
|
271
309
|
top
|
272
310
|
end
|
273
311
|
|
274
|
-
def color_table(input)
|
275
|
-
first = true
|
276
|
-
input.split(/\n/).map{|line|
|
277
|
-
if first
|
278
|
-
if line =~ /^\+-+/
|
279
|
-
line.gsub!(/^/, color('table border'))
|
280
|
-
else
|
281
|
-
first = false
|
282
|
-
line.gsub!(/\|/, "#{color('table border')}|#{color('table header')}")
|
283
|
-
end
|
284
|
-
elsif line.strip =~ /^[|:\- +]+$/
|
285
|
-
line.gsub!(/^(.*)$/, "#{color('table border')}\\1#{color('table color')}")
|
286
|
-
line.gsub!(/([:\-+]+)/,"#{color('table divider')}\\1#{color('table border')}")
|
287
|
-
else
|
288
|
-
line.gsub!(/\|/, "#{color('table border')}|#{color('table color')}")
|
289
|
-
end
|
290
|
-
}.join("\n")
|
291
|
-
end
|
292
|
-
|
293
|
-
def cleanup_tables(input)
|
294
|
-
|
295
|
-
in_table = false
|
296
|
-
header_row = false
|
297
|
-
all_content = []
|
298
|
-
this_table = []
|
299
|
-
orig_table = []
|
300
|
-
input.split(/\n/).each {|line|
|
301
|
-
if line =~ /(\|.*?)+/ && line !~ /^\s*~/
|
302
|
-
in_table = true
|
303
|
-
table_line = line.to_s.uncolor.strip.sub(/^\|?\s*/,'|').gsub(/\s*([\|:])\s*/,'\1')
|
304
|
-
|
305
|
-
if table_line.strip.gsub(/[\|:\- ]/,'') == ''
|
306
|
-
header_row = true
|
307
|
-
end
|
308
|
-
this_table.push(table_line)
|
309
|
-
orig_table.push(line)
|
310
|
-
else
|
311
|
-
if in_table
|
312
|
-
if this_table.length > 2
|
313
|
-
# if there's no header row, add one, cleanup requires it
|
314
|
-
unless header_row
|
315
|
-
cells = this_table[0].sub(/^\|/,'').scan(/.*?\|/).length
|
316
|
-
cell_row = '|' + ':-----|'*cells
|
317
|
-
this_table.insert(1, cell_row)
|
318
|
-
end
|
319
|
-
|
320
|
-
table = this_table.join("\n").strip
|
321
|
-
|
322
|
-
begin
|
323
|
-
formatted = MDTableCleanup.new(table)
|
324
|
-
res = formatted.to_md
|
325
|
-
res = color_table(res)
|
326
|
-
rescue
|
327
|
-
res = orig_table.join("\n")
|
328
|
-
end
|
329
|
-
all_content.push(res)
|
330
|
-
else
|
331
|
-
all_content.push(orig_table.join("\n"))
|
332
|
-
end
|
333
|
-
this_table = []
|
334
|
-
orig_table = []
|
335
|
-
end
|
336
|
-
in_table = false
|
337
|
-
header_row = false
|
338
|
-
all_content.push(line)
|
339
|
-
end
|
340
|
-
}
|
341
|
-
all_content.join("\n")
|
342
|
-
end
|
343
|
-
|
344
312
|
def clean_markers(input)
|
345
|
-
input.gsub!(/^(\e\[[\d;]+m)?[%~] ?/,'\1')
|
346
|
-
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?( +)/,' \3\1\2')
|
347
|
-
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?/,'\1\2')
|
348
|
-
input.gsub!(/(\e\[[\d;]+m)?@@@(\e\[[\d;]+m)?$/,'')
|
313
|
+
input.gsub!(/^(\e\[[\d;]+m)?[%~] ?/, '\1')
|
314
|
+
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?( +)/, ' \3\1\2')
|
315
|
+
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?/, '\1\2')
|
316
|
+
input.gsub!(/(\e\[[\d;]+m)?@@@(\e\[[\d;]+m)?$/, '')
|
349
317
|
input
|
350
318
|
end
|
351
319
|
|
352
320
|
def update_inline_links(input)
|
353
321
|
links = {}
|
354
322
|
counter = 1
|
355
|
-
input.gsub!(/(?<=\])\((.*?)\)/) do
|
356
|
-
links[counter] =
|
323
|
+
input.gsub!(/(?<=\])\((.*?)\)/) do
|
324
|
+
links[counter] = Regexp.last_match(1).uncolor
|
357
325
|
"[#{counter}]"
|
358
326
|
end
|
359
327
|
end
|
360
328
|
|
361
|
-
def find_color(line, nullable
|
329
|
+
def find_color(line, nullable: false)
|
362
330
|
return line if line.nil?
|
331
|
+
|
363
332
|
colors = line.scan(/\e\[[\d;]+m/)
|
364
|
-
if colors
|
333
|
+
if colors.size&.positive?
|
365
334
|
colors[-1]
|
366
335
|
else
|
367
336
|
nullable ? nil : xc
|
368
337
|
end
|
369
338
|
end
|
370
339
|
|
371
|
-
def
|
372
|
-
|
373
|
-
color('link brackets'),
|
374
|
-
"[",
|
375
|
-
color('link text'),
|
376
|
-
text,
|
377
|
-
color('link brackets'),
|
378
|
-
"](",
|
379
|
-
color('link url'),
|
380
|
-
url,
|
381
|
-
color('link brackets'),
|
382
|
-
")",
|
383
|
-
find_color(line)
|
384
|
-
].join
|
385
|
-
end
|
386
|
-
|
387
|
-
def color_image(line, text, url)
|
388
|
-
text.gsub!(/\e\[0m/,color('image title'))
|
389
|
-
|
390
|
-
[
|
391
|
-
color('image bang'),
|
392
|
-
"!",
|
393
|
-
color('image brackets'),
|
394
|
-
"[",
|
395
|
-
color('image title'),
|
396
|
-
text,
|
397
|
-
color('image brackets'),
|
398
|
-
"](",
|
399
|
-
color('image url'),
|
400
|
-
url,
|
401
|
-
color('image brackets'),
|
402
|
-
")",
|
403
|
-
find_color(line)
|
404
|
-
].join
|
405
|
-
end
|
406
|
-
|
407
|
-
def valid_lexer?(language)
|
408
|
-
lexers = %w(Clipper XBase Cucumber cucumber Gherkin gherkin RobotFramework robotframework abap ada ada95ada2005 ahk antlr-as antlr-actionscript antlr-cpp antlr-csharp antlr-c# antlr-java antlr-objc antlr-perl antlr-python antlr-ruby antlr-rb antlr apacheconf aconf apache applescript as actionscript as3 actionscript3 aspectj aspx-cs aspx-vb asy asymptote autoit Autoit awk gawk mawk nawk basemake bash sh ksh bat bbcode befunge blitzmax bmax boo brainfuck bf bro bugs winbugs openbugs c-objdump c ca65 cbmbas ceylon cfengine3 cf3 cfm cfs cheetah spitfire clojure clj cmake cobol cobolfree coffee-script coffeescript common-lisp cl console control coq cpp c++ cpp-objdump c++-objdumb cxx-objdump croc csharp c# css+django css+jinja css+erb css+ruby css+genshitext css+genshi css+lasso css+mako css+myghty css+php css+smarty css cuda cu cython pyx d-objdump d dart delphi pas pascal objectpascal dg diff udiff django jinja dpatch dtd duel Duel Engine Duel View JBST jbst JsonML+BST dylan-console dylan-repl dylan-lid lid dylan ec ecl elixir ex exs erb erl erlang evoque factor fan fancy fy felix flx fortran fsharp gas genshi kid xml+genshi xml+kid genshitext glsl gnuplot go gooddata-cl gosu groff nroff man groovy gst haml HAML haskell hs haxeml hxml html+cheetah html+spitfire html+django html+jinja html+evoque html+genshi html+kid html+lasso html+mako html+myghty html+php html+smarty html+velocity html http hx haXe hybris hy idl iex ini cfg io ioke ik irc jade JADE jags java jlcon js+cheetah javascript+cheetah js+spitfire javascript+spitfire js+django javascript+django js+jinja javascript+jinja js+erb javascript+erb js+ruby javascript+ruby js+genshitext js+genshi javascript+genshitext javascript+genshi js+lasso javascript+lasso js+mako javascript+mako js+myghty javascript+myghty js+php javascript+php js+smarty javascript+smarty js javascript json jsp julia jl kconfig menuconfig linux-config kernel-config koka kotlin lasso lassoscript lhs literate-haskell lighty lighttpd live-script livescript llvm logos logtalk lua make makefile mf bsdmake mako maql mason matlab matlabsession minid modelica modula2 m2 monkey moocode moon moonscript mscgen msc mupad mxml myghty mysql nasm nemerle newlisp newspeak nginx nimrod nim nsis nsi nsh numpy objdump objective-c++ objectivec++ obj-c++ objc++ objective-c objectivec obj-c objc objective-j objectivej obj-j objj ocaml octave ooc opa openedge abl progress perl pl php php3 php4 php5 plpgsql postgresql postgres postscript pot po pov powershell posh ps1 prolog properties protobuf psql postgresql-console postgres-console puppet py3tb pycon pypylog pypy pytb python py sage python3 py3 qml Qt Meta Language Qt modeling Language racket rkt ragel-c ragel-cpp ragel-d ragel-em ragel-java ragel-objc ragel-ruby ragel-rb ragel raw rb ruby duby rbcon irb rconsole rout rd rebol redcode registry rhtml html+erb html+ruby rst rest restructuredtext rust sass SASS scala scaml SCAML scheme scm scilab scss shell-session smali smalltalk squeak smarty sml snobol sourceslist sources.list sp spec splus s r sql sqlite3 squidconf squid.conf squid ssp stan systemverilog sv tcl tcsh csh tea tex latex text trac-wiki moin treetop ts urbiscript vala vapi vb.net vbnet velocity verilog v vgl vhdl vim xml+cheetah xml+spitfire xml+django xml+jinja xml+erb xml+ruby xml+evoque xml+lasso xml+mako xml+myghty xml+php xml+smarty xml+velocity xml xquery xqy xq xql xqm xslt xtend yaml)
|
409
|
-
return lexers.include? language.strip
|
410
|
-
end
|
411
|
-
|
412
|
-
def pad_max(block,eol='')
|
413
|
-
block.split(/\n/).map { |l|
|
340
|
+
def pad_max(block, eol='')
|
341
|
+
block.split(/\n/).map do |l|
|
414
342
|
new_code_line = l.gsub(/\t/, ' ')
|
415
343
|
orig_length = new_code_line.size + 8 + eol.size
|
416
344
|
pad_count = [@cols - orig_length, 0].max
|
@@ -418,539 +346,9 @@ module CLIMarkdown
|
|
418
346
|
[
|
419
347
|
new_code_line,
|
420
348
|
eol,
|
421
|
-
' ' *
|
349
|
+
' ' * [pad_count - 1, 0].max
|
422
350
|
].join
|
423
|
-
|
424
|
-
end
|
425
|
-
|
426
|
-
def hiliteCode(language, codeBlock, leader, first_indent, block)
|
427
|
-
new_indent = first_indent > 0 ? first_indent + 2 : 0
|
428
|
-
last_indent = first_indent == 0 ? 7 : 5
|
429
|
-
|
430
|
-
if exec_available('pygmentize') && language && valid_lexer?(language)
|
431
|
-
lexer = "-l #{language}"
|
432
|
-
begin
|
433
|
-
hilite, s = Open3.capture2(%Q{pygmentize -f terminal256 -O style=#{@theme['code_block']['pygments_theme']} #{lexer} 2> /dev/null}, :stdin_data=>codeBlock)
|
434
|
-
if s.success?
|
435
|
-
|
436
|
-
hilite = xc + hilite.split(/\n/).map{|l|
|
437
|
-
new_code_line = l.gsub(/\t/, ' ')
|
438
|
-
new_code_line.sub!(/^#{" "*first_indent}/,'')
|
439
|
-
[
|
440
|
-
"> ",
|
441
|
-
color('code_block marker'),
|
442
|
-
" "*first_indent,
|
443
|
-
"#{color('code_block bg')}#{l}"
|
444
|
-
].join
|
445
|
-
}.join("\n").blackout(@theme['code_block']['bg']) + "#{xc}\n"
|
446
|
-
end
|
447
|
-
rescue => e
|
448
|
-
@log.error(e)
|
449
|
-
hilite = block
|
450
|
-
end
|
451
|
-
else
|
452
|
-
hilite = codeBlock.split(/\n/).map do |line|
|
453
|
-
new_code_line = line.gsub(/\t/, ' ')
|
454
|
-
new_code_line.sub!(/^#{" "*first_indent}/,'')
|
455
|
-
new_code_line.gsub!(/ /, "#{color('code_block color')} ")
|
456
|
-
[
|
457
|
-
"> ",
|
458
|
-
color('code_block marker'),
|
459
|
-
" "*first_indent,
|
460
|
-
color('code_block color'),
|
461
|
-
new_code_line,
|
462
|
-
xc
|
463
|
-
].join
|
464
|
-
end.join("\n")
|
465
|
-
end
|
466
|
-
|
467
|
-
[
|
468
|
-
xc,
|
469
|
-
"\n",
|
470
|
-
" "*first_indent,
|
471
|
-
color('code_block border'),
|
472
|
-
'--[ ',
|
473
|
-
color('code_block title'),
|
474
|
-
leader.chomp,
|
475
|
-
color('code_block border'),
|
476
|
-
' ]',
|
477
|
-
'-'*(@cols-new_indent-leader.size-2),
|
478
|
-
xc,
|
479
|
-
"\n",
|
480
|
-
hilite.chomp,
|
481
|
-
"\n",
|
482
|
-
" "*(new_indent),
|
483
|
-
color('code_block border'),
|
484
|
-
'-'*(@cols-new_indent),
|
485
|
-
xc,
|
486
|
-
"\n"
|
487
|
-
].join
|
488
|
-
end
|
489
|
-
|
490
|
-
def convert_markdown(input)
|
491
|
-
## Replace setex headers with ATX
|
492
|
-
input.gsub!(/^([^\n]+)\n={3,}\s*$/m, "# \\1\n")
|
493
|
-
input.gsub!(/^([^\n]+?)\n-{3,}\s*$/m, "## \\1\n")
|
494
|
-
|
495
|
-
@headers = get_headers(input)
|
496
|
-
input += "\n\n@@@"
|
497
|
-
# yaml/MMD headers
|
498
|
-
in_yaml = false
|
499
|
-
if input.split("\n")[0] =~ /(?i-m)^---[ \t]*?(\n|$)/
|
500
|
-
@log.info("Found YAML")
|
501
|
-
# YAML
|
502
|
-
in_yaml = true
|
503
|
-
input.sub!(/(?i-m)^---[ \t]*\n([\s\S]*?)\n[\-.]{3}[ \t]*\n/) do |yaml|
|
504
|
-
m = Regexp.last_match
|
505
|
-
@log.info("Processing YAML Header")
|
506
|
-
m[0].split(/\n/).map {|line|
|
507
|
-
if line =~ /^[\-.]{3}\s*$/
|
508
|
-
line = color('metadata marker') + "%% " + color('metadata border') + line
|
509
|
-
else
|
510
|
-
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
511
|
-
line = color('metadata marker') + "%% " + color('metadata color') + line
|
512
|
-
end
|
513
|
-
if @cols - line.uncolor.size > 0
|
514
|
-
line += " "*(@cols-line.uncolor.size)
|
515
|
-
end
|
516
|
-
}.join("\n") + "#{xc}\n"
|
517
|
-
end
|
518
|
-
end
|
519
|
-
|
520
|
-
if !in_yaml && input.gsub(/\n/,' ') =~ /(?i-m)^\w.+:\s+\S+ /
|
521
|
-
@log.info("Found MMD Headers")
|
522
|
-
input.sub!(/(?i-m)^([\S ]+:[\s\S]*?)+(?=\n\n)/) do |mmd|
|
523
|
-
mmd.split(/\n/).map {|line|
|
524
|
-
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
525
|
-
line = color('metadata marker') + "%% " + color('metadata color') + line
|
526
|
-
if @cols - line.uncolor.size > 0
|
527
|
-
line += " "*(@cols - line.uncolor.size)
|
528
|
-
end
|
529
|
-
}.join("\n") + " "*@cols + "#{xc}\n"
|
530
|
-
end
|
531
|
-
|
532
|
-
end
|
533
|
-
|
534
|
-
# Gather reference links
|
535
|
-
input.gsub!(/^\s{,3}(?<![\e*])\[\b(.+)\b\]: +(.+)/) do |m|
|
536
|
-
match = Regexp.last_match
|
537
|
-
@ref_links[match[1]] = match[2]
|
538
|
-
''
|
539
|
-
end
|
540
|
-
|
541
|
-
# Gather footnotes (non-inline)
|
542
|
-
# TODO: Need to implement option to output links as references
|
543
|
-
input.gsub!(/^ {,3}(?<!\*)(?:\e\[[\d;]+m)*\[(?:\e\[[\d;]+m)*\^(?:\e\[[\d;]+m)*\b(.+)\b(?:\e\[[\d;]+m)*\]: *(.*?)\n/) do |m|
|
544
|
-
match = Regexp.last_match
|
545
|
-
@footnotes[match[1].uncolor] = match[2].uncolor
|
546
|
-
''
|
547
|
-
end
|
548
|
-
|
549
|
-
if @options[:section]
|
550
|
-
new_content = []
|
551
|
-
@options[:section].each do |sect|
|
552
|
-
in_section = false
|
553
|
-
top_level = 1
|
554
|
-
input.split(/\n/).each do |graf|
|
555
|
-
if graf =~ /^(#+) *(.*?)( *#+)?$/
|
556
|
-
m = Regexp.last_match
|
557
|
-
level = m[1].length
|
558
|
-
title = m[2]
|
559
|
-
if in_section
|
560
|
-
if level >= top_level
|
561
|
-
new_content.push(graf)
|
562
|
-
else
|
563
|
-
in_section = false
|
564
|
-
break
|
565
|
-
end
|
566
|
-
elsif title.downcase == @headers[sect - 1][1].downcase
|
567
|
-
in_section = true
|
568
|
-
top_level = level + 1
|
569
|
-
new_content.push(graf)
|
570
|
-
else
|
571
|
-
next
|
572
|
-
end
|
573
|
-
elsif in_section
|
574
|
-
new_content.push(graf)
|
575
|
-
end
|
576
|
-
end
|
577
|
-
end
|
578
|
-
input = new_content.join("\n")
|
579
|
-
end
|
580
|
-
|
581
|
-
# fenced code block parsing
|
582
|
-
input.gsub!(/(?i-m)(^[ \t]*[`~]{3,})([\s\S]*?)\n([\s\S]*?)\1/m) do
|
583
|
-
language = nil
|
584
|
-
m = Regexp.last_match
|
585
|
-
first_indent = m[1].gsub(/\t/, ' ').match(/^ */)[0].size
|
586
|
-
|
587
|
-
if m[2] && !m[2].strip.empty?
|
588
|
-
language = m[2].strip.split(/ /)[0]
|
589
|
-
code_block = pad_max(m[3].to_s, '')
|
590
|
-
leader = language || 'code'
|
591
|
-
else
|
592
|
-
first_line = m[3].to_s.split(/\n/)[0]
|
593
|
-
|
594
|
-
if first_line =~ %r{^\s*#!.*/.+}
|
595
|
-
shebang = first_line.match(%r{^\s*#!.*/(?:env )?([^/]+)$})
|
596
|
-
language = shebang[1]
|
597
|
-
code_block = m[3]
|
598
|
-
leader = shebang[1] || 'code'
|
599
|
-
else
|
600
|
-
code_block = pad_max(m[3].to_s, "#{color('code_block eol')}¬")
|
601
|
-
leader = language || 'code'
|
602
|
-
end
|
603
|
-
end
|
604
|
-
leader += xc
|
605
|
-
|
606
|
-
hiliteCode(language, code_block, leader, first_indent, m[0])
|
607
|
-
end
|
608
|
-
|
609
|
-
# h_adjust = highest_header(input) - 1
|
610
|
-
# input.gsub!(/^(#+)/) do |m|
|
611
|
-
# match = Regexp.last_match
|
612
|
-
# "#" * (match[1].length - h_adjust)
|
613
|
-
# end
|
614
|
-
#
|
615
|
-
# Headlines
|
616
|
-
@headers.each {|h|
|
617
|
-
input.sub!(/^#{Regexp.escape(h[2])}/m) do |m|
|
618
|
-
pad = ''
|
619
|
-
ansi = ''
|
620
|
-
case h[0].length
|
621
|
-
when 1
|
622
|
-
ansi = color('h1 color')
|
623
|
-
pad = color('h1 pad')
|
624
|
-
char = @theme['h1']['pad_char'] || "="
|
625
|
-
pad += h[1].length + 2 > @cols ? char*h[1].length : char*(@cols - (h[1].length + 1))
|
626
|
-
when 2
|
627
|
-
ansi = color('h2 color')
|
628
|
-
pad = color('h2 pad')
|
629
|
-
char = @theme['h2']['pad_char'] || "-"
|
630
|
-
pad += h[1].length + 2 > @cols ? char*h[1].length : char*(@cols - (h[1].length + 1))
|
631
|
-
when 3
|
632
|
-
ansi = color('h3 color')
|
633
|
-
when 4
|
634
|
-
ansi = color('h4 color')
|
635
|
-
when 5
|
636
|
-
ansi = color('h5 color')
|
637
|
-
else
|
638
|
-
ansi = color('h6 color')
|
639
|
-
end
|
640
|
-
|
641
|
-
# If we're in iTerm and not paginating, add
|
642
|
-
# iTerm Marks for navigation on h1-3
|
643
|
-
if h[0].length < 4 &&
|
644
|
-
ENV['TERM_PROGRAM'] =~ /^iterm/i &&
|
645
|
-
@options[:pager] == false
|
646
|
-
ansi = "\e]1337;SetMark\a" + ansi
|
647
|
-
end
|
648
|
-
|
649
|
-
"\n#{xc}#{ansi}#{h[1]} #{pad}#{xc}\n"
|
650
|
-
end
|
651
|
-
}
|
652
|
-
|
653
|
-
# remove empty links
|
654
|
-
input.gsub!(/\[(.*?)\]\(\s*?\)/, '\1')
|
655
|
-
input.gsub!(/\[(.*?)\]\[\]/, '[\1][\1]')
|
656
|
-
|
657
|
-
lines = input.split(/\n/)
|
658
|
-
|
659
|
-
# previous_indent = 0
|
660
|
-
|
661
|
-
lines.map!.with_index do |aLine, i|
|
662
|
-
line = aLine.dup
|
663
|
-
clean_line = line.dup.uncolor
|
664
|
-
|
665
|
-
|
666
|
-
if clean_line.uncolor =~ /(^[%~])/ # || clean_line.uncolor =~ /^( {4,}|\t+)/
|
667
|
-
## TODO: find indented code blocks and prevent highlighting
|
668
|
-
## Needs to miss block indented 1 level in lists
|
669
|
-
## Needs to catch lists in code
|
670
|
-
## Needs to avoid within fenced code blocks
|
671
|
-
# if line =~ /^([ \t]+)([^*-+]+)/
|
672
|
-
# indent = $1.gsub(/\t/, " ").size
|
673
|
-
# if indent >= previous_indent
|
674
|
-
# line = "~" + line
|
675
|
-
# end
|
676
|
-
# p [indent, previous_indent]
|
677
|
-
# previous_indent = indent
|
678
|
-
# end
|
679
|
-
else
|
680
|
-
# list items
|
681
|
-
# TODO: Fix ordered list numbering, pad numbers based on total number of list items
|
682
|
-
line.gsub!(/^(\s*)([*\-+]|\d+\.) /) do |m|
|
683
|
-
match = Regexp.last_match
|
684
|
-
last = find_color(match.pre_match)
|
685
|
-
mcolor = match[2] =~ /^\d+\./ ? 'list number' : 'list bullet'
|
686
|
-
indent = match[1] || ''
|
687
|
-
[
|
688
|
-
indent,
|
689
|
-
color(mcolor),
|
690
|
-
match[2], " ",
|
691
|
-
color('list color')
|
692
|
-
].join
|
693
|
-
end
|
694
|
-
|
695
|
-
# definition lists
|
696
|
-
line.gsub!(/^(:\s*)(.*?)/) do |m|
|
697
|
-
match = Regexp.last_match
|
698
|
-
[
|
699
|
-
color('dd marker'),
|
700
|
-
match[1],
|
701
|
-
" ",
|
702
|
-
color('dd color'),
|
703
|
-
match[2],
|
704
|
-
xc
|
705
|
-
].join
|
706
|
-
end
|
707
|
-
|
708
|
-
# place footnotes under paragraphs that reference them
|
709
|
-
if line =~ /\[(?:\e\[[\d;]+m)*\^(?:\e\[[\d;]+m)*(\S+)(?:\e\[[\d;]+m)*\]/
|
710
|
-
match = Regexp.last_match
|
711
|
-
key = match[1].uncolor
|
712
|
-
if @footnotes.key? key
|
713
|
-
line = "#{xc}#{line}"
|
714
|
-
line += "\n\n#{color('footnote brackets')}[#{color('footnote caret')}^#{color('footnote title')}#{key}#{color('footnote brackets')}]: #{color('footnote note')}#{@footnotes[key]}#{xc}"
|
715
|
-
@footnotes.delete(key)
|
716
|
-
end
|
717
|
-
end
|
718
|
-
|
719
|
-
# color footnote references
|
720
|
-
line.gsub!(/\[\^(\S+)\]/) do |m|
|
721
|
-
match = Regexp.last_match
|
722
|
-
last = find_color(match.pre_match, true)
|
723
|
-
counter = i
|
724
|
-
while last.nil? && counter > 0
|
725
|
-
counter -= 1
|
726
|
-
find_color(lines[counter])
|
727
|
-
end
|
728
|
-
"#{color('footnote brackets')}[#{color('footnote caret')}^#{color('footnote title')}#{match[1]}#{color('footnote brackets')}]" + (last ? last : xc)
|
729
|
-
end
|
730
|
-
|
731
|
-
# blockquotes
|
732
|
-
line.gsub!(/^(\s*>)+( .*?)?$/) do |m|
|
733
|
-
match = Regexp.last_match
|
734
|
-
last = find_color(match.pre_match, true)
|
735
|
-
counter = i
|
736
|
-
while last.nil? && counter > 0
|
737
|
-
counter -= 1
|
738
|
-
find_color(lines[counter])
|
739
|
-
end
|
740
|
-
"#{c(%i[b black])}#{match[1]}#{c(%i[x magenta])} #{match[2]}" + (last ? last : xc)
|
741
|
-
end
|
742
|
-
|
743
|
-
# make reference links inline
|
744
|
-
line.gsub!(/(?<![\e*])\[(\b.*?\b)?\]\[(\b.+?\b)?\]/) do |m|
|
745
|
-
match = Regexp.last_match
|
746
|
-
title = match[2] || ''
|
747
|
-
text = match[1] || ''
|
748
|
-
if match[2] && @ref_links.key?(title.downcase)
|
749
|
-
"[#{text}](#{@ref_links[title]})"
|
750
|
-
elsif match[1] && @ref_links.key?(text.downcase)
|
751
|
-
"[#{text}](#{@ref_links[text]})"
|
752
|
-
else
|
753
|
-
if input.match(/^#+\s*#{Regexp.escape(text)}/i)
|
754
|
-
"[#{text}](##{text})"
|
755
|
-
else
|
756
|
-
match[1]
|
757
|
-
end
|
758
|
-
end
|
759
|
-
end
|
760
|
-
|
761
|
-
# color inline links
|
762
|
-
line.gsub!(/(?<![\e*!])\[(\S.*?\S)\]\((\S.+?\S)\)/) do |m|
|
763
|
-
match = Regexp.last_match
|
764
|
-
color_link(match.pre_match, match[1], match[2])
|
765
|
-
end
|
766
|
-
|
767
|
-
# horizontal rules
|
768
|
-
line.gsub!(/^ {,3}([\-*] ?){3,}$/) do |m|
|
769
|
-
color('hr color') + '_'*@cols + xc
|
770
|
-
end
|
771
|
-
|
772
|
-
# escaped characters
|
773
|
-
line.gsub!(/\\(\S)/,'\1')
|
774
|
-
|
775
|
-
# bold, bold/italic
|
776
|
-
line.gsub!(/(?<pre>^|[ "'\(“])(?<open>[\*_]{2,3})(?<content>[^\*_\s][^\*_]+?[^\*_\s])[\*_]{2,3}/) do |m|
|
777
|
-
match = Regexp.last_match
|
778
|
-
last = find_color(match.pre_match, true)
|
779
|
-
counter = i
|
780
|
-
while last.nil? && counter > 0
|
781
|
-
counter -= 1
|
782
|
-
last = find_color(lines[counter])
|
783
|
-
end
|
784
|
-
emph = match['open'].length == 2 ? color('emphasis bold') : color('emphasis bold-italic')
|
785
|
-
"#{match['pre']}#{emph}#{match['content']}" + (last ? last : xc)
|
786
|
-
end
|
787
|
-
|
788
|
-
# italic
|
789
|
-
line.gsub!(/(?<pre>^|[ "'\(“])[\*_](?<content>[^\*_\s][^\*_]+?[^\*_\s])[\*_]/) do |m|
|
790
|
-
match = Regexp.last_match
|
791
|
-
last = find_color(match.pre_match, true)
|
792
|
-
counter = i
|
793
|
-
while last.nil? && counter > 0
|
794
|
-
counter -= 1
|
795
|
-
last = find_color(lines[counter])
|
796
|
-
end
|
797
|
-
"#{match['pre']}#{color('emphasis italic')}#{match[2]}" + (last ? last : xc)
|
798
|
-
end
|
799
|
-
|
800
|
-
# equations
|
801
|
-
line.gsub!(/((\\\\\[|\$\$)(.*?)(\\\\\]|\$\$)|(\\\\\(|\$)(.*?)(\\\\\)|\$))/) do |m|
|
802
|
-
match = Regexp.last_match
|
803
|
-
last = find_color(match.pre_match)
|
804
|
-
if match[2]
|
805
|
-
brackets = [match[2], match[4]]
|
806
|
-
equat = match[3]
|
807
|
-
else
|
808
|
-
brackets = [match[5], match[7]]
|
809
|
-
equat = match[6]
|
810
|
-
end
|
811
|
-
"#{c(%i[b black])}#{brackets[0]}#{xc}#{c(%i[b blue])}#{equat}#{c(%i[b black])}#{brackets[1]}" + (last ? last : xc)
|
812
|
-
end
|
813
|
-
|
814
|
-
# misc html
|
815
|
-
line.gsub!(%r{<br/?>}, "\n")
|
816
|
-
line.gsub!(%r{(?i-m)((</?)(\w+[\s\S]*?)(>))}) do
|
817
|
-
match = Regexp.last_match
|
818
|
-
last = find_color(match.pre_match)
|
819
|
-
[
|
820
|
-
color('html brackets'),
|
821
|
-
match[2],
|
822
|
-
color('html color'),
|
823
|
-
match[3],
|
824
|
-
color('html brackets'),
|
825
|
-
match[4],
|
826
|
-
last || xc
|
827
|
-
].join
|
828
|
-
end
|
829
|
-
|
830
|
-
# inline code spans
|
831
|
-
line.gsub!(/`(.*?)`/) do
|
832
|
-
match = Regexp.last_match
|
833
|
-
last = find_color(match.pre_match, true)
|
834
|
-
[
|
835
|
-
color('code_span marker'),
|
836
|
-
'`',
|
837
|
-
color('code_span color'),
|
838
|
-
match[1],
|
839
|
-
color('code_span marker'),
|
840
|
-
'`',
|
841
|
-
last || xc
|
842
|
-
].join
|
843
|
-
end
|
844
|
-
end
|
845
|
-
|
846
|
-
## Should force a foreground color but doesn't...
|
847
|
-
# unless line =~ /^\s*\e\[[\d;]+m/
|
848
|
-
# line.sub!(/^(\s*)/, "\\1#{color('text')}")
|
849
|
-
# end
|
850
|
-
|
851
|
-
line
|
852
|
-
end
|
853
|
-
|
854
|
-
input = lines.join("\n")
|
855
|
-
|
856
|
-
# images
|
857
|
-
input.gsub!(/^(.*?)!\[(.*)?\]\((.*?\.(?:png|gif|jpg))( +.*)?\)/) do
|
858
|
-
match = Regexp.last_match
|
859
|
-
if match[1].uncolor =~ /^( {4,}|\t)+/
|
860
|
-
match[0]
|
861
|
-
else
|
862
|
-
tail = match[4].nil? ? '' : " "+match[4].strip
|
863
|
-
result = nil
|
864
|
-
if (exec_available('imgcat') || exec_available('chafa')) && @options[:local_images]
|
865
|
-
if match[3]
|
866
|
-
img_path = match[3]
|
867
|
-
if img_path =~ /^http/ && @options[:remote_images]
|
868
|
-
|
869
|
-
if exec_available('chafa')
|
870
|
-
if File.directory?('.mdless_tmp')
|
871
|
-
FileUtils.rm_r '.mdless_tmp', force: true
|
872
|
-
end
|
873
|
-
Dir.mkdir('.mdless_tmp')
|
874
|
-
Dir.chdir('.mdless_tmp')
|
875
|
-
`curl -SsO #{img_path} 2> /dev/null`
|
876
|
-
tmp_img = File.basename(img_path)
|
877
|
-
img = `chafa "#{tmp_img}"`
|
878
|
-
pre = match[2].size > 0 ? " #{c(%i[d blue])}[#{match[2].strip}]\n" : ''
|
879
|
-
post = tail.size > 0 ? "\n #{c(%i[b blue])}-- #{tail} --" : ''
|
880
|
-
result = pre + img + post
|
881
|
-
Dir.chdir('..')
|
882
|
-
FileUtils.rm_r '.mdless_tmp', force: true
|
883
|
-
else
|
884
|
-
if exec_available('imgcat')
|
885
|
-
begin
|
886
|
-
res, s = Open3.capture2(%Q{curl -sS "#{img_path}" 2> /dev/null | imgcat})
|
887
|
-
|
888
|
-
if s.success?
|
889
|
-
pre = match[2].size > 0 ? " #{c(%i[d blue])}[#{match[2].strip}]\n" : ''
|
890
|
-
post = tail.size > 0 ? "\n #{c(%i[b blue])}-- #{tail} --" : ''
|
891
|
-
result = pre + res + post
|
892
|
-
end
|
893
|
-
rescue => e
|
894
|
-
@log.error(e)
|
895
|
-
end
|
896
|
-
else
|
897
|
-
@log.warn('No viewer for remote images')
|
898
|
-
end
|
899
|
-
end
|
900
|
-
else
|
901
|
-
if img_path =~ %r{^[~/]}
|
902
|
-
img_path = File.expand_path(img_path)
|
903
|
-
elsif @file
|
904
|
-
base = File.expand_path(File.dirname(@file))
|
905
|
-
img_path = File.join(base, img_path)
|
906
|
-
end
|
907
|
-
if File.exist?(img_path)
|
908
|
-
pre = !match[2].empty? ? " #{c(%i[d blue])}[#{match[2].strip}]\n" : ''
|
909
|
-
post = !tail.empty? ? "\n #{c(%i[b blue])}-- #{tail} --" : ''
|
910
|
-
if exec_available('chafa')
|
911
|
-
img = `chafa "#{img_path}"`
|
912
|
-
elsif exec_available('imgcat')
|
913
|
-
img = `imgcat "#{img_path}"`
|
914
|
-
end
|
915
|
-
result = pre + img + post
|
916
|
-
end
|
917
|
-
end
|
918
|
-
end
|
919
|
-
end
|
920
|
-
if result.nil?
|
921
|
-
match[1] + color_image(match.pre_match, match[2], match[3] + tail) + xc
|
922
|
-
else
|
923
|
-
match[1] + result + xc
|
924
|
-
end
|
925
|
-
end
|
926
|
-
end
|
927
|
-
|
928
|
-
@footnotes.each do |t, v|
|
929
|
-
input += [
|
930
|
-
"\n\n",
|
931
|
-
color('footnote brackets'),
|
932
|
-
'[',
|
933
|
-
color('footnote caret'),
|
934
|
-
'^',
|
935
|
-
color('footnote title'),
|
936
|
-
t,
|
937
|
-
color('footnote brackets'),
|
938
|
-
']: ',
|
939
|
-
color('footnote note'),
|
940
|
-
v,
|
941
|
-
xc
|
942
|
-
].join
|
943
|
-
end
|
944
|
-
|
945
|
-
@output += input
|
946
|
-
end
|
947
|
-
|
948
|
-
def exec_available(cli)
|
949
|
-
if File.exist?(File.expand_path(cli))
|
950
|
-
File.executable?(File.expand_path(cli))
|
951
|
-
else
|
952
|
-
system "which #{cli}", out: File::NULL, err: File::NULL
|
953
|
-
end
|
351
|
+
end.join("\n")
|
954
352
|
end
|
955
353
|
|
956
354
|
def page(text, &callback)
|
@@ -989,40 +387,37 @@ module CLIMarkdown
|
|
989
387
|
end
|
990
388
|
|
991
389
|
def printout
|
992
|
-
out = @output.rstrip.split(/\n/).map
|
390
|
+
out = @output.rstrip.split(/\n/).map do |p|
|
993
391
|
p.wrap(@cols, color('text'))
|
994
|
-
|
995
|
-
|
392
|
+
end.join("\n")
|
996
393
|
|
997
|
-
unless out
|
998
|
-
@log.warn
|
394
|
+
unless out.size&.positive?
|
395
|
+
@log.warn 'No results'
|
999
396
|
Process.exit
|
1000
397
|
end
|
1001
398
|
|
1002
|
-
out = cleanup_tables(out)
|
1003
399
|
out = clean_markers(out)
|
1004
|
-
out = out.gsub(/\n{2,}/m,"\n\n")
|
400
|
+
out = "#{out.gsub(/\n{2,}/m, "\n\n")}#{xc}"
|
1005
401
|
|
1006
|
-
unless @options[:color]
|
1007
|
-
out.uncolor!
|
1008
|
-
end
|
402
|
+
out.uncolor! unless @options[:color]
|
1009
403
|
|
1010
404
|
if @options[:pager]
|
1011
405
|
page(out)
|
1012
406
|
else
|
1013
|
-
$stdout.print
|
407
|
+
$stdout.print out.rstrip
|
1014
408
|
end
|
1015
409
|
end
|
1016
410
|
|
1017
411
|
def which_pager
|
1018
|
-
pagers = [ENV['PAGER'], ENV['GIT_PAGER']]
|
412
|
+
# pagers = [ENV['PAGER'], ENV['GIT_PAGER']]
|
413
|
+
pagers = [ENV['PAGER']]
|
1019
414
|
|
1020
|
-
if exec_available('git')
|
1021
|
-
|
1022
|
-
|
1023
|
-
end
|
415
|
+
# if exec_available('git')
|
416
|
+
# git_pager = `git config --get-all core.pager || true`.split.first
|
417
|
+
# git_pager && pagers.push(git_pager)
|
418
|
+
# end
|
1024
419
|
|
1025
|
-
pagers.concat(['
|
420
|
+
pagers.concat(['less', 'more', 'cat', 'pager'])
|
1026
421
|
|
1027
422
|
pagers.select! do |f|
|
1028
423
|
if f
|
@@ -1032,7 +427,7 @@ module CLIMarkdown
|
|
1032
427
|
@log.warn('most not allowed as pager')
|
1033
428
|
false
|
1034
429
|
else
|
1035
|
-
system "which #{f}", :
|
430
|
+
system "which #{f}", out: File::NULL, err: File::NULL
|
1036
431
|
end
|
1037
432
|
else
|
1038
433
|
false
|
@@ -1041,21 +436,25 @@ module CLIMarkdown
|
|
1041
436
|
|
1042
437
|
pg = pagers.first
|
1043
438
|
args = case pg
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
439
|
+
# when 'delta'
|
440
|
+
# ' --pager="less -Xr"'
|
441
|
+
when 'less'
|
442
|
+
' -Xr'
|
443
|
+
# when 'bat'
|
444
|
+
# ' -p --pager="less -Xr"'
|
445
|
+
else
|
446
|
+
''
|
447
|
+
end
|
1053
448
|
|
1054
449
|
[pg, args]
|
1055
450
|
end
|
1056
451
|
|
1057
|
-
def
|
1058
|
-
|
452
|
+
def exec_available(cli)
|
453
|
+
if File.exist?(File.expand_path(cli))
|
454
|
+
File.executable?(File.expand_path(cli))
|
455
|
+
else
|
456
|
+
system "which #{cli}", out: File::NULL, err: File::NULL
|
457
|
+
end
|
1059
458
|
end
|
1060
459
|
end
|
1061
460
|
end
|
data/lib/mdless/tables.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
module CLIMarkdown
|
2
2
|
class MDTableCleanup
|
3
|
-
|
4
|
-
PAD_CHAR = '⎕'
|
3
|
+
PAD_CHAR = "\u00A0"
|
5
4
|
|
6
5
|
def initialize(input)
|
7
6
|
@string = input
|
8
|
-
@format_row = []
|
9
7
|
end
|
10
8
|
|
11
9
|
def parse
|
10
|
+
@format_row = []
|
12
11
|
@table = []
|
13
|
-
|
12
|
+
fmt = []
|
14
13
|
cols = 0
|
15
14
|
rows = @string.split(/\r?\n/)
|
16
15
|
rows.each do |row|
|
@@ -19,29 +18,29 @@ module CLIMarkdown
|
|
19
18
|
row_array = row.split(/\|/)
|
20
19
|
row_array.map! { |cell| cell.strip }
|
21
20
|
if row =~ /^[\|:\- ]+$/
|
22
|
-
|
21
|
+
fmt = row_array
|
23
22
|
else
|
24
23
|
@table.push row_array
|
25
24
|
end
|
26
25
|
cols = row_array.length if row_array.length > cols
|
27
26
|
end
|
28
27
|
|
29
|
-
|
28
|
+
fmt.each_with_index do |cell, i|
|
30
29
|
cell.strip!
|
31
|
-
f =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
f = case cell
|
31
|
+
when /^:.*?:$/
|
32
|
+
:center
|
33
|
+
when /[^:]+:$/
|
34
|
+
:right
|
36
35
|
else
|
37
|
-
|
36
|
+
:just
|
38
37
|
end
|
39
38
|
@format_row.push(f)
|
40
|
-
|
39
|
+
end
|
41
40
|
|
42
41
|
if @format_row.length < cols
|
43
42
|
(cols - @format_row.length).times do
|
44
|
-
@format_row.push(
|
43
|
+
@format_row.push(:left)
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -60,9 +59,9 @@ module CLIMarkdown
|
|
60
59
|
@table ||= parse
|
61
60
|
end
|
62
61
|
|
63
|
-
def column_width(
|
62
|
+
def column_width(idx)
|
64
63
|
@widths ||= column_widths
|
65
|
-
@widths[
|
64
|
+
@widths[idx]
|
66
65
|
end
|
67
66
|
|
68
67
|
def column_widths
|
@@ -72,7 +71,7 @@ module CLIMarkdown
|
|
72
71
|
end
|
73
72
|
|
74
73
|
table.each do |row|
|
75
|
-
@format_row.each_with_index do |
|
74
|
+
@format_row.each_with_index do |_, i|
|
76
75
|
length = row[i].strip.length
|
77
76
|
@widths[i] = length if length > @widths[i]
|
78
77
|
end
|
@@ -81,32 +80,31 @@ module CLIMarkdown
|
|
81
80
|
@widths
|
82
81
|
end
|
83
82
|
|
84
|
-
def pad(string,
|
85
|
-
|
86
|
-
|
87
|
-
string.center(length, PAD_CHAR)
|
88
|
-
|
89
|
-
string.rjust(length, PAD_CHAR)
|
90
|
-
|
91
|
-
string.ljust(length, PAD_CHAR)
|
83
|
+
def pad(string, alignment, length)
|
84
|
+
case alignment
|
85
|
+
when :center
|
86
|
+
string.strip.center(length, PAD_CHAR)
|
87
|
+
when :right
|
88
|
+
string.strip.rjust(length, PAD_CHAR)
|
89
|
+
when :left
|
90
|
+
string.strip.ljust(length, PAD_CHAR)
|
92
91
|
else
|
93
|
-
string.ljust(length, PAD_CHAR)
|
92
|
+
string.strip.ljust(length, PAD_CHAR)
|
94
93
|
end
|
95
94
|
end
|
96
95
|
|
97
96
|
def separator(length, alignment)
|
98
|
-
out =
|
97
|
+
out = ''.ljust(length, '-')
|
99
98
|
case alignment
|
100
|
-
when
|
101
|
-
out
|
102
|
-
when
|
103
|
-
out
|
104
|
-
when
|
105
|
-
|
99
|
+
when :left
|
100
|
+
":#{out}-"
|
101
|
+
when :right
|
102
|
+
"-#{out}:"
|
103
|
+
when :center
|
104
|
+
":#{out}:"
|
106
105
|
else
|
107
|
-
|
106
|
+
"-#{out}-"
|
108
107
|
end
|
109
|
-
out
|
110
108
|
end
|
111
109
|
|
112
110
|
def header_separator_row
|
@@ -128,18 +126,14 @@ module CLIMarkdown
|
|
128
126
|
def to_md
|
129
127
|
output = []
|
130
128
|
t = table.clone
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
output.push("| #{row.join(' | ').lstrip} |")
|
129
|
+
t.each do |row|
|
130
|
+
new_row = row.map.with_index { |cell, i| pad(cell, @format_row[i], column_width(i)) }.join(' | ')
|
131
|
+
output.push("| #{new_row} |")
|
135
132
|
end
|
136
133
|
output.insert(1, header_separator_row)
|
137
134
|
output.insert(0, table_border)
|
138
135
|
output.push(table_border)
|
139
|
-
|
140
|
-
output.join("\n").gsub(/((?<=\| )#{PAD_CHAR}+|#{PAD_CHAR}+(?= \|))/) {|m|
|
141
|
-
" "*m.length
|
142
|
-
}
|
136
|
+
output.join("\n")
|
143
137
|
end
|
144
138
|
end
|
145
139
|
end
|
data/lib/mdless/theme.rb
CHANGED
@@ -70,6 +70,7 @@ module CLIMarkdown
|
|
70
70
|
'pygments_theme' => 'monokai'
|
71
71
|
},
|
72
72
|
'dd' => {
|
73
|
+
'term' => 'black on_white',
|
73
74
|
'marker' => 'd red',
|
74
75
|
'color' => 'b white'
|
75
76
|
},
|
@@ -86,7 +87,9 @@ module CLIMarkdown
|
|
86
87
|
'html' => {
|
87
88
|
'brackets' => 'd yellow on_black',
|
88
89
|
'color' => 'yellow on_black'
|
89
|
-
}
|
90
|
+
},
|
91
|
+
'super' => 'b green',
|
92
|
+
'text' => 'white'
|
90
93
|
}
|
91
94
|
|
92
95
|
def load_theme_file(theme_file)
|
data/lib/mdless/version.rb
CHANGED
data/lib/mdless.rb
CHANGED
@@ -3,11 +3,14 @@ require 'shellwords'
|
|
3
3
|
require 'open3'
|
4
4
|
require 'fileutils'
|
5
5
|
require 'logger'
|
6
|
+
require 'tty-which'
|
6
7
|
require 'mdless/version.rb'
|
7
8
|
require 'mdless/colors'
|
8
9
|
require 'mdless/tables'
|
9
10
|
require 'mdless/hash'
|
10
11
|
require 'mdless/theme'
|
12
|
+
require 'redcarpet'
|
13
|
+
require 'mdless/console'
|
11
14
|
require 'mdless/converter'
|
12
15
|
|
13
16
|
module CLIMarkdown
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mdless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redcarpet
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tty-which
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rake
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|