mdless 2.0.18 → 2.0.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mdless/colors.rb +8 -4
- data/lib/mdless/console.rb +74 -68
- data/lib/mdless/converter.rb +114 -93
- data/lib/mdless/string.rb +33 -27
- data/lib/mdless/taskpaper.rb +79 -9
- data/lib/mdless/version.rb +1 -1
- data/lib/mdless.rb +44 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '094b8ac8273e025bee23c350cc7aee02682b2445d22523d216b4e621d067ff9b'
|
4
|
+
data.tar.gz: 495dc941df5b4235c9bb5d7cccb37739e1cc783679d276d026d36a7a9dafc937
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f15d9fc92d6286708adc3060dfbc265a4064419eceecb25dfe4de3f7d60e1f033f7e2f157a34ef2940130ca1caea565e9fe43a2f5cab5f611c8f0d864830f54b
|
7
|
+
data.tar.gz: 9db7cd9e9914bd37bc1a97362e27ef169d899f89fcae0e2e8004da3bd48cffe539518e61396e0f325953a5008adbbb8b37debcbabffdce1d08268a7b73b27f2c
|
data/lib/mdless/colors.rb
CHANGED
@@ -133,7 +133,6 @@ module CLIMarkdown
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def wrap(width=78,foreground=:x)
|
136
|
-
|
137
136
|
if self.uncolor =~ /(^([%~] |\s*>)| +[=\-]{5,})/
|
138
137
|
return self
|
139
138
|
end
|
@@ -145,6 +144,9 @@ module CLIMarkdown
|
|
145
144
|
|
146
145
|
line += self.match(/^\s*/)[0].gsub(/\t/,' ')
|
147
146
|
input = self.dup # .gsub(/(\w-)(\w)/,'\1 \2')
|
147
|
+
input.gsub!(/\[.*?\]\(.*?\)/) do |link|
|
148
|
+
link.gsub(/ /, "\u00A0")
|
149
|
+
end
|
148
150
|
input.split(/\s+/).each do |word|
|
149
151
|
last_ansi = line.scan(/\e\[[\d;]+m/)[-1] || ''
|
150
152
|
if visible_width + word.size_clean >= width
|
@@ -158,9 +160,11 @@ module CLIMarkdown
|
|
158
160
|
visible_width += word.size_clean + 1
|
159
161
|
line << " " << last_ansi + word
|
160
162
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
163
|
+
end
|
164
|
+
lines << line + match(/\s*$/)[0] + xc(foreground) if line
|
165
|
+
lines.join("\n").gsub(/\[.*?\]\(.*?\)/) do |link|
|
166
|
+
link.gsub(/\u00A0/, ' ')
|
167
|
+
end
|
164
168
|
end
|
165
169
|
|
166
170
|
def c(args)
|
data/lib/mdless/console.rb
CHANGED
@@ -4,13 +4,13 @@ module Redcarpet
|
|
4
4
|
include CLIMarkdown::Colors
|
5
5
|
include CLIMarkdown::Theme
|
6
6
|
|
7
|
-
|
7
|
+
attr_accessor :headers
|
8
|
+
attr_writer :file
|
8
9
|
|
9
10
|
@@listitemid = 0
|
10
11
|
@@listid = 0
|
11
12
|
@@elementid = 0
|
12
13
|
@@footnotes = []
|
13
|
-
@@headers = []
|
14
14
|
@@links = []
|
15
15
|
@@footer_links = []
|
16
16
|
|
@@ -66,22 +66,22 @@ module Redcarpet
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def hilite_code(code_block, language)
|
69
|
-
if
|
70
|
-
|
71
|
-
|
69
|
+
if MDLess.options[:syntax_higlight] && !exec_available('pygmentize')
|
70
|
+
MDLess.log.error('Syntax highlighting requested by pygmentize is not available')
|
71
|
+
MDLess.options[:syntax_higlight] = false
|
72
72
|
end
|
73
73
|
|
74
74
|
longest_line = code_block.uncolor.split(/\n/).longest_element.length + 4
|
75
|
-
longest_line = longest_line >
|
75
|
+
longest_line = longest_line > MDLess.cols ? MDLess.cols : longest_line
|
76
76
|
|
77
|
-
if
|
77
|
+
if MDLess.options[:syntax_higlight]
|
78
78
|
pyg = TTY::Which.which('pygmentize')
|
79
79
|
lexer = language&.valid_lexer? ? "-l #{language}" : '-g'
|
80
80
|
begin
|
81
|
-
pygments_theme =
|
81
|
+
pygments_theme = MDLess.options[:pygments_theme] || MDLess.theme['code_block']['pygments_theme']
|
82
82
|
|
83
83
|
unless pygments_theme.valid_pygments_theme?
|
84
|
-
|
84
|
+
MDLess.log.error("Invalid Pygments theme #{pygments_theme}, defaulting to 'default' for highlighting")
|
85
85
|
pygments_theme = 'default'
|
86
86
|
end
|
87
87
|
|
@@ -100,10 +100,10 @@ module Redcarpet
|
|
100
100
|
'> ',
|
101
101
|
"#{color('code_block bg')}#{l.strip}#{xc}"
|
102
102
|
].join
|
103
|
-
end.join("\n").blackout(
|
103
|
+
end.join("\n").blackout(MDLess.theme['code_block']['bg']) + "#{xc}\n"
|
104
104
|
end
|
105
105
|
rescue StandardError => e
|
106
|
-
|
106
|
+
MDLess.log.error(e)
|
107
107
|
hilite = code_block
|
108
108
|
end
|
109
109
|
else
|
@@ -115,7 +115,7 @@ module Redcarpet
|
|
115
115
|
line,
|
116
116
|
xc
|
117
117
|
].join
|
118
|
-
end.join("\n").blackout(
|
118
|
+
end.join("\n").blackout(MDLess.theme['code_block']['bg']) + "#{xc}\n"
|
119
119
|
end
|
120
120
|
|
121
121
|
top_border = if language.nil? || language.empty?
|
@@ -142,17 +142,17 @@ module Redcarpet
|
|
142
142
|
def color(key)
|
143
143
|
val = nil
|
144
144
|
keys = key.split(/[ ,>]/)
|
145
|
-
if
|
146
|
-
val =
|
145
|
+
if MDLess.theme.key?(keys[0])
|
146
|
+
val = MDLess.theme[keys.shift]
|
147
147
|
else
|
148
|
-
|
148
|
+
MDLess.log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
|
149
149
|
return c([:reset])
|
150
150
|
end
|
151
151
|
keys.each do |k|
|
152
152
|
if val.key?(k)
|
153
153
|
val = val[k]
|
154
154
|
else
|
155
|
-
|
155
|
+
MDLess.log.error("Invalid theme key: #{k}")
|
156
156
|
return c([:reset])
|
157
157
|
end
|
158
158
|
end
|
@@ -171,11 +171,12 @@ module Redcarpet
|
|
171
171
|
|
172
172
|
def block_quote(quote)
|
173
173
|
ret = "\n\n"
|
174
|
-
quote.split(
|
174
|
+
quote.wrap(MDLess.cols, color('blockquote color')).split(/\n/).each do |line|
|
175
175
|
ret += [
|
176
176
|
color('blockquote marker color'),
|
177
|
-
|
177
|
+
MDLess.theme['blockquote']['marker']['character'],
|
178
178
|
color('blockquote color'),
|
179
|
+
' ',
|
179
180
|
line,
|
180
181
|
"\n"
|
181
182
|
].join('')
|
@@ -190,17 +191,18 @@ module Redcarpet
|
|
190
191
|
def header(text, header_level)
|
191
192
|
pad = ''
|
192
193
|
ansi = ''
|
194
|
+
text.clean_header_ids!
|
193
195
|
case header_level
|
194
196
|
when 1
|
195
197
|
ansi = color('h1 color')
|
196
198
|
pad = color('h1 pad')
|
197
|
-
char =
|
198
|
-
pad += text.length + 2 >
|
199
|
+
char = MDLess.theme['h1']['pad_char'] || '='
|
200
|
+
pad += text.length + 2 > MDLess.cols ? char * text.length : char * (MDLess.cols - (text.length + 1))
|
199
201
|
when 2
|
200
202
|
ansi = color('h2 color')
|
201
203
|
pad = color('h2 pad')
|
202
|
-
char =
|
203
|
-
pad += text.length + 2 >
|
204
|
+
char = MDLess.theme['h2']['pad_char'] || '-'
|
205
|
+
pad += text.length + 2 > MDLess.cols ? char * text.length : char * (MDLess.cols - (text.length + 1))
|
204
206
|
when 3
|
205
207
|
ansi = color('h3 color')
|
206
208
|
when 4
|
@@ -215,15 +217,15 @@ module Redcarpet
|
|
215
217
|
# iTerm Marks for navigation on h1-3
|
216
218
|
if header_level < 4 &&
|
217
219
|
ENV['TERM_PROGRAM'] =~ /^iterm/i &&
|
218
|
-
|
220
|
+
MDLess.options[:pager] == false
|
219
221
|
ansi = "\e]1337;SetMark\a#{ansi}"
|
220
222
|
end
|
221
223
|
|
222
|
-
"\n#{xc}#{ansi}#{text} #{pad}#{xc}\n\n"
|
224
|
+
"\n\n#{xc}#{ansi}#{text} #{pad}#{xc}\n\n"
|
223
225
|
end
|
224
226
|
|
225
227
|
def hrule()
|
226
|
-
"\n\n#{color('hr color')}#{'_' *
|
228
|
+
"\n\n#{color('hr color')}#{'_' * MDLess.cols}#{xc}\n\n"
|
227
229
|
end
|
228
230
|
|
229
231
|
def paragraph(text)
|
@@ -290,11 +292,11 @@ module Redcarpet
|
|
290
292
|
[
|
291
293
|
pre_element,
|
292
294
|
color('code_span marker'),
|
293
|
-
|
295
|
+
MDLess.theme['code_span']['character'],
|
294
296
|
color('code_span color'),
|
295
297
|
code,
|
296
298
|
color('code_span marker'),
|
297
|
-
|
299
|
+
MDLess.theme['code_span']['character'],
|
298
300
|
xc,
|
299
301
|
post_element
|
300
302
|
].join('')
|
@@ -304,9 +306,9 @@ module Redcarpet
|
|
304
306
|
[
|
305
307
|
pre_element,
|
306
308
|
color('emphasis bold'),
|
307
|
-
|
309
|
+
MDLess.theme['emphasis']['bold_character'],
|
308
310
|
text,
|
309
|
-
|
311
|
+
MDLess.theme['emphasis']['bold_character'],
|
310
312
|
xc,
|
311
313
|
post_element
|
312
314
|
].join
|
@@ -316,9 +318,9 @@ module Redcarpet
|
|
316
318
|
[
|
317
319
|
pre_element,
|
318
320
|
color('emphasis italic'),
|
319
|
-
|
321
|
+
MDLess.theme['emphasis']['italic_character'],
|
320
322
|
text,
|
321
|
-
|
323
|
+
MDLess.theme['emphasis']['italic_character'],
|
322
324
|
xc,
|
323
325
|
post_element
|
324
326
|
].join
|
@@ -328,11 +330,11 @@ module Redcarpet
|
|
328
330
|
[
|
329
331
|
pre_element,
|
330
332
|
color('emphasis bold-italic'),
|
331
|
-
|
332
|
-
|
333
|
+
MDLess.theme['emphasis']['italic_character'],
|
334
|
+
MDLess.theme['emphasis']['bold_character'],
|
333
335
|
text,
|
334
|
-
|
335
|
-
|
336
|
+
MDLess.theme['emphasis']['bold_character'],
|
337
|
+
MDLess.theme['emphasis']['italic_character'],
|
336
338
|
xc,
|
337
339
|
post_element
|
338
340
|
].join
|
@@ -528,7 +530,7 @@ module Redcarpet
|
|
528
530
|
lines = input.split(/\n/)
|
529
531
|
line1 = lines.shift
|
530
532
|
pre = ' ' * (indent + 1)
|
531
|
-
cols =
|
533
|
+
cols = MDLess.cols - pre.length
|
532
534
|
body = lines.map { |l| "#{pre}#{l}" }.join("\n")
|
533
535
|
"#{line1}\n#{body}"
|
534
536
|
end
|
@@ -622,8 +624,8 @@ module Redcarpet
|
|
622
624
|
end
|
623
625
|
|
624
626
|
def get_headers(input)
|
625
|
-
unless
|
626
|
-
|
627
|
+
unless @headers && !@headers.empty?
|
628
|
+
@headers = []
|
627
629
|
headers = input.scan(/^((?!#!)(\#{1,6})\s*([^#]+?)(?: #+)?\s*|(\S.+)\n([=-]+))$/i)
|
628
630
|
|
629
631
|
headers.each do |h|
|
@@ -639,7 +641,7 @@ module Redcarpet
|
|
639
641
|
hlevel = h[1].length
|
640
642
|
title = h[2]
|
641
643
|
end
|
642
|
-
|
644
|
+
@headers << [
|
643
645
|
'#' * hlevel,
|
644
646
|
title,
|
645
647
|
h[0]
|
@@ -647,7 +649,7 @@ module Redcarpet
|
|
647
649
|
end
|
648
650
|
end
|
649
651
|
|
650
|
-
|
652
|
+
@headers
|
651
653
|
end
|
652
654
|
|
653
655
|
def color_meta(text)
|
@@ -656,15 +658,15 @@ module Redcarpet
|
|
656
658
|
|
657
659
|
first_line = input.split("\n").first
|
658
660
|
if first_line =~ /(?i-m)^---[ \t]*?$/
|
659
|
-
|
661
|
+
MDLess.log.info('Found YAML')
|
660
662
|
# YAML
|
661
663
|
in_yaml = true
|
662
664
|
input.sub!(/(?i-m)^---[ \t]*\n([\s\S]*?)\n[-.]{3}[ \t]*\n/m) do
|
663
665
|
m = Regexp.last_match
|
664
|
-
|
666
|
+
MDLess.log.info('Processing YAML Header')
|
665
667
|
lines = m[0].split(/\n/)
|
666
668
|
longest = lines.longest_element.length
|
667
|
-
longest = longest <
|
669
|
+
longest = longest < MDLess.cols ? longest + 1 : MDLess.cols
|
668
670
|
lines.map do |line|
|
669
671
|
if line =~ /^[-.]{3}\s*$/
|
670
672
|
line = "#{color('metadata marker')}#{'%' * longest}"
|
@@ -680,13 +682,13 @@ module Redcarpet
|
|
680
682
|
end
|
681
683
|
|
682
684
|
if !in_yaml && first_line =~ /(?i-m)^[\w ]+:\s+\S+/
|
683
|
-
|
685
|
+
MDLess.log.info('Found MMD Headers')
|
684
686
|
input.sub!(/(?i-m)^([\S ]+:[\s\S]*?)+(?=\n\n)/) do |mmd|
|
685
687
|
lines = mmd.split(/\n/)
|
686
688
|
return mmd if lines.count > 20
|
687
689
|
|
688
690
|
longest = lines.inject { |memo, word| memo.length > word.length ? memo : word }.length
|
689
|
-
longest = longest <
|
691
|
+
longest = longest < MDLess.cols ? longest + 1 : MDLess.cols
|
690
692
|
lines.map do |line|
|
691
693
|
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
692
694
|
line = "#{color('metadata color')}#{line}"
|
@@ -706,14 +708,17 @@ module Redcarpet
|
|
706
708
|
input.gsub!(/^([^\n]+)\n={2,}\s*$/m, "# \\1\n")
|
707
709
|
input.gsub!(/^([^\n]+?)\n-{2,}\s*$/m, "## \\1\n")
|
708
710
|
|
709
|
-
|
711
|
+
@headers = get_headers(input)
|
710
712
|
|
711
|
-
if
|
713
|
+
if MDLess.options[:section]
|
712
714
|
new_content = []
|
713
|
-
|
715
|
+
MDLess.log.info("Matching section(s) #{MDLess.options[:section].join(', ')}")
|
716
|
+
MDLess.options[:section].each do |sect|
|
717
|
+
comparison = MDLess.options[:section][0].is_a?(String) ? :regex : :numeric
|
718
|
+
|
714
719
|
in_section = false
|
715
720
|
top_level = 1
|
716
|
-
input.split(/\n/).
|
721
|
+
input.split(/\n/).each_with_index do |graf, idx|
|
717
722
|
if graf =~ /^(#+) *(.*?)( *#+)?$/
|
718
723
|
m = Regexp.last_match
|
719
724
|
level = m[1].length
|
@@ -725,7 +730,8 @@ module Redcarpet
|
|
725
730
|
in_section = false
|
726
731
|
break
|
727
732
|
end
|
728
|
-
elsif title.downcase
|
733
|
+
elsif (comparison == :regex && title.downcase =~ sect.downcase.to_rx) ||
|
734
|
+
(comparison == :numeric && title.downcase == @headers[sect - 1][1].downcase)
|
729
735
|
in_section = true
|
730
736
|
top_level = level + 1
|
731
737
|
new_content.push(graf)
|
@@ -774,7 +780,7 @@ module Redcarpet
|
|
774
780
|
counter = 1
|
775
781
|
|
776
782
|
grafs.map! do |graf|
|
777
|
-
|
783
|
+
return "\n" if graf =~ /^ *\n$/
|
778
784
|
|
779
785
|
links_added = false
|
780
786
|
|
@@ -784,7 +790,7 @@ module Redcarpet
|
|
784
790
|
content = link[:content]
|
785
791
|
title = link[:title]&.uncolor
|
786
792
|
graf.gsub!(/#{Regexp.escape(link[:link])}/, color_link_reference(url, counter, content))
|
787
|
-
if
|
793
|
+
if MDLess.options[:links] == :paragraph
|
788
794
|
if links_added
|
789
795
|
graf += "\n#{color_reference_link(url, title, counter)}"
|
790
796
|
else
|
@@ -800,7 +806,7 @@ module Redcarpet
|
|
800
806
|
"\n#{graf}\n"
|
801
807
|
end
|
802
808
|
|
803
|
-
if
|
809
|
+
if MDLess.options[:links] == :paragraph
|
804
810
|
grafs.join("\n")
|
805
811
|
else
|
806
812
|
grafs.join("\n") + "\n#{@@footer_links.join("\n")}\n"
|
@@ -820,16 +826,16 @@ module Redcarpet
|
|
820
826
|
input.gsub(%r{<<img>>(.*?)<</img>>}) do
|
821
827
|
link, title, alt_text = Regexp.last_match(1).split(/\|\|/)
|
822
828
|
|
823
|
-
if (exec_available('imgcat') || exec_available('chafa')) &&
|
829
|
+
if (exec_available('imgcat') || exec_available('chafa')) && MDLess.options[:local_images]
|
824
830
|
if exec_available('imgcat')
|
825
|
-
|
831
|
+
MDLess.log.info('Using imgcat for image rendering')
|
826
832
|
elsif exec_available('chafa')
|
827
|
-
|
833
|
+
MDLess.log.info('Using chafa for image rendering')
|
828
834
|
end
|
829
835
|
img_path = link
|
830
|
-
if img_path =~ /^http/ &&
|
836
|
+
if img_path =~ /^http/ && MDLess.options[:remote_images]
|
831
837
|
if exec_available('imgcat')
|
832
|
-
|
838
|
+
MDLess.log.info('Using imgcat for image rendering')
|
833
839
|
begin
|
834
840
|
res, s = Open3.capture2(%(curl -sS "#{img_path}" 2> /dev/null | imgcat))
|
835
841
|
|
@@ -839,10 +845,10 @@ module Redcarpet
|
|
839
845
|
result = pre + res + post
|
840
846
|
end
|
841
847
|
rescue StandardError => e
|
842
|
-
|
848
|
+
MDLess.log.error(e)
|
843
849
|
end
|
844
850
|
elsif exec_available('chafa')
|
845
|
-
|
851
|
+
MDLess.log.info('Using chafa for image rendering')
|
846
852
|
term = '-f sixels'
|
847
853
|
term = ENV['TERMINAL_PROGRAM'] =~ /iterm/i ? '-f iterm' : term
|
848
854
|
term = ENV['TERMINAL_PROGRAM'] =~ /kitty/i ? '-f kitty' : term
|
@@ -858,13 +864,13 @@ module Redcarpet
|
|
858
864
|
Dir.chdir('..')
|
859
865
|
FileUtils.rm_r '.mdless_tmp', force: true
|
860
866
|
else
|
861
|
-
|
867
|
+
MDLess.log.warn('No viewer for remote images')
|
862
868
|
end
|
863
869
|
else
|
864
870
|
if img_path =~ %r{^[~/]}
|
865
871
|
img_path = File.expand_path(img_path)
|
866
|
-
elsif
|
867
|
-
base = File.expand_path(File.dirname(
|
872
|
+
elsif MDLess.file
|
873
|
+
base = File.expand_path(File.dirname(MDLess.file))
|
868
874
|
img_path = File.join(base, img_path)
|
869
875
|
end
|
870
876
|
if File.exist?(img_path)
|
@@ -956,9 +962,9 @@ module Redcarpet
|
|
956
962
|
def postprocess(input)
|
957
963
|
input.scrub!
|
958
964
|
|
959
|
-
input = highlight_wiki_links(input) if
|
965
|
+
input = highlight_wiki_links(input) if MDLess.options[:wiki_links]
|
960
966
|
|
961
|
-
if
|
967
|
+
if MDLess.options[:inline_footnotes]
|
962
968
|
input = insert_footnotes(input)
|
963
969
|
else
|
964
970
|
footnotes = @@footnotes.map.with_index do |fn, i|
|
@@ -975,11 +981,11 @@ module Redcarpet
|
|
975
981
|
# misc html
|
976
982
|
input.gsub!(%r{<br */?>}, "#{pre_element}\n#{post_element}")
|
977
983
|
# format links
|
978
|
-
input = reference_links(input) if
|
984
|
+
input = reference_links(input) if MDLess.options[:links] == :reference || MDLess.options[:links] == :paragraph
|
979
985
|
# lists
|
980
986
|
input = fix_lists(input)
|
981
|
-
input = render_images(input) if
|
982
|
-
input = highlight_tags(input) if
|
987
|
+
input = render_images(input) if MDLess.options[:local_images]
|
988
|
+
input = highlight_tags(input) if MDLess.options[:at_tags] || MDLess.options[:taskpaper]
|
983
989
|
fix_colors(input)
|
984
990
|
end
|
985
991
|
end
|
data/lib/mdless/converter.rb
CHANGED
@@ -4,39 +4,33 @@ require 'yaml'
|
|
4
4
|
module CLIMarkdown
|
5
5
|
class Converter
|
6
6
|
include Colors
|
7
|
-
include Theme
|
8
|
-
|
9
|
-
attr_reader :helpers, :log
|
10
7
|
|
11
8
|
def version
|
12
9
|
"#{CLIMarkdown::EXECUTABLE_NAME} #{CLIMarkdown::VERSION}"
|
13
10
|
end
|
14
11
|
|
15
12
|
def default(option, default)
|
16
|
-
if
|
17
|
-
@options[option] = default
|
18
|
-
end
|
13
|
+
MDLess.options[option] = default if MDLess.options[option].nil?
|
19
14
|
end
|
20
15
|
|
21
16
|
def initialize(args)
|
22
|
-
|
23
|
-
@log.level = Logger::WARN
|
17
|
+
MDLess.log.level = Logger::WARN
|
24
18
|
|
25
|
-
|
19
|
+
MDLess.options = {}
|
26
20
|
config = File.expand_path('~/.config/mdless/config.yml')
|
27
|
-
|
21
|
+
MDLess.options = YAML.load(IO.read(config)) if File.exist?(config)
|
28
22
|
|
29
23
|
optparse = OptionParser.new do |opts|
|
30
24
|
opts.banner = "#{version} by Brett Terpstra\n\n> Usage: #{CLIMarkdown::EXECUTABLE_NAME} [options] [path]\n\n"
|
31
25
|
|
32
26
|
default(:color, true)
|
33
27
|
opts.on('-c', '--[no-]color', 'Colorize output (default on)') do |c|
|
34
|
-
|
28
|
+
MDLess.options[:color] = c
|
35
29
|
end
|
36
30
|
|
37
31
|
opts.on('-d', '--debug LEVEL', 'Level of debug messages to output (1-4, 4 to see all messages)') do |level|
|
38
32
|
if level.to_i.positive? && level.to_i < 5
|
39
|
-
|
33
|
+
MDLess.log.level = 5 - level.to_i
|
40
34
|
else
|
41
35
|
puts 'Error: Debug level out of range (1-4)'
|
42
36
|
Process.exit 1
|
@@ -55,58 +49,64 @@ module CLIMarkdown
|
|
55
49
|
if exec_available('imgcat') || exec_available('chafa')
|
56
50
|
case type
|
57
51
|
when /^(r|b|a)/i
|
58
|
-
|
59
|
-
|
52
|
+
MDLess.options[:local_images] = true
|
53
|
+
MDLess.options[:remote_images] = true
|
60
54
|
when /^l/i
|
61
|
-
|
55
|
+
MDLess.options[:local_images] = true
|
62
56
|
when /^n/
|
63
|
-
|
64
|
-
|
57
|
+
MDLess.options[:local_images] = false
|
58
|
+
MDLess.options[:remote_images] = false
|
65
59
|
end
|
66
60
|
else
|
67
|
-
|
61
|
+
MDLess.log.warn('images turned on but imgcat/chafa not found')
|
68
62
|
end
|
69
63
|
end
|
70
64
|
|
71
65
|
opts.on('-I', '--all-images', 'Include local and remote images in output (requires imgcat or chafa)') do
|
72
66
|
if exec_available('imgcat') || exec_available('chafa') # && ENV['TERM_PROGRAM'] == 'iTerm.app'
|
73
|
-
|
74
|
-
|
67
|
+
MDLess.options[:local_images] = true
|
68
|
+
MDLess.options[:remote_images] = true
|
75
69
|
else
|
76
|
-
|
70
|
+
MDLess.log.warn('images turned on but imgcat/chafa not found')
|
77
71
|
end
|
78
72
|
end
|
79
73
|
|
80
|
-
|
81
74
|
default(:list, false)
|
82
75
|
opts.on('-l', '--list', 'List headers in document and exit') do
|
83
|
-
|
76
|
+
MDLess.options[:list] = true
|
84
77
|
end
|
85
78
|
|
86
79
|
default(:pager, true)
|
87
80
|
opts.on('-p', '--[no-]pager', 'Formatted output to pager (default on)') do |p|
|
88
|
-
|
81
|
+
MDLess.options[:pager] = p
|
89
82
|
end
|
90
83
|
|
91
84
|
default(:pager, true)
|
92
85
|
opts.on('-P', 'Disable pager (same as --no-pager)') do
|
93
|
-
|
86
|
+
MDLess.options[:pager] = false
|
94
87
|
end
|
95
88
|
|
96
89
|
default(:section, nil)
|
97
90
|
opts.on('-s', '--section=NUMBER[,NUMBER]',
|
98
91
|
'Output only a headline-based section of the input (numeric from --list)') do |section|
|
99
|
-
|
92
|
+
sections = section.split(/ *, */).map(&:strip)
|
93
|
+
MDLess.options[:section] = sections.map do |sect|
|
94
|
+
if sect =~ /^\d+$/
|
95
|
+
sect.to_i
|
96
|
+
else
|
97
|
+
sect
|
98
|
+
end
|
99
|
+
end
|
100
100
|
end
|
101
101
|
|
102
102
|
default(:theme, 'default')
|
103
103
|
opts.on('-t', '--theme=THEME_NAME', 'Specify an alternate color theme to load') do |theme|
|
104
|
-
|
104
|
+
MDLess.options[:theme] = theme
|
105
105
|
end
|
106
106
|
|
107
107
|
default(:at_tags, false)
|
108
108
|
opts.on('-@', '--at_tags', 'Highlight @tags and values in the document') do
|
109
|
-
|
109
|
+
MDLess.options[:at_tags] = true
|
110
110
|
end
|
111
111
|
|
112
112
|
opts.on('-v', '--version', 'Display version number') do
|
@@ -116,37 +116,37 @@ module CLIMarkdown
|
|
116
116
|
|
117
117
|
default(:width, TTY::Screen.cols)
|
118
118
|
opts.on('-w', '--width=COLUMNS', 'Column width to format for (default: terminal width)') do |columns|
|
119
|
-
|
119
|
+
MDLess.options[:width] = columns.to_i
|
120
120
|
end
|
121
121
|
cols = TTY::Screen.cols
|
122
|
-
|
122
|
+
MDLess.options[:width] = cols if MDLess.options[:width] > cols
|
123
123
|
|
124
124
|
default(:autolink, true)
|
125
125
|
opts.on('--[no-]autolink', 'Convert bare URLs and emails to <links>') do |p|
|
126
|
-
|
126
|
+
MDLess.options[:autolink] = p
|
127
127
|
end
|
128
128
|
|
129
129
|
default(:inline_footnotes, false)
|
130
130
|
opts.on('--[no-]inline_footnotes',
|
131
131
|
'Display footnotes immediately after the paragraph that references them') do |p|
|
132
|
-
|
132
|
+
MDLess.options[:inline_footnotes] = p
|
133
133
|
end
|
134
134
|
|
135
135
|
default(:intra_emphasis, true)
|
136
136
|
opts.on('--[no-]intra-emphasis', 'Parse emphasis inside of words (e.g. Mark_down_)') do |opt|
|
137
|
-
|
137
|
+
MDLess.options[:intra_emphasis] = opt
|
138
138
|
end
|
139
139
|
|
140
140
|
default(:lax_spacing, true)
|
141
141
|
opts.on('--[no-]lax-spacing', 'Allow lax spacing') do |opt|
|
142
|
-
|
142
|
+
MDLess.options[:lax_spacing] = opt
|
143
143
|
end
|
144
144
|
|
145
145
|
default(:links, :inline)
|
146
146
|
opts.on('--links=FORMAT',
|
147
147
|
'Link style ([inline, reference, paragraph], default inline,
|
148
148
|
"paragraph" will position reference links after each paragraph)') do |fmt|
|
149
|
-
|
149
|
+
MDLess.options[:links] = case fmt
|
150
150
|
when /^:?r/i
|
151
151
|
:reference
|
152
152
|
when /^:?p/i
|
@@ -158,11 +158,11 @@ module CLIMarkdown
|
|
158
158
|
|
159
159
|
default(:syntax_higlight, false)
|
160
160
|
opts.on('--[no-]syntax', 'Syntax highlight code blocks') do |p|
|
161
|
-
|
161
|
+
MDLess.options[:syntax_higlight] = p
|
162
162
|
end
|
163
163
|
|
164
|
-
|
165
|
-
case
|
164
|
+
MDLess.options[:taskpaper] = if MDLess.options[:taskpaper]
|
165
|
+
case MDLess.options[:taskpaper].to_s
|
166
166
|
when /^[ty1]/
|
167
167
|
true
|
168
168
|
when /^a/
|
@@ -174,7 +174,7 @@ module CLIMarkdown
|
|
174
174
|
false
|
175
175
|
end
|
176
176
|
opts.on('--taskpaper=OPTION', 'Highlight TaskPaper format (true|false|auto)') do |tp|
|
177
|
-
|
177
|
+
MDLess.options[:taskpaper] = case tp
|
178
178
|
when /^[ty1]/
|
179
179
|
true
|
180
180
|
when /^a/
|
@@ -186,12 +186,12 @@ module CLIMarkdown
|
|
186
186
|
|
187
187
|
default(:update_config, false)
|
188
188
|
opts.on('--update_config', 'Update the configuration file with new keys and current command line options') do
|
189
|
-
|
189
|
+
MDLess.options[:update_config] = true
|
190
190
|
end
|
191
191
|
|
192
192
|
default(:wiki_links, false)
|
193
193
|
opts.on('--[no-]wiki-links', 'Highlight [[wiki links]]') do |opt|
|
194
|
-
|
194
|
+
MDLess.options[:wiki_links] = opt
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
@@ -202,20 +202,20 @@ module CLIMarkdown
|
|
202
202
|
exit 1
|
203
203
|
end
|
204
204
|
|
205
|
-
if !File.exist?(config) ||
|
205
|
+
if !File.exist?(config) || MDLess.options[:update_config]
|
206
206
|
FileUtils.mkdir_p(File.dirname(config))
|
207
207
|
File.open(config, 'w') do |f|
|
208
|
-
opts =
|
208
|
+
opts = MDLess.options.dup
|
209
209
|
opts.delete(:list)
|
210
210
|
opts.delete(:section)
|
211
211
|
opts.delete(:update_config)
|
212
|
+
opts = opts.keys.map(&:to_s).sort.map { |k| [k.to_sym, opts[k.to_sym]] }.to_h
|
212
213
|
f.puts YAML.dump(opts)
|
213
214
|
warn "Config file saved to #{config}"
|
214
215
|
end
|
215
216
|
end
|
216
217
|
|
217
|
-
|
218
|
-
@cols = @options[:width] - 2
|
218
|
+
MDLess.cols = MDLess.options[:width] - 2
|
219
219
|
|
220
220
|
@output = ''
|
221
221
|
@headers = []
|
@@ -226,19 +226,15 @@ module CLIMarkdown
|
|
226
226
|
@footnotes = {}
|
227
227
|
|
228
228
|
renderer = Redcarpet::Render::Console.new
|
229
|
-
renderer.theme = @theme
|
230
|
-
renderer.cols = @cols
|
231
|
-
renderer.log = @log
|
232
|
-
renderer.options = @options
|
233
229
|
|
234
230
|
markdown = Redcarpet::Markdown.new(renderer,
|
235
|
-
no_intra_emphasis:
|
236
|
-
autolink:
|
231
|
+
no_intra_emphasis: !MDLess.options[:intra_emphasis],
|
232
|
+
autolink: MDLess.options[:autolink],
|
237
233
|
fenced_code_blocks: true,
|
238
234
|
footnotes: true,
|
239
235
|
hard_wrap: false,
|
240
236
|
highlight: true,
|
241
|
-
lax_spacing:
|
237
|
+
lax_spacing: MDLess.options[:lax_spacing],
|
242
238
|
quote: false,
|
243
239
|
space_after_headers: false,
|
244
240
|
strikethrough: true,
|
@@ -249,9 +245,9 @@ module CLIMarkdown
|
|
249
245
|
if !args.empty?
|
250
246
|
files = args.delete_if { |f| !File.exist?(f) }
|
251
247
|
files.each do |file|
|
252
|
-
|
253
|
-
|
254
|
-
|
248
|
+
MDLess.log.info(%(Processing "#{file}"))
|
249
|
+
MDLess.file = file
|
250
|
+
|
255
251
|
begin
|
256
252
|
input = IO.read(file).force_encoding('utf-8')
|
257
253
|
rescue StandardError
|
@@ -262,26 +258,27 @@ module CLIMarkdown
|
|
262
258
|
input.scrub!
|
263
259
|
input.gsub!(/\r?\n/, "\n")
|
264
260
|
|
265
|
-
if
|
266
|
-
|
261
|
+
if MDLess.options[:taskpaper] == :auto
|
262
|
+
MDLess.options[:taskpaper] = if CLIMarkdown::TaskPaper.is_taskpaper?(input)
|
263
|
+
MDLess.log.info('TaskPaper detected')
|
264
|
+
true
|
265
|
+
else
|
266
|
+
false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
if MDLess.options[:list]
|
271
|
+
if MDLess.options[:taskpaper]
|
272
|
+
puts CLIMarkdown::TaskPaper.list_projects(input)
|
273
|
+
else
|
274
|
+
puts list_headers(input)
|
275
|
+
end
|
267
276
|
Process.exit 0
|
268
277
|
else
|
269
|
-
if
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
elsif CLIMarkdown::TaskPaper.is_taskpaper?(input)
|
274
|
-
@log.info('TaskPaper document detected')
|
275
|
-
true
|
276
|
-
else
|
277
|
-
false
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
if @options[:taskpaper]
|
282
|
-
input = input.color_meta(@theme, @log, @cols)
|
283
|
-
input = CLIMarkdown::TaskPaper.highlight(input, @theme)
|
284
|
-
@output = input.highlight_tags(@theme, @log)
|
278
|
+
if MDLess.options[:taskpaper]
|
279
|
+
input = input.color_meta(MDLess.cols)
|
280
|
+
input = CLIMarkdown::TaskPaper.highlight(input)
|
281
|
+
@output = input.highlight_tags
|
285
282
|
else
|
286
283
|
@output = markdown.render(input)
|
287
284
|
end
|
@@ -289,14 +286,34 @@ module CLIMarkdown
|
|
289
286
|
end
|
290
287
|
printout
|
291
288
|
elsif !$stdin.isatty
|
292
|
-
|
289
|
+
MDLess.file = nil
|
293
290
|
input = $stdin.read.scrub
|
294
291
|
input.gsub!(/\r?\n/, "\n")
|
295
|
-
|
296
|
-
|
292
|
+
|
293
|
+
if MDLess.options[:taskpaper] == :auto
|
294
|
+
MDLess.options[:taskpaper] = if CLIMarkdown::TaskPaper.is_taskpaper?(input)
|
295
|
+
MDLess.log.info('TaskPaper detected')
|
296
|
+
true
|
297
|
+
else
|
298
|
+
false
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
if MDLess.options[:list]
|
303
|
+
if MDLess.options[:taskpaper]
|
304
|
+
puts CLIMarkdown::TaskPaper.list_projects(input)
|
305
|
+
else
|
306
|
+
puts list_headers(input)
|
307
|
+
end
|
297
308
|
Process.exit 0
|
298
309
|
else
|
299
|
-
|
310
|
+
if MDLess.options[:taskpaper]
|
311
|
+
input = input.color_meta(MDLess.cols)
|
312
|
+
input = CLIMarkdown::TaskPaper.highlight(input)
|
313
|
+
@output = input.highlight_tags
|
314
|
+
else
|
315
|
+
@output = markdown.render(input)
|
316
|
+
end
|
300
317
|
end
|
301
318
|
printout
|
302
319
|
else
|
@@ -308,17 +325,17 @@ module CLIMarkdown
|
|
308
325
|
def color(key)
|
309
326
|
val = nil
|
310
327
|
keys = key.split(/[ ,>]/)
|
311
|
-
if
|
312
|
-
val =
|
328
|
+
if MDLess.theme.key?(keys[0])
|
329
|
+
val = MDLess.theme[keys.shift]
|
313
330
|
else
|
314
|
-
|
331
|
+
MDLess.log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
|
315
332
|
return c([:reset])
|
316
333
|
end
|
317
334
|
keys.each do |k|
|
318
335
|
if val.key?(k)
|
319
336
|
val = val[k]
|
320
337
|
else
|
321
|
-
|
338
|
+
MDLess.log.error("Invalid theme key: #{k}")
|
322
339
|
return c([:reset])
|
323
340
|
end
|
324
341
|
end
|
@@ -410,8 +427,8 @@ module CLIMarkdown
|
|
410
427
|
|
411
428
|
def clean_markers(input)
|
412
429
|
input.gsub!(/^(\e\[[\d;]+m)?[%~] ?/, '\1')
|
413
|
-
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?( +)/, ' \3\1\2')
|
414
|
-
input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?/, '\1\2')
|
430
|
+
# input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?( +)/, ' \3\1\2')
|
431
|
+
# input.gsub!(/^(\e\[[\d;]+m)*>(\e\[[\d;]+m)?/, '\1\2')
|
415
432
|
input.gsub!(/(\e\[[\d;]+m)?@@@(\e\[[\d;]+m)?$/, '')
|
416
433
|
input
|
417
434
|
end
|
@@ -440,7 +457,7 @@ module CLIMarkdown
|
|
440
457
|
block.split(/\n/).map do |l|
|
441
458
|
new_code_line = l.gsub(/\t/, ' ')
|
442
459
|
orig_length = new_code_line.size + 8 + eol.size
|
443
|
-
pad_count = [
|
460
|
+
pad_count = [MDLess.cols - orig_length, 0].max
|
444
461
|
|
445
462
|
[
|
446
463
|
new_code_line,
|
@@ -464,11 +481,11 @@ module CLIMarkdown
|
|
464
481
|
IO.select [input]
|
465
482
|
|
466
483
|
pager = which_pager
|
467
|
-
|
484
|
+
MDLess.log.info("Using #{pager} as pager")
|
468
485
|
begin
|
469
486
|
exec(pager.join(' '))
|
470
487
|
rescue SystemCallError => e
|
471
|
-
|
488
|
+
MDLess.log.error(e)
|
472
489
|
exit 1
|
473
490
|
end
|
474
491
|
end
|
@@ -486,21 +503,25 @@ module CLIMarkdown
|
|
486
503
|
end
|
487
504
|
|
488
505
|
def printout
|
489
|
-
|
490
|
-
|
491
|
-
|
506
|
+
if MDLess.options[:taskpaper]
|
507
|
+
out = @output
|
508
|
+
else
|
509
|
+
out = @output.rstrip.split(/\n/).map do |p|
|
510
|
+
p.wrap(MDLess.cols, color('text'))
|
511
|
+
end.join("\n")
|
512
|
+
end
|
492
513
|
|
493
514
|
unless out.size&.positive?
|
494
|
-
|
515
|
+
MDLess.log.warn 'No results'
|
495
516
|
Process.exit
|
496
517
|
end
|
497
518
|
|
498
519
|
out = clean_markers(out)
|
499
520
|
out = "#{out.gsub(/\n{2,}/m, "\n\n")}#{xc}"
|
500
521
|
|
501
|
-
out.uncolor! unless
|
522
|
+
out.uncolor! unless MDLess.options[:color]
|
502
523
|
|
503
|
-
if
|
524
|
+
if MDLess.options[:pager]
|
504
525
|
page(out)
|
505
526
|
else
|
506
527
|
$stdout.print out.rstrip
|
@@ -523,7 +544,7 @@ module CLIMarkdown
|
|
523
544
|
if f.strip =~ /[ |]/
|
524
545
|
f
|
525
546
|
elsif f == 'most'
|
526
|
-
|
547
|
+
MDLess.log.warn('most not allowed as pager')
|
527
548
|
false
|
528
549
|
else
|
529
550
|
system "which #{f}", out: File::NULL, err: File::NULL
|
data/lib/mdless/string.rb
CHANGED
@@ -12,20 +12,20 @@ class ::String
|
|
12
12
|
replace clean_empty_lines
|
13
13
|
end
|
14
14
|
|
15
|
-
def color(key
|
15
|
+
def color(key)
|
16
16
|
val = nil
|
17
17
|
keys = key.split(/[ ,>]/)
|
18
|
-
if theme.key?(keys[0])
|
19
|
-
val = theme[keys.shift]
|
18
|
+
if MDLess.theme.key?(keys[0])
|
19
|
+
val = MDLess.theme[keys.shift]
|
20
20
|
else
|
21
|
-
log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
|
21
|
+
MDLess.log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
|
22
22
|
return c([:reset])
|
23
23
|
end
|
24
24
|
keys.each do |k|
|
25
25
|
if val.key?(k)
|
26
26
|
val = val[k]
|
27
27
|
else
|
28
|
-
log.error("Invalid theme key: #{k}")
|
28
|
+
MDLess.log.error("Invalid theme key: #{k}")
|
29
29
|
return c([:reset])
|
30
30
|
end
|
31
31
|
end
|
@@ -38,9 +38,21 @@ class ::String
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
41
|
+
def to_rx(distance: 2, string_start: false)
|
42
|
+
chars = downcase.split(//)
|
43
|
+
pre = string_start ? '^' : '^.*?'
|
44
|
+
/#{pre}#{chars.join(".{,#{distance}}")}.*?$/
|
45
|
+
end
|
46
|
+
|
47
|
+
def clean_header_ids!
|
48
|
+
replace clean_header_ids
|
49
|
+
end
|
50
|
+
|
51
|
+
def clean_header_ids
|
52
|
+
gsub(/ +\[.*?\] *$/, '').gsub(/ *\{#.*?\} *$/, '').strip
|
53
|
+
end
|
54
|
+
|
55
|
+
def color_meta(cols)
|
44
56
|
@cols = cols
|
45
57
|
input = dup
|
46
58
|
input.clean_empty_lines!
|
@@ -48,21 +60,21 @@ class ::String
|
|
48
60
|
in_yaml = false
|
49
61
|
first_line = input.split("\n").first
|
50
62
|
if first_line =~ /(?i-m)^---[ \t]*?$/
|
51
|
-
|
63
|
+
MDLess.log.info('Found YAML')
|
52
64
|
# YAML
|
53
65
|
in_yaml = true
|
54
66
|
input.sub!(/(?i-m)^---[ \t]*\n([\s\S]*?)\n[-.]{3}[ \t]*\n/m) do
|
55
67
|
m = Regexp.last_match
|
56
|
-
|
68
|
+
MDLess.log.info('Processing YAML Header')
|
57
69
|
lines = m[0].split(/\n/)
|
58
70
|
longest = lines.inject { |memo, word| memo.length > word.length ? memo : word }.length
|
59
71
|
longest = longest < @cols ? longest + 1 : @cols
|
60
72
|
lines.map do |line|
|
61
73
|
if line =~ /^[-.]{3}\s*$/
|
62
|
-
line = "#{color('metadata marker'
|
74
|
+
line = "#{color('metadata marker')}#{'%' * longest}"
|
63
75
|
else
|
64
76
|
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
65
|
-
line = "#{color('metadata color'
|
77
|
+
line = "#{color('metadata color')}#{line}"
|
66
78
|
end
|
67
79
|
|
68
80
|
line += "\u00A0" * (longest - line.uncolor.strip.length) + xc
|
@@ -72,7 +84,7 @@ class ::String
|
|
72
84
|
end
|
73
85
|
|
74
86
|
if !in_yaml && first_line =~ /(?i-m)^[\w ]+:\s+\S+/
|
75
|
-
|
87
|
+
MDLess.log.info('Found MMD Headers')
|
76
88
|
input.sub!(/(?i-m)^([\S ]+:[\s\S]*?)+(?=\n\n)/) do |mmd|
|
77
89
|
lines = mmd.split(/\n/)
|
78
90
|
return mmd if lines.count > 20
|
@@ -81,7 +93,7 @@ class ::String
|
|
81
93
|
longest = longest < @cols ? longest + 1 : @cols
|
82
94
|
lines.map do |line|
|
83
95
|
line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
|
84
|
-
line = "#{color('metadata color'
|
96
|
+
line = "#{color('metadata color')}#{line}"
|
85
97
|
line += "\u00A0" * (longest - line.uncolor.strip.length)
|
86
98
|
line + xc
|
87
99
|
end.join("\n") + "#{"\u00A0" * longest}#{xc}\n"
|
@@ -91,9 +103,10 @@ class ::String
|
|
91
103
|
input
|
92
104
|
end
|
93
105
|
|
94
|
-
def highlight_tags
|
95
|
-
|
96
|
-
|
106
|
+
def highlight_tags
|
107
|
+
log = MDLess.log
|
108
|
+
tag_color = color('at_tags tag')
|
109
|
+
value_color = color('at_tags value')
|
97
110
|
gsub(/(?<pre>\s|m)(?<tag>@[^ \].?!,("']+)(?:(?<lparen>\()(?<value>.*?)(?<rparen>\)))?/) do
|
98
111
|
m = Regexp.last_match
|
99
112
|
last_color = m.pre_match.last_color_code
|
@@ -122,20 +135,13 @@ class ::String
|
|
122
135
|
|
123
136
|
def valid_pygments_theme?
|
124
137
|
return false unless TTY::Which.exist?('pygmentize')
|
125
|
-
|
126
|
-
|
127
|
-
styles = res.scan(/\* ([\w-]+):/).map { |l| l[0] }
|
128
|
-
styles.include?(self)
|
138
|
+
|
139
|
+
MDLess.pygments_styles.include?(self)
|
129
140
|
end
|
130
141
|
|
131
142
|
def valid_lexer?
|
132
143
|
return false unless TTY::Which.exist?('pygmentize')
|
133
|
-
pyg = TTY::Which.which('pygmentize')
|
134
|
-
res = `#{pyg} -L lexers`
|
135
|
-
lexers = res.scan(/\* ([\w-]+(?:, [\w-]+)*):/).map { |l| l[0] }
|
136
|
-
lexers_a = []
|
137
|
-
lexers.each { |l| lexers_a.concat(l.split(/, /)) }
|
138
144
|
|
139
|
-
|
145
|
+
MDLess.pygments_lexers.include?(self.downcase)
|
140
146
|
end
|
141
147
|
end
|
data/lib/mdless/taskpaper.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module CLIMarkdown
|
4
4
|
module TaskPaper
|
5
5
|
TASK_RX = /^(?<indent>(?: |\t)*?)(?<marker>-)(?<task>\s+\S.*?)$/
|
6
|
-
PROJECT_RX = /^(?<indent>(?: |\t)*?)(?<project>[^- \t].*?:)(?<tags>
|
6
|
+
PROJECT_RX = /^(?<indent>(?: |\t)*?)(?<project>[^- \t].*?:)(?<tags> +@\S+)*$/
|
7
7
|
NOTE_RX = /^(?<indent>(?: |\t)+)(?<note>(?<!- ).*?(?!:))$/
|
8
8
|
|
9
9
|
class << self
|
@@ -13,8 +13,8 @@ module CLIMarkdown
|
|
13
13
|
def color(key)
|
14
14
|
val = nil
|
15
15
|
keys = key.split(/[ ,>]/)
|
16
|
-
if
|
17
|
-
val =
|
16
|
+
if MDLess.theme.key?(keys[0])
|
17
|
+
val = MDLess.theme[keys.shift]
|
18
18
|
else
|
19
19
|
@log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
|
20
20
|
return c([:reset])
|
@@ -36,25 +36,95 @@ module CLIMarkdown
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
def remove_meta(input)
|
40
|
+
first_line = input.split("\n").first
|
41
|
+
if first_line =~ /(?i-m)^---[ \t]*?$/
|
42
|
+
input.sub!(/(?im)^---[ \t]*\n([\s\S]*?)\n[-.]{3}[ \t]*\n/, '')
|
43
|
+
elsif first_line =~ /(?i-m)^[\w ]+:\s+\S+/
|
44
|
+
input.sub!(/(?im)^([\S ]+:[\s\S]*?)+(?=\n\n)/, '')
|
45
|
+
end
|
46
|
+
input
|
47
|
+
end
|
48
|
+
|
39
49
|
def is_taskpaper?(input)
|
40
|
-
|
50
|
+
return true if MDLess.file =~ /\.taskpaper$/
|
51
|
+
|
52
|
+
projects = sections(input)
|
53
|
+
|
41
54
|
tasks = 0
|
42
55
|
if projects.count > 1
|
43
|
-
projects.each do |proj|
|
44
|
-
tasks +=
|
56
|
+
projects.each do |proj, content|
|
57
|
+
tasks += content['content'].scan(TASK_RX).count
|
45
58
|
end
|
46
59
|
end
|
47
60
|
|
48
|
-
tasks >= 6
|
61
|
+
if tasks >= 6
|
62
|
+
return true
|
63
|
+
else
|
64
|
+
tst = remove_meta(input)
|
65
|
+
tst = tst.gsub(PROJECT_RX, '')
|
66
|
+
tst = tst.gsub(TASK_RX, '')
|
67
|
+
tst = tst.gsub(/^ *\n$/, '')
|
68
|
+
return tst.strip.length == 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def section(input, string)
|
73
|
+
sects = sections(input)
|
74
|
+
sects_to_s(sects.filter { |k, _| k.downcase =~ string.downcase.to_rx })
|
75
|
+
end
|
76
|
+
|
77
|
+
def sects_to_s(sects)
|
78
|
+
sects.map do |k, v|
|
79
|
+
"#{k}#{v['content']}"
|
80
|
+
end.join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
def indent(input, idnt)
|
84
|
+
input.split(/\n/).map do |line|
|
85
|
+
line.sub(/^#{idnt}/, '')
|
86
|
+
end.join("\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
def sections(input)
|
90
|
+
heirarchy = {}
|
91
|
+
sects = input.to_enum(:scan, /(?mix)
|
92
|
+
(?<=\n|\A)(?<indent>(?: |\t)*?)
|
93
|
+
(?<project>[^- \t\n].*?:)\s*(?=\n)
|
94
|
+
(?<content>.*?)
|
95
|
+
(?=\n\k<indent>\S.*?:|\Z)$/).map { Regexp.last_match }
|
96
|
+
sects.each do |sect|
|
97
|
+
heirarchy[sect['project']] = {}
|
98
|
+
heirarchy[sect['project']]['content'] = indent(sect['content'], sect['indent'])
|
99
|
+
heirarchy = heirarchy.merge(sections(sect['content']))
|
100
|
+
end
|
101
|
+
|
102
|
+
heirarchy
|
49
103
|
end
|
50
104
|
|
51
|
-
def
|
52
|
-
|
105
|
+
def list_projects(input)
|
106
|
+
projects = input.to_enum(:scan, PROJECT_RX).map { Regexp.last_match }
|
107
|
+
projects.delete_if { |proj| proj['project'] =~ /^[ \n]*$/ }
|
108
|
+
projects.map! { |proj| "#{color('taskpaper marker')}#{proj['indent']}- #{color('taskpaper project')}#{proj['project'].sub(/:$/, '')}" }
|
109
|
+
projects.join("\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
def highlight(input)
|
53
113
|
mc = color('taskpaper marker')
|
54
114
|
tc = color('taskpaper task')
|
55
115
|
pc = color('taskpaper project')
|
56
116
|
nc = color('taskpaper note')
|
57
117
|
|
118
|
+
if MDLess.options[:section]
|
119
|
+
matches = []
|
120
|
+
MDLess.options[:section].each do |sect|
|
121
|
+
matches << section(input, sect)
|
122
|
+
end
|
123
|
+
input = matches.join("\n")
|
124
|
+
end
|
125
|
+
|
126
|
+
input.gsub!(/\t/, ' ')
|
127
|
+
|
58
128
|
input.gsub!(PROJECT_RX) do
|
59
129
|
m = Regexp.last_match
|
60
130
|
"#{m['indent']}#{pc}#{m['project']}#{m['tags']}"
|
data/lib/mdless/version.rb
CHANGED
data/lib/mdless.rb
CHANGED
@@ -20,3 +20,47 @@ require 'mdless/converter'
|
|
20
20
|
module CLIMarkdown
|
21
21
|
EXECUTABLE_NAME = 'mdless'
|
22
22
|
end
|
23
|
+
|
24
|
+
module MDLess
|
25
|
+
class << self
|
26
|
+
include CLIMarkdown::Theme
|
27
|
+
attr_accessor :options, :cols, :file
|
28
|
+
|
29
|
+
def log
|
30
|
+
@log ||= Logger.new($stderr)
|
31
|
+
end
|
32
|
+
|
33
|
+
def log_level(level)
|
34
|
+
@log.level = level
|
35
|
+
end
|
36
|
+
|
37
|
+
def theme
|
38
|
+
@theme ||= load_theme(@options[:theme])
|
39
|
+
end
|
40
|
+
|
41
|
+
def pygments_styles
|
42
|
+
@pygments_styles ||= read_pygments_styles
|
43
|
+
end
|
44
|
+
|
45
|
+
def pygments_lexers
|
46
|
+
@pygments_lexers ||= read_pygments_lexers
|
47
|
+
end
|
48
|
+
|
49
|
+
def read_pygments_styles
|
50
|
+
MDLess.log.info 'Reading Pygments styles'
|
51
|
+
pyg = TTY::Which.which('pygmentize')
|
52
|
+
res = `#{pyg} -L styles`
|
53
|
+
res.scan(/\* ([\w-]+):/).map { |l| l[0] }
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_pygments_lexers
|
57
|
+
MDLess.log.info 'Reading Pygments lexers'
|
58
|
+
pyg = TTY::Which.which('pygmentize')
|
59
|
+
res = `#{pyg} -L lexers`
|
60
|
+
lexers = res.scan(/\* ([\w-]+(?:, [\w-]+)*):/).map { |l| l[0] }
|
61
|
+
lexers_a = []
|
62
|
+
lexers.each { |l| lexers_a.concat(l.split(/, /)) }
|
63
|
+
lexers_a
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mdless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.19
|
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-
|
11
|
+
date: 2023-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redcarpet
|