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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8cad0405ab64bec59625210dfbf2708d23609ccc5f682b1a7c26d435ffb3d49
4
- data.tar.gz: 747d4a04599b35b21a1a83c2607145cac4c0c5748acf6d0bde8ec024bcb7fd69
3
+ metadata.gz: '094b8ac8273e025bee23c350cc7aee02682b2445d22523d216b4e621d067ff9b'
4
+ data.tar.gz: 495dc941df5b4235c9bb5d7cccb37739e1cc783679d276d026d36a7a9dafc937
5
5
  SHA512:
6
- metadata.gz: aacd16705ef1fa770536f10b3642fa536a6db9418248c760e9c47dce8ffbfca9fb55401d7f0507ae82bf6805080f530777d640eebb41dc513772c2c98b5c1e66
7
- data.tar.gz: 69243a2f1968d871b222740ea9f3a8dd8f76895be6c49e8fb59db20f2ef85c78046b880eab0e95f4a6e1a6b377954904b15670d4f7ed1397cfa821da7a70f857
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
- end
162
- lines << line + self.match(/\s*$/)[0] + xc(foreground) if line
163
- return lines.join("\n") # .gsub(/\- (\S)/,'-\1')
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)
@@ -4,13 +4,13 @@ module Redcarpet
4
4
  include CLIMarkdown::Colors
5
5
  include CLIMarkdown::Theme
6
6
 
7
- attr_writer :theme, :cols, :log, :options, :file
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 @options[:syntax_higlight] && !exec_available('pygmentize')
70
- @log.error('Syntax highlighting requested by pygmentize is not available')
71
- @options[:syntax_higlight] = false
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 > @cols ? @cols : longest_line
75
+ longest_line = longest_line > MDLess.cols ? MDLess.cols : longest_line
76
76
 
