Tuesday, March 27, 2012

Pretty Dartdoc

‹prev | My Chain | next›

I was eventually able to get my dartdoc for Hipster MVC published to GitHub pages:


This is a bit of a pain to accomplish. In my master branch, I dartdoc Hipster MVC into the default docs directory. I use .gitignore to ignore that directory both in master and in the gh-pages branch. Then I switch to the gh-pages branch and copy the generated contents of docs into the top-level of the branch:
➜  hipster-mvc git:(master) dart ~/src/dart-sdk/lib/dartdoc/dartdoc.dart main.dart
Documented 11 libraries, 677 types, and 5544 members.
➜  hipster-mvc git:(master) git co gh-pages
Switched to branch 'gh-pages'
➜  hipster-mvc git:(gh-pages) cp -r docs/* .
➜  hipster-mvc git:(gh-pages) ✗ gst
# On branch gh-pages
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   index.html
#
no changes added to commit (use "git add" and/or "git commit -a")
It would be nice if the wiki tab in GitHub supported HTML (and if dartdoc didn't delete and recreate the target directory), but this will do for now.

There are a couple of problems with the generated documentation. First off, the documentation title:


And the page title as well:


The title should read "Hipster MVC Documentation", not "Dart Documentation".

Next, there are several unnecessary core Dart libraries included in the documentation:


If I want to lookup methods on Map, I will head straight for the online Dart API documentation. I will not recall that "I make use of that library in Hipster MVC so why not look there?" Besides, it clutters things up.

Lastly, and somewhat related to the previous point, the sidebar navigation also includes references to the core Dart libraries:


This is different than the inline documentation because the sidebar navigation is not hard-coded on every page of dartdoc. Rather it is defined in a nav.json file which is loaded over Ajax. This saves space (the sidebar contains a lot of redundant information) and lets dartdoc dynamically open sub-menus.

One other nice-to-have with the documentation is a place to stick a small blurb about the project (including a link back to the main GitHub repository).

To verify that I my understanding of nav.json is correct, I hand-edit it, removing the "core", "coreimpl", "html", etc. entries. For good measure, I also hand-edit index.html removing the same information. I then publish to GitHub and... I now have just the Hipster docs:


That is all well and good, but I am not going to hand edit my documentation every time I make a code change. I need to either post-process the generated documentation or modify the code that generates it. Either way I need do it in Dart—this is a Dart chain after all. In the end, I opt to modify the dartdoc code in-place. I have the feeling that I will learn more Dart by working with someone else's code.

Looking through the dart-sdk's lib/dartdoc/dartdoc.dart, it seems that most of the heavy lifting is done in Dartdoc#document(). In there, a list of sorted files is pulled from the frogc compiler's "world":
// ...
      world.resolveAll();

      // Sort the libraries by name (not key).
      _sortedLibraries = world.libraries.getValues();
// ...
If I change that to be the list of values with a name that contains "hipster":
//  ...
      // Sort the libraries by name (not key).
      _sortedLibraries = world.
        libraries.
        getValues().
        filter((library) {
          return library.name.contains('hipster');
        });
// ...
Then regenerating and pushing to GitHub pages, I get auto-generated API docs for just Hipster MVC:


The mainTitle is already an instance variable on Dartdoc (meaning there is an instance getter). I do the same for contains so that I do not have to hard-code 'hipster':
/** Only generate documentation for libraries whose names contains this */
  Pattern contains = '';
Then I add some additional poor-man's command-line processing:
// ...
    switch (arg) {
      // ...
      default:
        if (arg.startsWith('--out=')) {
          outputDir = arg.substring('--out='.length);
        }
        else if (arg.startsWith('--title=')) {
          title = arg.substring('--title='.length);
        }
        else if (arg.startsWith('--contains=')) {
          contains = arg.substring('--contains='.length);
        }
        else {
          print('Unknown option: $arg');
          return;
        }
        break;
    }
// ...
And use them to drive Dartdoc:
// ...
  if (title != null) dartdoc.mainTitle = title;
  if (contains != null) dartdoc.contains = contains;
// ...
Now, I can pass the title and a string that all libraries should match:
dart ~/src/dart-sdk/lib/dartdoc/dartdoc.dart \
   --contains=hipster \
   --title='Hipster MVC Documentation' \
   main.dart
And now I have some pretty decent Hipster MVC documentation:


For good measure, I add a description command line switch and make Hipster MVC that much better.

For what it's worth, I have documented my changes to dartdoc in a github repository.


Day #338

No comments:

Post a Comment