Skip to content

Commit

Permalink
Recognize DeclareSynonym, with cascade of related changes
Browse files Browse the repository at this point in the history
This change began with recognizing DeclareSynonym and cascaded to involve a
number of related changes. First, in order for DeclareSynonym to be useful,
there must be some way to specify the manual item type for the identifier
being declared. Hence this change also implements a @ItemType command to allow
that. (Adding @ItemType also achieves the main recommendaton in issue gap-packages#74.)

In documenting these two changes, it seemed best to have their documentation
in gap/Parser.gi next to their implementation, on the general principle that
documentation should be as close to the relevant code as possible. Further, in
an effort at dogfooding, it seemed best for that documentation to be in
AutoDoc format. This turned into a change in which doc/Comments.xml became
doc/Comments.autodoc and became much smaller, as most of the documentation
moved to gap/Parser.gi and gap/Markdown.gi.

Being able to use Autodoc to document most of Autodoc then entailed a few
other changes. First, Markdown-like syntax with backticks for code expressions
that would suppress the AutoDoc behavior of the quoted items was
essential. Second, it was useful to add an @Index command for generating the
index entries. And third, reassembling Chapter 2 of the manual from portions
in several files necessitated allowing sections and subsections inside a chunk
to move with that chunk, and be inserted into the chapter or section at the
point at which the chunk was inserted.

(An implementation note on this last change. It was greatly facilitated by
merging the current_item ambient variable with the context_stack. The
current_item was in essence functioning as the top of the stack anyway. Having
the top of the stack in one variable and the rest of the stack in another made
some of the code a bit confusing. This change touched a large fraction of code
in the Parser, but in the end improved the locality of reference in that code
considerably, and I think made it clearer what is going on during parsing.)

In the course of these changes, it was trivial to add the ability to have code
blocks which add at the current location, with no @insertcode necessary. So
that change is included here as well.

Finally, with so many changes, extensive testing was clearly critical. So this
commit also beefs up the worksheet.tst, and adds a new test dogfood.tst which
consists of generating the AutoDoc manual and verifying that it matches the
expected contents of the manual.

I do understand that this turned into more of a complex of changes than would
ordinarily be packed into a single commit. But this portmanteau seemed a
fairly natural stable point in which the motivating changes
(DeclareSynonym/@ItemType) are made, documented, and tested in a cohesive way.

I would be happy to do the work of separating this out into more than one
successive change to AutoDoc if that would help advance these changes. Please
just let me know what pieces you'd like it carved up into, and in what order.

Thanks very much for your consideration.

Resolves: gap-packages#60, gap-packages#74, gap-packages#174, gap-packages#175, gap-packages#176, gap-packages#177, gap-packages#178.
It also makes further progress on gap-packages#48, in adding the backtick code quotes, and
on the testing issues gap-packages#57 and gap-packages#152. It also touches on Issue gap-packages#112, in that to
use makedoc.g in testing, I needed to add a backdoor to prevent it from
QUITting. Note also that the motivation in the original post for Issue gap-packages#144
was the need to document DeclareSynonym.
  • Loading branch information
Glen Whitney authored and fingolfin committed May 22, 2019
1 parent 70ffb8a commit 68dc62a
Show file tree
Hide file tree
Showing 8 changed files with 698 additions and 237 deletions.
173 changes: 173 additions & 0 deletions doc/Comments.autodoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
@Chapter Comments
@ChapterTitle &AutoDoc; documentation comments

You can document declarations of global functions and variables, operations,
attributes etc. by inserting **&AutoDoc; comments** into your sources
before these declarations.
An &AutoDoc; comment always starts with `#!`.
This is also the smallest possible &AutoDoc; command.
If you want your declaration documented, just write
`#!` at the line before the documentation. For example:

@BeginLogSession
#!
DeclareOperation( "AnOperation",
[ IsList ] );
@EndLogSession

This will produce a manual entry for the operation `AnOperation`.
<P/>

Inside of &AutoDoc; comments, **&AutoDoc; commands**
starting with `@` can be used to control the output &AutoDoc; produces.


@Section Declarations
@SectionTitle Documenting declarations

In the bare form above, the manual entry for `AnOperation` will not
contain much more than the name of the operation. In order to change
this, there are several commands you can put into the &AutoDoc; comment
before the declaration. Currently, the following commands are provided:

@InsertChunk documenting_declaration_commands
&nbsp;<Br/>&nbsp;<Br/>
As an example, a full &AutoDoc; comment with all the most common options could
look like this:

@BeginLogSession
#! @Description
#! Computes the list of lists of degrees of ordinary characters
#! associated to the <A>p</A>-blocks of the group <A>G</A>
#! with <A>p</A>-modular character table <A>modtbl</A>
#! and underlying ordinary character table <A>ordtbl</A>.
#! @Returns a list
#! @Arguments modtbl
#! @Group CharacterDegreesOfBlocks
#! @FunctionLabel chardegblocks
#! @ChapterInfo Blocks, Attributes
DeclareAttribute( "CharacterDegreesOfBlocks",
IsBrauerTable );
@EndLogSession

@Section Recognized declarations
@InsertChunk recognized_declarators

Note that in the particular case of `DeclareSynonym`, &AutoDoc; has no way to
infer the argument list, so when documenting such a declaration, you must use
the `@Arguments` command to supply that information.

@Section Others
@SectionTitle Other documentation comments

There are also some commands which can be used in &AutoDoc; comments
that are not associated to any one particular declaration. This is useful for
additional text in your documentation, examples, mathematical chapters, etc..

@InsertChunk other_commands
@EndSection

@InsertChunk titlepage_commands

@Section PlainText
@SectionLabel Plain
@SectionTitle Plain text files

AutoDoc plain text files work exactly like AutoDoc comments, except that the
`#!` is unnecessary at the beginning of a line which should be documented.
Files that have the suffix .autodoc will automatically regarded as plain text files,
while the commands `@AutoDocPlainText` and `@EndAutoDocPlainText`
(<Ref Subsect="Subsection_PlainCommands"/>) mark regions in other files which
should be regarded as plain text &AutoDoc; parts. All commands can be used
in a plain text section of a file, without the leading `#!`.

@Section Groups
@SectionLabel Groups
@SectionTitle Grouping

In &GAPDoc;, it is possible to make groups of ManItems, i.e., when documenting
a function, operation, etc., it is possible to group them into suitable chunks.
This can be particularly useful if there are several definitions of an operation
with several different argument types, all doing more or less the same to the arguments.
Then their manual items can be grouped, sharing the same description and return type information.
Note that it is currently not possible to give a header to the Group in the manual,
but the generated ManItem heading of the first entry will be used.

Note that group names are globally unique throughout the whole manual.
That is, groups with the same name are in fact merged into a single group, even if they
were declared in different source files.
Thus you can have multiple `@BeginGroup/@EndGroup` pairs using the
same group name, in different places, and these all will refer to the same group.

Moreover, this means that you can add items to a group via the `@Group` command
in the &AutoDoc; comment of an arbitrary declaration, at any time.

The following code
@BeginLogSession
#! @BeginGroup Group1

#! @Description
#! First sentence.
DeclareOperation( "FirstOperation", [ IsInt ] );

#! @Description
#! Second sentence.
DeclareOperation( "SecondOperation", [ IsInt, IsGroup ] );

#! @EndGroup

## .. Stuff ..

#! @Description
#! Third sentence.
#! @Group Group1
KeyDependentOperation( "ThirdOperation", IsGroup, IsInt, "prime );
@EndLogSession

produces the following:

<ManSection Label="Group1">
<Oper Arg="arg" Name="FirstOperation" Label="for IsInt"/>
<Oper Arg="arg1,arg2" Name="SecondOperation" Label="for IsInt, IsGroup"/>
<Oper Arg="arg1,arg2" Name="ThirdOperation" Label="for IsGroup, IsInt"/>
<Returns></Returns>
<Description>
First sentence.
Second sentence.
Third sentence.
</Description>
</ManSection>

@Section Level
@SectionLabel Level

Levels can be set to not write certain parts in the manual by default.
Every entry has by default the level 0. The command `@Level` can
be used to set the level of the following part to a higher level, for
example 1, and prevent it from being printed to the manual by default.
However, if one sets the level to a higher value in the autodoc option of
<C>AutoDoc()</C>, the parts will be included in the manual at the specific
place.

@BeginLogSession
#! This text will be printed to the manual.
#! @Level 1
#! This text will be printed to the manual if created with level 1 or higher.
#! @Level 2
#! This text will be printed to the manual if created with level 2 or higher.
#! @ResetLevel
#! This text will be printed to the manual.
@EndLogSession

@Section MarkdownExtension
@SectionTitle Markdown-like formatting of text in &AutoDoc;

&AutoDoc; has some convenient ways to insert special format into text, like
math formulas and lists. The syntax for them are inspired by Markdown and LaTeX,
but do not follow them strictly. Neither are all features of the Markdown
language supported. The following subsections describe the Markdown-like
conventions that may be used.

@InsertChunk markdown_conventions

@EndSection
4 changes: 2 additions & 2 deletions doc/Tutorials.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ DeclareOperation( "ToricVariety", [ IsConvexObject ] );
]]></Listing>

