This is a post I initially wrote elsewhere, but which I'm porting here as it shows once more why I think subdomains and normal web operations in the Wolfram Cloud are important features (and which I imagine should not be much work to implement)
This post is going to be long on design and relatively short on code. As usual, it's exposition of something I spent a while developing and have cooked into one of my packages .
I'm going to talk about how to make and distribute documentation in Mathematica, with specific emphasis on the automatic generation of documentation.
To start, though, we'll break down the types of things that we find in the documentation and which we'll want to support in a package. At this point there are about 10 different types of documentation formats out there:
Service Connection pages
Of these, there are really only 4 distinct types of things that we see in the documentation folders:
And of these I opted to only implement the first three as they are the ones I use the most.
Ref pages come in a few different flavors, but the most common one, and the most common type of documentation in general, is the symbol page.
As of version 11.1 symbol pages look like this:
Which can be split into 5 major parts
We'll need to include each of these when we build our own docs
Guides have four parts. In the interest of space I won't show an actual image of a guide, but you can see one via:
These parts are
Title and abstract
Tutorials are so much more flexible than guides of symbol pages that it only really makes sense to discuss three sections, with an optional fourth
This flexibility makes them both easier and harder to handle.
Now that we know what kinds of things we need to include we can move to how to include them. Before jumping into the actual code, though, it's worth noting that WRI does provide a tool for building docs.
Workbench is Wolfram's primary IDE. It's a plugin to Eclipse
There's no absolute reason not to use Workbench. Indeed it's probably got the lowest barrier to entry given that it's semi-battle-tested by WRI.
On the other hand, using Workbench restricts ones possibilities. It doesn't always stay up-to-date (as of writing this the documentation it builds by default is still on version 11.0) and by using it we lose the edge of our knowledge of Mathematica.
In general, Mathematica will be the best tool for manipulating Mathematica documents, so my view is why not simply provide a Mathematica package to do it?
The package that I developed is called DocGen (inspired by, but not nearly as clever as, doxygen ). It uses my larger BTools toolchain to provide extra linkages to the entire Mathematica ecosystem.
The first place any documentation starts is as a documentation template. This is simply a notebook with cell-types attached that will be post-processed into a full documentation notebook. We can make a new one for a Symbol Page like:
(* to load DocGen *)
DocGen["SymbolPage", MyFunction, Method->"Template"]//CreateDocument;
We can simply type in this notebook to add content. I'll fille this out, then generate it and we'll see what happens:
We can see that for the most part it looks the same, but now the notebook is formatted properly for use with the Documentation Center
A similar workflow is implemented for guides and tutorials
This by itself doesn't give us much of a leg-up on Workbench. In fact, this template system may even be a little bit worse than Workbench's DocuTools (although significantly less bloated and quicker to use).
What does make this powerful is how it allows us to now automatically generate documentation, as all we need to do it extract parameters from a Symbol or context and feed them into this type of template.
The actual details behind this can be a bit gory, but you can read about them in my post on StackExchange . In general we simply take the Symbol and extract the usage patterns to fill out the usage table, the calling patterns to fill out the details, make a few sample Examples based on the calling patterns, and provide See Also links based on camel-case similarity in the first hump.
This is what DocGen does by default, so for instance we can do:
And we get a fully-functional documentation notebook automatically
We can take this further, though, and do the same for an entire set of contexts to link a package or multiple packages together:
This gives us a really powerful way to provide accessible documentation with a minimum of effort. In all, that makes it much more likely that the documentation will actually get made.
This also rewards good package design as the better ones definition patterns are, the clearer the documentation built will be.
Simply building the documentation isn't enough, though. Good documentation should serve as an advertisement for one's package. So the next thing to do is design a distribution system that allows us to publicize and distribute our documentation effectively. To do that we'll want to start with building some paclets for our docs.
I've talked about paclets before on a number of occasions, so I won't go into depth on them now, but if you want a refresher you can look here .
When we build our documentation paclets, we'll want to make them to have four properties:
They are obviously documentation, not code
They do not interfere or interact with the package they document in anyway
They are modularized as much as possible
They can be easily updated and versioned
The last one is easy using the paclet manager and a paclet server. The second to last can be done easily if packages are well partitioned into subcontexts. The first and second, however, are a little bit trickier.
If we have a paclet call "MyPaclet" and we wanted to distribute its documentation separately, we couldn't simply call the documentation paclet "MyPaclet" as well. Instead, we'll follow suit with what WRI does for many of its subpaclets, such as "ServiceConnections" and curated data and append a qualifier to the paclet name. So instead of "MyPaclet" we'll call it "Documentation_MyPaclet" to make it obvious where it comes from.
The issue with this is that it breaks our simple lookup procedure, but happily it's simple enough to fix this. In the "Documentation" extension to a Paclet expression we find the option "LinkBase" . This specifies what the lookup-root for things in the paclet should be. For instance if there is a page at "Guides/MyPaclet" in our "Documentation_MyPaclet" paclet, by using "MyPaclet" as the "LinkBase" this page will resolve to "MyPaclet/guide/MyPaclet" on lookup, and so the documentation will behave as expected.
Overall, then, we'll have our Paclet expression look like:
Name -> "Documentation_MyPaclet",
Version -> "1.0.0",
"Language" -> "English",
"LinkBase" -> "MyPaclet",
"MainPage" -> "Guides/MyPaclet"
This is something I go over here as well.
With these documentation paclets in hand, we can go one step further and build a paclet server for our documentation (see the refresher link for paclets for a refresher on paclet servers, too). This will be an entirely generic paclet server, but it will serve as a way to easily share documentation in small chunks, just like curated data. I set up a server for all the documentation I've built here which looks like:
People can then install pieces of documentation from there, like:
And they'll be immediately ready to use in Mathematica.
One last thing to comment on is how we can take our documentation Notebooks and turn them into true HTML documentation which you can peruse on the web. This will involve taking some pieces out of Workbench (but which I've made accessible from a paclet server, so no worries if you don't want to download Workbench).
Workbench provides some facilities for generating HTML documentation. These facilities are (as of when I wrote this package) limited to 11.0-style documentation, but that's more than good enough for most things.
The main thing I needed to do was apply a thorough cleaning to the documentation pages I generated to make sure the finicky package that actually generates the HTML (called Transmogrify which is in turn called by a higher-level package called DocumentationBuild ) won't hang when it reaches a directive it doesn't know how to process.
After that, the main issue was simply making sure all the appropriate resources are deployed, and then we're good to go.
I've built this into DocGen as a method. So if you want to build out HTML documentation for a paclet or set of paclets you can do it like:
And this can be deployed to the web to use as documentation. If we want that we can simply run it with CloudDeploy->True and it will do so.
Alternately DocGen also support deploying built HTML automatically, which we can see in the "Methods" :
The best way to do this is passing the directory with all the HTML:
DocGen["HTML", PacletFind["Documentation_BTools*"], Method->"Deploy"];
We can take this a step further though, and build a wrapper around this type of functionality to get it to upload
This creates a site where we can browse all of the exposed documentation, much like the paclet server we had before:
And with that, I think, we're done.
A follow up to the previous post. Originally here.
Here's a quick example of how we can use some of the stuff I showed in this post to build out full documentation by hand.
This post will be using my DocGen system which is built into a large package of mine that supports other parts of the development process as well. This package lives in a paclet on my paclet server. You can install it with:
If you want to learn more about making paclet servers you can read more here .
We're going to make use of a palette I designed for helping with the documentation system. After you've installed BTools you can open it by looking for BTools▸DocGen in the palettes menu:
The entire palette looks like this:
If we look at it, it essentially has five sections:
The first of these chooses the paclet and lets us open the relevant config files. The second lets us open any built documentation pages for this context. The third actually generates the content and optionally deploys it to the cloud. The fourth will make documentation indices for pre version 11.2 and the last creates a new documentation paclet.
We start off by making a new paclet to hold our documentation. To do that we just use the button. It pops up a window:
And we simply provide the name of our paclet. The current project will then switch to "Templating" :
And we see the paclet info and config file have been built:
First we go to the "Context Pages" menu and select "Symbol Page Template"
"Symbol Page Template"
This opens up a notebook with templates for all the symbols in the context:
We can fill out the content for a page:
And then we use the docked cell on the template page to build out the HTML (with the bracket for the content we're generating selected):
This then builds a doc page for us:
Alternately we can do this from the palette using the "Generate and Save" menu to save the page to the current paclet:
"Generate and Save"
And then if we go to the "Open Symbol Page" menu we see we've generated and saved the page:
"Open Symbol Page"
Alternately, if we opted not to select a cell bracket the package will generate a page for every template in the InputNotebook . Or if we had selected multiple brackets pages would be generated for each. This gives us a way to easily update our documentation from a saved template. Here's what we get if we did it with no bracket selected:
The DocGen palette doesn't directly support deploying this paclet to a server, but we can use the Paclet Server Manager palette that also comes with BTools to do that. I won't go into the details, though, as they're documented here .
We can also use these template to build HTML documentation. To do this without deployment we can simply select the "Symbol Web Pages" action from the "Generate from Notebook" menu (again with the bracket selected):
"Symbol Web Pages"
"Generate from Notebook"
This builds to a temporary directory (it also mirrors the display assets it needs the first time it's generated). The Cloud Deploy option comes into play when using the "Generate and Save" menu.
After this copies every thing and generates the HTML we end up with a page that looks like:
Which of course looks like the 11.0 docs, but until a new version of "Transmogrify" and "DocumentationBuild" come out this is what we're restricted to. That, unfortunately, is outside of my control so we'll stop at this.