diff --git a/gap/DocumentationTree.gi b/gap/DocumentationTree.gi
index b54f43b2..d190dd38 100644
--- a/gap/DocumentationTree.gi
+++ b/gap/DocumentationTree.gi
@@ -251,7 +251,8 @@ InstallMethod( DocumentationChunk, [ IsTreeForDocumentation, IsString ],
if IsBound( tree!.chunks.( name ) ) then
return tree!.chunks.( name );
fi;
- node := rec( content := [ ] );
+ node := rec( content := [ ],
+ content_source_positions := [ ] );
ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeChunkNodes,
Label, name );
node!.is_defined := false;
@@ -266,9 +267,12 @@ InstallMethod( DocumentationManItem, [ ],
local node;
node := rec( description := [ ],
- return_value := [ ] );
+ description_source_positions := [ ],
+ return_value := [ ],
+ return_value_source_positions := [ ] );
ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeNodesForManItem );
node!.content := node!.description;
+ node!.content_source_field := "description_source_positions";
return node;
end );
@@ -280,7 +284,8 @@ InstallMethod( DocumentationGroup, [ IsTreeForDocumentation, IsString ],
if IsBound( tree!.cached_nodes_by_label.( name ) ) then
return tree!.cached_nodes_by_label.( name );
fi;
- group := rec( content := [ ] );
+ group := rec( content := [ ],
+ content_source_positions := [ ] );
ObjectifyWithAttributes( group, TheTypeOfDocumentationTreeNodesForGroup,
Label, name );
tree!.cached_nodes_by_label.( name ) := group;
@@ -414,11 +419,13 @@ end );
#############################################
BindGlobal( "AUTODOC_ConvertHeadingToGAPDocXML",
- function( heading )
+ function( heading, source_position )
local converted_heading;
- converted_heading :=
- AUTODOC_ConvertMarkdownToGAPDocXML( [ NormalizedWhitespace( heading ) ] );
+ converted_heading := AUTODOC_ConvertMarkdownToGAPDocXML(
+ [ NormalizedWhitespace( heading ) ],
+ [ source_position ]
+ );
if not ForAll( converted_heading, IsString ) then
Error( "headings must convert to inline GAPDoc XML" );
fi;
@@ -429,7 +436,7 @@ end );
BindGlobal( "AUTODOC_WriteStructuralNode",
function( node, element_name, stream )
- local heading;
+ local heading, title_source_position;
if ForAll( node!.content, IsEmptyNode ) then
return false;
@@ -437,10 +444,12 @@ BindGlobal( "AUTODOC_WriteStructuralNode",
if IsBound( node!.title_string ) then
heading := NormalizedWhitespace( node!.title_string );
+ title_source_position := node!.title_string_source_position;
else
heading := ReplacedString( node!.name, "_", " " );
+ title_source_position := fail;
fi;
- heading := AUTODOC_ConvertHeadingToGAPDocXML( heading );
+ heading := AUTODOC_ConvertHeadingToGAPDocXML( heading, title_source_position );
AppendTo( stream, "<", element_name, " Label=\"", Label( node ), "\">\n" );
AppendTo( stream, "", heading, "\n\n" );
@@ -491,7 +500,11 @@ BindGlobal( "WriteChunks",
fi;
AppendTo( chunks_stream, "<#GAPDoc Label=\"", current_chunk_name, "\">\n" );
if IsBound( current_chunk!.content ) then
- WriteDocumentation( current_chunk!.content, chunks_stream );
+ AUTODOC_WriteDocumentationListWithSource(
+ current_chunk!.content,
+ current_chunk!.content_source_positions,
+ chunks_stream
+ );
fi;
AppendTo( chunks_stream, "\n<#/GAPDoc>\n" );
od;
@@ -566,29 +579,14 @@ InstallMethod( WriteDocumentation, [ IsList, IsStream ],
local current_string_list, i, FlushConvertedStrings;
FlushConvertedStrings := function()
- local converted_string_list, in_cdata, item;
if current_string_list = [ ] then
return;
fi;
- converted_string_list := AUTODOC_ConvertMarkdownToGAPDocXML( current_string_list );
- in_cdata := false;
- for item in converted_string_list do
- if not IsString( item ) then
- WriteDocumentation( item, filestream );
- continue;
- fi;
- if AUTODOC_LineStartsCDATA( item ) then
- in_cdata := true;
- fi;
- if in_cdata = true then
- AppendTo( filestream, Chomp( item ), "\n" );
- else
- WriteDocumentation( item, filestream );
- fi;
- if AUTODOC_LineEndsCDATA( item ) then
- in_cdata := false;
- fi;
- od;
+ AUTODOC_WriteStringListWithSource(
+ current_string_list,
+ fail,
+ filestream
+ );
current_string_list := [ ];
end;
diff --git a/gap/Markdown.gi b/gap/Markdown.gi
index 843b195b..8dfe6d1e 100644
--- a/gap/Markdown.gi
+++ b/gap/Markdown.gi
@@ -47,7 +47,7 @@ end );
##
BindGlobal( "AUTODOC_ConvertInlineBackticksInLine",
- function( string, keyword_set )
+ function( string, keyword_set, source_position )
local opening_pos, closing_pos, inline_content, tag_name, search_string;
while PositionSublist( string, "`" ) <> fail do
@@ -55,7 +55,16 @@ BindGlobal( "AUTODOC_ConvertInlineBackticksInLine",
search_string := string{ [ opening_pos + 1 .. Length( string ) ] };
closing_pos := PositionSublist( search_string, "`" );
if closing_pos = fail then
- Error( "did you forget some `" );
+ if source_position = fail then
+ Error( "did you forget some `" );
+ fi;
+ Error(
+ "did you forget some `,\n",
+ "at ",
+ source_position.filename,
+ ":",
+ source_position.line
+ );
fi;
closing_pos := opening_pos + closing_pos;
@@ -94,12 +103,21 @@ end );
##
BindGlobal( "AUTODOC_ConvertFencedMarkdownBlocks",
- function( string_list )
- local i, converted_string_list, skipped, trimmed_line,
+ function( arg )
+ local converted_source_positions, converted_string_list, i, skipped,
+ string_list, trimmed_line,
fence_char, fence_length, info_string, fence_element, code_block,
- fence_content;
+ fence_content, source_positions;
+
+ string_list := arg[ 1 ];
+ if Length( arg ) > 1 then
+ source_positions := arg[ 2 ];
+ else
+ source_positions := ListWithIdenticalEntries( Length( string_list ), fail );
+ fi;
converted_string_list := [ ];
+ converted_source_positions := [ ];
i := 1;
skipped := false;
while i <= Length( string_list ) do
@@ -108,6 +126,7 @@ BindGlobal( "AUTODOC_ConvertFencedMarkdownBlocks",
fi;
if skipped = true then
Add( converted_string_list, string_list[ i ] );
+ Add( converted_source_positions, source_positions[ i ] );
if AUTODOC_LineEndsCDATA( string_list[ i ] ) then
skipped := false;
fi;
@@ -146,6 +165,7 @@ BindGlobal( "AUTODOC_ConvertFencedMarkdownBlocks",
od;
Add( converted_string_list,
DocumentationVerbatim( fence_element, rec( ), fence_content ) );
+ Add( converted_source_positions, source_positions[ i - Length( fence_content ) - 1 ] );
if code_block = true then
i := i + 1;
continue;
@@ -154,10 +174,14 @@ BindGlobal( "AUTODOC_ConvertFencedMarkdownBlocks",
fi;
fi;
Add( converted_string_list, string_list[ i ] );
+ Add( converted_source_positions, source_positions[ i ] );
i := i + 1;
od;
- return converted_string_list;
+ return rec(
+ items := converted_string_list,
+ source_positions := converted_source_positions
+ );
end );
##
@@ -181,10 +205,11 @@ end );
##
BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
- function( string_list )
+ function( string_list, source_positions )
local i, current_list, current_string, max_line_length,
current_position, already_in_list, command_list_with_translation, beginning,
- commands, position_of_command, insert, beginning_whitespaces, temp, string_list_temp, skipped,
+ commands, opening_source_position, position_of_command, insert,
+ beginning_whitespaces, temp, string_list_temp, skipped,
already_inserted_paragraph, in_list, in_item, converted_string_list,
keyword_set;
@@ -193,7 +218,11 @@ BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
keyword_set := Set( ALL_KEYWORDS() );
AUTODOC_ForEachNonCDATALine( string_list, function( i )
string_list[ i ] :=
- AUTODOC_ConvertInlineBackticksInLine( string_list[ i ], keyword_set );
+ AUTODOC_ConvertInlineBackticksInLine(
+ string_list[ i ],
+ keyword_set,
+ source_positions[ i ]
+ );
end );
## Check for paragraphs by turning an empty string into
@@ -245,13 +274,17 @@ BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
fi;
if already_in_list = false then
Add( string_list, "- ", i );
+ Add( source_positions, fail, i );
Add( string_list, "
", i );
+ Add( source_positions, fail, i );
i := i + 2;
string_list[ i ] := string_list[ i ]{[ current_position + 2 .. Length( string_list[ i ] ) ]};
already_in_list := true;
else
Add( string_list, "- ", i );
+ Add( source_positions, fail, i );
Add( string_list, "
", i );
+ Add( source_positions, fail, i );
i := i + 2;
string_list[ i ] := string_list[ i ]{[ current_position + 2 .. Length( string_list[ i ] ) ]};
fi;
@@ -270,14 +303,18 @@ BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
elif already_in_list = true and PositionSublist( string_list[ i ], " " ) > current_position then
already_in_list := false;
Add( string_list, "
", i );
+ Add( source_positions, fail, i );
Add( string_list, " ", i );
+ Add( source_positions, fail, i );
i := i + 2;
fi;
i := i + 1;
od;
if already_in_list = true then
Add( string_list, "" );
+ Add( source_positions, fail );
Add( string_list, "" );
+ Add( source_positions, fail );
fi;
current_position := current_position + 1;
od;
@@ -320,13 +357,16 @@ BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
for commands in command_list_with_translation do
beginning := true;
+ opening_source_position := fail;
AUTODOC_ForEachNonCDATALine( string_list, function( i )
while PositionSublist( string_list[ i ], commands[ 1 ] ) <> fail do
position_of_command := PositionSublist( string_list[ i ], commands[ 1 ] );
if beginning = true then
insert := Concatenation( "<", commands[ 2 ], ">" );
+ opening_source_position := source_positions[ i ];
else
insert := Concatenation( "", commands[ 2 ], ">" );
+ opening_source_position := fail;
fi;
string_list[ i ] := INSERT_IN_STRING_WITH_REPLACE( string_list[ i ], insert, position_of_command, Length( commands[ 1 ] ) );
beginning := not beginning;
@@ -334,7 +374,18 @@ BindGlobal( "AUTODOC_ConvertMarkdownStringsToGAPDocXML",
end );
if beginning = false then
- Error( "did you forget some ", commands[ 1 ] );
+ if opening_source_position = fail then
+ Error( "did you forget some ", commands[ 1 ] );
+ fi;
+ Error(
+ "did you forget some ",
+ commands[ 1 ],
+ ",\n",
+ "at ",
+ opening_source_position.filename,
+ ":",
+ opening_source_position.line
+ );
fi;
od;
@@ -343,27 +394,42 @@ end );
##
InstallGlobalFunction( AUTODOC_ConvertMarkdownToGAPDocXML,
- function( string_list )
- local converted_items, current_string_list, item, FlushStringList;
+ function( string_list, source_positions )
+ local converted_blocks, converted_items, current_source_positions,
+ current_string_list, i, FlushStringList;
+
+ if source_positions = fail then
+ source_positions := ListWithIdenticalEntries( Length( string_list ), fail );
+ fi;
converted_items := [ ];
current_string_list := [ ];
+ current_source_positions := [ ];
FlushStringList := function()
if current_string_list = [ ] then
return;
fi;
Append( converted_items,
- AUTODOC_ConvertMarkdownStringsToGAPDocXML( current_string_list ) );
+ AUTODOC_ConvertMarkdownStringsToGAPDocXML(
+ current_string_list,
+ current_source_positions
+ ) );
current_string_list := [ ];
+ current_source_positions := [ ];
end;
- for item in AUTODOC_ConvertFencedMarkdownBlocks( string_list ) do
- if IsString( item ) then
- Add( current_string_list, item );
+ converted_blocks := AUTODOC_ConvertFencedMarkdownBlocks(
+ string_list,
+ source_positions
+ );
+ for i in [ 1 .. Length( converted_blocks.items ) ] do
+ if IsString( converted_blocks.items[ i ] ) then
+ Add( current_string_list, converted_blocks.items[ i ] );
+ Add( current_source_positions, converted_blocks.source_positions[ i ] );
else
FlushStringList();
- Add( converted_items, item );
+ Add( converted_items, converted_blocks.items[ i ] );
fi;
od;
FlushStringList();
diff --git a/gap/Parser.gi b/gap/Parser.gi
index 6a110c28..21b269c2 100644
--- a/gap/Parser.gi
+++ b/gap/Parser.gi
@@ -287,13 +287,14 @@ end );
InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
function( filename_list, tree, default_chapter_data )
local ApplyFilterInfoToCurrentItem, CreateTitleItemFunction, CurrentItem,
- CurrentOrNewManItem, DeclarationDelimiterPosition, ErrorWithPos,
+ CurrentOrNewManItem, CurrentSourcePosition,
+ DeclarationDelimiterPosition, ErrorWithPos,
FinishCurrentManItem, HasCurrentItem, IsMatchingMarkdownFence,
MarkdownFenceFromLine, NormalizeInputLine, NormalizeItemType,
NormalizedReadLine, ReadBracketedFilterString, ReadCode,
ReadExample, ReadInstallMethodArguments,
ReadInstallMethodFilterString, ReadLineWithLineCount,
- ReadSessionExample, Reset, ScanDeclarePart, ScanForDeclarationPart,
+ ReadSessionExample, RecordStringSourcePosition, Reset, ScanDeclarePart, ScanForDeclarationPart,
ScanInstallMethodPart, SetCurrentItem,
#
active_title_item_is_multiline, active_title_item_name,
@@ -331,6 +332,26 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
list := Concatenation(arg, [ ",\n", "at ", filename, ":", line_number]);
CallFuncList(Error, list);
end;
+ CurrentSourcePosition := function()
+ return rec( filename := filename, line := line_number );
+ end;
+ RecordStringSourcePosition := function( item )
+ local source_field;
+ if not ( IsRecord( item ) or IsTreeForDocumentationNode( item ) ) then
+ return;
+ fi;
+ if IsBound( item!.content_source_field ) then
+ source_field := item!.content_source_field;
+ elif IsBound( item!.content_source_positions ) then
+ source_field := "content_source_positions";
+ else
+ return;
+ fi;
+ if not IsBound( item!.( source_field ) ) then
+ item!.( source_field ) := [ ];
+ fi;
+ Add( item!.( source_field ), CurrentSourcePosition() );
+ end;
HasCurrentItem := function( )
return Length( context_stack ) > 0;
end;
@@ -828,6 +849,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
fi;
scope_chapter := ChapterInTree( tree, chapter_info[ 1 ] );
scope_chapter!.title_string := current_command[ 2 ];
+ scope_chapter!.title_string_source_position := CurrentSourcePosition();
end,
@Section := function()
@@ -856,6 +878,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
fi;
scope_section := SectionInTree( tree, chapter_info[ 1 ], chapter_info[ 2 ] );
scope_section!.title_string := current_command[ 2 ];
+ scope_section!.title_string_source_position := CurrentSourcePosition();
end,
@Subsection := function()
@@ -883,6 +906,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
fi;
scope_subsection := SubsectionInTree( tree, chapter_info[ 1 ], chapter_info[ 2 ], chapter_info[ 3 ] );
scope_subsection!.title_string := current_command[ 2 ];
+ scope_subsection!.title_string_source_position := CurrentSourcePosition();
end,
@BeginGroup := function()
@@ -899,25 +923,30 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
@Description := function()
CurrentOrNewManItem();
CurrentItem()!.content := CurrentItem()!.description;
+ CurrentItem()!.content_source_field := "description_source_positions";
NormalizeWhitespace( current_command[ 2 ] );
if current_command[ 2 ] <> "" then
Add( CurrentItem(), current_command[ 2 ] );
+ RecordStringSourcePosition( CurrentItem() );
fi;
end,
@Returns := function()
CurrentOrNewManItem();
CurrentItem()!.content := CurrentItem()!.return_value;
+ CurrentItem()!.content_source_field := "return_value_source_positions";
if IsBound( CurrentItem()!.item_type ) and CurrentItem()!.item_type = "Var" then
CurrentItem()!.item_type := "Func";
if not IsBound( CurrentItem()!.arguments ) or CurrentItem()!.arguments = fail then
CurrentItem()!.arguments := "arg";
fi;
CurrentItem()!.return_value := [ ];
+ CurrentItem()!.return_value_source_positions := [ ];
elif not IsBound( CurrentItem()!.item_type ) then
CurrentItem()!.declaration_is_function := true;
fi;
if current_command[ 2 ] <> "" then
Add( CurrentItem(), current_command[ 2 ] );
+ RecordStringSourcePosition( CurrentItem() );
fi;
end,
@Arguments := function()
@@ -964,6 +993,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
fi;
group_obj := DocumentationGroup( tree, group_name, chap_info );
group_obj!.title_string := current_command[ 2 ];
+ group_obj!.title_string_source_position := CurrentSourcePosition();
end,
@ChapterInfo := function()
local current_chapter_info;
@@ -1097,6 +1127,7 @@ InstallGlobalFunction( AutoDoc_Parser_ReadFiles,
return;
fi;
Add( CurrentItem(), current_command[ 2 ] );
+ RecordStringSourcePosition( CurrentItem() );
end,
@BeginLatexOnly := function()
local alt_node;
diff --git a/gap/ToolFunctions.gd b/gap/ToolFunctions.gd
index f191e341..224cca59 100644
--- a/gap/ToolFunctions.gd
+++ b/gap/ToolFunctions.gd
@@ -12,6 +12,8 @@ DeclareGlobalFunction( "AUTODOC_LineEndsCDATA" );
DeclareGlobalFunction( "AUTODOC_EscapeCDATAContent" );
DeclareGlobalFunction( "AUTODOC_OutputTextFile" );
+DeclareGlobalFunction( "AUTODOC_WriteDocumentationListWithSource" );
+DeclareGlobalFunction( "AUTODOC_WriteStringListWithSource" );
DeclareGlobalFunction( "AutoDoc_WriteDocEntry" );
DeclareGlobalFunction( "AUTODOC_Diff" );
diff --git a/gap/ToolFunctions.gi b/gap/ToolFunctions.gi
index c5fcc17e..e3c41b39 100644
--- a/gap/ToolFunctions.gi
+++ b/gap/ToolFunctions.gi
@@ -94,7 +94,8 @@ end );
##
InstallGlobalFunction( AutoDoc_WriteDocEntry,
function( filestream, list_of_records, heading )
- local return_value, description, current_description, labels, i,
+ local return_value, return_value_sources, description,
+ description_sources, current_description, labels, i,
item_type_info;
# look for a good return value (it should be the same everywhere)
@@ -102,9 +103,11 @@ InstallGlobalFunction( AutoDoc_WriteDocEntry,
if IsBound( i!.return_value ) then
if IsList( i!.return_value ) and Length( i!.return_value ) > 0 then
return_value := i!.return_value;
+ return_value_sources := i!.return_value_source_positions;
break;
elif IsBool( i!.return_value ) then
return_value := i!.return_value;
+ return_value_sources := [ ];
break;
fi;
fi;
@@ -112,6 +115,7 @@ InstallGlobalFunction( AutoDoc_WriteDocEntry,
if not IsBound( return_value ) then
return_value := false;
+ return_value_sources := [ ];
fi;
if IsList( return_value ) and ( not IsString( return_value ) ) and return_value <> "" then
@@ -120,12 +124,14 @@ InstallGlobalFunction( AutoDoc_WriteDocEntry,
# collect description (for readability not in the loop above)
description := [ ];
+ description_sources := [ ];
for i in list_of_records do
current_description := i!.description;
if IsString( current_description ) then
current_description := [ current_description ];
fi;
- description := Concatenation( description, current_description );
+ Append( description, current_description );
+ Append( description_sources, i!.description_source_positions );
od;
labels := [ ];
@@ -178,17 +184,88 @@ InstallGlobalFunction( AutoDoc_WriteDocEntry,
return_value := [ return_value ];
fi;
AppendTo( filestream, " " );
- WriteDocumentation( return_value, filestream );
+ AUTODOC_WriteDocumentationListWithSource(
+ return_value,
+ return_value_sources,
+ filestream
+ );
AppendTo( filestream, "\n" );
fi;
AppendTo( filestream, " \n" );
- WriteDocumentation( description, filestream );
+ AUTODOC_WriteDocumentationListWithSource(
+ description,
+ description_sources,
+ filestream
+ );
AppendTo( filestream, " \n" );
AppendTo( filestream, "\n\n" );
end );
+InstallGlobalFunction( AUTODOC_WriteDocumentationListWithSource,
+ function( node_list, source_positions, filestream )
+ local current_source_positions, current_string_list, i, next_source_index,
+ FlushConvertedStrings;
+
+ FlushConvertedStrings := function()
+ AUTODOC_WriteStringListWithSource(
+ current_string_list,
+ current_source_positions,
+ filestream
+ );
+ current_string_list := [ ];
+ current_source_positions := [ ];
+ end;
+
+ current_string_list := [ ];
+ current_source_positions := [ ];
+ next_source_index := 1;
+ for i in [ 1 .. Length( node_list ) ] do
+ if IsString( node_list[ i ] ) then
+ Add( current_string_list, ShallowCopy( node_list[ i ] ) );
+ if source_positions = fail or next_source_index > Length( source_positions ) then
+ Add( current_source_positions, fail );
+ else
+ Add( current_source_positions, source_positions[ next_source_index ] );
+ fi;
+ next_source_index := next_source_index + 1;
+ else
+ FlushConvertedStrings();
+ WriteDocumentation( node_list[ i ], filestream );
+ fi;
+ od;
+ FlushConvertedStrings();
+end );
+
+InstallGlobalFunction( AUTODOC_WriteStringListWithSource,
+ function( string_list, source_positions, filestream )
+ local converted_string_list, in_cdata, item;
+
+ if string_list = [ ] then
+ return;
+ fi;
+ converted_string_list := AUTODOC_ConvertMarkdownToGAPDocXML( string_list, source_positions );
+ in_cdata := false;
+ for item in converted_string_list do
+ if not IsString( item ) then
+ WriteDocumentation( item, filestream );
+ continue;
+ fi;
+ if AUTODOC_LineStartsCDATA( item ) then
+ in_cdata := true;
+ fi;
+ if in_cdata = true then
+ AppendTo( filestream, Chomp( item ), "\n" );
+ else
+ WriteDocumentation( item, filestream );
+ fi;
+ if AUTODOC_LineEndsCDATA( item ) then
+ in_cdata := false;
+ fi;
+ od;
+end );
+
InstallGlobalFunction( AUTODOC_Diff,
function(args...)
local diff;
diff --git a/tst/errorwithpos.tst b/tst/errorwithpos.tst
index 07cf9e2a..179b818a 100644
--- a/tst/errorwithpos.tst
+++ b/tst/errorwithpos.tst
@@ -11,6 +11,22 @@ gap> ParseFixture := function( arg )
> AutoDoc_Parser_ReadFiles( [ arg[ 1 ] ], tree, default_chapter_data );
> return tree;
> end;;
+gap> RenderFixtureDescription := function( file, item_name )
+> local tree, section, item, rendered, stream;
+> tree := ParseFixture( file );
+> section := SectionInTree( tree, "Parser", "Markdown_errors" );
+> item := First( section!.content,
+> x -> IsBound( x!.name ) and x!.name = item_name );
+> rendered := "";
+> stream := OutputTextString( rendered, true );
+> SetPrintFormattingStatus( stream, false );
+> AUTODOC_WriteStringListWithSource(
+> item!.description,
+> item!.description_source_positions,
+> stream );
+> CloseStream( stream );
+> return rendered;
+> end;;
#
# control: valid parser input still works
@@ -120,3 +136,17 @@ at tst/errorwithpos/break.g:1
gap> ParseFixture( "tst/errorwithpos/unknown-command.g" );
Error, unknown AutoDoc command @NotACommand,
at tst/errorwithpos/unknown-command.g:1
+
+#
+# markdown syntax errors should also report file and line
+#
+gap> RenderFixtureDescription(
+> "tst/errorwithpos/markdown-backtick-unbalanced.g",
+> "BacktickOp" );
+Error, did you forget some `,
+at tst/errorwithpos/markdown-backtick-unbalanced.g:3
+gap> RenderFixtureDescription(
+> "tst/errorwithpos/markdown-emph-unbalanced.g",
+> "EmphOp" );
+Error, did you forget some **,
+at tst/errorwithpos/markdown-emph-unbalanced.g:3
diff --git a/tst/errorwithpos/markdown-backtick-unbalanced.g b/tst/errorwithpos/markdown-backtick-unbalanced.g
new file mode 100644
index 00000000..259d8065
--- /dev/null
+++ b/tst/errorwithpos/markdown-backtick-unbalanced.g
@@ -0,0 +1,4 @@
+#! @Chapter Parser
+#! @Section Markdown errors
+#! @Description Text with `unterminated code span
+DeclareGlobalFunction( "BacktickOp" );
diff --git a/tst/errorwithpos/markdown-emph-unbalanced.g b/tst/errorwithpos/markdown-emph-unbalanced.g
new file mode 100644
index 00000000..752e306c
--- /dev/null
+++ b/tst/errorwithpos/markdown-emph-unbalanced.g
@@ -0,0 +1,4 @@
+#! @Chapter Parser
+#! @Section Markdown errors
+#! @Description Text with **unterminated emphasis
+DeclareGlobalFunction( "EmphOp" );
diff --git a/tst/markdown.tst b/tst/markdown.tst
index 075d305a..2e5e6b8c 100644
--- a/tst/markdown.tst
+++ b/tst/markdown.tst
@@ -26,7 +26,7 @@ gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "gap> 1 + 1;",
> "2",
> "```"
-> ]);;
+> ], fail);;
gap> Length(markdown_verbatim);
1
gap> IsTreeForDocumentationNode(markdown_verbatim[1]);
@@ -43,7 +43,7 @@ gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "fi;",
> "```",
> "After"
-> ]);;
+> ], fail);;
gap> markdown_verbatim[1];
"Before"
gap> IsTreeForDocumentationNode(markdown_verbatim[2]);
@@ -58,7 +58,7 @@ gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "~~~",
> "gap> [[2]]>[[1]];",
> "~~~"
-> ]);;
+> ], fail);;
gap> markdown_verbatim[1]!.content;
[ "gap> [[2]]>[[1]];" ]
gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
@@ -66,14 +66,14 @@ gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "gap> 1 + 1;",
> "2",
> "```"
-> ]);;
+> ], fail);;
gap> markdown_verbatim[1]!.element_name;
"Example"
gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "```@log",
> "#I some log message",
> "```"
-> ]);;
+> ], fail);;
gap> markdown_verbatim[1]!.element_name;
"Log"
gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
@@ -85,13 +85,13 @@ gap> markdown_verbatim := AUTODOC_ConvertMarkdownToGAPDocXML([
> "#! @InsertCode Increment",
> "## Code is inserted here.",
> "```"
-> ]);;
+> ], fail);;
gap> markdown_verbatim[1]!.content;
[ "#! @BeginCode Increment", "i := i + 1;", "#! @EndCode", "",
"#! @InsertCode Increment", "## Code is inserted here." ]
gap> AUTODOC_ConvertMarkdownToGAPDocXML([
> "` & more`"
-> ]) = [
+> ], fail) = [
> "<Log attr="x"> & more"
> ];
true