Skip to content

Commit e821db1

Browse files
fingolfincodex
andcommitted
markdown: handle inline code spans safely
Escape XML-special characters in Markdown-style inline backtick spans and convert them before paragraph and list handling so literals such as <Log> and <List> work correctly in .autodoc files. Refactor the inline-backtick logic into local Markdown helpers and cover the behavior with focused misc tests. Co-authored-by: Codex <codex@openai.com>
1 parent 95215df commit e821db1

7 files changed

Lines changed: 118 additions & 71 deletions

File tree

doc/Comments.autodoc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Inside of &AutoDoc; comments, <E>&AutoDoc; commands</E>
2626
starting with <C>@</C> can be used to control the output &AutoDoc; produces.
2727
Any comment line that does <E>not</E> start with an &AutoDoc; command is treated
2828
as regular documentation text and may contain (almost) arbitrary &GAPDoc; XML
29-
markup, such as <C>&lt;Ref&gt;</C>, <C>&lt;A&gt;</C>, <C>&lt;List&gt;</C>, and similar tags.
29+
markup, such as `<Ref>`, `<A>`, `<List>`, and similar tags.
3030
This lets you use the normal &GAPDoc; formatting toolbox directly inside
3131
&AutoDoc; comments.
3232

@@ -287,7 +287,7 @@ first entry in the group. See <Ref Sect="Section_Groups"/> for more information.
287287
@Index "@BeginExample" `@BeginExample`
288288
@Index "@EndExample" `@EndExample`
289289
<C>@BeginExample</C> marks the start of an example to be put into the manual.
290-
It differs from &GAPDoc;'s <C>&lt;Example></C> (see <Ref Subsect="Log" BookName="gapdoc"/>),
290+
It differs from &GAPDoc;'s `<Example>` (see <Ref Subsect="Log" BookName="gapdoc"/>),
291291
in that it expects actual code (not in a comment) interspersed with comments,
292292
to allow for examples files that can be both executed by &GAP;, and parsed
293293
by &AutoDoc;. To achieve this, &GAP; commands are not preceded by a comment,
@@ -323,7 +323,7 @@ Section <Ref Sect="Subsection_Tut:IntegrateExisting:EverythingThere"/>).
323323
@Index "@EndExampleSession" `@EndExampleSession`
324324
<C>@BeginExampleSession</C> marks the start of an example to be put into the manual,
325325
while <C>@EndExampleSession</C> ends the example block.
326-
It is the direct analog of &GAPDoc;'s <C>&lt;Example></C> (see <Ref Subsect="Log" BookName="gapdoc"/>).
326+
It is the direct analog of &GAPDoc;'s `<Example>` (see <Ref Subsect="Log" BookName="gapdoc"/>).
327327

328328
To illustrate this command, consider this input:
329329
```@listing
@@ -368,7 +368,7 @@ The &AutoDoc; command <C>@Log</C> is an alias of <C>@BeginLog</C>.
368368
@Index "@EndLogSession" `@EndLogSession`
369369
Works just like the <C>@BeginExampleSession</C> command, but the example
370370
will not be tested if manual examples are run. It is the direct analog of
371-
&GAPDoc;'s <C>&lt;Log></C> (see <Ref Subsect="Log" BookName="gapdoc"/>).
371+
&GAPDoc;'s `<Log>` (see <Ref Subsect="Log" BookName="gapdoc"/>).
372372
The &AutoDoc; command <C>@LogSession</C> is an alias of <C>@BeginLogSession</C>.
373373

374374
@Subsection <C>@DoNotReadRestOfFile</C>
@@ -483,7 +483,7 @@ in the HTML and text versions of the manual or worksheet, but not in the PDF ver
483483
@Index "@Index" <C>@Index <A>key</A> [<A>entry text</A>]</C>
484484
Adds an index entry to the current documentation text.
485485
The command <C>@Index key entry text</C> generates
486-
<C>&lt;Index Key="key"&gt;entry text&lt;/Index&gt;</C>.
486+
`<Index Key="key">entry text</Index>`.
487487
If no entry text is provided, then the entry text is empty.
488488
To use keys containing spaces, wrap the key in double quotes, e.g.
489489
<C>@Index "my key" entry text</C>.
@@ -589,7 +589,7 @@ written in <F>.autodoc</F> format.
589589
@Subsection First Subsection
590590

591591
This text belongs directly to the manual chapter.
592-
It can use XML tags such as <A>arg</A> or <C>&lt;Ref ...&gt;</C>.
592+
It can use XML tags such as <A>arg</A> or <Ref .../>.
593593

594594
@BeginExampleSession
595595
gap> S5 := SymmetricGroup(5);
@@ -792,7 +792,7 @@ One can insert verbatim code blocks by placing the code between lines
792792
fence may optionally be followed by an info string. The values
793793
<C>@listing</C>, <C>@example</C>, and <C>@log</C> select the corresponding
794794
GAPDoc element; any other value is currently ignored. If nothing is specified
795-
the default is to generate <C>&lt;Listing&gt;</C>. Example:
795+
the default is to generate `<Listing>`. Example:
796796

797797
````@listing
798798
#! ```@listing

