CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it

Views: 418346
#############################################################################
##
##  AutoDoc package
##
##  Copyright 2012-2016
##    Sebastian Gutsche, University of Kaiserslautern
##    Max Horn, Justus-Liebig-Universität Gießen
##
## Licensed under the GPL 2 or later.
##
#############################################################################

##
BindGlobal( "AUTODOC_IdentifierLetters",
            "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" );

DeclareRepresentation( "IsTreeForDocumentationRep",
        IsAttributeStoringRep and IsTreeForDocumentation,
        [ ] );

BindGlobal( "TheFamilyOfDocumentationTrees",
        NewFamily( "TheFamilyOfDocumentationTrees" ) );

BindGlobal( "TheTypeOfDocumentationTrees",
        NewType( TheFamilyOfDocumentationTrees,
                IsTreeForDocumentationRep ) );

## Metatype, specify later
DeclareRepresentation( "IsTreeForDocumentationNodeRep",
        IsAttributeStoringRep and IsTreeForDocumentationNode,
        [ ] );

BindGlobal( "TheFamilyOfDocumentationTreeNodes",
        NewFamily( "TheFamilyOfDocumentationTreeNodes" ) );

BindGlobal( "TheTypeOfDocumentationTreeNodes",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeRep ) );

## Chapter node
DeclareRepresentation( "IsTreeForDocumentationNodeForChapterRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForChapter",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForChapterRep ) );

## Section node
DeclareRepresentation( "IsTreeForDocumentationNodeForSectionRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForSection",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForSectionRep ) );

## Subsection node
DeclareRepresentation( "IsTreeForDocumentationNodeForSubsectionRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForSubsection",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForSubsectionRep ) );

## Text node
DeclareRepresentation( "IsTreeForDocumentationNodeForTextRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForText",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForTextRep ) );

## ManItem node
DeclareRepresentation( "IsTreeForDocumentationNodeForManItemRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForManItem",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForManItemRep ) );

## Group Node
DeclareRepresentation( "IsTreeForDocumentationNodeForGroupRep",
        IsTreeForDocumentationNodeRep,
        [ ] );

BindGlobal( "TheTypeOfDocumentationTreeNodesForGroup",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationNodeForGroupRep ) );

## DeclareRepresentation
DeclareRepresentation( "IsTreeForDocumentationDummyNodeRep",
                       IsTreeForDocumentationNodeRep,
                       [ ] );

BindGlobal( "TheTypeOfDocumentationTreeDummyNodes",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationDummyNodeRep ) );

## DeclareRepresentation
DeclareRepresentation( "IsTreeForDocumentationExampleNodeRep",
                       IsTreeForDocumentationNodeRep,
                       [ ] );

BindGlobal( "TheTypeOfDocumentationTreeExampleNodes",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationExampleNodeRep ) );

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

BindGlobal( "TheTypeOfDocumentationTreeCodeNodes",
        NewType( TheFamilyOfDocumentationTreeNodes,
                IsTreeForDocumentationCodeNodeRep ) );

###################################
##
## Tools
##
###################################

##
InstallGlobalFunction( AUTODOC_TREE_NODE_NAME_ITERATOR,
  function( tree )
    local curr_val;

    curr_val := tree!.node_name_iterator;
    tree!.node_name_iterator := curr_val + 1;
    return curr_val;
end );

##
InstallGlobalFunction( AUTODOC_LABEL_OF_CONTEXT,
  function( context )
    local label;
    if not IsList( context ) then
        Error( "wrong type of context" );
    fi;
    if IsString( context ) then
        label := context;
    elif Length( context ) = 1 then
        label := Concatenation( "Chapter_", context[ 1 ] );
    elif Length( context ) = 2 then
        label := Concatenation( "Chapter_", context[ 1 ], "_Section_", context[ 2 ] );
    elif Length( context ) = 3 then
        label := Concatenation( "Chapter_", context[ 1 ], "_Section_", context[ 2 ], "_Subsection_", context[ 3 ] );
    else
        Error( "wrong type of context" );
    fi;
    label := Filtered(label, x -> x in AUTODOC_IdentifierLetters);
    return label;
end );

###################################
##
## Constructors
##
###################################

##
InstallMethod( DocumentationTree, [ ],
  function( )
    local tree;

    tree := rec(
                  content := [ ],   # a list of nodes
                  nodes_by_label := rec( ),
                  node_name_iterator := 0,
                  current_level := 0,
                  TitlePage := rec( )
            );
    ObjectifyWithAttributes( tree, TheTypeOfDocumentationTrees );
    return tree;
end );

