- Roxygen parses the file(s) and creates an object
roxyPackagewhich is a roxygen representation of the package and is essentially a list ofRoxyBlockobjects. EachRoxyBlockobject contains two slots, one @doc of classRoxyDocand another one @object of class ANY, such that any object can be fit inside. In other wordsRoxyBlock, is an abstract representation of:
##' My cool function
##'
##' @title
##' @param a
##' @examples
foo <- function(a = 34) a^5RoxyBlock can also contain @name slot (to hold "foo" above), but may be it's
not necessary.
- Each
RoxyDocis an abstract representation of the documentation block:
##' My cool function
##'
##' @title cool_function
##' @param a
##' @examples bla blaAnd is essentially a list of RoxyTag objects. RoxyTag is a root class containing at
least one slot @text. All tags are represented by their own classes:
setClass("RoxyTag", list(text = "character"))
setClass("tagTitle", contains = "RoxyTag")
setClass("tagParam", contains = "RoxyTag")
setClass("tagSuperParam", list(super = "numeric"), contains = "tagParam")When Roxygen fist parses the file it reads the documentation block as above
and tokenises it into tag objects according to the name @title, @param
etc. That is each object's text property is populated by the corresponding
text, nothing more.
At this stage, tags are raw. They have not been processed as yet.
- Next stage is the processing of the tags. There is a processTag method which is defined for each tag class. It should return a transformed object of the same class as the input one. Most of the tags don't need processing so the generic method is just identity.
setGeneric("processTag",
def = function(tag, object, roxydoc, roxypackage) standardGeneric("processTag"),
signature = c("tag", "object"), # for time being donn't dispatch on roxydox and roxypackage
useAsDefault = function(tag, object, roxydoc, roxypackage) tag)Usually this method would populate other slots which are specific to every tag, but it might have a global effect as well. How is this implemented is a another good question :). It depends on the situation I guess.
One option is to have a roxyPackage objects stored globally. And then every
processTag method can access that object and modify in place. This way one
processTag call can prepare other roxyBlocks for subsequent processTag
invocations. Then there is no need to pass roxypackage argument to processTag
at all.
Another option is to implement a local exchange mechanism only for those tags
which need that. That is add an additional slot of class "environment" and
populate it with the same environment on the initialization of the RoxyTag
objects.
-
Once all the
RoxyDocobjects are processed , they are stored again in aRoxyPackageobject which is again a collection ofRoxyBlockobjects which contain an target object (@object) and aRoxyDoc(@doc) object which is now a collection of all (already processed)RoxyTagobjects. -
Generating documentation. You need one method for each type of the documentation
outRd,outHTML,outMarkDown. Which is dispatched on two objects first is a documentation (be itRoxyDocorRoxyTag) and another one is the documented object itself.
setGeneric("outRd", def = function(doc, object) standardGeneric("outRd"))-
Other functionality like namespace generation and description, can be implemented just as functions taking one argument, a RoxyPackage object.
-
Other generics like,
roxyTemaplate,roxyUpdatecould be defined onRoxyDocorRoxyTagobjects correspondingly.
To extend a tag system, a user will have to define a new derived class for his
tag setClass("MyCoolUsageTag", list(coolSlot = "numeric", contains = "tagUsage")) and define the parseTag and outRd methods for the tag and the
objects which should have this tag in the documentation.
Observation: In the above proposal RoxyPackage, RoxyBlock, RoxyDoc and RoxyTag
objects are used in for dual purpose. First to store the row output from a
preparser, and then, in a second pos-processing stage they are used as a storage
for processed tags. This is probably not a big deal, as parsed objects are a
super entities of raw objects, so the same RoxyTag class can be used as a
representation of an raw tag, as well as the processed tag..