doc/Tutorials.autodoc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ DeclareOperation( "ToricVariety", [ IsConvexObject ] );
132132
In these comment lines, you can freely use normal &GAPDoc; XML markup
133133
(with the usual exceptions for lines starting with <C>@</C>, which are
134134
interpreted as &AutoDoc; commands). So, for instance, tags like
135-
<C>&lt;Ref&gt;</C>, <C>&lt;A&gt;</C>, <C>&lt;K&gt;</C>, <C>&lt;List&gt;</C>, etc. can be written
135+
`<Ref>`, `<A>`, `<K>`, `<List>`, etc. can be written
136136
directly in <C>#!</C> comment text.
137137

138138
For chapter text, tutorial material, worked examples, and similar prose, some
@@ -511,27 +511,27 @@ of the package info record are taken into account:
511511
<List>
512512

513513
<Mark>PackageName</Mark><Item>
514-
This is used to set the <C>&lt;Title></C> element of the
514+
This is used to set the `<Title>` element of the
515515
title page.
516516
</Item>
517517

518518
<Mark>Subtitle</Mark><Item>
519-
This is used to set the <C>&lt;Subtitle></C> element of the
519+
This is used to set the `<Subtitle>` element of the
520520
title page.
521521
</Item>
522522

523523
<Mark>Version</Mark><Item>
524-
This is used to set the <C>&lt;Version></C> element of the
524+
This is used to set the `<Version>` element of the
525525
title page, with the string <Q>Version </Q> prepended.
526526
</Item>
527527

528528
<Mark>Date</Mark><Item>
529-
This is used to set the <C>&lt;Date></C> element of the
529+
This is used to set the `<Date>` element of the
530530
title page.
531531
</Item>
532532

533533
<Mark>Persons</Mark><Item>
534-
This is used to generate <C>&lt;Author></C> elements in the
534+
This is used to generate `<Author>` elements in the
535535
generated title page.
536536
</Item>
537537

