kramdown-man 0.1.9 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../man/version'
3
+ require 'kramdown/man/version'
4
4
 
5
5
  require 'kramdown/converter/base'
6
6
 
7
7
  module Kramdown
8
- module Converter
8
+ module Man
9
9
  #
10
10
  # Converts markdown into a roff man-page.
11
11
  #
12
- class Man < ::Kramdown::Converter::Base
12
+ # @since 1.0.0
13
+ #
14
+ # @api private
15
+ #
16
+ class Converter < Kramdown::Converter::Base
13
17
 
14
18
  # Comment header
15
- HEADER = [
16
- ".\\\" Generated by kramdown-man #{::Kramdown::Man::VERSION}",
17
- ".\\\" https://github.com/postmodern/kramdown-man#readme"
18
- ].join("\n")
19
+ HEADER = <<~ROFF
20
+ .\\\" Generated by kramdown-man #{VERSION}
21
+ .\\\" https://github.com/postmodern/kramdown-man#readme
22
+ ROFF
19
23
 
20
24
  # Typographic Symbols and their UTF8 chars
21
25
  TYPOGRAPHIC_SYMS = {
@@ -427,7 +431,7 @@ module Kramdown
427
431
  # The roff output.
428
432
  #
429
433
  def convert(root)
430
- "#{HEADER}\n#{convert_root(root)}"
434
+ "#{HEADER}#{convert_root(root)}"
431
435
  end
432
436
 
433
437
  #
@@ -440,9 +444,7 @@ module Kramdown
440
444
  # The roff output.
441
445
  #
442
446
  def convert_root(root)
443
- root.children.map { |child|
444
- convert_element(child)
445
- }.compact.join("\n")
447
+ convert_children_of(root)
446
448
  end
447
449
 
448
450
  #
@@ -456,20 +458,19 @@ module Kramdown
456
458
  #
457
459
  def convert_element(element)
458
460
  method = "convert_#{element.type}"
461
+
459
462
  send(method,element) if respond_to?(method)
460
463
  end
461
-
464
+
462
465
  #
463
466
  # Converts a `kd:blank` element.
464
467
  #
465
468
  # @param [Kramdown::Element] blank
466
469
  # A `kd:blank` element.
467
470
  #
468
- # @return [String]
469
- # The roff output.
471
+ # @return [nil]
470
472
  #
471
473
  def convert_blank(blank)
472
- '.LP'
473
474
  end
474
475
 
475
476
  #
@@ -482,7 +483,7 @@ module Kramdown
482
483
  # The roff output.
483
484
  #
484
485
  def convert_text(text)
485
- escape(text.value.gsub(/^( +|\t)/,''))
486
+ escape(text.value)
486
487
  end
487
488
 
488
489
  #
@@ -524,23 +525,30 @@ module Kramdown
524
525
  text = header.options[:raw_text]
525
526
 
526
527
  case header.options[:level]
527
- when 1 then ".TH #{text}"
528
- when 2 then ".SH #{text}"
529
- else ".SS #{text}"
528
+ when 1
529
+ <<~ROFF
530
+ .TH #{text}
531
+ ROFF
532
+ when 2
533
+ <<~ROFF
534
+ .SH #{text}
535
+ ROFF
536
+ else
537
+ <<~ROFF
538
+ .SS #{text}
539
+ ROFF
530
540
  end
531
541
  end
532
542
 
533
543
  #
534
- # Converts a `kd:hr` element.
544
+ # Ignore `kd:hr` elements.
535
545
  #
536
546
  # @param [Kramdown::Element] hr
537
547
  # A `kd:hr` element.
538
548
  #
539
- # @return [String]
540
- # The roff output.
549
+ # @return [nil]
541
550
  #
542
551
  def convert_hr(hr)
543
- ".ti 0\n\\l'\\n(.lu'"
544
552
  end
545
553
 
546
554
  #
@@ -553,9 +561,17 @@ module Kramdown
553
561
  # The roff output.
554
562
  #
555
563
  def convert_ul(ul)
556
- content = ul.children.map { |li| convert_ul_li(li) }.join("\n")
564
+ contents = String.new(encoding: Encoding::UTF_8)
565
+
566
+ ul.children.each do |li|
567
+ contents << convert_ul_li(li)
568
+ end
557
569
 
558
- return ".RS\n#{content}\n.RE"
570
+ return <<~ROFF
571
+ .RS
572
+ #{contents.chomp}
573
+ .RE
574
+ ROFF
559
575
  end
560
576
 
561
577
  #
@@ -568,15 +584,27 @@ module Kramdown
568
584
  # The roff output.
569
585
  #
570
586
  def convert_ul_li(li)
571
- li.children.each_with_index.map { |child,index|
587
+ roff = String.new(encoding: Encoding::UTF_8)
588
+
589
+ li.children.each_with_index do |child,index|
572
590
  if child.type == :p
573
- content = convert_children(child.children)
591
+ contents = convert_children_of(child)
574
592
 
575
- if index == 0 then ".IP \\(bu 2\n#{content}"
576
- else ".IP \\( 2\n#{content}"
577
- end
593
+ roff << if index == 0
594
+ <<~ROFF
595
+ .IP \\(bu 2
596
+ #{contents.chomp}
597
+ ROFF
598
+ else
599
+ <<~ROFF
600
+ .IP \\( 2
601
+ #{contents.chomp}
602
+ ROFF
603
+ end
578
604
  end
579
- }.compact.join("\n")
605
+ end
606
+
607
+ return roff
580
608
  end
581
609
 
582
610
  #
@@ -591,10 +619,18 @@ module Kramdown
591
619
  def convert_ol(ol)
592
620
  @ol_index += 1
593
621
 
594
- header = ".nr step#{@ol_index} 0 1"
595
- content = ol.children.map { |li| convert_ol_li(li) }.join("\n")
622
+ contents = String.new(encoding: Encoding::UTF_8)
623
+
624
+ ol.children.each do |li|
625
+ contents << convert_ol_li(li)
626
+ end
596
627
 
597
- return "#{header}\n.RS\n#{content}\n.RE"
628
+ return <<~ROFF
629
+ .nr step#{@ol_index} 0 1
630
+ .RS
631
+ #{contents.chomp}
632
+ .RE
633
+ ROFF
598
634
  end
599
635
 
600
636
  #
@@ -607,15 +643,133 @@ module Kramdown
607
643
  # The roff output.
608
644
  #
609
645
  def convert_ol_li(li)
610
- li.children.each_with_index.map { |child,index|
646
+ roff = String.new(encoding: Encoding::UTF_8)
647
+
648
+ li.children.each_with_index do |child,index|
611
649
  if child.type == :p
612
- content = convert_children(child.children)
650
+ contents = convert_children_of(child)
651
+
652
+ roff << if index == 0
653
+ <<~ROFF
654
+ .IP \\n+[step#{@ol_index}]
655
+ #{contents.chomp}
656
+ ROFF
657
+ else
658
+ <<~ROFF
659
+ .IP \\n
660
+ #{contents.chomp}
661
+ ROFF
662
+ end
663
+ end
664
+ end
665
+
666
+ return roff
667
+ end
668
+
669
+ #
670
+ # Converts a `kd:dl` element.
671
+ #
672
+ # @param [Kramdown::Element] dl
673
+ # A `kd:dl` element.
674
+ #
675
+ # @return [String]
676
+ # The roff output.
677
+ #
678
+ def convert_dl(dl)
679
+ roff = String.new(encoding: Encoding::UTF_8)
680
+
681
+ dt_index = 0
682
+ dd_index = 0
683
+
684
+ dl.children.each do |element|
685
+ case element.type
686
+ when :dt
687
+ roff << convert_dt(element, index: dt_index)
688
+
689
+ dt_index += 1 # increment the dt count
690
+ dd_index = 0 # reset the dd count
691
+ when :dd
692
+ roff << convert_dd(element, index: dd_index)
693
+
694
+ dd_index += 1 # increment the dd count
695
+ dt_index = 0 # reset the dt count
696
+ else
697
+ roff << convert(element)
698
+
699
+ # reset both the dt_index and dd_index counters
700
+ dt_index = 0
701
+ dd_index = 0
702
+ end
703
+ end
704
+
705
+ return roff
706
+ end
707
+
708
+ #
709
+ # Converts a `kd:dt` element within a `kd:dl`.
710
+ #
711
+ # @param [Kramdown::Element] dt
712
+ # A `kd:dt` element.
713
+ #
714
+ # @param [Integer] index
715
+ # The index of the `kd:dt` element. Used to indicate whether this is the
716
+ # first `kd:dt` element or additional `kd:dt` elements.
717
+ #
718
+ # @return [String]
719
+ # The roff output.
720
+ #
721
+ def convert_dt(dt, index: 0)
722
+ text = convert_text_elements(dt.children)
723
+
724
+ if index == 0
725
+ <<~ROFF
726
+ .TP
727
+ #{text}
728
+ ROFF
729
+ else
730
+ <<~ROFF
731
+ .TQ
732
+ #{text}
733
+ ROFF
734
+ end
735
+ end
736
+
737
+ #
738
+ # Converts a `kd:dd` element within a `kd:dd`.
739
+ #
740
+ # @param [Kramdown::Element] dd
741
+ # A `kd:dd` element.
742
+ #
743
+ # @param [Integer] index
744
+ # The index of the `kd:dd` element. Used to indicate whether this is the
745
+ # first `kd:dd` element following a `kd;dt` element or additional
746
+ # `kd:dt` elements.
747
+ #
748
+ # @return [String]
749
+ # The roff output.
750
+ #
751
+ def convert_dd(dd, index: 0)
752
+ roff = String.new(encoding: Encoding::UTF_8)
753
+
754
+ dd.children.each_with_index do |child,child_index|
755
+ if index == 0 && child_index == 0 && child.type == :p
756
+ contents = convert_children_of(child)
613
757
 
614
- if index == 0 then ".IP \\n+[step#{@ol_index}]\n#{content}"
615
- else ".IP \\n\n#{content}"
758
+ # omit the .PP macro for the first paragraph
759
+ roff << "#{contents.chomp}\n"
760
+ else
761
+ if (contents = convert_element(child))
762
+ # indent all other following paragraphs or other elements
763
+ roff << <<~ROFF
764
+ .RS
765
+ #{contents.chomp}
766
+ .RE
767
+ ROFF
616
768
  end
617
769
  end
618
- }.compact.join("\n")
770
+ end
771
+
772
+ return roff
619
773
  end
620
774
 
621
775
  #
@@ -641,14 +795,13 @@ module Kramdown
641
795
  # The roff output.
642
796
  #
643
797
  def convert_blockquote(blockquote)
644
- content = blockquote.children.map { |child|
645
- case child.type
646
- when :p then convert_children(child.children)
647
- else convert_element(child)
648
- end
649
- }.join("\n")
798
+ contents = convert_children_of(blockquote)
650
799
 
651
- return ".PP\n.RS\n#{content}\n.RE"
800
+ return <<~ROFF
801
+ .RS
802
+ #{contents.chomp}
803
+ .RE
804
+ ROFF
652
805
  end
653
806
 
654
807
  #
@@ -661,7 +814,16 @@ module Kramdown
661
814
  # The roff output.
662
815
  #
663
816
  def convert_codeblock(codeblock)
664
- ".nf\n#{escape(codeblock.value).rstrip}\n.fi"
817
+ contents = escape(codeblock.value)
818
+
819
+ return <<~ROFF
820
+ .PP
821
+ .RS 4
822
+ .EX
823
+ #{contents.chomp}
824
+ .EE
825
+ .RE
826
+ ROFF
665
827
  end
666
828
 
667
829
  #
@@ -674,9 +836,15 @@ module Kramdown
674
836
  # The roff output.
675
837
  #
676
838
  def convert_comment(comment)
677
- comment.value.lines.map { |line|
678
- ".\\\" #{line}"
679
- }.join("\n")
839
+ roff = String.new(encoding: Encoding::UTF_8)
840
+
841
+ comment.value.lines.each do |line|
842
+ roff << <<~ROFF
843
+ .\\" #{line}
844
+ ROFF
845
+ end
846
+
847
+ return roff
680
848
  end
681
849
 
682
850
  #
@@ -689,26 +857,12 @@ module Kramdown
689
857
  # The roff output.
690
858
  #
691
859
  def convert_p(p)
692
- children = p.children
693
-
694
- if ((children.length >= 2) && (children.first.type == :codespan ||
695
- children.first.type == :em ||
696
- children.first.type == :strong))
697
- newline = children.find_index { |el|
698
- el.type == :text && el.value.start_with?("\n")
699
- }
860
+ contents = convert_text_elements(p.children)
700
861
 
701
- if newline
702
- first_line = convert_children(children[0...newline])
703
- rest = convert_children(children[newline..-1]).strip
704
-
705
- ".TP\n#{first_line}\n#{rest}"
706
- else
707
- ".PP\n#{convert_children(children)}"
708
- end
709
- else
710
- ".PP\n#{convert_children(children)}"
711
- end
862
+ return <<~ROFF
863
+ .PP
864
+ #{contents.chomp}
865
+ ROFF
712
866
  end
713
867
 
714
868
  #
@@ -721,7 +875,7 @@ module Kramdown
721
875
  # The roff output.
722
876
  #
723
877
  def convert_em(em)
724
- "\\fI#{convert_children(em.children)}\\fP"
878
+ "\\fI#{convert_text_elements(em.children)}\\fP"
725
879
  end
726
880
 
727
881
  #
@@ -734,7 +888,7 @@ module Kramdown
734
888
  # The roff output.
735
889
  #
736
890
  def convert_strong(strong)
737
- "\\fB#{convert_children(strong.children)}\\fP"
891
+ "\\fB#{convert_text_elements(strong.children)}\\fP"
738
892
  end
739
893
 
740
894
  #
@@ -747,7 +901,22 @@ module Kramdown
747
901
  # The roff output.
748
902
  #
749
903
  def convert_codespan(codespan)
750
- "\\fB#{codespan.value}\\fR"
904
+ # ``` code fence blocks are parsed as kd:codespans
905
+ if codespan.options[:codespan_delimiter] == '```'
906
+ # ignore the first and last newlines
907
+ contents = escape(codespan.value[1..-2])
908
+
909
+ <<~ROFF
910
+ .PP
911
+ .RS 4
912
+ .EX
913
+ #{contents}
914
+ .EE
915
+ .RE
916
+ ROFF
917
+ else
918
+ "\\fB#{escape(codespan.value)}\\fR"
919
+ end
751
920
  end
752
921
 
753
922
  #
@@ -760,45 +929,122 @@ module Kramdown
760
929
  # The roff output.
761
930
  #
762
931
  def convert_a(a)
763
- href = escape(a.attr['href'])
932
+ href = a.attr['href']
764
933
  scheme, path = href.split(':',2)
765
934
 
766
- text = convert_children(a.children)
935
+ text = convert_text_elements(a.children)
767
936
 
768
937
  case scheme
769
938
  when 'mailto'
770
- email = path
939
+ email = escape(path)
771
940
 
772
941
  unless text == email
773
- "#{text}\n.MT #{email}\n.ME"
942
+ <<~ROFF
943
+ #{text.chomp}
944
+ .MT #{email}
945
+ .ME
946
+ ROFF
774
947
  else
775
- "\n.MT #{email}\n.ME"
948
+ <<~ROFF
949
+ .MT #{email}
950
+ .ME
951
+ ROFF
776
952
  end
777
953
  when 'man'
778
- if (match = path.match(/\A(?<page>[A-Za-z0-9_-]+)(?:\((?<section>\d[a-z]?)\)|\\\.(?<section>\d[a-z]?))\z/))
779
- page = match[:page]
780
- section = match[:section]
954
+ if (match = path.match(/\A(?<page>[A-Za-z0-9_-]+)(?:\((?<section>\d[a-z]?)\)|\.(?<section>\d[a-z]?))\z/))
955
+ man_page_link(match[:page],match[:section])
956
+ else
957
+ page = escape(path)
781
958
 
782
- "\n.BR #{page} (#{section})"
959
+ <<~ROFF
960
+ .BR #{page}
961
+ ROFF
962
+ end
963
+ else
964
+ if (match = href.match(/(?<page>[A-Za-z0-9_-]+)\.(?<section>\d[a-z]?)\.md\z/))
965
+ man_page_link(match[:page],match[:section])
783
966
  else
784
- "\n.BR #{path}"
967
+ <<~ROFF
968
+ #{text.chomp}
969
+ .UR #{escape(href)}
970
+ .UE
971
+ ROFF
785
972
  end
973
+ end
974
+ end
975
+
976
+ #
977
+ # Outputs a man page link.
978
+ #
979
+ # @param [String] page
980
+ # The man page name.
981
+ #
982
+ # @param [String, nil] section
983
+ # The optional section of the man page.
984
+ #
985
+ # @return [String]
986
+ # The roff output.
987
+ #
988
+ def man_page_link(page,section=nil)
989
+ if section
990
+ <<~ROFF
991
+ .BR #{escape(page)} (#{escape(section)})
992
+ ROFF
786
993
  else
787
- "#{text}\n.UR #{href}\n.UE"
994
+ <<~ROFF
995
+ .BR #{escape(page)}
996
+ ROFF
788
997
  end
789
998
  end
790
999
 
791
1000
  #
792
1001
  # Converts the children of an element.
793
1002
  #
794
- # @param [Array<Kramdown::Element>] children
795
- # The children of an element.
1003
+ # @param [Array<Kramdown::Element>] element
1004
+ # The elements to convert.
1005
+ #
1006
+ # @return [String]
1007
+ # The combined roff output.
1008
+ #
1009
+ def convert_children_of(element)
1010
+ roff = String.new(encoding: Encoding::UTF_8)
1011
+
1012
+ element.children.each do |child|
1013
+ if (contents = convert_element(child))
1014
+ roff << contents
1015
+ end
1016
+ end
1017
+
1018
+ return roff
1019
+ end
1020
+
1021
+ #
1022
+ # Converts the children of an element.
1023
+ #
1024
+ # @param [Array<Kramdown::Element>] elements
1025
+ # The text elements to convert.
796
1026
  #
797
1027
  # @return [String]
798
1028
  # The roff output.
799
1029
  #
800
- def convert_children(children)
801
- children.map { |child| convert_element(child) }.join.strip
1030
+ def convert_text_elements(elements)
1031
+ roff = String.new(encoding: Encoding::UTF_8)
1032
+
1033
+ elements.each do |element|
1034
+ if (contents = convert_element(element))
1035
+ if contents.start_with?('.') && !roff.empty? && !roff.end_with?("\n")
1036
+ # roff macross must exist on their own line
1037
+ roff << "\n#{contents}"
1038
+ elsif contents.start_with?(' ') && roff.end_with?("\n")
1039
+ # remove leadning whitespace following a newline
1040
+ roff << contents.lstrip
1041
+ else
1042
+ roff << contents
1043
+ end
1044
+ end
1045
+ end
1046
+
1047
+ return roff
802
1048
  end
803
1049
 
804
1050
  #
@@ -811,7 +1057,7 @@ module Kramdown
811
1057
  # The escaped text.
812
1058
  #
813
1059
  def escape(text)
814
- text.gsub(GLYPH_REGEXP) { |char| GLYPHS[char] }
1060
+ text.gsub(GLYPH_REGEXP,GLYPHS)
815
1061
  end
816
1062
 
817
1063
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'kramdown'
4
- require_relative '../converter/man'
4
+ require 'kramdown/man'
5
5
 
6
6
  require 'rake/tasklib'
7
7
 
@@ -3,6 +3,6 @@
3
3
  module Kramdown
4
4
  module Man
5
5
  # kramdown-man version
6
- VERSION = "0.1.9"
6
+ VERSION = "1.0.1"
7
7
  end
8
8
  end
data/lib/kramdown/man.rb CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  require 'kramdown'
4
4
 
5
- # HACK: load our version of kramdown/converter/man.rb and not kramdown's
6
- require_relative './converter/man'
7
- require_relative './man/version'
5
+ require 'kramdown/man/version'
6
+ require 'kramdown/man/converter'
7
+
8
+ module Kramdown
9
+ class Document
10
+ #
11
+ # Converts the parsed markdown document to a man page.
12
+ #
13
+ # @param [Hash] options
14
+ # Additional options.
15
+ #
16
+ # @return [String]
17
+ # The converted man page contents.
18
+ #
19
+ # @since 1.0.0
20
+ #
21
+ # @api public
22
+ #
23
+ def to_man(options={})
24
+ output, warnings = Kramdown::Man::Converter.convert(@root,options)
25
+
26
+ @warnings.concat(warnings)
27
+ return output
28
+ end
29
+ end
30
+ end