## create a chapter, section or subsection
InstallMethod( StructurePartInTree, [ IsTreeForDocumentation, IsList ],
  function( tree, context )
    local label, parent, new_node, type;
    
    if IsEmpty( context ) then
        return 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 );
    fi;

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

    new_node := rec( content := [ ],
                     level := tree!.current_level,
                     name := context[ Length( context ) ],
                     chapter_info := context );
    if Length( context ) = 1 then
        type := TheTypeOfDocumentationTreeNodesForChapter;
    elif Length( context ) = 2 then
        type := TheTypeOfDocumentationTreeNodesForSection;
    elif Length( context ) = 3 then
        type := TheTypeOfDocumentationTreeNodesForSubsection;
    fi;
    ObjectifyWithAttributes( new_node, type, Label, label );

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

##
InstallMethod( DocumentationExample, [ IsTreeForDocumentation, IsList ],
  function( tree, context )
    local node;

    node := DocumentationExample( tree );
    Add( tree, node, context );
    return node;
end );

##
InstallMethod( DocumentationExample, [ IsTreeForDocumentation ],
  function( tree )
    local node, label;

    node := rec( content := [ ],
                 level := tree!.current_level );
    label := Concatenation( "Example_", String( AUTODOC_TREE_NODE_NAME_ITERATOR( tree ) ) );
    ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeExampleNodes,
                             Label, label );
    tree!.nodes_by_label.( label ) := node;
    return node;
end );

##
InstallMethod( DocumentationDummy, [ IsTreeForDocumentation, IsString, IsList ],
  function( tree, name, context )
    local node;

    node := DocumentationDummy( tree, name );
    Add( tree, node, context );
    return node;
end );

##
InstallMethod( DocumentationDummy, [ IsTreeForDocumentation, IsString ],
  function( tree, name )
    local node;

    name := Concatenation( "System_", name );
    if IsBound( tree!.nodes_by_label.( name ) ) then
        return tree!.nodes_by_label.( name );
    fi;
    node := rec( content := [ ],
                 level := tree!.current_level );
    ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeDummyNodes,
                              Label, name );
    tree!.nodes_by_label.( name ) := node;
    return node;
end );

##
InstallMethod( DocumentationCode, [ IsTreeForDocumentation, IsString, IsList ],
  function( tree, name, context )
    local node;
    
    node := DocumentationGroup( tree, name );
    Add( tree, node, context );
    return node;
end );

##
InstallMethod( DocumentationCode, [ IsTreeForDocumentation, IsString ],
  function( tree, name )
    local node;
    
    name := Concatenation( "System_", name );
    
    node := rec( content := [ ],
                 level := tree!.current_level );
    
    ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeCodeNodes,
                             Label, name );
    
    if IsBound( tree!.nodes_by_label.( name ) ) then
        Add( tree!.nodes_by_label.( name )!.content, node );
    fi;
    
    tree!.nodes_by_label.( name ) := node;
    return node;
end );

##
InstallMethod( DocumentationManItem, [ IsTreeForDocumentation ],
  function( tree )
    local node, name;

    node := rec( description := [ ],
                 return_value := [ ],
                 level := tree!.current_level );
    ObjectifyWithAttributes( node, TheTypeOfDocumentationTreeNodesForManItem );
    name := Concatenation( "ManItem_", String( AUTODOC_TREE_NODE_NAME_ITERATOR( tree ) ) );
    tree!.nodes_by_label.( name ) := node;
    node!.content := node!.description;
    return node;
end );

##
InstallMethod( SetManItemToDescription, [ IsTreeForDocumentationNodeForManItemRep ],
  function( node )
    node!.content := node!.description;
end );

##
InstallMethod( SetManItemToReturnValue, [ IsTreeForDocumentationNodeForManItemRep ],
  function( node )
    node!.content := node!.return_value;
end );

##
InstallMethod( DocumentationGroup, [ IsTreeForDocumentation, IsString ],
  function( tree, group_name )
    local group, name;

    name := Concatenation( "GROUP_", group_name );
    if IsBound( tree!.nodes_by_label.( name ) ) then
        return tree!.nodes_by_label.( name );
    fi;
    group := rec( content := [ ],
                  level := tree!.current_level
    );
    ObjectifyWithAttributes( group, TheTypeOfDocumentationTreeNodesForGroup,
                             Label, name );
    tree!.nodes_by_label.( name ) := group;
    group!.is_added := false;
    return group;
end );

##
InstallMethod( DocumentationGroup, [ IsTreeForDocumentation, IsString, IsList ],
  function( tree, group_name, context )
    local name, group;

    name := Concatenation( "GROUP_", group_name );
    if IsBound( tree!.nodes_by_label.( name ) ) then
        return tree!.nodes_by_label.( name );
    fi;
    context := AUTODOC_LABEL_OF_CONTEXT( context );
    group := DocumentationGroup( tree, group_name );
    Add( tree!.nodes_by_label.( context ), group );
    group!.is_added := true;
    return group;
end );