@@ -562,7 +562,7 @@ SetPackageInfo( rec(
562562
)
563563
) );
564564
```
565-
This inserts <C>&lt;Copyright></C> and <C>&lt;Acknowledgements></C> blocks into
565+
This inserts `<Copyright>` and `<Acknowledgements>` blocks into
566566
the generated <F>title.xml</F>.
567567

568568
<C>PkgInfo.AutoDoc.TitlePage</C> has exactly the same meaning as

gap/Markdown.gd

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@
55
#
66
# SPDX-License-Identifier: GPL-2.0-or-later
77

8-
DeclareGlobalFunction( "INSERT_IN_STRING_WITH_REPLACE" );
98
DeclareGlobalFunction( "CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML" );

gap/Markdown.gi

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# SPDX-License-Identifier: GPL-2.0-or-later
77

88
##
9-
InstallGlobalFunction( INSERT_IN_STRING_WITH_REPLACE,
9+
BindGlobal( "INSERT_IN_STRING_WITH_REPLACE",
1010
function( string, new_string, position, nr_letters_to_be_replaced )
1111
return Concatenation(
1212
string{[ 1 .. position - 1 ]},
@@ -15,6 +15,70 @@ InstallGlobalFunction( INSERT_IN_STRING_WITH_REPLACE,
1515
);
1616
end );
1717

18+
##
19+
BindGlobal( "AUTODOC_EscapeXMLTextForInlineCode",
20+
function( string )
21+
local escaped_string, split_pos;
22+
23+
escaped_string := "";
24+
while Length(string) > 0 do
25+
split_pos := PositionProperty( string, c -> c in "&\"<>" );
26+
if split_pos = fail then
27+
Append( escaped_string, string );
28+
break;
29+
fi;
30+
if split_pos > 1 then
31+
Append( escaped_string, string{ [ 1 .. split_pos - 1 ] } );
32+
fi;
33+
if string[ split_pos ] = '&' then
34+
Append( escaped_string, "&amp;" );
35+
elif string[ split_pos ] = '"' then
36+
Append( escaped_string, "&quot;" );
37+
elif string[ split_pos ] = '<' then
38+
Append( escaped_string, "&lt;" );
39+
else
40+
Append( escaped_string, "&gt;" );
41+
fi;
42+
string := string{ [ split_pos + 1 .. Length( string ) ] };
43+
od;
44+
45+
return escaped_string;
46+
end );
47+
48+
##
49+
BindGlobal( "AUTODOC_ConvertInlineBackticksInLine",
50+
function( string, keyword_set )
51+
local opening_pos, closing_pos, inline_content, tag_name;
52+
53+
while PositionSublist( string, "`" ) <> fail do
54+
opening_pos := PositionSublist( string, "`" );
55+
closing_pos := PositionSublist( string, "`", opening_pos + 1 );
56+
if closing_pos = fail then
57+
Error( "did you forget some `" );
58+
fi;
59+
60+
if opening_pos + 1 <= closing_pos - 1 then
61+
inline_content := string{ [ opening_pos + 1 .. closing_pos - 1 ] };
62+
else
63+
inline_content := "";
64+
fi;
65+
if inline_content in keyword_set then
66+
tag_name := "Keyword";
67+
else
68+
tag_name := "Code";
69+
fi;
70+
string := Concatenation(
71+
string{ [ 1 .. opening_pos - 1 ] },
72+
"<", tag_name, ">",
73+
AUTODOC_EscapeXMLTextForInlineCode( inline_content ),
74+
"</", tag_name, ">",
75+
string{ [ closing_pos + 1 .. Length( string ) ] }
76+
);
77+
od;
78+
79+
return string;
80+
end );
81+
1882
##
1983
InstallGlobalFunction( CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML,
2084
function( string_list )
@@ -23,8 +87,7 @@ InstallGlobalFunction( CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML,
2387
commands, position_of_command, insert, beginning_whitespaces, temp, string_list_temp, skipped,
2488
already_inserted_paragraph, in_list, in_item, converted_string_list,
2589
fence_char, fence_length, trimmed_line, code_block, info_string,
26-
fence_element, keyword_set, opening_pos, closing_pos, inline_content,
27-
tag_name;
90+
fence_element, keyword_set;
2891

2992
converted_string_list := [ ];
3093
i := 1;
@@ -94,6 +157,25 @@ InstallGlobalFunction( CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML,
94157
od;
95158
string_list := converted_string_list;
96159

160+
# Convert inline backticks before list detection so literal tags such as
161+
# `<List>` inside code spans do not look like structural GAPDoc tags.
162+
keyword_set := Set( ALL_KEYWORDS() );
163+
skipped := false;
164+
for i in [ 1 .. Length( string_list ) ] do
165+
if PositionSublist( string_list[ i ], "<![CDATA[" ) <> fail then
166+
skipped := true;
167+
fi;
168+
if PositionSublist( string_list[ i ], "]]>" ) <> fail then
169+
skipped := false;
170+
continue;
171+
fi;
172+
if skipped = true then
173+
continue;
174+
fi;
175+
string_list[ i ] :=
176+
AUTODOC_ConvertInlineBackticksInLine( string_list[ i ], keyword_set );
177+
od;
178+
97179
## Check for paragraphs by turning an empty string into <P/>
98180

99181
already_inserted_paragraph := false;
@@ -218,45 +300,6 @@ InstallGlobalFunction( CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML,
218300
[ "$", "Math" ],
219301
[ "**", "Emph" ],
220302
[ "__", "Emph" ] ];
221-
keyword_set := Set( ALL_KEYWORDS() );
222-
223-
skipped := false;
224-
for i in [ 1 .. Length( string_list ) ] do
225-
if PositionSublist( string_list[ i ], "<![CDATA[" ) <> fail then
226-
skipped := true;
227-
fi;
228-
if PositionSublist( string_list[ i ], "]]>" ) <> fail then
229-
skipped := false;
230-
continue;
231-
fi;
232-
if skipped = true then
233-
continue;
234-
fi;
235-
236-
while PositionSublist( string_list[ i ], "`" ) <> fail do
237-
opening_pos := PositionSublist( string_list[ i ], "`" );
238-
closing_pos := PositionSublist( string_list[ i ], "`", opening_pos + 1 );
239-
if closing_pos = fail then
240-
Error( "did you forget some `" );
241-
fi;
242-
243-
if opening_pos + 1 <= closing_pos - 1 then
244-
inline_content := string_list[ i ]{ [ opening_pos + 1 .. closing_pos - 1 ] };
245-
else
246-
inline_content := "";
247-
fi;
248-
if inline_content in keyword_set then
249-
tag_name := "Keyword";
250-
else
251-
tag_name := "Code";
252-
fi;
253-
string_list[ i ] := Concatenation(
254-
string_list[ i ]{ [ 1 .. opening_pos - 1 ] },
255-
"<", tag_name, ">", inline_content, "</", tag_name, ">",
256-
string_list[ i ]{ [ closing_pos + 1 .. Length( string_list[ i ] ) ] }
257-
);
258-
od;
259-
od;
260303

261304
## special handling for \$
262305
for i in [ 1 .. Length( string_list ) ] do

tst/manual.expected/_Chapter_Comments.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Inside of &AutoDoc; comments, <E>&AutoDoc; commands</E>
2929
starting with <C>@</C> can be used to control the output &AutoDoc; produces.
3030
Any comment line that does <E>not</E> start with an &AutoDoc; command is treated
3131
as regular documentation text and may contain (almost) arbitrary &GAPDoc; XML
32-
markup, such as <C>&lt;Ref&gt;</C>, <C>&lt;A&gt;</C>, <C>&lt;List&gt;</C>, and similar tags.
32+
markup, such as <Code>&lt;Ref&gt;</Code>, <Code>&lt;A&gt;</Code>, <Code>&lt;List&gt;</Code>, and similar tags.
3333
This lets you use the normal &GAPDoc; formatting toolbox directly inside
3434
&AutoDoc; comments.
3535
<P/>
@@ -344,7 +344,7 @@ first entry in the group. See <Ref Sect="Section_Groups"/> for more information.
344344
<Index Key="@BeginExample"><Code>@BeginExample</Code></Index>
345345
<Index Key="@EndExample"><Code>@EndExample</Code></Index>
346346
<C>@BeginExample</C> marks the start of an example to be put into the manual.
347-
It differs from &GAPDoc;'s <C>&lt;Example></C> (see <Ref Subsect="Log" BookName="gapdoc"/>),
347+
It differs from &GAPDoc;'s <Code>&lt;Example&gt;</Code> (see <Ref Subsect="Log" BookName="gapdoc"/>),
348348
in that it expects actual code (not in a comment) interspersed with comments,
349349
to allow for examples files that can be both executed by &GAP;, and parsed
350350
by &AutoDoc;. To achieve this, &GAP; commands are not preceded by a comment,
@@ -384,7 +384,7 @@ Section <Ref Sect="Subsection_Tut:IntegrateExisting:EverythingThere"/>).
384384
<Index Key="@EndExampleSession"><Code>@EndExampleSession</Code></Index>
385385
<C>@BeginExampleSession</C> marks the start of an example to be put into the manual,
386386
while <C>@EndExampleSession</C> ends the example block.
387-
It is the direct analog of &GAPDoc;'s <C>&lt;Example></C> (see <Ref Subsect="Log" BookName="gapdoc"/>).
387+
It is the direct analog of &GAPDoc;'s <Code>&lt;Example&gt;</Code> (see <Ref Subsect="Log" BookName="gapdoc"/>).
388388
<P/>
389389
To illustrate this command, consider this input:
390390
<Listing><![CDATA[
@@ -437,7 +437,7 @@ The &AutoDoc; command <C>@Log</C> is an alias of <C>@BeginLog</C>.
437437
<Index Key="@EndLogSession"><Code>@EndLogSession</Code></Index>
438438
Works just like the <C>@BeginExampleSession</C> command, but the example
439439
will not be tested if manual examples are run. It is the direct analog of
440-
&GAPDoc;'s <C>&lt;Log></C> (see <Ref Subsect="Log" BookName="gapdoc"/>).
440+
&GAPDoc;'s <Code>&lt;Log&gt;</Code> (see <Ref Subsect="Log" BookName="gapdoc"/>).
441441
The &AutoDoc; command <C>@LogSession</C> is an alias of <C>@BeginLogSession</C>.
442442
<P/>
443443
</Subsection>
@@ -576,7 +576,7 @@ in the HTML and text versions of the manual or worksheet, but not in the PDF ver
576576
<Index Key="@Index"><C>@Index <A>key</A> [<A>entry text</A>]</C></Index>
577577
Adds an index entry to the current documentation text.
578578
The command <C>@Index key entry text</C> generates
579-
<C>&lt;Index Key="key"&gt;entry text&lt;/Index&gt;</C>.
579+
<Code>&lt;Index Key=&quot;key&quot;&gt;entry text&lt;/Index&gt;</Code>.
580580
If no entry text is provided, then the entry text is empty.
581581
To use keys containing spaces, wrap the key in double quotes, e.g.
582582
<C>@Index "my key" entry text</C>.
@@ -935,7 +935,7 @@ One can insert verbatim code blocks by placing the code between lines
935935
fence may optionally be followed by an info string. The values
936936
<C>@listing</C>, <C>@example</C>, and <C>@log</C> select the corresponding
937937
GAPDoc element; any other value is currently ignored. If nothing is specified
938-
the default is to generate <C>&lt;Listing&gt;</C>. Example:
938+
the default is to generate <Code>&lt;Listing&gt;</Code>. Example:
939939
<P/>
940940
<Listing><![CDATA[
941941
#! ```@listing

tst/manual.expected/_Chapter_Tutorials.xml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ DeclareOperation( "ToricVariety", [ IsConvexObject ] );
152152
In these comment lines, you can freely use normal &GAPDoc; XML markup
153153
(with the usual exceptions for lines starting with <C>@</C>, which are
154154
interpreted as &AutoDoc; commands). So, for instance, tags like
155-
<C>&lt;Ref&gt;</C>, <C>&lt;A&gt;</C>, <C>&lt;K&gt;</C>, <C>&lt;List&gt;</C>, etc. can be written
155+
<Code>&lt;Ref&gt;</Code>, <Code>&lt;A&gt;</Code>, <Code>&lt;K&gt;</Code>, <Code>&lt;List&gt;</Code>, etc. can be written
156156
directly in <C>#!</C> comment text.
157157
<P/>
158158
For chapter text, tutorial material, worked examples, and similar prose, some
@@ -557,23 +557,23 @@ of the package info record are taken into account:
557557
<P/>
558558
<List>
559559
<Mark>PackageName</Mark><Item>
560-
This is used to set the <C>&lt;Title></C> element of the
560+
This is used to set the <Code>&lt;Title&gt;</Code> element of the
561561
title page.
562562
</Item>
563563
<Mark>Subtitle</Mark><Item>
564-
This is used to set the <C>&lt;Subtitle></C> element of the
564+
This is used to set the <Code>&lt;Subtitle&gt;</Code> element of the
565565
title page.
566566
</Item>
567567
<Mark>Version</Mark><Item>
568-
This is used to set the <C>&lt;Version></C> element of the
568+
This is used to set the <Code>&lt;Version&gt;</Code> element of the
569569
title page, with the string <Q>Version </Q> prepended.
570570
</Item>
571571
<Mark>Date</Mark><Item>
572-
This is used to set the <C>&lt;Date></C> element of the
572+
This is used to set the <Code>&lt;Date&gt;</Code> element of the
573573
title page.
574574
</Item>
575575
<Mark>Persons</Mark><Item>
576-
This is used to generate <C>&lt;Author></C> elements in the
576+
This is used to generate <Code>&lt;Author&gt;</Code> elements in the
577577
generated title page.
578578
</Item>
579579
<Mark>PackageDoc</Mark><Item>
@@ -602,7 +602,7 @@ SetPackageInfo( rec(
602602
)
603603
) );
604604
]]></Listing>
605-
This inserts <C>&lt;Copyright></C> and <C>&lt;Acknowledgements></C> blocks into
605+
This inserts <Code>&lt;Copyright&gt;</Code> and <Code>&lt;Acknowledgements&gt;</Code> blocks into
606606
the generated <F>title.xml</F>.
607607
<P/>
608608
<C>PkgInfo.AutoDoc.TitlePage</C> has exactly the same meaning as

0 commit comments

Comments
 (0)