md2man 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Md2Man
2
+ VERSION = "1.0.1"
3
+ end
data/lib/md2man.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'md2man/version'
2
+ require 'md2man/engine'
data/man/man1/md2man.1 ADDED
@@ -0,0 +1,79 @@
1
+ .TH MD2MAN 1 "2011\-12\-06" "1.0.1"
2
+ .SH NAME
3
+ .PP
4
+ md2man \- convert
5
+ .BR markdown (7)
6
+ into
7
+ .BR roff (7)
8
+ .SH SYNOPSIS
9
+ .PP
10
+ \fB\fCmd2man\fR [\fIOPTION\fP]... [\fIFILE\fP]
11
+ .SH DESCRIPTION
12
+ .PP
13
+ md2man
14
+ .UR https://github.com/sunaku/md2man
15
+ .UE
16
+ converts
17
+ .BR markdown (7)
18
+ input from the given \fIFILE\fP into
19
+ .BR roff (7)
20
+ using
21
+ Redcarpet2
22
+ .UR https://github.com/tanoku/redcarpet
23
+ .UE
24
+ and then prints the result to the standard output stream. If
25
+ \fIFILE\fP is not given, then the standard input stream is read in its place.
26
+ .SS Document Format
27
+ .PP
28
+ The following additional semantics are attached to
29
+ .BR markdown (7):
30
+ .RS
31
+ .IP \(bu 2
32
+ There can be at most one top\-level heading (H1). It is emitted as \fB\fC.TH\fR
33
+ in the
34
+ .BR roff (7)
35
+ output, specifying the UNIX man page's header and footer.
36
+ .IP \(bu 2
37
+ Paragraphs whose lines are all uniformly indented by two spaces are
38
+ considered to be "indented paragraphs". They are unindented accordingly
39
+ before emission as \fB\fC.IP\fR in the
40
+ .BR roff (7)
41
+ output.
42
+ .IP \(bu 2
43
+ Paragraphs whose subsequent lines (all except the first) are uniformly
44
+ indented by two spaces are considered to be a "tagged paragraphs". They
45
+ are unindented accordingly before emission as \fB\fC.TP\fR in the
46
+ .BR roff (7)
47
+ output.
48
+ .RE
49
+ .SS Markdown Extensions
50
+ .PP
51
+ The following Redcarpet2
52
+ .UR https://github.com/tanoku/redcarpet
53
+ .UE
54
+ extensions for
55
+ .BR markdown (7)
56
+ are enabled:
57
+ .RS
58
+ .IP \(bu 2
59
+ tables
60
+ .IP \(bu 2
61
+ autolink
62
+ .IP \(bu 2
63
+ superscript
64
+ .IP \(bu 2
65
+ strikethrough
66
+ .IP \(bu 2
67
+ no_intra_emphasis
68
+ .IP \(bu 2
69
+ fenced_code_blocks
70
+ .RE
71
+ .SH OPTIONS
72
+ .TP
73
+ \fB\fC-h\fR, \fB\fC--help\fR
74
+ Display this help manual using
75
+ .BR man (1).
76
+ .SH SEE ALSO
77
+ .PP
78
+ .BR markdown (7),
79
+ .BR roff (7)
data/md2man.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "md2man/version"
4
+ require "binman/gemspec"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "md2man"
8
+ s.version = Md2Man::VERSION
9
+ s.authors,
10
+ s.email = File.read('LICENSE').scan(/Copyright \d+ (.+) <(.+?)>/).transpose
11
+ s.homepage = "http://github.com/sunaku/md2man"
12
+ s.summary = "UNIX man pages using Redcarpet2"
13
+ s.description = nil
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ # s.add_development_dependency "rspec"
22
+ # s.add_runtime_dependency "rest-client"
23
+ s.add_runtime_dependency "redcarpet", ">= 2.0.0b5", "< 3"
24
+ s.add_development_dependency "minitest", ">= 2.7.0", "< 3"
25
+ end
@@ -0,0 +1,584 @@
1
+ # http://www.linuxjournal.com/article/1158
2
+ # http://www.premvet.co.uk/premvet/faq/roff.html
3
+ # http://www.linuxhowtos.org/System/creatingman.htm
4
+ # http://www.fnal.gov/docs/products/ups/ReferenceManual/html/manpages.html
5
+ # http://serverfault.com/questions/109490/how-do-i-write-man-pages
6
+ # man groff_man
7
+ # man 7 groff
8
+
9
+ require 'test_helper'
10
+ require 'md2man/engine'
11
+
12
+ describe Md2Man::Roff do
13
+ before do
14
+ @markdown = Redcarpet::Markdown.new(
15
+ Md2Man::Engine,
16
+ :tables => true,
17
+ :autolink => true,
18
+ )
19
+ end
20
+
21
+ SPACE = 0x20.chr
22
+
23
+ def heredoc document
24
+ document.gsub(/^\s*\|/, '').chomp
25
+ end
26
+
27
+ it 'renders nothing as nothing' do
28
+ @markdown.render('').must_be_empty
29
+ end
30
+
31
+ it 'renders paragraphs' do
32
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
33
+ |just some paragraph
34
+ |spanning
35
+ |multiple
36
+ |lines
37
+ |but within 4-space indent
38
+ INPUT
39
+ |.PP
40
+ |just some paragraph
41
+ |spanning
42
+ |multiple
43
+ |lines
44
+ |but within 4\\-space indent
45
+ OUTPUT
46
+ end
47
+
48
+ it 'renders paragraphs with unevenly indented bodies' do
49
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
50
+ |just some paragraph
51
+ | spanning
52
+ | multiple
53
+ | lines
54
+ |but within 4-space indent
55
+ INPUT
56
+ |.PP
57
+ |just some paragraph
58
+ | spanning
59
+ | multiple
60
+ | lines
61
+ |but within 4\\-space indent
62
+ OUTPUT
63
+ end
64
+
65
+ it 'renders tagged paragraphs with uniformly two-space indented bodies' do
66
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
67
+ |just some paragraph
68
+ | spanning
69
+ | multiple
70
+ | lines
71
+ | but within 4-space indent
72
+ INPUT
73
+ |.TP
74
+ |just some paragraph
75
+ |spanning
76
+ |multiple
77
+ |lines
78
+ |but within 4\\-space indent
79
+ OUTPUT
80
+ end
81
+
82
+ it 'renders indented paragraphs that are uniformly two-space indented' do
83
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
84
+ | just some paragraph
85
+ | spanning
86
+ | multiple
87
+ | lines
88
+ | but within 4-space indent
89
+ INPUT
90
+ |.IP
91
+ |just some paragraph
92
+ |spanning
93
+ |multiple
94
+ |lines
95
+ |but within 4\\-space indent
96
+ OUTPUT
97
+ end
98
+
99
+ it 'renders tagged, indented, and normal paragraphs' do
100
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
101
+ |This is a
102
+ |normal paragraph.
103
+ |
104
+ |This is a
105
+ | tagged paragraph.
106
+ |
107
+ | This is another
108
+ |tagged paragraph.
109
+ |
110
+ | This is an
111
+ | indented
112
+ | paragraph.
113
+ |
114
+ |This
115
+ | is another
116
+ | normal
117
+ | paragraph.
118
+ INPUT
119
+ |.PP
120
+ |This is a
121
+ |normal paragraph.
122
+ |.TP
123
+ |This is a
124
+ |tagged paragraph.
125
+ |.TP
126
+ |This is another
127
+ |tagged paragraph.
128
+ |.IP
129
+ |This is an
130
+ |indented
131
+ |paragraph.
132
+ |.PP
133
+ |This
134
+ | is another
135
+ | normal
136
+ | paragraph.
137
+ OUTPUT
138
+ end
139
+ it 'escapes hyphens in normal text' do
140
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
141
+ |pre-process
142
+ INPUT
143
+ |.PP
144
+ |pre\\-process
145
+ OUTPUT
146
+
147
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
148
+ |1-5
149
+ INPUT
150
+ |.PP
151
+ |1\\-5
152
+ OUTPUT
153
+ end
154
+
155
+ it 'renders emphasis' do
156
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
157
+ |just *some paragraph*
158
+ | spanning
159
+ | multiple
160
+ | *lines*
161
+ |but within 4-*space* indent
162
+ INPUT
163
+ |.PP
164
+ |just \\fIsome paragraph\\fP
165
+ | spanning
166
+ | multiple
167
+ | \\fIlines\\fP
168
+ |but within 4\\-\\fIspace\\fP indent
169
+ OUTPUT
170
+ end
171
+
172
+ it 'renders double emphasis' do
173
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
174
+ |just **some paragraph**
175
+ | spanning
176
+ | multiple
177
+ | **lines**
178
+ |but within 4-**space** indent
179
+ INPUT
180
+ |.PP
181
+ |just \\fBsome paragraph\\fP
182
+ | spanning
183
+ | multiple
184
+ | \\fBlines\\fP
185
+ |but within 4\\-\\fBspace\\fP indent
186
+ OUTPUT
187
+ end
188
+
189
+ it 'renders triple emphasis' do
190
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
191
+ |just ***some paragraph***
192
+ | spanning
193
+ | multiple
194
+ | ***lines***
195
+ |but within 4-***space*** indent
196
+ INPUT
197
+ |.PP
198
+ |just \\s+2\\fBsome paragraph\\fP\\s-2
199
+ | spanning
200
+ | multiple
201
+ | \\s+2\\fBlines\\fP\\s-2
202
+ |but within 4\\-\\s+2\\fBspace\\fP\\s-2 indent
203
+ OUTPUT
204
+ end
205
+
206
+ it 'renders top-level headings' do
207
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
208
+ |just some h1 heading
209
+ |====================
210
+ INPUT
211
+ |.TH just some h1 heading
212
+ OUTPUT
213
+
214
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
215
+ |BINMAN 1 "2011-11-05" "1.1.0" "Ruby User Manuals"
216
+ |=================================================
217
+ INPUT
218
+ |.TH BINMAN 1 "2011\\-11\\-05" "1.1.0" "Ruby User Manuals"
219
+ OUTPUT
220
+ end
221
+
222
+ it 'renders 2nd-level headings' do
223
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
224
+ |just some h2 heading
225
+ |--------------------
226
+ INPUT
227
+ |.SH just some h2 heading
228
+ OUTPUT
229
+ end
230
+
231
+ it 'renders level 3..6 headings' do
232
+ (3..6).each do |level|
233
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
234
+ |#{'#' * level} just some subheading
235
+ INPUT
236
+ |.SS just some subheading
237
+ OUTPUT
238
+ end
239
+ end
240
+
241
+ it 'renders linebreaks (2 spaces at EOL)' do
242
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
243
+ |line#{SPACE}#{SPACE}
244
+ |break
245
+ INPUT
246
+ |.PP
247
+ |line
248
+ |.br
249
+ |break
250
+ OUTPUT
251
+ end
252
+
253
+ it 'renders blockquotes' do
254
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
255
+ |>just some paragraph
256
+ |> spanning
257
+ |> multiple
258
+ |> lines
259
+ |>but within 4-space indent
260
+ INPUT
261
+ |.RS
262
+ |just some paragraph
263
+ |spanning
264
+ | multiple
265
+ | lines
266
+ |but within 4\\-space indent
267
+ |.RE
268
+ OUTPUT
269
+ end
270
+
271
+ it 'renders code blocks' do
272
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
273
+ | single preformatted line
274
+ INPUT
275
+ |.nf
276
+ |single preformatted line
277
+ |.fi
278
+ OUTPUT
279
+
280
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
281
+ | just some *paragraph*
282
+ | spanning
283
+ | **multiple**
284
+ | > lines
285
+ | with 4-space indent
286
+ INPUT
287
+ |.nf
288
+ |just some *paragraph*
289
+ | spanning
290
+ | **multiple**
291
+ |> lines
292
+ |with 4-space indent
293
+ |.fi
294
+ OUTPUT
295
+ end
296
+
297
+ it 'renders code spans' do
298
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
299
+ |here is `some code` for you
300
+ INPUT
301
+ |.PP
302
+ |here is \\fB\\fCsome code\\fR for you
303
+ OUTPUT
304
+ end
305
+
306
+ it 'renders hyperlinks' do
307
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
308
+ |Send [me](mailto:foo@bar.baz), e-mail.
309
+ INPUT
310
+ |.PP
311
+ |Send me
312
+ |.MT foo@bar.baz
313
+ |.ME , e\\-mail.
314
+ OUTPUT
315
+
316
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
317
+ |Take [me](http://myself), somewhere.
318
+ INPUT
319
+ |.PP
320
+ |Take me
321
+ |.UR http://myself
322
+ |.UE , somewhere.
323
+ OUTPUT
324
+
325
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
326
+ |Mail me foo@bar.baz now.
327
+ INPUT
328
+ |.PP
329
+ |Mail me#{SPACE}
330
+ |.MT foo@bar.baz
331
+ |.ME
332
+ |now.
333
+ OUTPUT
334
+
335
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
336
+ |Take me http://www.somewhere now.
337
+ INPUT
338
+ |.PP
339
+ |Take me#{SPACE}
340
+ |.UR http://www.somewhere
341
+ |.UE
342
+ |now.
343
+ OUTPUT
344
+ end
345
+
346
+ it 'renders unordered lists' do
347
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
348
+ |Here is an unordered list:
349
+ |
350
+ |* foo
351
+ | * bar
352
+ | * baz
353
+ |* qux
354
+ INPUT
355
+ |.PP
356
+ |Here is an unordered list:
357
+ |.RS
358
+ |.IP \\(bu 2
359
+ |foo
360
+ |.RS
361
+ |.IP \\(bu 2
362
+ |bar
363
+ |.RS
364
+ |.IP \\(bu 2
365
+ |baz
366
+ |.RE
367
+ |.RE
368
+ |.IP \\(bu 2
369
+ |qux
370
+ |.RE
371
+ OUTPUT
372
+ end
373
+
374
+ it 'renders unordered lists while squashing first paragraphs' do
375
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
376
+ |Here is an unordered list:
377
+ |
378
+ | * here is a
379
+ | paragraph
380
+ |
381
+ | here is a
382
+ | subparagraph
383
+ INPUT
384
+ |.PP
385
+ |Here is an unordered list:
386
+ |.RS
387
+ |.IP \\(bu 2
388
+ |here is a
389
+ |paragraph
390
+ |.PP
391
+ |here is a
392
+ |subparagraph
393
+ |.RE
394
+ OUTPUT
395
+ end
396
+
397
+ it 'renders ordered lists' do
398
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
399
+ |Here is an ordered list:
400
+ |
401
+ |1. foo
402
+ | 1. bar
403
+ | 1. baz
404
+ |2. qux
405
+ INPUT
406
+ |.PP
407
+ |Here is an ordered list:
408
+ |.nr step2 0 1
409
+ |.RS
410
+ |.IP \\n+[step2]
411
+ |foo
412
+ |.nr step1 0 1
413
+ |.RS
414
+ |.IP \\n+[step1]
415
+ |bar
416
+ |.nr step0 0 1
417
+ |.RS
418
+ |.IP \\n+[step0]
419
+ |baz
420
+ |.RE
421
+ |.RE
422
+ |.IP \\n+[step2]
423
+ |qux
424
+ |.RE
425
+ OUTPUT
426
+ end
427
+
428
+ it 'renders ordered lists while squashing first paragraphs' do
429
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
430
+ |Here is an ordered list:
431
+ |
432
+ |1. here is a
433
+ | paragraph
434
+ |
435
+ | here is a
436
+ | subparagraph
437
+ INPUT
438
+ |.PP
439
+ |Here is an ordered list:
440
+ |.nr step0 0 1
441
+ |.RS
442
+ |.IP \\n+[step0]
443
+ |here is a
444
+ |paragraph
445
+ |.PP
446
+ |here is a
447
+ |subparagraph
448
+ |.RE
449
+ OUTPUT
450
+ end
451
+
452
+ it 'renders horizontal rules' do
453
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
454
+ |foo
455
+ |
456
+ |* * *
457
+ |
458
+ |bar
459
+ INPUT
460
+ |.PP
461
+ |foo
462
+ |.ti 0
463
+ |\\l'\\n(.lu'
464
+ |.PP
465
+ |bar
466
+ OUTPUT
467
+ end
468
+
469
+ it 'renders horizontal rules inside blockquotes' do
470
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
471
+ |>foo
472
+ |>
473
+ |>* * *
474
+ |>
475
+ |>bar
476
+ INPUT
477
+ |.RS
478
+ |foo
479
+ |.ti 0
480
+ |\\l'\\n(.lu'
481
+ |.PP
482
+ |bar
483
+ |.RE
484
+ OUTPUT
485
+ end
486
+
487
+ it 'renders some named entities' do
488
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
489
+ |&middot; &#183;
490
+ |&copy; &#169;
491
+ |&cent; &#162;
492
+ |&Dagger; &#8225;
493
+ |&deg; &#176;
494
+ |&dagger; &#8224;
495
+ |&quot; &#34;
496
+ |&mdash; &#8212;
497
+ |&ndash; &#8211;
498
+ |&reg; &#174;
499
+ |&sect; &#167;
500
+ |&oline; &#8254;
501
+ |&equiv; &#8801;
502
+ |&ge; &#8805;
503
+ |&le; &#8804;
504
+ |&ne; &#8800;
505
+ |&rarr; &#8594;
506
+ |&larr; &#8592;
507
+ |&plusmn; &#177;
508
+ INPUT
509
+ |.PP
510
+ |\\[pc] \\[pc]
511
+ |\\[co] \\[co]
512
+ |\\[ct] \\[ct]
513
+ |\\[dd] \\[dd]
514
+ |\\[de] \\[de]
515
+ |\\[dg] \\[dg]
516
+ |\\[dq] \\[dq]
517
+ |\\[em] \\[em]
518
+ |\\[en] \\[en]
519
+ |\\[rg] \\[rg]
520
+ |\\[sc] \\[sc]
521
+ |\\[rn] \\[rn]
522
+ |\\[==] \\[==]
523
+ |\\[>=] \\[>=]
524
+ |\\[<=] \\[<=]
525
+ |\\[!=] \\[!=]
526
+ |\\[->] \\[->]
527
+ |\\[<-] \\[<-]
528
+ |\\[+-] \\[+-]
529
+ OUTPUT
530
+ end
531
+
532
+ it 'renders tables' do
533
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
534
+ |First Header | Second Header
535
+ |------------- | -------------
536
+ |First Content | Second Content
537
+ INPUT
538
+ |.TS
539
+ |allbox;
540
+ |cb cb
541
+ |l l
542
+ |.
543
+ |First Header\tSecond Header
544
+ |First Content\tSecond Content
545
+ |.TE
546
+ OUTPUT
547
+ end
548
+
549
+ it 'renders tables with alignment' do
550
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
551
+ || Item | Value |
552
+ || --------- | -----:|
553
+ || Computer | $1600 |
554
+ || Phone | $12 |
555
+ || Pipe | $1 |
556
+ INPUT
557
+ |.TS
558
+ |allbox;
559
+ |cb cb
560
+ |l r
561
+ |l r
562
+ |l r
563
+ |.
564
+ |Item\tValue
565
+ |Computer\t$1600
566
+ |Phone\t$12
567
+ |Pipe\t$1
568
+ |.TE
569
+ OUTPUT
570
+ end
571
+
572
+ it 'renders references to other man pages as hyperlinks' do
573
+ @markdown.render(heredoc(<<-INPUT)).must_equal(heredoc(<<-OUTPUT))
574
+ |convert them from markdown(7) into roff(7), using
575
+ INPUT
576
+ |.PP
577
+ |convert them from#{SPACE}
578
+ |.BR markdown (7)#{SPACE}
579
+ |into#{SPACE}
580
+ |.BR roff (7),#{SPACE}
581
+ |using
582
+ OUTPUT
583
+ end
584
+ end
@@ -0,0 +1,2 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'