##
InstallMethod( Add, [ IsTreeForDocumentationNode, IsTreeForDocumentationNode ],
  function( parent_node, node )
    Add( parent_node!.content, node );
end );

##
InstallMethod( Add, [ IsTreeForDocumentationNode, IsString ],
  function( parent_node, string )
    Add( parent_node!.content, string );
end );

##
InstallMethod( Add, [ IsTreeForDocumentation, IsTreeForDocumentationNodeForManItemRep and HasChapterInfo ],
  function( tree, node )
    local chapter_info, section;
    chapter_info := ChapterInfo( node );
    section := SectionInTree( tree, chapter_info[ 1 ], chapter_info[ 2 ] );
    Add( section, node );
end );

##
InstallMethod( Add, [ IsTreeForDocumentation, IsTreeForDocumentationNodeForManItemRep and HasGroupName ],
  function( tree, node )
    local group;
    group := DocumentationGroup( tree, GroupName( node ) );
    Add( group, node );
end );

##
InstallMethod( Add, [ IsTreeForDocumentation, IsTreeForDocumentationNodeForManItemRep and HasGroupName and HasChapterInfo ],
  function( tree, node )
    local chapter_info, group;
    chapter_info := ChapterInfo( node );
    group := DocumentationGroup( tree, GroupName( node ), chapter_info );
    Add( group, node );
end );

##
InstallMethod( Add, [ IsTreeForDocumentation, IsTreeForDocumentationNode, IsList ],
  function( tree, node, context )
    local label, context_node;
    label := AUTODOC_LABEL_OF_CONTEXT( context );
    context_node := tree!.nodes_by_label.(label);
    Add( context_node, node );
end );

##
InstallMethod( IsEmptyNode, [ IsTreeForDocumentationNode ],
  function( node )
    if IsBound( node!.content ) then
        return ForAll( node!.content, IsEmptyNode );
    fi;
    return false;
end );

##
InstallMethod( IsEmptyNode, [ IsString ],
  function( node )
    return node = "";
end );

##
InstallMethod( IsEmptyNode, [ IsTreeForDocumentationNodeForManItemRep ],
  function( node )
    return false;
end );

####################################
##
## Add functions
##
####################################

## 
InstallMethod( ChapterInTree, [ IsTreeForDocumentation, IsString ],
  function( tree, name )
    return StructurePartInTree( tree, [ name ] );
end );

##
InstallMethod( SectionInTree, [ IsTreeForDocumentation, IsString, IsString ],
  function( tree, chapter_name, section_name )
    return StructurePartInTree( tree, [ chapter_name, section_name ] );
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 );

#############################################
##
## Write functions
##
#############################################

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentation, IsDirectory ],
  function( tree, path_to_xmlfiles )
    local stream, i;

    stream := AUTODOC_OutputTextFile( path_to_xmlfiles, _AUTODOC_GLOBAL_OPTION_RECORD.AutoDocMainFile );
    AppendTo( stream, AUTODOC_XML_HEADER );
    for i in tree!.content do
        if not IsTreeForDocumentationNodeForChapterRep( i ) then
            Error( "this should never happen" );
        fi;
        ## FIXME: If there is anything else than a chapter, this will break!
        WriteDocumentation( i, stream, path_to_xmlfiles );
    od;
    # Workaround for issue #65
    if IsEmpty( tree!.content ) then
        AppendTo( stream, " \n" );
    fi;
    CloseStream( stream );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationNodeForChapterRep, IsStream, IsDirectory ],
  function( node, stream, path_to_xmlfiles )
    local filename, chapter_stream, label, replaced_name, additional_label;

    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    if ForAll( node!.content, IsEmptyNode ) then
        return;
    fi;
    label := Label( node );
    if IsBound( node!.additional_label ) then
        additional_label := node!.additional_label;
    else
        additional_label := label;
    fi;

    # Remove any characters outside of A-Za-z0-9 and -, +, _ from the filename.
    # See issues #77 and #78
    filename := Filtered( additional_label, x -> x in AUTODOC_IdentifierLetters);
    filename := Concatenation( "_", filename, ".xml" );

    chapter_stream := AUTODOC_OutputTextFile( path_to_xmlfiles, filename );
    AppendTo( stream, "<#Include SYSTEM \"", filename, "\">\n" );
    AppendTo( chapter_stream, AUTODOC_XML_HEADER );
    AppendTo( chapter_stream, "<Chapter Label=\"", additional_label ,"\">\n" );
    replaced_name := ReplacedString( node!.name, "_", " " );
    AppendTo( chapter_stream, Concatenation( [ "<Heading>", replaced_name, "</Heading>\n\n" ] ) );
    WriteDocumentation( node!.content, chapter_stream );
    AppendTo( chapter_stream, "</Chapter>\n\n" );
    CloseStream( chapter_stream );
