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