kramdown-man 0.1.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,20 +1,25 @@
1
- # encoding: utf-8
2
- require_relative '../man/version'
1
+ # frozen_string_literal: true
2
+
3
+ require 'kramdown/man/version'
3
4
 
4
5
  require 'kramdown/converter/base'
5
6
 
6
7
  module Kramdown
7
- module Converter
8
+ module Man
8
9
  #
9
10
  # Converts markdown into a roff man-page.
10
11
  #
11
- class Man < ::Kramdown::Converter::Base
12
+ # @since 1.0.0
13
+ #
14
+ # @api private
15
+ #
16
+ class Converter < Kramdown::Converter::Base
12
17
 
13
18
  # Comment header
14
- HEADER = [
15
- ".\\\" Generated by kramdown-man #{::Kramdown::Man::VERSION}",
16
- ".\\\" https://github.com/postmodern/kramdown-man#readme"
17
- ].join("\n")
19
+ HEADER = <<~ROFF
20
+ .\\\" Generated by kramdown-man #{VERSION}
21
+ .\\\" https://github.com/postmodern/kramdown-man#readme
22
+ ROFF
18
23
 
19
24
  # Typographic Symbols and their UTF8 chars
20
25
  TYPOGRAPHIC_SYMS = {
@@ -426,7 +431,7 @@ module Kramdown
426
431
  # The roff output.
427
432
  #
428
433
  def convert(root)
429
- "#{HEADER}\n#{convert_root(root)}"
434
+ "#{HEADER}#{convert_root(root)}"
430
435
  end
431
436
 
432
437
  #
@@ -439,9 +444,7 @@ module Kramdown
439
444
  # The roff output.
440
445
  #
441
446
  def convert_root(root)
442
- root.children.map { |child|
443
- convert_element(child)
444
- }.compact.join("\n")
447
+ convert_children_of(root)
445
448
  end
446
449
 
447
450
  #
@@ -455,20 +458,19 @@ module Kramdown
455
458
  #
456
459
  def convert_element(element)
457
460
  method = "convert_#{element.type}"
461
+
458
462
  send(method,element) if respond_to?(method)
459
463
  end
460
-
464
+
461
465
  #
462
466
  # Converts a `kd:blank` element.
463
467
  #
464
468
  # @param [Kramdown::Element] blank
465
469
  # A `kd:blank` element.
466
470
  #
467
- # @return [String]
468
- # The roff output.
471
+ # @return [nil]
469
472
  #
470
473
  def convert_blank(blank)
471
- '.LP'
472
474
  end
473
475
 
474
476
  #
@@ -481,7 +483,7 @@ module Kramdown
481
483
  # The roff output.
482
484
  #
483
485
  def convert_text(text)
484
- escape(text.value.gsub(/^( +|\t)/,''))
486
+ escape(text.value)
485
487
  end
486
488
 
487
489
  #
@@ -523,23 +525,30 @@ module Kramdown
523
525
  text = header.options[:raw_text]
524
526
 
525
527
  case header.options[:level]
526
- when 1 then ".TH #{text}"
527
- when 2 then ".SH #{text}"
528
- 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
529
540
  end
530
541
  end
531
542
 
532
543
  #
533
- # Converts a `kd:hr` element.
544
+ # Ignore `kd:hr` elements.
534
545
  #
535
546
  # @param [Kramdown::Element] hr
536
547
  # A `kd:hr` element.
537
548
  #
538
- # @return [String]
539
- # The roff output.
549
+ # @return [nil]
540
550
  #
541
551
  def convert_hr(hr)
542
- ".ti 0\n\\l'\\n(.lu'"
543
552
  end
544
553
 
545
554
  #
@@ -552,9 +561,17 @@ module Kramdown
552
561
  # The roff output.
553
562
  #
554
563
  def convert_ul(ul)
555
- content = ul.children.map { |li| convert_ul_li(li) }.join("\n")
564
+ contents = String.new(encoding: Encoding::UTF_8)
556
565
 
557
- return ".RS\n#{content}\n.RE"
566
+ ul.children.each do |li|
567
+ contents << convert_ul_li(li)
568
+ end
569
+
570
+ return <<~ROFF
571
+ .RS
572
+ #{contents.chomp}
573
+ .RE
574
+ ROFF
558
575
  end
559
576
 
560
577
  #
@@ -567,15 +584,27 @@ module Kramdown
567
584
  # The roff output.
568
585
  #
569
586
  def convert_ul_li(li)
570
- 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|
571
590
  if child.type == :p
572
- content = convert_children(child.children)
591
+ contents = convert_children_of(child)
573
592
 
574
- if index == 0 then ".IP \\(bu 2\n#{content}"
575
- else ".IP \\( 2\n#{content}"
576
- 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
577
604
  end
578
- }.compact.join("\n")
605
+ end
606
+
607
+ return roff
579
608
  end
