kramdown-man 0.1.9 → 1.0.0

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
613
665
 
614
- if index == 0 then ".IP \\n+[step#{@ol_index}]\n#{content}"
615
- else ".IP \\n\n#{content}"
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)
757
+
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
  #
@@ -760,45 +914,122 @@ module Kramdown
760
914
  # The roff output.
761
915
  #
762
916
  def convert_a(a)
763
- href = escape(a.attr['href'])
917
+ href = a.attr['href']
764
918
  scheme, path = href.split(':',2)
765
919
 
766
- text = convert_children(a.children)
920
+ text = convert_text_elements(a.children)
767
921
 
768
922
  case scheme
769
923
  when 'mailto'
770
- email = path
924
+ email = escape(path)
771
925
 
772
926
  unless text == email
773
- "#{text}\n.MT #{email}\n.ME"
927
+ <<~ROFF
928
+ #{text.chomp}
929
+ .MT #{email}
930
+ .ME
931
+ ROFF
774
932
  else
775
- "\n.MT #{email}\n.ME"
933
+ <<~ROFF
934
+ .MT #{email}
935
+ .ME
936
+ ROFF
776
937
  end
777
938
  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]
939
+ if (match = path.match(/\A(?<page>[A-Za-z0-9_-]+)(?:\((?<section>\d[a-z]?)\)|\.(?<section>\d[a-z]?))\z/))
940
+ man_page_link(match[:page],match[:section])
941
+ else
942
+ page = escape(path)
781
943
 
782
- "\n.BR #{page} (#{section})"
944
+ <<~ROFF
945
+ .BR #{page}
946
+ ROFF
947
+ end
948
+ else
949
+ if (match = href.match(/(?<page>[A-Za-z0-9_-]+)\.(?<section>\d[a-z]?)\.md\z/))
950
+ man_page_link(match[:page],match[:section])
783
951
  else
784
- "\n.BR #{path}"
952
+ <<~ROFF
953
+ #{text.chomp}
954
+ .UR #{escape(href)}
955
+ .UE
956
+ ROFF
785
957
  end
958
+ end
959
+ end
960
+
961
+ #
962
+ # Outputs a man page link.
963
+ #
964
+ # @param [String] page
965
+ # The man page name.
966
+ #
967
+ # @param [String, nil] section
968
+ # The optional section of the man page.
969
+ #
970
+ # @return [String]
971
+ # The roff output.
972
+ #
973
+ def man_page_link(page,section=nil)
974
+ if section
975
+ <<~ROFF
976
+ .BR #{escape(page)} (#{escape(section)})
977
+ ROFF
786
978
  else
787
- "#{text}\n.UR #{href}\n.UE"
979
+ <<~ROFF
980
+ .BR #{escape(page)}
981
+ ROFF
982
+ end
983
+ end
984
+
985
+ #
986
+ # Converts the children of an element.
987
+ #
988
+ # @param [Array<Kramdown::Element>] element
989
+ # The elements to convert.
990
+ #
991
+ # @return [String]
992
+ # The combined roff output.
993
+ #
994
+ def convert_children_of(element)
995
+ roff = String.new(encoding: Encoding::UTF_8)
996
+
997
+ element.children.each do |child|
998
+ if (contents = convert_element(child))
999
+ roff << contents
1000
+ end
788
1001
  end
1002
+
1003
+ return roff
789
1004
  end
790
1005
 
791
1006
  #
792
1007
  # Converts the children of an element.
793
1008
  #
794
- # @param [Array<Kramdown::Element>] children
795
- # The children of an element.
1009
+ # @param [Array<Kramdown::Element>] elements
1010
+ # The text elements to convert.
796
1011
  #
797
1012
  # @return [String]
798
1013
  # The roff output.
799
1014
  #
800
- def convert_children(children)
801
- children.map { |child| convert_element(child) }.join.strip
1015
+ def convert_text_elements(elements)
1016
+ roff = String.new(encoding: Encoding::UTF_8)
1017
+
1018
+ elements.each do |element|
1019
+ if (contents = convert_element(element))
1020
+ if contents.start_with?('.') && !roff.empty? && !roff.end_with?("\n")
1021
+ # roff macross must exist on their own line
1022
+ roff << "\n#{contents}"
1023
+ elsif contents.start_with?(' ') && roff.end_with?("\n")
1024
+ # remove leadning whitespace following a newline
1025
+ roff << contents.lstrip
1026
+ else
1027
+ roff << contents
1028
+ end
1029
+ end
1030
+ end
1031
+
1032
+ return roff
802
1033
  end
803
1034
 
804
1035
  #
@@ -811,7 +1042,7 @@ module Kramdown
811
1042
  # The escaped text.
812
1043
  #
813
1044
  def escape(text)
814
- text.gsub(GLYPH_REGEXP) { |char| GLYPHS[char] }
1045
+ text.gsub(GLYPH_REGEXP,GLYPHS)
815
1046
  end
816
1047
 
817
1048
  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.0"
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