For a thorough description of what you can do with &AutoDoc;
documentation comments, please refer to chapter <Ref Chap="Comments"/>.
documentation comments, please refer to chapter <Ref Chap="Chapter_Comments"/>.
<P/>

<!--
Expand Down Expand Up @@ -326,7 +326,7 @@ is named <File>manual.xml</File>.
The files <File>PackageInfo.g</File>, <File>makedoc.g</File>,
<File>doc/title.xml</File> and <File>doc/PackageName.xml</File>
(if it exists) will be altered by this procedure,
so it may be wise to keep backup copies.
so it may be wise to keep backup copies.
<P/>
You should have copies of the &AutoDoc; files <File>PackageInfo.g</File>
and <File>makedoc.g</File> to hand when reading these instructions.
Expand Down
3 changes: 3 additions & 0 deletions gap/DocumentationTree.gd
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ DeclareOperation( "DocumentationTree", [ ] );
DeclareOperation( "StructurePartInTree", [ IsTreeForDocumentation, IsList ] );
DeclareOperation( "ChapterInTree", [ IsTreeForDocumentation, IsString ] );
DeclareOperation( "SectionInTree", [ IsTreeForDocumentation, IsString, IsString ] );
DeclareOperation( "SectionAsChildOf", [ IsTreeForDocumentation, IsList, IsObject ] );
DeclareOperation( "SubsectionInTree", [ IsTreeForDocumentation, IsString, IsString, IsString ] );
DeclareOperation( "SubsectionAsChildOf", [ IsTreeForDocumentation, IsList, IsObject ] );
DeclareOperation( "DocumentationExample", [ IsTreeForDocumentation, IsList ] );
DeclareOperation( "DocumentationExample", [ IsTreeForDocumentation ] );
DeclareOperation( "DocumentationIndexEntry", [ IsTreeForDocumentation, IsString, IsString ] );
DeclareOperation( "DocumentationDummy", [ IsTreeForDocumentation, IsString, IsList ] );
DeclareOperation( "DocumentationDummy", [ IsTreeForDocumentation, IsString ] );
DeclareOperation( "DocumentationCode", [ IsTreeForDocumentation, IsString, IsList ] );
Expand Down
99 changes: 91 additions & 8 deletions gap/DocumentationTree.gi
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ BindGlobal( "TheTypeOfDocumentationTreeCodeNodes",
NewType( TheFamilyOfDocumentationTreeNodes,
IsTreeForDocumentationCodeNodeRep ) );

