kramdown-man 0.1.9 → 1.0.1

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