77
- if @options[:syntax_higlight]
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 = @options[:pygments_theme] || @theme['code_block']['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
- @log.error("Invalid Pygments theme #{pygments_theme}, defaulting to 'default' for highlighting")
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(@theme['code_block']['bg']) + "#{xc}\n"
103
+ end.join("\n").blackout(MDLess.theme['code_block']['bg']) + "#{xc}\n"
104
104
  end
105
105
  rescue StandardError => e
106
- @log.error(e)
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(@theme['code_block']['bg']) + "#{xc}\n"
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 @theme.key?(keys[0])
146
- val = @theme[keys.shift]
145
+ if MDLess.theme.key?(keys[0])
146
+ val = MDLess.theme[keys.shift]
147
147
  else
148
- @log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
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
- @log.error("Invalid theme key: #{k}")
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("\n").each do |line|
174
+ quote.wrap(MDLess.cols, color('blockquote color')).split(/\n/).each do |line|
175
175
  ret += [
176
176
  color('blockquote marker color'),
177
- @theme['blockquote']['marker']['character'],
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 = @theme['h1']['pad_char'] || '='
198
- pad += text.length + 2 > @cols ? char * text.length : char * (@cols - (text.length + 1))
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 = @theme['h2']['pad_char'] || '-'
203
- pad += text.length + 2 > @cols ? char * text.length : char * (@cols - (text.length + 1))
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
- @options[:pager] == false
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')}#{'_' * @cols}#{xc}\n\n"
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
- @theme['code_span']['character'],
295
+ MDLess.theme['code_span']['character'],
294
296
  color('code_span color'),
295
297
  code,
296
298
  color('code_span marker'),
297
- @theme['code_span']['character'],
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
- @theme['emphasis']['bold_character'],
309
+ MDLess.theme['emphasis']['bold_character'],
308
310
  text,
309
- @theme['emphasis']['bold_character'],
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
- @theme['emphasis']['italic_character'],
321
+ MDLess.theme['emphasis']['italic_character'],
320
322
  text,
321
- @theme['emphasis']['italic_character'],
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
- @theme['emphasis']['italic_character'],
332
- @theme['emphasis']['bold_character'],
333
+ MDLess.theme['emphasis']['italic_character'],
334
+ MDLess.theme['emphasis']['bold_character'],
333
335
  text,
334
- @theme['emphasis']['bold_character'],
335
- @theme['emphasis']['italic_character'],
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 = @cols - pre.length
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 @@headers && !@@headers.empty?
626
- @@headers = []
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
- @@headers << [
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
- @@headers
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
- @log.info('Found YAML')
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
- @log.info('Processing YAML Header')
666
+ MDLess.log.info('Processing YAML Header')
665
667
  lines = m[0].split(/\n/)
666
668
  longest = lines.longest_element.length
667
- longest = longest < @cols ? longest + 1 : @cols
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
- @log.info('Found MMD Headers')
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 < @cols ? longest + 1 : @cols
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
- @@headers = get_headers(input)
711
+ @headers = get_headers(input)
710
712
 
711
- if @options[:section]
713
+ if MDLess.options[:section]
712
714
  new_content = []
713
- @options[:section].each do |sect|
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/).each do |graf|
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 == @@headers[sect - 1][1].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
- next if graf =~ /^$/
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 @options[:links] == :paragraph
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 @options[:links] == :paragraph
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')) && @options[:local_images]
829
+ if (exec_available('imgcat') || exec_available('chafa')) && MDLess.options[:local_images]
824
830
  if exec_available('imgcat')
825
- @log.info('Using imgcat for image rendering')
831
+ MDLess.log.info('Using imgcat for image rendering')
826
832
  elsif exec_available('chafa')
827
- @log.info('Using chafa for image rendering')
833
+ MDLess.log.info('Using chafa for image rendering')
828
834
  end
829
835
  img_path = link
830
- if img_path =~ /^http/ && @options[:remote_images]
836
+ if img_path =~ /^http/ && MDLess.options[:remote_images]
831
837
  if exec_available('imgcat')
832
- @log.info('Using imgcat for image rendering')
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
- @log.error(e)
848
+ MDLess.log.error(e)
843
849
  end
844
850
  elsif exec_available('chafa')
845
- @log.info('Using chafa for image rendering')
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
- @log.warn('No viewer for remote images')
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 @file
867
- base = File.expand_path(File.dirname(@file))
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 @options[:wiki_links]
965
+ input = highlight_wiki_links(input) if MDLess.options[:wiki_links]
960
966
 
961
- if @options[:inline_footnotes]
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 @options[:links] == :reference || @options[:links] == :paragraph
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 @options[:local_images]
982
- input = highlight_tags(input) if @options[:at_tags] || @options[:taskpaper]
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
@@ -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 @options[option].nil?
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
- @log = Logger.new($stderr)
23
- @log.level = Logger::WARN
17
+ MDLess.log.level = Logger::WARN
24
18
 
25
- @options = {}
19
+ MDLess.options = {}
26
20
  config = File.expand_path('~/.config/mdless/config.yml')
27
- @options = YAML.load(IO.read(config)) if File.exist?(config)
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
- @options[:color] = c
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
- @log.level = 5 - level.to_i
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
- @options[:local_images] = true
59
- @options[:remote_images] = true
52
+ MDLess.options[:local_images] = true
53
+ MDLess.options[:remote_images] = true
60
54
  when /^l/i
61
- @options[:local_images] = true
55
+ MDLess.options[:local_images] = true
62
56
  when /^n/
63
- @options[:local_images] = false
64
- @options[:remote_images] = false
57
+ MDLess.options[:local_images] = false
58
+ MDLess.options[:remote_images] = false
65
59
  end
66
60
  else
67
- @log.warn('images turned on but imgcat/chafa not found')
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
- @options[:local_images] = true
74
- @options[:remote_images] = true
67
+ MDLess.options[:local_images] = true
68
+ MDLess.options[:remote_images] = true
75
69
  else
76
- @log.warn('images turned on but imgcat/chafa not found')
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
- @options[:list] = true
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
- @options[:pager] = p
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
- @options[:pager] = false
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
- @options[:section] = section.split(/ *, */).map(&:strip).map(&:to_i)
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
- @options[:theme] = theme
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
- @options[:at_tags] = true
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
- @options[:width] = columns.to_i
119
+ MDLess.options[:width] = columns.to_i
120
120
  end
121
121
  cols = TTY::Screen.cols
122
- @options[:width] = cols if @options[:width] > cols
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
- @options[:autolink] = p
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
- @options[:inline_footnotes] = p
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
- @options[:intra_emphasis] = opt
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
- @options[:lax_spacing] = opt
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
- @options[:links] = case fmt
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
- @options[:syntax_higlight] = p
161
+ MDLess.options[:syntax_higlight] = p
162
162
  end
163
163
 
164
- @options[:taskpaper] = if @options[:taskpaper]
165
- case @options[:taskpaper].to_s
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
- @options[:taskpaper] = case tp
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
- @options[:update_config] = true
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
- @options[:wiki_links] = opt
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) || @options[:update_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 = @options.dup
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
- @theme = load_theme(@options[:theme])
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: !@options[:intra_emphasis],
236
- autolink: @options[: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: @options[: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
- @log.info(%(Processing "#{file}"))
253
- @file = file
254
- renderer.file = @file
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 @options[:list]
266
- puts list_headers(input)
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 @options[:taskpaper] == :auto
270
- @options[:taskpaper] = if file =~ /\.taskpaper/
271
- @log.info('TaskPaper extension detected')
272
- true
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
- @file = nil
289
+ MDLess.file = nil
293
290
  input = $stdin.read.scrub
294
291
  input.gsub!(/\r?\n/, "\n")
295
- if @options[:list]
296
- puts list_headers(input)
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
- @output = markdown.render(input)
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 @theme.key?(keys[0])
312
- val = @theme[keys.shift]
328
+ if MDLess.theme.key?(keys[0])
329
+ val = MDLess.theme[keys.shift]
313
330
  else
314
- @log.error("Invalid theme key: #{key}") unless keys[0] =~ /^text/
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
- @log.error("Invalid theme key: #{k}")
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 = [@cols - orig_length, 0].max
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
- @log.info("Using #{pager} as pager")
484
+ MDLess.log.info("Using #{pager} as pager")
468
485
  begin
469
486
  exec(pager.join(' '))
470
487
  rescue SystemCallError => e
471
- @log.error(e)
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
- out = @output.rstrip.split(/\n/).map do |p|
490
- p.wrap(@cols, color('text'))
491
- end.join("\n")
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
- @log.warn 'No results'
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 @options[:color]
522
+ out.uncolor! unless MDLess.options[:color]
502
523
 
503
- if @options[:pager]
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
- @log.warn('most not allowed as pager')
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, theme, log)
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 color_meta(theme, log, cols)
42
- @theme = theme
43
- @log = log
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
- @log.info('Found YAML')
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
- @log.info('Processing YAML Header')
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', @theme, @log)}#{'%' * longest}"
74
+ line = "#{color('metadata marker')}#{'%' * longest}"
63
75
  else
64
76
  line.sub!(/^(.*?:)[ \t]+(\S)/, '\1 \2')
65
- line = "#{color('metadata color', @theme, @log)}#{line}"
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
- @log.info('Found MMD Headers')
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', @theme, @log)}#{line}"
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(theme, log)
95
- tag_color = color('at_tags tag', theme, log)
96
- value_color = color('at_tags value', theme, log)
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
- pyg = TTY::Which.which('pygmentize')
126
- res = `#{pyg} -L styles`
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
- lexers_a.include?(self.downcase)
145
+ MDLess.pygments_lexers.include?(self.downcase)
140
146
  end
141
147
  end
@@ -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> @\S+)*$/
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 @theme.key?(keys[0])
17
- val = @theme[keys.shift]
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
- projects = input.split(PROJECT_RX)
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 += proj.scan(TASK_RX).count
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 highlight(input, theme)
52
- @theme = theme
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']}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CLIMarkdown
4
- VERSION = '2.0.18'
4
+ VERSION = '2.0.19'
5
5
  end
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.18
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-26 00:00:00.000000000 Z
11
+ date: 2023-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redcarpet