DeclareRepresentation( "IsTreeForDocumentationIndexNodeRep",
IsTreeForDocumentationNodeRep,
[ ] );

BindGlobal( "TheTypeOfDocumentationTreeIndexNodes",
NewType( TheFamilyOfDocumentationTreeNodes,
IsTreeForDocumentationIndexNodeRep ) );

###################################
##
## Tools
Expand Down Expand Up @@ -173,22 +181,26 @@ InstallMethod( DocumentationTree, [ ],
end );

## create a chapter, section or subsection
InstallMethod( StructurePartInTree, [ IsTreeForDocumentation, IsList ],
## This first helper, given tree and context, returns a list of two entries.
## The second entry is a node to represent the structure part at that context.
## The first is true if that structure part did not yet exist, in which case the
## node returned as the second entry is "floating," i.e., not yet added
## anywhere in the tree. If the first entry is false, the node existed in the
## tree earlier (and is presumed to have been added in some appropriate place).
StructurePartMaybeFloating@ :=
function( tree, context )
local label, parent, new_node, type;
local label, new_node, type;

if IsEmpty( context ) then
return tree;
return [ false, tree];
fi;

# if the part already exist, use that
label := AUTODOC_LABEL_OF_CONTEXT( context );
if IsBound( tree!.nodes_by_label.( label ) ) then
return tree!.nodes_by_label.( label );
return [false, tree!.nodes_by_label.( label )];
fi;

parent := StructurePartInTree( tree, context{[1..Length(context)-1]} );

new_node := rec( content := [ ],
level := tree!.current_level,
name := context[ Length( context ) ],
Expand All @@ -203,8 +215,20 @@ InstallMethod( StructurePartInTree, [ IsTreeForDocumentation, IsList ],
ObjectifyWithAttributes( new_node, type, Label, label );

tree!.nodes_by_label.( label ) := new_node;
Add( parent!.content, new_node );
return new_node;
return [true, new_node];
end;

# This method creates the structure part and adds it at the current position
# in the next higher structure part.
InstallMethod( StructurePartInTree, [ IsTreeForDocumentation, IsList ],
function( tree, context )
local parent, trial;
trial := StructurePartMaybeFloating@( tree, context );
if trial[ 1 ] then
parent := StructurePartInTree( tree, context{[ 1..Length(context)-1 ]} );
Add( parent!.content, trial[ 2 ] );
fi;
return trial[ 2 ];
end );

##
Expand All @@ -231,6 +255,22 @@ InstallMethod( DocumentationExample, [ IsTreeForDocumentation ],
return node;
end );

##
InstallMethod( DocumentationIndexEntry,
[ IsTreeForDocumentation, IsString, IsString ],
function( tree, ikey, entry )
local node, label;

node := rec( content := [ entry ],
key := ikey,
level := tree!.current_level );
label := Concatenation( "Index_", String( AUTODOC_TREE_NODE_NAME_ITERATOR( tree ) ) );
ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeIndexNodes,
Label, label );
tree!.nodes_by_label.( label ) := node;
return node;
end );

##
InstallMethod( DocumentationDummy, [ IsTreeForDocumentation, IsString, IsList ],
function( tree, name, context )
Expand Down Expand Up @@ -437,12 +477,46 @@ InstallMethod( SectionInTree, [ IsTreeForDocumentation, IsString, IsString ],
return StructurePartInTree( tree, [ chapter_name, section_name ] );
end );

## This method creates the section and if it is not already in the tree,
## adds it as a child of the specified node (rather than as a child of its
## chapter in the documentation tree). This is useful for implementing chunks,
## which can move sections around.
InstallMethod( SectionAsChildOf, [ IsTreeForDocumentation, IsList, IsObject ],
function( tree, context, parent )
local section_try;
if Length( context ) <> 2 then
Error( "Request for section at ill-formed context", String( context ) );
fi;
section_try := StructurePartMaybeFloating@( tree, context );
if section_try[ 1 ] then
Add(parent!.content, section_try[ 2 ]);
fi;
return section_try[ 2 ];
end );

##
InstallMethod( SubsectionInTree, [ IsTreeForDocumentation, IsString, IsString, IsString ],
function( tree, chapter_name, section_name, subsection_name )
return StructurePartInTree( tree, [ chapter_name, section_name, subsection_name ] );
end );

## This method creates the subsection and if it is not already in the tree,
## adds it as a child of the specified node (rather than as a child of its
## section in the documentation tree). This is useful for implementing chunks,
## which can move subsections around.
InstallMethod( SubsectionAsChildOf, [ IsTreeForDocumentation, IsList, IsObject ],
function( tree, context, parent )
local subsection_try;
if Length( context ) <> 3 then
Error( "Request for subsection at ill-formed context", String( context ) );
fi;
subsection_try := StructurePartMaybeFloating@( tree, context );
if subsection_try[ 1 ] then
Add(parent!.content, subsection_try[ 2 ]);
fi;
return subsection_try[ 2 ];
end );

#############################################
##
## Write functions
Expand Down Expand Up @@ -656,6 +730,15 @@ InstallMethod( WriteDocumentation, [ IsTreeForDocumentationExampleNodeRep, IsStr
AppendTo( filestream, "]]></", inserted_string, ">\n\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationIndexNodeRep, IsStream ],
function( node, filestream )
if node!.level > ValueOption( "level_value" ) then return; fi;
AppendTo( filestream, "<Index Key=\"", node!.key, "\">" );
WriteDocumentation( node!.content, filestream );
AppendTo( filestream, "</Index>\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationCodeNodeRep, IsStream ],
function( node, filestream )
Expand Down
11 changes: 11 additions & 0 deletions gap/Magic.gd
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,20 @@
#!
#! <Mark><A>files</A></Mark>
#! <Item>
#! <Label Name="AutodocFilesOption"/>
#! A list of files (given by paths relative to the package directory)
#! to be scanned for &AutoDoc; documentation comments.
#! Usually it is more convenient to use <A>autodoc.scan_dirs</A>, see below.
#! However, the files in this list are always scanned **before**
#! the ones found by <A>scan_dirs</A>, and they are scanned in
#! the order listed, so this option can be useful for controlling
#! the order that &AutoDoc; processes files, which in turn
#! can be useful to ensure that your manual is presented in the
#! desired order. For example, you might use this option to
#! designate a "master file" which declares all chapters in the
#! desired order. Those chapters can then be filled in by
#! documentation in your source files, which may be processed in an
#! order not relevant to the flow of your documentation.
#! </Item>
#!
#! <Mark><A>scan_dirs</A></Mark>
Expand Down
Loading

0 comments on commit 68dc62a

Please sign in to comment.