package provide xotcl::doc-tools 0.1 package require XOTcl # # Study for documentation classes for XOTcl2. # # Compared to the "old" @ docmentation effort, this is a rather # light-weight structure based on xotcl 2 language features. The # documentation classes build an (extensible) object structure which # is used as a basis for some renderers. In general, the classes are # defined in a way they can be used for # # a) building documentation outside the source code artefacts, or # # b) inside code artefacts (value added method definition commands # providing extra arguments for the documentation). The # documentation commands could reuse there names/arguments # etc. directly from the method definition by issueing these # commands inside the method definition methods. # # One could provide lint-like features to signal, whether the # documentation is in sync with actually defined methods (when these # are available). # namespace eval ::xodoc {} namespace eval ::xodoc-tools { ::xotcl::use xotcl2 # # A few helper commands: # - "@" is a conveniant way for creating new objects with less syntactic overhead # - "sorted" is used to sort instances by values of a specified attribute # proc @ {class name args} {$class new -name $name {*}$args} proc sorted {instances sortedBy} { set order [list] foreach v $instances {lappend order [list $v [$v eval [list set :$sortedBy]]]} set result [list] foreach pair [lsort -index 1 $order] {lappend result [lindex $pair 0]} return $result } Class create DocClass -superclass Class { # # DocClass is a meta class for named doc entities # :method createOrConfigure {id arguments} { namespace eval $id {} if {[::xotcl::objectproperty $id object]} { $id configure {*}$arguments } else { :create $id {*}$arguments } } } Class create docEntity { # # docEntity is the base class for the documentation classes # # every docEntity must be created with a "doc" value and can have # an optional initcmd :method objectparameter args {next {doc __initcmd:initcmd,optional}} :attribute doc #the following two cases (incremental multivalued) could be nicer :attribute {variants:multivalued ""} {set :incremental 1} :attribute {params:multivalued ""} {set :incremental 1} # @method _doc # # The method _doc can be use to obtain the value of the documentation # from another doc entity. This should avoid redundant documentation pieces. :method _doc {doc use what value} { if {$doc ne ""} {return $doc} if {$use ne ""} { foreach thing {xotclCmd xotclClass} { set docobj [$thing id $use] if {[::xotcl::objectproperty $docobj object]} break } if {[::xotcl::objectproperty $docobj object]} { if {![$docobj exists $what]} {error "no attribute $what in $docobj"} set names [list] foreach v [$docobj $what] { if {[$v name] eq $value} {return [$v doc]} lappend names [$v name] } error "can't use $use, no $what with name $value in $docobj (available: $names)" } else { error "can't use $use, no documentation object $docobj" } } } # @method param # # The method param is currently used for documenting parameters of # tcl-commands and xotcl methods. Most probably, it should cover # object parameters as well. The parameters are identified by a # name and ar part of another documentation entitiy # :method param {param doc {-use ""}} { set flags [list -param $param] if {[llength $param]>1} { lappend flags -default [lindex $param 1] set param [lindex $param 0] } set name $param if {[regexp {^(.*):(.*)$} $param _ name spec]} { lappend flags -spec $spec } lappend flags -fullname param @ xotclCmdParam $name -partof [self] {*}$flags [:_doc $doc $use params $name] } # @method variant # # variants are used in cases, where depending on a parameter, the # semantics of a command (and therefore its documentation) is # completely different. A typical case are subcommands in Tcl. # :method variant {name doc {-use ""}} { @ xotclCmdVariant $name -partof [self] [:_doc $doc $use variants $name] } # @method text # # text is used to access the content of doc of an docEntity, and # performs substitution on it. The substitution is not essential, # but looks for now convenient. # :method text {} {subst ${:doc}} } # @class docPart # # An docPart is a part of the documentation, defined by a # separate object. Every docPart is associated to another # documentation entity and is identified by a name. # Class create docPart -superclass docEntity { #:method objectparameter args {next {doc -use}} :attribute name:required :attribute partof:required :attribute use } # # variant and param are docParts: # Class create xotclCmdVariant -superclass docPart { :method init {} {${:partof} variants add [self] end} } Class create xotclCmdParam -superclass docPart { :attribute param :attribute fullname :attribute spec :attribute default :method init {} {${:partof} params add [self] end} } # # Now, define some kinds of docEntities. The toplevel docEntities # are named objects in the ::xoDoc namespace to ease access to it. # # We define here the following toplevel docEntities (e.g. xotclObject will follow): # - xotclCmd # - xotclClass # # The xotcl methods are defined as docParts. # - xotclMethod # DocClass create xotclCmd -superclass docEntity { :attribute name :attribute arguments :attribute {returns ""} :object method id {name} {return ::xodoc::cmd::[string trimleft $name :]} :object method new args { foreach {att value} $args {if {$att eq "-name"} {set name $value}} :createOrConfigure [:id $name] $args } } DocClass create xotclClass -superclass docEntity { :attribute name :attribute {methods:multivalued ""} {set :incremental 1} :object method id {name} {return ::xodoc::object::[string trimleft $name :]} :object method new args { foreach {att value} $args {if {$att eq "-name"} {set name $value}} :createOrConfigure [:id $name] $args } } # # xotclMethod is a named entity, which is part of some other # docEntity (a class or an object). We might be able to use the # "use" parameter for registered aliases to be able to refer to the # documentation of the original method. # DocClass create xotclMethod -superclass docPart { :attribute {scope class} :attribute {modifier public} :attribute arguments :attribute {returns ""} :object method id {partof scope name} { return ::xodoc::method::[string trimleft $partof :]::${scope}::${name} } :object method new args { foreach {att value} $args { if {$att eq "-partof"} {set partof $value} if {$att eq "-name"} {set name $value} if {$att eq "-scope"} {set scope $value} } if {![info exists scope]} { if {[::xotcl::objectproperty $partof class]} { set scope class } elseif {[::xotcl::objectproperty $partof object]} { set scope object } else { set scope class } } :createOrConfigure [:id $partof $scope $name] $args } :method init {} {[xotclClass id ${:partof}] methods add [self] end} :method signature {} { if {[info exists :arguments]} { set arguments ${:arguments} } else { set arguments [list] foreach p [:params] {lappend arguments [$p param]} } set result "obj ${:name} $arguments" } } } namespace eval ::xodoc-tools { # # Provide a simple HTML renderer. For now, we make our life simple # by defining for the different supported docEntities different methods. # # We could think about a java-doc style renderer... # Class create HTMLrenderer { # render command pieces in the text :method cmd {text} {return <@TT>$text@TT>} # # render xotcl commands # :method renderCmd {} { puts "