mdless 2.0.18 → 2.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|