github-markup 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +1 -0
- data/.gitignore +1 -1
- data/.gitmodules +3 -0
- data/.travis.yml +4 -1
- data/Dockerfile +39 -0
- data/HISTORY.md +4 -0
- data/README.md +11 -2
- data/github-markup.gemspec +2 -1
- data/lib/github-markup.rb +1 -1
- data/lib/github/commands/pod62html +10 -0
- data/lib/github/markup.rb +1 -0
- data/lib/github/markup/implementation.rb +5 -1
- data/lib/github/markups.rb +1 -0
- data/test/markup_test.rb +2 -3
- data/test/markups/README.pod6 +151 -0
- data/test/markups/README.pod6.html +128 -0
- data/vendor/Pod-To-HTML/LICENSE +201 -0
- data/vendor/Pod-To-HTML/META6.json +20 -0
- data/vendor/Pod-To-HTML/README.md +96 -0
- data/vendor/Pod-To-HTML/README.pod6 +42 -0
- data/vendor/Pod-To-HTML/TODO +5 -0
- data/vendor/Pod-To-HTML/lib/Pod/To/HTML.pm +691 -0
- data/vendor/Pod-To-HTML/resources/examples/01-parse-files.p6 +7 -0
- data/vendor/Pod-To-HTML/resources/examples/README.md +13 -0
- data/vendor/Pod-To-HTML/resources/examples/main.mustache +26 -0
- data/vendor/Pod-To-HTML/resources/examples/render.p6 +17 -0
- data/vendor/Pod-To-HTML/resources/templates/main.mustache +32 -0
- data/vendor/Pod-To-HTML/t/010-basic.t +29 -0
- data/vendor/Pod-To-HTML/t/020-code.t +49 -0
- data/vendor/Pod-To-HTML/t/030-comment.t +15 -0
- data/vendor/Pod-To-HTML/t/040-lists.t +82 -0
- data/vendor/Pod-To-HTML/t/050-format-x-index.t +47 -0
- data/vendor/Pod-To-HTML/t/060-table.t +108 -0
- data/vendor/Pod-To-HTML/t/070-headings.t +45 -0
- data/vendor/Pod-To-HTML/t/075-defn.t +45 -0
- data/vendor/Pod-To-HTML/t/080-lang.t +14 -0
- data/vendor/Pod-To-HTML/t/090-css.t +16 -0
- data/vendor/Pod-To-HTML/t/100-issue-37.t +16 -0
- data/vendor/Pod-To-HTML/t/110-issue-41.t +18 -0
- data/vendor/Pod-To-HTML/t/120-templates.t +42 -0
- data/vendor/Pod-To-HTML/t/templates/main.mustache +33 -0
- metadata +39 -6
@@ -0,0 +1,201 @@
|
|
1
|
+
The Artistic License 2.0
|
2
|
+
|
3
|
+
Copyright (c) 2000-2006, The Perl Foundation.
|
4
|
+
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
6
|
+
of this license document, but changing it is not allowed.
|
7
|
+
|
8
|
+
Preamble
|
9
|
+
|
10
|
+
This license establishes the terms under which a given free software
|
11
|
+
Package may be copied, modified, distributed, and/or redistributed.
|
12
|
+
The intent is that the Copyright Holder maintains some artistic
|
13
|
+
control over the development of that Package while still keeping the
|
14
|
+
Package available as open source and free software.
|
15
|
+
|
16
|
+
You are always permitted to make arrangements wholly outside of this
|
17
|
+
license directly with the Copyright Holder of a given Package. If the
|
18
|
+
terms of this license do not permit the full use that you propose to
|
19
|
+
make of the Package, you should contact the Copyright Holder and seek
|
20
|
+
a different licensing arrangement.
|
21
|
+
|
22
|
+
Definitions
|
23
|
+
|
24
|
+
"Copyright Holder" means the individual(s) or organization(s)
|
25
|
+
named in the copyright notice for the entire Package.
|
26
|
+
|
27
|
+
"Contributor" means any party that has contributed code or other
|
28
|
+
material to the Package, in accordance with the Copyright Holder's
|
29
|
+
procedures.
|
30
|
+
|
31
|
+
"You" and "your" means any person who would like to copy,
|
32
|
+
distribute, or modify the Package.
|
33
|
+
|
34
|
+
"Package" means the collection of files distributed by the
|
35
|
+
Copyright Holder, and derivatives of that collection and/or of
|
36
|
+
those files. A given Package may consist of either the Standard
|
37
|
+
Version, or a Modified Version.
|
38
|
+
|
39
|
+
"Distribute" means providing a copy of the Package or making it
|
40
|
+
accessible to anyone else, or in the case of a company or
|
41
|
+
organization, to others outside of your company or organization.
|
42
|
+
|
43
|
+
"Distributor Fee" means any fee that you charge for Distributing
|
44
|
+
this Package or providing support for this Package to another
|
45
|
+
party. It does not mean licensing fees.
|
46
|
+
|
47
|
+
"Standard Version" refers to the Package if it has not been
|
48
|
+
modified, or has been modified only in ways explicitly requested
|
49
|
+
by the Copyright Holder.
|
50
|
+
|
51
|
+
"Modified Version" means the Package, if it has been changed, and
|
52
|
+
such changes were not explicitly requested by the Copyright
|
53
|
+
Holder.
|
54
|
+
|
55
|
+
"Original License" means this Artistic License as Distributed with
|
56
|
+
the Standard Version of the Package, in its current version or as
|
57
|
+
it may be modified by The Perl Foundation in the future.
|
58
|
+
|
59
|
+
"Source" form means the source code, documentation source, and
|
60
|
+
configuration files for the Package.
|
61
|
+
|
62
|
+
"Compiled" form means the compiled bytecode, object code, binary,
|
63
|
+
or any other form resulting from mechanical transformation or
|
64
|
+
translation of the Source form.
|
65
|
+
|
66
|
+
|
67
|
+
Permission for Use and Modification Without Distribution
|
68
|
+
|
69
|
+
(1) You are permitted to use the Standard Version and create and use
|
70
|
+
Modified Versions for any purpose without restriction, provided that
|
71
|
+
you do not Distribute the Modified Version.
|
72
|
+
|
73
|
+
|
74
|
+
Permissions for Redistribution of the Standard Version
|
75
|
+
|
76
|
+
(2) You may Distribute verbatim copies of the Source form of the
|
77
|
+
Standard Version of this Package in any medium without restriction,
|
78
|
+
either gratis or for a Distributor Fee, provided that you duplicate
|
79
|
+
all of the original copyright notices and associated disclaimers. At
|
80
|
+
your discretion, such verbatim copies may or may not include a
|
81
|
+
Compiled form of the Package.
|
82
|
+
|
83
|
+
(3) You may apply any bug fixes, portability changes, and other
|
84
|
+
modifications made available from the Copyright Holder. The resulting
|
85
|
+
Package will still be considered the Standard Version, and as such
|
86
|
+
will be subject to the Original License.
|
87
|
+
|
88
|
+
|
89
|
+
Distribution of Modified Versions of the Package as Source
|
90
|
+
|
91
|
+
(4) You may Distribute your Modified Version as Source (either gratis
|
92
|
+
or for a Distributor Fee, and with or without a Compiled form of the
|
93
|
+
Modified Version) provided that you clearly document how it differs
|
94
|
+
from the Standard Version, including, but not limited to, documenting
|
95
|
+
any non-standard features, executables, or modules, and provided that
|
96
|
+
you do at least ONE of the following:
|
97
|
+
|
98
|
+
(a) make the Modified Version available to the Copyright Holder
|
99
|
+
of the Standard Version, under the Original License, so that the
|
100
|
+
Copyright Holder may include your modifications in the Standard
|
101
|
+
Version.
|
102
|
+
|
103
|
+
(b) ensure that installation of your Modified Version does not
|
104
|
+
prevent the user installing or running the Standard Version. In
|
105
|
+
addition, the Modified Version must bear a name that is different
|
106
|
+
from the name of the Standard Version.
|
107
|
+
|
108
|
+
(c) allow anyone who receives a copy of the Modified Version to
|
109
|
+
make the Source form of the Modified Version available to others
|
110
|
+
under
|
111
|
+
|
112
|
+
(i) the Original License or
|
113
|
+
|
114
|
+
(ii) a license that permits the licensee to freely copy,
|
115
|
+
modify and redistribute the Modified Version using the same
|
116
|
+
licensing terms that apply to the copy that the licensee
|
117
|
+
received, and requires that the Source form of the Modified
|
118
|
+
Version, and of any works derived from it, be made freely
|
119
|
+
available in that license fees are prohibited but Distributor
|
120
|
+
Fees are allowed.
|
121
|
+
|
122
|
+
|
123
|
+
Distribution of Compiled Forms of the Standard Version
|
124
|
+
or Modified Versions without the Source
|
125
|
+
|
126
|
+
(5) You may Distribute Compiled forms of the Standard Version without
|
127
|
+
the Source, provided that you include complete instructions on how to
|
128
|
+
get the Source of the Standard Version. Such instructions must be
|
129
|
+
valid at the time of your distribution. If these instructions, at any
|
130
|
+
time while you are carrying out such distribution, become invalid, you
|
131
|
+
must provide new instructions on demand or cease further distribution.
|
132
|
+
If you provide valid instructions or cease distribution within thirty
|
133
|
+
days after you become aware that the instructions are invalid, then
|
134
|
+
you do not forfeit any of your rights under this license.
|
135
|
+
|
136
|
+
(6) You may Distribute a Modified Version in Compiled form without
|
137
|
+
the Source, provided that you comply with Section 4 with respect to
|
138
|
+
the Source of the Modified Version.
|
139
|
+
|
140
|
+
|
141
|
+
Aggregating or Linking the Package
|
142
|
+
|
143
|
+
(7) You may aggregate the Package (either the Standard Version or
|
144
|
+
Modified Version) with other packages and Distribute the resulting
|
145
|
+
aggregation provided that you do not charge a licensing fee for the
|
146
|
+
Package. Distributor Fees are permitted, and licensing fees for other
|
147
|
+
components in the aggregation are permitted. The terms of this license
|
148
|
+
apply to the use and Distribution of the Standard or Modified Versions
|
149
|
+
as included in the aggregation.
|
150
|
+
|
151
|
+
(8) You are permitted to link Modified and Standard Versions with
|
152
|
+
other works, to embed the Package in a larger work of your own, or to
|
153
|
+
build stand-alone binary or bytecode versions of applications that
|
154
|
+
include the Package, and Distribute the result without restriction,
|
155
|
+
provided the result does not expose a direct interface to the Package.
|
156
|
+
|
157
|
+
|
158
|
+
Items That are Not Considered Part of a Modified Version
|
159
|
+
|
160
|
+
(9) Works (including, but not limited to, modules and scripts) that
|
161
|
+
merely extend or make use of the Package, do not, by themselves, cause
|
162
|
+
the Package to be a Modified Version. In addition, such works are not
|
163
|
+
considered parts of the Package itself, and are not subject to the
|
164
|
+
terms of this license.
|
165
|
+
|
166
|
+
|
167
|
+
General Provisions
|
168
|
+
|
169
|
+
(10) Any use, modification, and distribution of the Standard or
|
170
|
+
Modified Versions is governed by this Artistic License. By using,
|
171
|
+
modifying or distributing the Package, you accept this license. Do not
|
172
|
+
use, modify, or distribute the Package, if you do not accept this
|
173
|
+
license.
|
174
|
+
|
175
|
+
(11) If your Modified Version has been derived from a Modified
|
176
|
+
Version made by someone other than you, you are nevertheless required
|
177
|
+
to ensure that your Modified Version complies with the requirements of
|
178
|
+
this license.
|
179
|
+
|
180
|
+
(12) This license does not grant you the right to use any trademark,
|
181
|
+
service mark, tradename, or logo of the Copyright Holder.
|
182
|
+
|
183
|
+
(13) This license includes the non-exclusive, worldwide,
|
184
|
+
free-of-charge patent license to make, have made, use, offer to sell,
|
185
|
+
sell, import and otherwise transfer the Package with respect to any
|
186
|
+
patent claims licensable by the Copyright Holder that are necessarily
|
187
|
+
infringed by the Package. If you institute patent litigation
|
188
|
+
(including a cross-claim or counterclaim) against any party alleging
|
189
|
+
that the Package constitutes direct or contributory patent
|
190
|
+
infringement, then this Artistic License to you shall terminate on the
|
191
|
+
date that such litigation is filed.
|
192
|
+
|
193
|
+
(14) Disclaimer of Warranty:
|
194
|
+
THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
|
195
|
+
IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
|
196
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
|
197
|
+
NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
|
198
|
+
LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
|
199
|
+
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
200
|
+
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
|
201
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"perl" : "6.*",
|
3
|
+
"name" : "Pod::To::HTML",
|
4
|
+
"version" : "0.4.0",
|
5
|
+
"author" : "Perl 6",
|
6
|
+
"description" : "Convert Perl 6 Pod to HTML",
|
7
|
+
"license" : "Artistic-2.0",
|
8
|
+
"depends" : [
|
9
|
+
"URI",
|
10
|
+
"Template::Mustache"
|
11
|
+
],
|
12
|
+
"test-depends" : ["Test::Output"],
|
13
|
+
"provides" : {
|
14
|
+
"Pod::To::HTML" : "lib/Pod/To/HTML.pm"
|
15
|
+
},
|
16
|
+
"resources": [
|
17
|
+
"templates/main.mustache"
|
18
|
+
],
|
19
|
+
"source-url" : "git://github.com/perl6/Pod-To-HTML.git"
|
20
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Pod::To::HTML
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/perl6/Pod-To-HTML.svg?branch=master)](https://travis-ci.org/perl6/Pod-To-HTML)
|
4
|
+
|
5
|
+
Render Perl 6 Pod as HTML
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
This module is in the [Perl 6 ecosystem](https://modules.perl6.org), so you install it in the usual way:
|
10
|
+
|
11
|
+
zef install Pod::To::HTML
|
12
|
+
|
13
|
+
**Note**: Perl 6 2018.06 introduces changes on how non-breaking
|
14
|
+
whitespace was handled; this is now included in the tests. If
|
15
|
+
the installation fails, please upgrade to Perl 6 >= 2018.06 or
|
16
|
+
simply disregard the test and install with `--force` if that
|
17
|
+
particular feature is of no use to you.
|
18
|
+
|
19
|
+
**Note**: Perl6 2018.11 introduced handling of Definition blocks,
|
20
|
+
`Defn`. Please upgrade if you are using that feature in the
|
21
|
+
documentation.
|
22
|
+
|
23
|
+
## SYNOPSIS
|
24
|
+
|
25
|
+
From the command line:
|
26
|
+
|
27
|
+
perl6 --doc=HTML lib/FancyModule.pm > FancyModule.html
|
28
|
+
|
29
|
+
From within Perl 6:
|
30
|
+
|
31
|
+
```perl6
|
32
|
+
# Pod Block
|
33
|
+
=pod My I<super B<awesome>> embedded C<pod> document!
|
34
|
+
|
35
|
+
say Pod::To::HTML.render($=pod[0]);
|
36
|
+
|
37
|
+
# Pod file
|
38
|
+
say Pod::To::HTML.render('your/file.pod'.IO, header =>
|
39
|
+
"your-custom-header-inside-body", footer =>
|
40
|
+
"your-custom-footer-inside-body", head-fields
|
41
|
+
=> "tags-inside-head", lang => "document
|
42
|
+
language (defaults to 'en')", default-title =
|
43
|
+
'No =title was found so we use this', css-url
|
44
|
+
=> 'https://example.com/css.css'); # specify
|
45
|
+
css-url as empty string to disable CSS
|
46
|
+
inclusion
|
47
|
+
|
48
|
+
# Pod string
|
49
|
+
|
50
|
+
my $pod = q:to/END/;
|
51
|
+
=pod
|
52
|
+
My I<super B<awesome>> embedded C<pod>
|
53
|
+
document!
|
54
|
+
END
|
55
|
+
say Pod::To::HTML.render($pod,
|
56
|
+
header =>"your-custom-header-inside-body",
|
57
|
+
footer => "your-custom-footer-inside-body",
|
58
|
+
head-fields => "tags-inside-head",
|
59
|
+
lang => "document language (defaults to 'en')",
|
60
|
+
default-title => 'No =title was found so we use this');
|
61
|
+
|
62
|
+
# If you want to use a specific template
|
63
|
+
say pod2html $=pod[0], :templates("lib/templates");
|
64
|
+
# main.mustache should be in that directory
|
65
|
+
|
66
|
+
|
67
|
+
```
|
68
|
+
## DESCRIPTION
|
69
|
+
|
70
|
+
`Pod::To::HTML` takes a Pod 6 tree and outputs correspondingly
|
71
|
+
formatted HTML. Generally this is done via the command line,
|
72
|
+
using`perl6 --doc=HTML`, which extracts the pod from the document and
|
73
|
+
feeds it to `Pod::To::HTML`. The other route is with the `render`
|
74
|
+
method (called by `--doc=HTML`), which creates a complete HTML
|
75
|
+
document from the Pod tree it is called with.
|
76
|
+
|
77
|
+
Optionally, a custom header/fooder/head-fields can be
|
78
|
+
provided. These can be used to link to custom CSS stylesheets and
|
79
|
+
JavaScript libraries.
|
80
|
+
|
81
|
+
## Examples
|
82
|
+
|
83
|
+
Check the [`examples`](resources/examples/README.md) directory (which should have been installed with your distribution, or is right here if you download from source) for a few illustrative examples.
|
84
|
+
|
85
|
+
## DEBUGGING
|
86
|
+
|
87
|
+
You can set the `P6DOC_DEBUG` environmental variable to make the
|
88
|
+
module produce some debugging information.
|
89
|
+
|
90
|
+
|
91
|
+
## LICENSE
|
92
|
+
|
93
|
+
You can use and distribute this module under the terms of the The Artistic License 2.0. See the LICENSE file included in this distribution for complete details.
|
94
|
+
|
95
|
+
The META6.json file of this distribution may be distributed and modified without restrictions or attribution.
|
96
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
=begin pod
|
2
|
+
=TITLE Pod::To::HTML
|
3
|
+
|
4
|
+
Render Pod6 as HTML
|
5
|
+
|
6
|
+
=begin SYNOPSIS
|
7
|
+
|
8
|
+
Use it integrated with Perl 6 documentation system
|
9
|
+
|
10
|
+
perl6 --doc=HTML lib/FancyModule.pm > FancyModule.html
|
11
|
+
|
12
|
+
Or as an external function to render your documentation
|
13
|
+
|
14
|
+
=begin code
|
15
|
+
=pod My I<super B<awesome>> embedded C<pod> document!
|
16
|
+
|
17
|
+
say Pod::To::HTML.render($=pod[0]);
|
18
|
+
=end code
|
19
|
+
|
20
|
+
=end SYNOPSIS
|
21
|
+
|
22
|
+
=begin DESCRIPTION
|
23
|
+
|
24
|
+
B<C<Pod::To::HTML>> takes a C<Pod> tree and outputs correspondingly
|
25
|
+
formatted C<HTML>. Generally this is via C<perl6 --doc=HTML>, which
|
26
|
+
extracts the pod from the document and feeds it to C<Pod::To::HTML>. The
|
27
|
+
other route is with the C<render> method (called by C<--doc=HTML>),
|
28
|
+
which creates a complete C<HTML> document from the C<Pod> tree it is
|
29
|
+
called with.
|
30
|
+
|
31
|
+
=end DESCRIPTION
|
32
|
+
|
33
|
+
|
34
|
+
=begin LICENSE
|
35
|
+
|
36
|
+
You can use and distribute this module under the terms of the The Artistic License 2.0. See the LICENSE file included in this distribution for complete details.
|
37
|
+
|
38
|
+
The META6.json file of this distribution may be distributed and modified without restrictions or attribution.
|
39
|
+
|
40
|
+
=end LICENSE
|
41
|
+
|
42
|
+
=end pod
|
@@ -0,0 +1,691 @@
|
|
1
|
+
unit class Pod::To::HTML;
|
2
|
+
use URI::Escape;
|
3
|
+
use Template::Mustache;
|
4
|
+
|
5
|
+
#try require Term::ANSIColor <&colored>;
|
6
|
+
#if &colored.defined {
|
7
|
+
#&colored = -> $t, $c { $t };
|
8
|
+
#}
|
9
|
+
|
10
|
+
sub colored($text, $how) {
|
11
|
+
$text
|
12
|
+
}
|
13
|
+
|
14
|
+
multi method render($pod) {
|
15
|
+
pod2html($pod)
|
16
|
+
}
|
17
|
+
|
18
|
+
multi method render(Array $pod, Str :$header = '', Str :$footer = '', Str :head-fields($head) = '', :$default-title = '', :$lang = 'en') {
|
19
|
+
pod2html($pod, :$header, :$footer, :$head, :$default-title, :$lang)
|
20
|
+
}
|
21
|
+
|
22
|
+
multi method render(Pod::Block $pod, Str :$header = '', Str :$footer = '', Str :head-fields($head) = '', :$default-title = '', :$lang = 'en') {
|
23
|
+
pod2html($pod, :$header, :$footer, :$head, :$default-title, :$lang)
|
24
|
+
}
|
25
|
+
|
26
|
+
multi method render(IO::Path $file, Str :$header = '', Str :$footer = '', Str :head-fields($head) = '', :$default-title = '', :$lang = 'en') {
|
27
|
+
use MONKEY-SEE-NO-EVAL;
|
28
|
+
pod2html(EVAL($file.slurp ~ "\n\$=pod"), :$header, :$footer, :$head, :$default-title, :$lang);
|
29
|
+
}
|
30
|
+
|
31
|
+
multi method render(Str $pod-string, Str :$header = '', Str :$footer = '', Str :head-fields($head) = '', :$default-title = '', :$lang = 'en') {
|
32
|
+
use MONKEY-SEE-NO-EVAL;
|
33
|
+
pod2html(EVAL($pod-string ~ "\n\$=pod"), :$header, :$footer, :$head, :$default-title, :$lang);
|
34
|
+
}
|
35
|
+
|
36
|
+
# FIXME: this code's a horrible mess. It'd be really helpful to have a module providing a generic
|
37
|
+
# way to walk a Pod tree and invoke callbacks on each node, that would reduce the multispaghetti at
|
38
|
+
# the bottom to something much more readable.
|
39
|
+
|
40
|
+
my &url = {$_};
|
41
|
+
my $title;
|
42
|
+
my $subtitle;
|
43
|
+
my @meta;
|
44
|
+
my @indexes;
|
45
|
+
my @body;
|
46
|
+
my @footnotes;
|
47
|
+
my %crossrefs;
|
48
|
+
|
49
|
+
# see <https://docs.perl6.org/language/traps#Constants_are_Compile_Time>
|
50
|
+
my $DEBUG := %*ENV<P6DOC_DEBUG>;
|
51
|
+
|
52
|
+
sub Debug(Callable $c) { $c() if $DEBUG; }
|
53
|
+
|
54
|
+
sub escape_html(Str $str --> Str ) {
|
55
|
+
return $str unless ( $str ~~ /<[ & < > " ' { ]>/ ) or ( $str ~~ / ' ' / );
|
56
|
+
$str.trans( [ q{&}, q{<}, q{>}, q{"}, q{'}, q{ } ] =>
|
57
|
+
[ q{&}, q{<}, q{>}, q{"}, q{'}, q{ }]);
|
58
|
+
}
|
59
|
+
|
60
|
+
sub unescape_html(Str $str --> Str ) {
|
61
|
+
$str.trans( [ rx{'&'}, rx{'<'}, rx{'>'}, rx{'"'}, rx{'''} ] =>
|
62
|
+
[ q{&}, q{<}, q{>}, q{"}, q{'} ] );
|
63
|
+
}
|
64
|
+
|
65
|
+
sub escape_id ($id) {
|
66
|
+
$id.trim.subst(/\s+/, '_', :g)
|
67
|
+
.subst('"', '"', :g)
|
68
|
+
.subst(' ', '_', :g)
|
69
|
+
.subst(''', "'", :g);
|
70
|
+
}
|
71
|
+
|
72
|
+
multi visit(Nil, |a) {
|
73
|
+
Debug { note colored("visit called for Nil", "bold") }
|
74
|
+
}
|
75
|
+
|
76
|
+
multi visit($root, :&pre, :&post, :&assemble = -> *% { Nil }) {
|
77
|
+
Debug { note colored("visit called for ", "bold") ~ $root.perl }
|
78
|
+
my ($pre, $post);
|
79
|
+
$pre = pre($root) if defined ⪯
|
80
|
+
|
81
|
+
my @content = $root.?contents.map: {visit $_, :&pre, :&post, :&assemble};
|
82
|
+
$post = post($root, :@content) if defined &post;
|
83
|
+
|
84
|
+
return assemble(:$pre, :$post, :@content, :node($root));
|
85
|
+
}
|
86
|
+
|
87
|
+
class Pod::List is Pod::Block { };
|
88
|
+
class Pod::DefnList is Pod::Block { };
|
89
|
+
BEGIN { if ::('Pod::Defn') ~~ Failure { CORE::Pod::<Defn> := class {} } }
|
90
|
+
|
91
|
+
sub assemble-list-items(:@content, :$node, *% ) {
|
92
|
+
my @newcont;
|
93
|
+
my $foundone = False;
|
94
|
+
my $everwarn = False;
|
95
|
+
|
96
|
+
my $atlevel = 0;
|
97
|
+
my @pushalias;
|
98
|
+
|
99
|
+
my sub oodwarn($got, $want) {
|
100
|
+
unless $everwarn {
|
101
|
+
warn "=item$got without preceding =item$want found!";
|
102
|
+
$everwarn = True;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
for @content {
|
107
|
+
when Pod::Item {
|
108
|
+
$foundone = True;
|
109
|
+
|
110
|
+
# here we deal with @newcont being empty (first list), or with the
|
111
|
+
# last element not being a list (new list)
|
112
|
+
unless +@newcont && @newcont[*-1] ~~ Pod::List {
|
113
|
+
@newcont.push(Pod::List.new());
|
114
|
+
if $_.level > 1 {
|
115
|
+
oodwarn($_.level, 1);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
# only bother doing the binding business if we're at a different
|
120
|
+
# level than previous items
|
121
|
+
if $_.level != $atlevel {
|
122
|
+
# guaranteed to be bound to a Pod::List (see above 'unless')
|
123
|
+
@pushalias := @newcont[*-1].contents;
|
124
|
+
|
125
|
+
for 2..($_.level) -> $L {
|
126
|
+
unless +@pushalias && @pushalias[*-1] ~~ Pod::List {
|
127
|
+
@pushalias.push(Pod::List.new());
|
128
|
+
if +@pushalias == 1 { # we had to push a sublist to a list with no =items
|
129
|
+
oodwarn($OUTER::_.level, $L);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
@pushalias := @pushalias[*-1].contents;
|
133
|
+
}
|
134
|
+
|
135
|
+
$atlevel = $_.level;
|
136
|
+
}
|
137
|
+
|
138
|
+
@pushalias.push($_);
|
139
|
+
}
|
140
|
+
# This is simpler than lists because we don't need to
|
141
|
+
# list
|
142
|
+
when Pod::Defn {
|
143
|
+
$foundone = True;
|
144
|
+
unless +@newcont && @newcont[*-1] ~~ Pod::DefnList {
|
145
|
+
@newcont.push(Pod::DefnList.new());
|
146
|
+
}
|
147
|
+
@newcont[*-1].contents.push($_);
|
148
|
+
}
|
149
|
+
|
150
|
+
default {
|
151
|
+
@newcont.push($_);
|
152
|
+
$atlevel = 0;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
return $foundone ?? $node.clone(contents => @newcont) !! $node;
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
#| Converts a Pod tree to a HTML document using templates
|
161
|
+
sub pod2html(
|
162
|
+
$pod,
|
163
|
+
:&url = -> $url { $url },
|
164
|
+
:$head = '',
|
165
|
+
:$header = '',
|
166
|
+
:$footer = '',
|
167
|
+
:$default-title,
|
168
|
+
:$css-url = '//design.perl6.org/perl.css',
|
169
|
+
:$templates = Str,
|
170
|
+
:$lang = 'en'
|
171
|
+
--> Str ) is export {
|
172
|
+
|
173
|
+
my $template-file = %?RESOURCES<templates/main.mustache>;
|
174
|
+
with $templates {
|
175
|
+
if "$templates/main.mustache".IO ~~ :f {
|
176
|
+
$template-file = "$templates/main.mustache".IO
|
177
|
+
}
|
178
|
+
else {
|
179
|
+
note "$templates does not contain required templates. Using default.";
|
180
|
+
}
|
181
|
+
}
|
182
|
+
($title, $subtitle, @meta, @indexes, @body, @footnotes) = ();
|
183
|
+
#| Keep count of how many footnotes we've output.
|
184
|
+
my Int $*done-notes = 0;
|
185
|
+
&OUTER::url = &url;
|
186
|
+
@body.push: node2html($pod.map: { visit $_, :assemble(&assemble-list-items) });
|
187
|
+
my $title_html = $title // $default-title // '';
|
188
|
+
|
189
|
+
my Template::Mustache $main-tm .= new;
|
190
|
+
return $main-tm.render( $template-file.IO.slurp, :literal, (
|
191
|
+
:$lang,
|
192
|
+
:title($title_html),
|
193
|
+
:$subtitle,
|
194
|
+
:css($css-url),
|
195
|
+
:meta( do-metadata ),
|
196
|
+
:$head,
|
197
|
+
:toc( do-toc($pod) ),
|
198
|
+
:$header,
|
199
|
+
:@body,
|
200
|
+
:footnotes( do-footnotes ),
|
201
|
+
:$footer).hash
|
202
|
+
);
|
203
|
+
|
204
|
+
=comment out
|
205
|
+
my $prelude = qq:to/END/;
|
206
|
+
<!doctype html>
|
207
|
+
<html lang="$lang">
|
208
|
+
<head>
|
209
|
+
<title>{ $title_html }</title>
|
210
|
+
<meta charset="UTF-8" />
|
211
|
+
<style>
|
212
|
+
/* code gets the browser-default font
|
213
|
+
* kbd gets a slightly less common monospace font
|
214
|
+
* samp gets the hard pixelly fonts
|
215
|
+
*/
|
216
|
+
kbd \{ font-family: "Droid Sans Mono", "Luxi Mono", "Inconsolata", monospace }
|
217
|
+
samp \{ font-family: "Terminus", "Courier", "Lucida Console", monospace }
|
218
|
+
/* WHATWG HTML frowns on the use of <u> because it looks like a link,
|
219
|
+
* so we make it not look like one.
|
220
|
+
*/
|
221
|
+
u \{ text-decoration: none }
|
222
|
+
.nested \{
|
223
|
+
margin-left: 3em;
|
224
|
+
}
|
225
|
+
// footnote things:
|
226
|
+
aside, u \{ opacity: 0.7 }
|
227
|
+
a[id^="fn-"]:target \{ background: #ff0 }
|
228
|
+
</style>
|
229
|
+
{ qq|<link rel="stylesheet" href="$css-url">| if $css-url }
|
230
|
+
{ do-metadata() // () }
|
231
|
+
$head
|
232
|
+
</head>
|
233
|
+
<body class="pod">
|
234
|
+
<div id="___top"></div>
|
235
|
+
$header
|
236
|
+
END
|
237
|
+
=comment out
|
238
|
+
return join(qq{\n},
|
239
|
+
$prelude,
|
240
|
+
( $title.defined ?? "<h1 class='title'>{$title_html}</h1>"
|
241
|
+
!! () ),
|
242
|
+
( $subtitle.defined ?? "<p class='subtitle'>{$subtitle}</p>"
|
243
|
+
!! () ),
|
244
|
+
( my $ToC := do-toc($pod) // () ),
|
245
|
+
'<div class="pod-body', ($ToC ?? '' !! ' no-toc'), '">',@body,'</div>',
|
246
|
+
do-footnotes(),
|
247
|
+
$footer,
|
248
|
+
'</body>',
|
249
|
+
"</html>\n"
|
250
|
+
);
|
251
|
+
|
252
|
+
}
|
253
|
+
|
254
|
+
#| Returns accumulated metadata as a string of C«<meta>» tags
|
255
|
+
sub do-metadata( --> Str ) {
|
256
|
+
return +@meta ?? '' !! @meta.map(-> $p {
|
257
|
+
qq[<meta name="{escape_html($p.key)}" value="{node2text($p.value)}" />]
|
258
|
+
}).join("\n");
|
259
|
+
}
|
260
|
+
|
261
|
+
#| Turns accumulated headings into a nested-C«<ol>» table of contents
|
262
|
+
sub do-toc($pod --> Str ) {
|
263
|
+
my @levels is default(0) = 0;
|
264
|
+
|
265
|
+
my proto sub find-headings($node, :$inside-heading){*}
|
266
|
+
|
267
|
+
multi sub find-headings(Str $s is raw, :$inside-heading){
|
268
|
+
$inside-heading ?? $s.trim.&escape_html !! ''
|
269
|
+
}
|
270
|
+
|
271
|
+
multi sub find-headings(Pod::FormattingCode $node is raw where *.type eq 'C', :$inside-heading){
|
272
|
+
my $html = $node.contents.map(*.&find-headings(:$inside-heading));
|
273
|
+
$inside-heading ?? qq[<code class="pod-code-inline">{$html}</code>] !! ''
|
274
|
+
}
|
275
|
+
|
276
|
+
multi sub find-headings(Pod::Heading $node is raw, :$inside-heading) {
|
277
|
+
@levels.splice($node.level) if $node.level < +@levels;
|
278
|
+
@levels[$node.level-1]++;
|
279
|
+
my $level-hierarchy = @levels.join('.'); # e.g. §4.2.12
|
280
|
+
my $text = $node.contents.map(*.&find-headings(inside-heading => True));
|
281
|
+
my $link = escape_id(node2text($node.contents));
|
282
|
+
qq[<tr class="toc-level-{$node.level}"><td class="toc-number">{$level-hierarchy}</td><td class="toc-text"><a href="#$link">{$text}</a></td></tr>\n];
|
283
|
+
}
|
284
|
+
|
285
|
+
multi sub find-headings(Positional \list, :$inside-heading){
|
286
|
+
list.map(*.&find-headings(:$inside-heading))
|
287
|
+
}
|
288
|
+
|
289
|
+
multi sub find-headings(Pod::Block $node is raw, :$inside-heading){
|
290
|
+
$node.contents.map(*.&find-headings(:$inside-heading))
|
291
|
+
}
|
292
|
+
|
293
|
+
multi sub find-headings(Pod::Config $node, :$inside-heading){
|
294
|
+
''
|
295
|
+
}
|
296
|
+
|
297
|
+
multi sub find-headings(Pod::Raw $node is raw, :$inside-heading){
|
298
|
+
$node.contents.map(*.&find-headings(:$inside-heading))
|
299
|
+
}
|
300
|
+
|
301
|
+
my $html = find-headings($pod);
|
302
|
+
$html.trim ??
|
303
|
+
qq:to/EOH/
|
304
|
+
<nav class="indexgroup">
|
305
|
+
<table id="TOC">
|
306
|
+
<caption><h2 id="TOC_Title">Table of Contents</h2></caption>
|
307
|
+
{$html}
|
308
|
+
</table>
|
309
|
+
</nav>
|
310
|
+
EOH
|
311
|
+
!! ''
|
312
|
+
}
|
313
|
+
|
314
|
+
#| Flushes accumulated footnotes since last call. The idea here is that we can stick calls to this
|
315
|
+
#| before each C«</section>» tag (once we have those per-header) and have notes that are visually
|
316
|
+
#| and semantically attached to the section.
|
317
|
+
sub do-footnotes( --> Str ) {
|
318
|
+
return '' unless @footnotes;
|
319
|
+
|
320
|
+
my Int $current-note = $*done-notes + 1;
|
321
|
+
my $notes = @footnotes.kv.map(-> $k, $v {
|
322
|
+
my $num = $k + $current-note;
|
323
|
+
qq{<li><a href="#fn-ref-$num" id="fn-$num">[↑]</a> $v </li>\n}
|
324
|
+
}).join;
|
325
|
+
|
326
|
+
$*done-notes += @footnotes;
|
327
|
+
@footnotes = ();
|
328
|
+
|
329
|
+
return qq[<aside><ol start="$current-note">\n]
|
330
|
+
~ $notes
|
331
|
+
~ qq[</ol></aside>\n];
|
332
|
+
}
|
333
|
+
|
334
|
+
#| block level or below
|
335
|
+
proto sub node2html(| --> Str ) is export {*}
|
336
|
+
multi sub node2html($node) {
|
337
|
+
Debug { note colored("Generic node2html called for ", "bold") ~ $node.perl };
|
338
|
+
return node2inline($node);
|
339
|
+
}
|
340
|
+
|
341
|
+
multi sub node2html(Pod::Block::Declarator $node) {
|
342
|
+
given $node.WHEREFORE {
|
343
|
+
when Routine {
|
344
|
+
"<article>\n"
|
345
|
+
~ '<code class="pod-code-inline">'
|
346
|
+
~ node2text($node.WHEREFORE.name ~ $node.WHEREFORE.signature.perl)
|
347
|
+
~ "</code>:\n"
|
348
|
+
~ node2html($node.contents)
|
349
|
+
~ "\n</article>\n";
|
350
|
+
}
|
351
|
+
default {
|
352
|
+
Debug { note "I don't know what {$node.WHEREFORE.WHAT.perl} is. Assuming class..." };
|
353
|
+
"<h1>"~ node2html([$node.WHEREFORE.perl, q{: }, $node.contents])~ "</h1>";
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
|
358
|
+
multi sub node2html(Pod::Block::Code $node) {
|
359
|
+
Debug { note colored("Code node2html called for ", "bold") ~ $node.gist };
|
360
|
+
if %*POD2HTML-CALLBACKS and %*POD2HTML-CALLBACKS<code> -> &cb {
|
361
|
+
return cb :$node, default => sub ($node) {
|
362
|
+
return '<pre class="pod-block-code">' ~ node2inline($node.contents) ~ "</pre>\n"
|
363
|
+
}
|
364
|
+
}
|
365
|
+
else {
|
366
|
+
return '<pre class="pod-block-code">' ~ node2inline($node.contents) ~ "</pre>\n"
|
367
|
+
}
|
368
|
+
|
369
|
+
}
|
370
|
+
|
371
|
+
multi sub node2html(Pod::Block::Comment $node) {
|
372
|
+
Debug { note colored("Comment node2html called for ", "bold") ~ $node.gist };
|
373
|
+
return '';
|
374
|
+
}
|
375
|
+
|
376
|
+
multi sub node2html(Pod::Block::Named $node) {
|
377
|
+
Debug { note colored("Named Block node2html called for ", "bold") ~ $node.gist };
|
378
|
+
given $node.name {
|
379
|
+
when 'config' { return '' }
|
380
|
+
when 'nested' {
|
381
|
+
return qq{<div class="nested">\n} ~ node2html($node.contents) ~ qq{\n</div>\n};
|
382
|
+
}
|
383
|
+
when 'output' { return qq[<pre class="pod-block-named-outout">\n] ~ node2inline($node.contents) ~ "</pre>\n"; }
|
384
|
+
when 'pod' {
|
385
|
+
return qq[<span class="{$node.config<class>}">\n{node2html($node.contents)}</span>\n]
|
386
|
+
if $node.config<class>;
|
387
|
+
return node2html($node.contents);
|
388
|
+
}
|
389
|
+
when 'para' { return node2html($node.contents[0]); }
|
390
|
+
when 'Image' {
|
391
|
+
my $url;
|
392
|
+
if $node.contents == 1 {
|
393
|
+
my $n = $node.contents[0];
|
394
|
+
if $n ~~ Str {
|
395
|
+
$url = $n;
|
396
|
+
}
|
397
|
+
elsif ($n ~~ Pod::Block::Para) && $n.contents == 1 {
|
398
|
+
$url = $n.contents[0] if $n.contents[0] ~~ Str;
|
399
|
+
}
|
400
|
+
}
|
401
|
+
unless $url.defined {
|
402
|
+
die "Found an Image block, but don't know how to extract the image URL :(";
|
403
|
+
}
|
404
|
+
return qq[<img src="$url" />];
|
405
|
+
}
|
406
|
+
when 'Xhtml' | 'Html' {
|
407
|
+
unescape_html node2rawhtml $node.contents
|
408
|
+
}
|
409
|
+
default {
|
410
|
+
if $node.name eq 'TITLE' {
|
411
|
+
$title = node2text($node.contents);
|
412
|
+
return '';
|
413
|
+
}
|
414
|
+
if $node.name eq 'SUBTITLE' {
|
415
|
+
$subtitle = node2text($node.contents);
|
416
|
+
return '';
|
417
|
+
}
|
418
|
+
elsif $node.name ~~ any(<VERSION DESCRIPTION AUTHOR COPYRIGHT SUMMARY>)
|
419
|
+
and $node.contents[0] ~~ Pod::Block::Para {
|
420
|
+
@meta.push: Pair.new(
|
421
|
+
key => $node.name.lc,
|
422
|
+
value => $node.contents
|
423
|
+
);
|
424
|
+
}
|
425
|
+
|
426
|
+
return '<section>'
|
427
|
+
~ "<h1>{$node.name}</h1>\n"
|
428
|
+
~ node2html($node.contents)
|
429
|
+
~ "</section>\n";
|
430
|
+
}
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
sub node2rawhtml(Positional $node) {
|
435
|
+
return $node.map({ node2rawtext $_ }).join
|
436
|
+
}
|
437
|
+
|
438
|
+
multi sub node2html(Pod::Block::Para $node) {
|
439
|
+
Debug { note colored("Para node2html called for ", "bold") ~ $node.gist };
|
440
|
+
return '<p>' ~ node2inline($node.contents) ~ "</p>\n";
|
441
|
+
}
|
442
|
+
|
443
|
+
multi sub node2html(Pod::Block::Table $node) {
|
444
|
+
Debug { note colored("Table node2html called for ", "bold") ~ $node.gist };
|
445
|
+
my @r = $node.config<class>??'<table class="pod-table '~$node.config<class>~'">'!!'<table class="pod-table">';
|
446
|
+
|
447
|
+
if $node.caption -> $c {
|
448
|
+
@r.push("<caption>{node2inline($c)}</caption>");
|
449
|
+
}
|
450
|
+
|
451
|
+
if $node.headers {
|
452
|
+
@r.push(
|
453
|
+
'<thead><tr>',
|
454
|
+
$node.headers.map(-> $cell {
|
455
|
+
"<th>{node2html($cell)}</th>"
|
456
|
+
}),
|
457
|
+
'</tr></thead>'
|
458
|
+
);
|
459
|
+
}
|
460
|
+
|
461
|
+
@r.push(
|
462
|
+
'<tbody>',
|
463
|
+
$node.contents.map(-> $line {
|
464
|
+
'<tr>',
|
465
|
+
$line.list.map(-> $cell {
|
466
|
+
"<td>{node2html($cell)}</td>"
|
467
|
+
}),
|
468
|
+
'</tr>'
|
469
|
+
}),
|
470
|
+
'</tbody>',
|
471
|
+
'</table>'
|
472
|
+
);
|
473
|
+
|
474
|
+
return @r.join("\n");
|
475
|
+
}
|
476
|
+
|
477
|
+
multi sub node2html(Pod::Config $node) {
|
478
|
+
Debug { note colored("Config node2html called for ", "bold") ~ $node.perl };
|
479
|
+
return '';
|
480
|
+
}
|
481
|
+
|
482
|
+
multi sub node2html(Pod::DefnList $node ) {
|
483
|
+
return "<dl>\n" ~ node2html($node.contents) ~ "\n</dl>\n";
|
484
|
+
|
485
|
+
}
|
486
|
+
multi sub node2html(Pod::Defn $node) {
|
487
|
+
|
488
|
+
"<dt>" ~ node2html($node.term) ~ "</dt>\n" ~
|
489
|
+
"<dd>" ~ node2html($node.contents) ~ "</dd>\n";
|
490
|
+
}
|
491
|
+
|
492
|
+
# TODO: would like some way to wrap these and the following content in a <section>; this might be
|
493
|
+
# the same way we get lists working...
|
494
|
+
multi sub node2html(Pod::Heading $node) {
|
495
|
+
Debug { note colored("Heading node2html called for ", "bold") ~ $node.gist };
|
496
|
+
my $lvl = min($node.level, 6); #= HTML only has 6 levels of numbered headings
|
497
|
+
my %escaped = (
|
498
|
+
id => escape_id(node2rawtext($node.contents)),
|
499
|
+
html => node2inline($node.contents),
|
500
|
+
);
|
501
|
+
|
502
|
+
%escaped<uri> = uri_escape %escaped<id>;
|
503
|
+
|
504
|
+
@indexes.push: Pair.new(key => $lvl, value => %escaped);
|
505
|
+
|
506
|
+
my $content;
|
507
|
+
if ( %escaped<html> ~~ m{href .+ \<\/a\>} ) {
|
508
|
+
$content = %escaped<html>;
|
509
|
+
} else {
|
510
|
+
$content = qq[<a class="u" href="#___top" title="go to top of document">]
|
511
|
+
~ %escaped<html>
|
512
|
+
~ qq[</a>];
|
513
|
+
}
|
514
|
+
|
515
|
+
return sprintf('<h%d id="%s">', $lvl, %escaped<id>)
|
516
|
+
~ $content ~ qq[</h{$lvl}>\n];
|
517
|
+
}
|
518
|
+
|
519
|
+
# FIXME
|
520
|
+
multi sub node2html(Pod::List $node) {
|
521
|
+
return '<ul>' ~ node2html($node.contents) ~ "</ul>\n";
|
522
|
+
}
|
523
|
+
multi sub node2html(Pod::Item $node) {
|
524
|
+
Debug { note colored("List Item node2html called for ", "bold") ~ $node.gist };
|
525
|
+
return '<li>' ~ node2html($node.contents) ~ "</li>\n";
|
526
|
+
}
|
527
|
+
|
528
|
+
multi sub node2html(Positional $node) {
|
529
|
+
return $node.map({ node2html($_) }).join
|
530
|
+
}
|
531
|
+
|
532
|
+
multi sub node2html(Str $node) {
|
533
|
+
return escape_html($node);
|
534
|
+
}
|
535
|
+
|
536
|
+
|
537
|
+
#| inline level or below
|
538
|
+
multi sub node2inline($node --> Str ) {
|
539
|
+
Debug { note colored("missing a node2inline multi for ", "bold") ~ $node.gist };
|
540
|
+
return node2text($node);
|
541
|
+
}
|
542
|
+
|
543
|
+
multi sub node2inline(Pod::Block::Para $node --> Str ) {
|
544
|
+
return node2inline($node.contents);
|
545
|
+
}
|
546
|
+
|
547
|
+
multi sub node2inline(Pod::FormattingCode $node --> Str ) {
|
548
|
+
my %basic-html = (
|
549
|
+
B => 'strong', #= Basis
|
550
|
+
C => 'code', #= Code
|
551
|
+
I => 'em', #= Important
|
552
|
+
K => 'kbd', #= Keyboard
|
553
|
+
R => 'var', #= Replaceable
|
554
|
+
T => 'samp', #= Terminal
|
555
|
+
U => 'u', #= Unusual (css: text-decoration-line: underline)
|
556
|
+
);
|
557
|
+
|
558
|
+
given $node.type {
|
559
|
+
when any(%basic-html.keys) {
|
560
|
+
return q{<} ~ %basic-html{$_} ~ q{>}
|
561
|
+
~ node2inline($node.contents)
|
562
|
+
~ q{</} ~ %basic-html{$_} ~ q{>};
|
563
|
+
}
|
564
|
+
|
565
|
+
# Escape
|
566
|
+
when 'E' {
|
567
|
+
return $node.meta.map({
|
568
|
+
when Int { "&#$_;" }
|
569
|
+
when Str { "&$_;" }
|
570
|
+
}).join;
|
571
|
+
}
|
572
|
+
|
573
|
+
# Note
|
574
|
+
when 'N' {
|
575
|
+
@footnotes.push(node2inline($node.contents));
|
576
|
+
|
577
|
+
my $id = +@footnotes;
|
578
|
+
return qq{<a href="#fn-$id" id="fn-ref-$id">[$id]</a>};
|
579
|
+
}
|
580
|
+
|
581
|
+
# Links
|
582
|
+
when 'L' {
|
583
|
+
my $text = node2inline($node.contents);
|
584
|
+
my $url = $node.meta[0] || node2text($node.contents);
|
585
|
+
if $text ~~ /^'#'/ {
|
586
|
+
# if we have an internal-only link, strip the # from the text.
|
587
|
+
$text = $/.postmatch
|
588
|
+
}
|
589
|
+
$url = url(unescape_html($url));
|
590
|
+
if $url ~~ /^'#'/ {
|
591
|
+
$url = '#' ~ uri_escape( escape_id($/.postmatch) )
|
592
|
+
}
|
593
|
+
return qq[<a href="$url">{$text}</a>]
|
594
|
+
}
|
595
|
+
|
596
|
+
# zero-width comment
|
597
|
+
when 'Z' {
|
598
|
+
return '';
|
599
|
+
}
|
600
|
+
|
601
|
+
when 'D' {
|
602
|
+
# TODO memorise these definitions (in $node.meta) and display them properly
|
603
|
+
my $text = node2inline($node.contents);
|
604
|
+
return qq[<defn>{$text}</defn>]
|
605
|
+
}
|
606
|
+
|
607
|
+
when 'X' {
|
608
|
+
multi sub recurse-until-str(Str:D $s){ $s }
|
609
|
+
multi sub recurse-until-str(Pod::Block $n){ $n.contents>>.&recurse-until-str().join }
|
610
|
+
|
611
|
+
my $index-text = recurse-until-str($node).join;
|
612
|
+
my @indices = $node.meta;
|
613
|
+
my $index-name-attr = qq[index-entry{@indices ?? '-' !! ''}{@indices.join('-')}{$index-text ?? '-' !! ''}$index-text].subst('_', '__', :g).subst(' ', '_', :g).subst('%', '%25', :g).subst('#', '%23', :g);
|
614
|
+
|
615
|
+
my $text = node2inline($node.contents);
|
616
|
+
%crossrefs{$_} = $text for @indices;
|
617
|
+
|
618
|
+
return qq[<a name="$index-name-attr"><span class="index-entry">$text\</span></a>] if $text;
|
619
|
+
return qq[<a name="$index-name-attr"></a>];
|
620
|
+
}
|
621
|
+
|
622
|
+
# Stuff I haven't figured out yet
|
623
|
+
default {
|
624
|
+
Debug { note colored("missing handling for a formatting code of type ", "red") ~ $node.type }
|
625
|
+
return qq{<kbd class="pod2html-todo">$node.type()<}
|
626
|
+
~ node2inline($node.contents)
|
627
|
+
~ q{></kbd>};
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
|
632
|
+
multi sub node2inline(Positional $node --> Str ) {
|
633
|
+
return $node.map({ node2inline($_) }).join;
|
634
|
+
}
|
635
|
+
|
636
|
+
multi sub node2inline(Str $node --> Str ) {
|
637
|
+
return escape_html($node);
|
638
|
+
}
|
639
|
+
|
640
|
+
|
641
|
+
#| HTML-escaped text
|
642
|
+
multi sub node2text($node --> Str ) {
|
643
|
+
Debug { note colored("missing a node2text multi for ", "red") ~ $node.perl };
|
644
|
+
return escape_html(node2rawtext($node));
|
645
|
+
}
|
646
|
+
|
647
|
+
multi sub node2text(Pod::Block::Para $node --> Str ) {
|
648
|
+
return node2text($node.contents);
|
649
|
+
}
|
650
|
+
|
651
|
+
multi sub node2text(Pod::Raw $node --> Str ) {
|
652
|
+
my $t = $node.target;
|
653
|
+
if $t && lc($t) eq 'html' {
|
654
|
+
$node.contents.join
|
655
|
+
}
|
656
|
+
else {
|
657
|
+
'';
|
658
|
+
}
|
659
|
+
}
|
660
|
+
|
661
|
+
# FIXME: a lot of these multis are identical except the function name used...
|
662
|
+
# there has to be a better way to write this?
|
663
|
+
multi sub node2text(Positional $node --> Str ) {
|
664
|
+
return $node.map({ node2text($_) }).join;
|
665
|
+
}
|
666
|
+
|
667
|
+
multi sub node2text(Str $node --> Str ) {
|
668
|
+
return escape_html($node);
|
669
|
+
}
|
670
|
+
|
671
|
+
|
672
|
+
#| plain, unescaped text
|
673
|
+
multi sub node2rawtext($node --> Str ) {
|
674
|
+
Debug { note colored("Generic node2rawtext called with ", "red") ~ $node.perl };
|
675
|
+
return $node.Str;
|
676
|
+
}
|
677
|
+
|
678
|
+
multi sub node2rawtext(Pod::Block $node --> Str ) {
|
679
|
+
Debug { note colored("node2rawtext called for ", "bold") ~ $node.gist };
|
680
|
+
return node2rawtext($node.contents);
|
681
|
+
}
|
682
|
+
|
683
|
+
multi sub node2rawtext(Positional $node --> Str ) {
|
684
|
+
return $node.map({ node2rawtext($_) }).join;
|
685
|
+
}
|
686
|
+
|
687
|
+
multi sub node2rawtext(Str $node --> Str ) {
|
688
|
+
return $node;
|
689
|
+
}
|
690
|
+
|
691
|
+
# vim: expandtab shiftwidth=4 ft=perl6
|