580
609
 
581
610
  #
@@ -590,10 +619,18 @@ module Kramdown
590
619
  def convert_ol(ol)
591
620
  @ol_index += 1
592
621
 
593
- header = ".nr step#{@ol_index} 0 1"
594
- 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
595
627
 
596
- 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
597
634
  end
598
635
 
599
636
  #
@@ -606,15 +643,133 @@ module Kramdown
606
643
  # The roff output.
607
644
  #
608
645
  def convert_ol_li(li)
609
- 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|
610
649
  if child.type == :p
611
- 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
612
668
 
613
- if index == 0 then ".IP \\n+[step#{@ol_index}]\n#{content}"
614
- else ".IP \\n\n#{content}"
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
615
768
  end
616
769
  end
617
- }.compact.join("\n")
770
+ end
771
+
772
+ return roff
618
773
  end
619
774
 
620
775
  #
@@ -640,14 +795,13 @@ module Kramdown
640
795
  # The roff output.
641
796
  #
642
797
  def convert_blockquote(blockquote)
643
- content = blockquote.children.map { |child|
644
- case child.type
645
- when :p then convert_children(child.children)
646
- else convert_element(child)
647
- end
648
- }.join("\n")
798
+ contents = convert_children_of(blockquote)
649
799
 
650
- return ".PP\n.RS\n#{content}\n.RE"
800
+ return <<~ROFF
801
+ .RS
802
+ #{contents.chomp}
803
+ .RE
804
+ ROFF
651
805
  end
652
806
 
653
807
  #
@@ -660,7 +814,16 @@ module Kramdown
660
814
  # The roff output.
661
815
  #
662
816
  def convert_codeblock(codeblock)
663
- ".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
664
827
  end
665
828
 
666
829
  #
@@ -673,9 +836,15 @@ module Kramdown
673
836
  # The roff output.
674
837
  #
675
838
  def convert_comment(comment)
676
- comment.value.lines.map { |line|
677
- ".\\\" #{line}"
678
- }.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
679
848
  end
680
849
 
681
850
  #
@@ -688,26 +857,12 @@ module Kramdown
688
857
  # The roff output.
689
858
  #
690
859
  def convert_p(p)
691
- children = p.children
692
-
693
- if ((children.length >= 2) && (children.first.type == :codespan ||
694
- children.first.type == :em ||
695
- children.first.type == :strong))
696
- newline = children.find_index { |el|
697
- el.type == :text && el.value.start_with?("\n")
698
- }
860
+ contents = convert_text_elements(p.children)
699
861
 
700
- if newline
701
- first_line = convert_children(children[0...newline])
702
- rest = convert_children(children[newline..-1]).strip
703
-
704
- ".TP\n#{first_line}\n#{rest}"
705
- else
706
- ".HP\n#{convert_children(children)}"
707
- end
708
- else
709
- ".PP\n#{convert_children(children)}"
710
- end
862
+ return <<~ROFF
863
+ .PP
864
+ #{contents.chomp}
865
+ ROFF
711
866
  end
712
867
 
713
868
  #