end );

##
InstallMethod( WriteDocumentation, [ IsList, IsStream ],
  function( node_list, filestream )
    local current_string_list, i, last_position;

    i := 1;
    current_string_list := [ ];
    for i in [ 1 .. Length( node_list ) ] do
        if IsString( node_list[ i ] ) then
            Add( current_string_list, node_list[ i ] );
        else
            if current_string_list <> [ ] then
                current_string_list := CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML( current_string_list );
                Perform( current_string_list, function( i ) WriteDocumentation( i, filestream ); end );
                current_string_list := [ ];
            fi;
            WriteDocumentation( node_list[ i ], filestream );
            AppendTo( filestream, "\n" );
        fi;
    od;
    if current_string_list <> [ ] then
        current_string_list := CONVERT_LIST_OF_STRINGS_IN_MARKDOWN_TO_GAPDOC_XML( current_string_list );
        Perform( current_string_list, function( i ) WriteDocumentation( i, filestream ); end );
    fi;
end );

##
InstallMethod( WriteDocumentation, [ IsString, IsStream ],
  function( text, filestream )
    ## In case the list is empty, do nothing.
    ## Once the empty string = empty list bug is fixed,
    ## this could be removed.
    NormalizeWhitespace( text );
    if text = "" then
        return;
    fi;
    AppendTo( filestream, text, "\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationNodeForSectionRep, IsStream ],
  function( node, filestream )
    local replaced_name, label, additional_label;

    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    if ForAll( node!.content, IsEmptyNode ) then
        return;
    fi;
    if IsBound( node!.additional_label ) then
        label := node!.additional_label;
    else
        label := Label( node );;
    fi;
    AppendTo( filestream, "<Section Label=\"", label, "\">\n" );
    replaced_name := ReplacedString( node!.name, "_", " " );
    AppendTo( filestream, Concatenation( [ "<Heading>", replaced_name, "</Heading>\n\n" ] ) );
    WriteDocumentation( node!.content, filestream );
    AppendTo( filestream, "</Section>\n\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationNodeForSubsectionRep, IsStream ],
  function( node, filestream )
    local replaced_name, label, additional_label;

    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    if ForAll( node!.content, IsEmptyNode ) then
        return;
    fi;
    if IsBound( node!.additional_label ) then
        label := node!.additional_label;
    else
        label := Label( node );
    fi;
    AppendTo( filestream, "<Subsection Label=\"", label, "\">\n" );
    replaced_name := ReplacedString( node!.name, "_", " " );
    AppendTo( filestream, Concatenation( [ "<Heading>", replaced_name, "</Heading>\n\n" ] ) );
    WriteDocumentation( node!.content, filestream );
    AppendTo( filestream, "</Subsection>\n\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationNodeForManItemRep, IsStream ],
  function( node, filestream )
    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    AutoDoc_WriteDocEntry( filestream, [ node ] );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationNodeForGroupRep, IsStream ],
  function( node, filestream )
    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    AutoDoc_WriteDocEntry( filestream, node!.content );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationDummyNodeRep, IsStream ],
  function( node, filestream )
    if IsBound( node!.content ) then
        WriteDocumentation( node!.content, filestream );
    fi;
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationExampleNodeRep, IsStream ],
  function( node, filestream )
    local contents, i, tested, inserted_string;

    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    contents := node!.content;
    tested := node!.is_tested_example;
    if tested = true then
        inserted_string := "Example";
    elif tested = false then
        inserted_string := "Log";
    else
        Error( "This should not happen!" );
    fi;
    AppendTo( filestream, "<", inserted_string, "><![CDATA[\n" );
    for i in contents do
        AppendTo( filestream, i, "\n" );
    od;
    AppendTo( filestream, "]]></", inserted_string, ">\n\n" );
end );

##
InstallMethod( WriteDocumentation, [ IsTreeForDocumentationCodeNodeRep, IsStream ],
  function( node, filestream )
    local content, i;
    
    if node!.level > ValueOption( "level_value" ) then
        return;
    fi;
    
    content := node!.content;
    
    if content = [ ] then
        return;
    fi;
    
    AppendTo( filestream, "<Listing Type=\"Code\"><![CDATA[\n" );
    for i in content do
        AppendTo( filestream, i, "\n" );
    od;
    AppendTo( filestream, "]]></Listing>\n" );
end );