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.
- checksums.yaml +4 -4
- data/.gitignore +4 -3
- data/.yardopts +1 -1
- data/ChangeLog.md +55 -0
- data/Gemfile +6 -3
- data/README.md +152 -54
- data/bin/kramdown-man +3 -12
- data/lib/kramdown/man/cli.rb +202 -0
- data/lib/kramdown/{converter/man.rb → man/converter.rb} +318 -87
- data/lib/kramdown/man/task.rb +1 -1
- data/lib/kramdown/man/version.rb +1 -1
- data/lib/kramdown/man.rb +26 -3
- data/man/kramdown-man.1 +191 -110
- data/man/kramdown-man.1.md +90 -32
- data/spec/cli_spec.rb +218 -0
- data/spec/converter_spec.rb +969 -0
- data/spec/{document_spec.rb → integration_spec.rb} +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +7 -5
- data/spec/converter/man_spec.rb +0 -579
@@ -1,21 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'kramdown/man/version'
|
4
4
|
|
5
5
|
require 'kramdown/converter/base'
|
6
6
|
|
7
7
|
module Kramdown
|
8
|
-
module
|
8
|
+
module Man
|
9
9
|
#
|
10
10
|
# Converts markdown into a roff man-page.
|
11
11
|
#
|
12
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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}
|
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
|
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 [
|
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
|
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
|
528
|
-
|
529
|
-
|
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
|
-
#
|
544
|
+
# Ignore `kd:hr` elements.
|
535
545
|
#
|
536
546
|
# @param [Kramdown::Element] hr
|
537
547
|
# A `kd:hr` element.
|
538
548
|
#
|
539
|
-
# @return [
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
591
|
+
contents = convert_children_of(child)
|
574
592
|
|
575
|
-
if index == 0
|
576
|
-
|
577
|
-
|
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
|
-
|
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
|
-
|
595
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
615
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
678
|
-
|
679
|
-
|
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
|
-
|
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
|
-
|
702
|
-
|
703
|
-
|
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#{
|
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#{
|
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 =
|
917
|
+
href = a.attr['href']
|
764
918
|
scheme, path = href.split(':',2)
|
765
919
|
|
766
|
-
text =
|
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
|
-
|
927
|
+
<<~ROFF
|
928
|
+
#{text.chomp}
|
929
|
+
.MT #{email}
|
930
|
+
.ME
|
931
|
+
ROFF
|
774
932
|
else
|
775
|
-
|
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]?)\)
|
779
|
-
|
780
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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>]
|
795
|
-
# The
|
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
|
801
|
-
|
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)
|
1045
|
+
text.gsub(GLYPH_REGEXP,GLYPHS)
|
815
1046
|
end
|
816
1047
|
|
817
1048
|
end
|
data/lib/kramdown/man/task.rb
CHANGED
data/lib/kramdown/man/version.rb
CHANGED
data/lib/kramdown/man.rb
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
|
3
3
|
require 'kramdown'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|