@@ -720,7 +875,7 @@ module Kramdown
720
875
  # The roff output.
721
876
  #
722
877
  def convert_em(em)
723
- "\\fI#{convert_children(em.children)}\\fP"
878
+ "\\fI#{convert_text_elements(em.children)}\\fP"
724
879
  end
725
880
 
726
881
  #
@@ -733,7 +888,7 @@ module Kramdown
733
888
  # The roff output.
734
889
  #
735
890
  def convert_strong(strong)
736
- "\\fB#{convert_children(strong.children)}\\fP"
891
+ "\\fB#{convert_text_elements(strong.children)}\\fP"
737
892
  end
738
893
 
739
894
  #
@@ -759,38 +914,122 @@ module Kramdown
759
914
  # The roff output.
760
915
  #
761
916
  def convert_a(a)
762
- href = escape(a.attr['href'])
763
- text = convert_children(a.children)
917
+ href = a.attr['href']
918
+ scheme, path = href.split(':',2)
764
919
 
765
- case href
766
- when /^mailto:/
767
- email = href[7..-1]
920
+ text = convert_text_elements(a.children)
768
921
 
769
- unless text == email then "#{text}\n.MT #{email}\n.ME"
770
- else "\n.MT #{email}\n.ME"
922
+ case scheme
923
+ when 'mailto'
924
+ email = escape(path)
925
+
926
+ unless text == email
927
+ <<~ROFF
928
+ #{text.chomp}
929
+ .MT #{email}
930
+ .ME
931
+ ROFF
932
+ else
933
+ <<~ROFF
934
+ .MT #{email}
935
+ .ME
936
+ ROFF
771
937
  end
772
- when /^man:/
773
- match = href.match(/man:([A-Za-z0-9_-]+)(?:\((\d[a-z]?)\))?/)
938
+ when 'man'
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)
774
943
 
775
- if match[2] then "\n.BR #{match[1]} (#{match[2]})"
776
- else "\n.BR #{match[1]}"
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])
951
+ else
952
+ <<~ROFF
953
+ #{text.chomp}
954
+ .UR #{escape(href)}
955
+ .UE
956
+ ROFF
777
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
778
978
  else
779
- "#{text}\n.UR #{href}\n.UE"
979
+ <<~ROFF
980
+ .BR #{escape(page)}
981
+ ROFF
780
982
  end
781
983
  end
782
984
 
783
985
  #
784
986
  # Converts the children of an element.
785
987
  #
786
- # @param [Array<Kramdown::Element>] children
787
- # The children of an element.
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
1001
+ end
1002
+
1003
+ return roff
1004
+ end
1005
+
1006
+ #
1007
+ # Converts the children of an element.
1008
+ #
1009
+ # @param [Array<Kramdown::Element>] elements
1010
+ # The text elements to convert.
788
1011
  #
789
1012
  # @return [String]
790
1013
  # The roff output.
791
1014
  #
792
- def convert_children(children)
793
- 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
794
1033
  end
795
1034
 
796
1035
  #
@@ -803,7 +1042,7 @@ module Kramdown
803
1042
  # The escaped text.
804
1043
  #
805
1044
  def escape(text)
806
- text.gsub(GLYPH_REGEXP) { |char| GLYPHS[char] }
1045
+ text.gsub(GLYPH_REGEXP,GLYPHS)
807
1046
  end
808
1047
 
809
1048
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kramdown'
2
- require_relative '../converter/man'
4
+ require 'kramdown/man'
3
5
 
4
6
  require 'rake/tasklib'
5
7
 
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Kramdown
2
4
  module Man
3
5
  # kramdown-man version
4
- VERSION = "0.1.8"
6
+ VERSION = "1.0.0"
5
7
  end
6
8
  end
data/lib/kramdown/man.rb CHANGED
@@ -1,5 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kramdown'
2
4
 
3
- # HACK: load our version of kramdown/converter/man.rb and not kramdown's
4
- require_relative './converter/man'
5
- 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