Ducktype parser extensions

When designing Ducktype, I wanted people to be able to extend the syntax, but I wanted extensions to be declared and defined, so we don’t end up with something like the mess of Markdown flavors. So a Ducktype file can start with a @ducktype/ declaration that declares the version of the Ducktype syntax and any extensions in use. For example:

@ducktype/1.0 if/1.0

This declares that we’re using version 1.0 of the Ducktype syntax,that we want an extension called if, and that we want version 1.0 of that extension.

Up until last week, extensions were just theoretical. I’ve now added two extension points to the Ducktype parser, and I plan to add three or four more. Both of these are exercised in the _test extension, which is fairly well commented so you can learn from it.

Let’s look at the extensions we have, plus the ones I plan to add.

Block line parser

This extension is implemented. It allows extensions to handle really any sort of line in block context, adding any sort of new syntax. Extensions only get to access to lines after headings, comments, fences, and a few other things are handled. This is a limitation, but it’s one that makes writing extensions much easier.

Let’s look at an actual example that uses this extension: Mallard Conditionals. You can use Mallard Conditionals in Ducktype just fine without any syntax extension. Just declare the namespace and use the elements like any other block element:

@namespace if

= Conditional Example

[if:if test=target:html]
  This is a paragraph only shown in HTML.

But with the if/1.0 Ducktype syntax extension, we can skip the namespace declaration and use a shorthand for tests:

@ducktype/1.0 if/1.0

= Conditional Example

? target:html
  This is a paragraph only shown in HTML.

We even have special syntax for branching with if:choose elements:

@ducktype/1.0 if/1.0

= Conditional Branching Example

  ? platform:fedora
    This paragraph is only shown on Fedora.
  ? platform:ubuntu
    This paragraph is only shown on Ubuntu.
    This paragraph is shown on any other operating system.

(As of right now, you actually have to use if/experimental instead of if/1.0. But that extension is pretty solid, so I’ll change it to if/1.0 along with the 1.0 release of the parser.)

Directive handler

Ducktype files can have parser directives at the top. We’ve just seen the @namespace parser directive to declare a namespace. There is an implemented extension point for extensions to handle parser directives, but not yet a real-world extension that uses it.

Extensions only get to handle directives with a prefix matching the extension name. For example, the _test extension only gets to see directives that look like @_test:foo.

Block element handler

This extension is not yet implemented. I want extensions to be able to handle standard-looking block declarations with a prefix. For example, I want the _test extension to be able to do something with a block declaration that looks like this:


In principle, you could handle this with the current block line parser extension point, but you’d have to handle parsing the block declaration by yourself, and it might span multiple lines. That’s not ideal.

Importantly, I want both block line parsers and block element handlers to be able to register themselves to handle future lines, so they can have special syntax in following lines. Here is how an extension for CSV-formatted tables might look:

@ducktype/1.0 csv/1.0

= CSV Table Example

[csv:table frame=all rules=rows]
one, two, three
eins, zwei, drei
uno, dos, tres

Inline element handler

This extension is not yet implemented. Similar to block element handlers, I want extensions to be able to handle standard-looking inline markup. For example, I want the _test extension to be able to do something with inline markup that looks like this:

$_test:foo(here is the content)

For example, a gnome extension could make links to GitLab issue reports easier:


Inline text parser

This extension is not yet implemented. I also want extensions to be able to handle arbitrary inline markup extensions, things that don’t even look like regular Ducktype markup. This is what you would need to create Markdown-like inline markup like *emphasis* and `monospace`.

This extension might have to come in two flavors: before standard parsing and after. And it may be tricky because you want each extension to get a crack at whatever text content was output by other extensions, except you probably also want extensions to be able to block further parsing in some cases.

All in all, I’m really happy with the Ducktype syntax and parser, and how easy it’s been to write extension points so far.

Sidebars in yelp-xsl 3.30

It’s now easier to add sidebars to HTML output in yelp-xsl 3.30. When I finally landed the HTML modernization changes in 3.28, I added the ability to have sidebars without completely mucking up the layout. There’s a main element that’s a horizontal flexbox, and you could implement html.sidebar.custom to put stuff in main. But you’d have to do sizing and styling yourself, and you’d have to implement all the stuff you want in there.

I wanted to make sidebars easy in Pintail, so I started implementing some stock sidebars there. Then I realized I could move most of that work into yelp-xsl.

With 3.30, you can now set what you want to see in the sidebars using the html.sidebar.left and html.sidebar.right parameters. These are both space-separated lists of words, where each word is a sidebar component. In yelp-xsl, we have two components out of the box: contents to give you a table of contents for the whole document, and sections to give you a list of sections on the current page. You can also use the special blank token to force a sidebar to appear without actually adding anything to it.

More importantly, you can add your own components. So you can still make fully custom sidebar content while letting yelp-xsl do the rest of the work. For example, let’s say you wanted a left sidebar with a table of contents followed by a Google ad. You could set html.sidebar.left to "contents googlead". Then add a template to your extension stylesheet like this:

     match="token[. = 'googlead']">
  <!-- Put Google's stuff here -->

Pintail will add further sidebar components, such as one to switch between versions of a document, a language selector, and a search bar.

If you use yelp-build, you can pass the extension stylesheet with -x. The extension styleshet is also where you’d set the parameters when using yelp-build.

With Pintail, you’ll be able to set the parameters in your pintail.cfg file.

sidebar_left = contents
sidebar_right = search languages

What’s more, you’ll be able to set these on a per-directory basis. So, for example, on nightly builds you could add a warning message. In your extension stylesheet:

     match="token[. = 'nightly']">
  <p style="color: red">NIGHTLY BUILD!</p>

Then your pintail.cfg would look like this:

sidebar_left = contents
sidebar_right = search languages

sidebar_left = contents nightly

Math Tricks for Kilograms and Pounds

I’m going to share some math tricks for converting between kilograms and pounds, something I often deal with when weightlifting. This post is long, but if you stick around to the end, I’ll share the super secret divisibility rule for 11. (Originally posted to Facebook. Reposted on my blog at Jim Campbell’s suggestion.)

If you find a mathematician or engineer who’s good at doing math in their head and ask them how they do it, you’ll find they have a handful of techniques they apply in different situations. Many of these techniques involve turning problems into things we’re already good at. What are we good at? Well, multiplying and dividing by 10 is trivial. It’s just moving a decimal point. And most of us are pretty good at doubling and halving things. So, multiplying and dividing by 10 and 2 are sweet spots.

1kg is approximately 2.2lb. (If you need better precision, use a computer.) So to convert kilograms to pounds, we have to multiply by 2.2. Let’s pull out that distributive law. 2.2x = (2 + 0.2)x = 2x + 0.2x = 2x + (2x)/10. We’ve reduced the problem to multiplication by 2 and division by 10.

“Woah Shaun, stop with the algebra!” OK. Take the kilograms. Double it. Take that double, shift the decimal place. Add the double and the decimal-shifted double. For example, take 150kg, a respectable squat weight. Double it = 300. Divide by 10 = 30. Add these two together = 330lb. (Google will tell you the answer is 330.693lb.)

But what about converting pounds back to kilograms? Do we have to divide by 2.2? That sounds awful. But division by 2.2 is multiplication by 5/11. Does that make it any better? Yes! Really? YES! Division by 11 is awesome, and for some reason, nobody learns it in school.

To divide by 11, first divide by 10. That’s your current total. Divide that by 10 and subtract the result from the current total. That’s your new current total. Divide that by 10 and add it to the current total. That’s your new current total. Continue dividing by 10 and alternating addition and subtraction. Do this until you die of exhaustion, you see the two-digit repeating pattern, or you’re happy with the precision. Recall that 2.2 was only an approximate conversion to begin with, so I stop when all the action is after the decimal point. Round whole numbers are good enough for me.

But we needed to multiply by 5/11, not just 1/11. No worries. To multiply by x/11, instead of starting with 1/10, start with x/10. Luckily, 5/10 is just 1/2. We like dividing by 2.

My body weight is about 190lb. What is that in kilograms? Half of 190 is 95. Divide by 10 for 9.5. Subtract 9.5 from 95 for 85.5. Divide 9.5 by 10 for almost one. Addition is next, so let’s call it 86kg. (Google will tell you the answer is 86.1826kg.)

So there you go. Quick tricks to help you get approximate conversions between kilograms and pounds in your head.

But what about the super secret divisibility rule for 11 I promised? It follows the same pattern as the technique I gave for dividing by 11. Just do alternating addition and subtraction on the digits of a number. If the result is divisible by 11, so is the original number. Is 1936 divisible by 11? 1-9+3-6 = -11. It sure is.

Eleven is awesome.

*tap* *tap* *tap* testing testing *tap* *tap* *tap*

Still here? Cool. Here’s a bonus tip that wasn’t in my original post. All that stuff about 11? It works the same way for whatever number “11” happens to represent in any radix. Need to divide by 11_8 (decimal 9) in octal? Same division algorithm. Need to check divisibility by 11_16 (decimal 17) in hexadecimal? Same division rule. Looking for some fun weekend math? Take a look at the divisibility rules you know, figure out why they work, and use that to figure out what divisibility rules you have in other bases. Hint: every divisibility rule I can name stems from three basic kinds of tests.

Restricted Funds in Non-Profit Accounting

I’ve served as treasurer for three separate organizations over the last six years. Two of them are US 501(c)(3) non-profit organizations. The other is a consumer-owned cooperative. I’m not an accountant, but I’ve learned a lot about accounting, and each organization has forced me to learn something new.

Today’s adventure is learning how to deal with restricted funds, or funds that have to be used for a particular purpose. I’m going to show four different techniques for dealing with restricted funds, along with some pros and cons.

Restricted funds are conceptually similar to earmarked funds and fiscal sponsorships. In all cases, you have a specific amount of money that is supposed to be used for a specific purpose. I’ll make the following distinction: Both restricted funds and earmarked funds are short-term situations where you expect to spend the money, zero the balance, and stop looking at it. When I talk about restricted funds, I mean funds where you are legally obligated to spend and account for every penny, and pay back any money you did not spend. This could be a grant where the granting organization places requirements on the funds. When I talk about earmarked funds, I mean funds that you have designated for a particular purpose, but where you’re not legally obligated to spend it all, and you could roll a small leftover balance into general funds. This could be money from a targeted fundraising campaign.

Fiscal sponsorships, however, are long-term arrangements where you handle the money for a group of people doing something in line with your charitable cause. They officially operate under your organization so they don’t have to deal with all the non-profit paperwork, but they have a certain amount of autonomy, and importantly, their funds are theirs.

I’ll use the term targeted funds to refer to all three. The following techniques all conceivably work for each type of targeted funds, with different pros and cons on each. None of these techniques are, in my opinion, perfect. Also, by the way, I have to assume you have a passing familiarity with double-entry accounting and split transactions, among other things. Explaining those is another blog post. Onward…

Asset Subaccount

With this technique, you create a subaccount of your primary asset account (probably a checking account), and post all transactions for the targeted funds to that account. This has the advantage of letting you use expense and income accounts however you want, and it might be OK if you have very small and very simple transactions.

I don’t like this technique, though, for a number of reasons. It can lead to really crazy split transactions. Split transactions where money comes from separate income accounts are fine. Split transactions where money goes to multiple asset accounts are weird. Split transactions where both happen hurt my brain.

This really falls apart when you have multiple very liquid asset accounts. By that I mean accounts you regularly make payments from. This could be a checking account, a PayPal account with money in it, a prepaid postage account for bulk mailings, or a prepaid purchase card at a store. (I’ve dealt with all of these.) Or you might have liability accounts you regularly expense from, like a credit card or an expense account at a store.

If your targeted funds are a subaccount of your primary checking, what happens when you use another account to pay with those funds? If you used a credit card, you probably need to use a split transaction when you make your monthly payment. I hope you remember how much. You’d better look through your transaction report. I don’t even know what to do if you carry a balance on the card. And if you paid with PayPal or another asset account, you should really transfer money from checking to cover it, and record that as coming out of the subaccount. What a pain. The easier approach is probably just to use a journal entry to move money from the targeted subaccount to its parent. But that basically means your books will be littered with fake adjustment entries.

Also, there’s a big question mark around how well your accounting tools deals with transactions in a subaccount of a checking account when doing things like reconciliation or automatic transaction importing. My advice is that asset accounts on your books should match real accounts. This technique is just a hack.

Actual Separate Account

If accounts in your books should always match real accounts, why not create an actual separate checking account for the targeted funds? This technique involves quite a bit of overhead, and it’s not something you’re going to do for short-term restricted or earmarked funds. But it could work for fiscal sponsors, with the added benefit that you could even allow your sponsored organization access to that account.

This at least ensures that accounts are accounts and transactions are transactions, but it can still be tricky if you want to spend those targeted funds with other payment methods like a prepaid account or a credit card. If you do that, you’ll have to write an actual check (or make an actual bank transfer) to pay one part of your organization from another part. That’s a hassle, although it is sensible.

There’s also an issue of minimum balances. Your bank may insist on a minimum balance on all checking accounts. You probably don’t care about a minimum balance for your sponsored organizations. You might even be OK with them holding a zero balance. (In the case of restricted or earmarked funds, a zero balance is your goal.) You just might be OK with allowing sponsored funds to be temporarily negative. Banks don’t care for that, so you would have to spend money from your general account, and then record the money owed in some sort of weird entry that is both accounts payable and accounts receivable.

Liability Account

This is probably the technique your accountant will recommend, especially for legally restricted funds like restricted donations. A liability account is a type of account you use to record money you owe. You can make purchases with a liability account. You can pay into it to pay it off. A credit card, for example, is a liability account. A credit card purchase is a transaction involving a liability account and (usually) an expense account. A payment toward your credit card balance is a transaction involving a liability account and an asset account. Liability accounts can be other types of debts, or other types of money you have to spend, like taxes payable.

Money you have to spend. Like that big grant that has to be spent for a specific purpose. It’s strange to think about a grant as a liability, but from an accounting perspective, it fits.

This has the advantage of quickly showing you how much you need to spend and how much money you have in general funds, right on the balance sheet. No other technique makes those numbers quite as clear. The downside is that it’s a black box on an activity statement. You don’t get to use expense and income accounts for anything, and if you want to get any sense of how much you’ve actually spent, or how you’ve spent it, you have to dig into transaction reports. That’s doable, but I don’t enjoy it.

One other thing to consider with this technique is what to do with a not-quite-zero balance. If you overspend, you’ll need to do a split transaction with part of it coming from a general expense account. Otherwise you’ll spend the rest of your life with a weird negative liability on your balance sheet. And if it’s earmarked funds where you can roll a small remaining balance into general funds, make sure to post a transaction that moves that money from the liability account to an income account

If you deal with a very large number of restricted grants, and you need to be absolutely certain they all get spent entirely and in a timely fashion, you should strongly consider this approach. You should also talk to an actual accountant, preferably one with non-profit experience, instead of reading a blog by some guy on the internet.

Income and Expense Accounts

Finally, you can just create specific income and expense accounts to track money into and out of targeted funds, respectively. You can do this alongside the first two techniques involving separate asset accounts, but one of the advantages of those techniques is that you don’t absolutely have to use targeted income and expense accounts. With those techniques, you could record payments to a contractor in your general “Contractor Payments” expense account, if you want.

With this technique, you have to make absolutely certain that every transaction involving the targeted funds is posted to a targeted income or expense account. If you don’t, you will never be able to find the targeted fund balance, you’ll start treating that money as general funds, and some people will be very very upset with you. But frankly, with any technique, you need to be careful to file things correctly.

The biggest downside of this technique is that it tells you nothing on your balance sheet. Your balance sheet won’t tell you how much targeted funds you’ve spent, how much you still need to spend, or how much general funds you actually have after accounting for targeted funds. For some people, that will be a really big deal. If restricted grants or fiscal sponsorships constitute a very large portion of your finances, you probably want to see them on the balance sheet. If you handle targeted funds only rarely, and your general funds are comparatively large, it’s less of an issue.

The really nice thing about this technique is that you still get to see income and expenses in actual income and expense accounts. It shows up in your activity statement. It can show up in your budget report, if you want to budget it. You aren’t even limited to using a single account for expenses. You could have a parent expense account for the specific targeted fund, then subaccounts for things like contractor pay, material costs, etc.

Since this technique doesn’t show you anything on a balance sheet, you’ll need to use an activity statement to see the balance of any targeted funds. Grab an activity statement covering at least the entire period of time the targeted funds have been active (or just for all time), then subtract the targeted expense account from the targeted income account to see how much you have left to spend. Or much more simply, most accounting software lets you run an activity statement on a specific list of accounts. Run one on just your targeted accounts, and it will tell you the balance right at the bottom.

This might seem awkward, but it’s actually no different from what you probably do for non-targeted funds with matched income and expense accounts. For example, if you run a large event, you probably have one or more income categories specifically for that event, and one or more expense categories specifically for that event. If you want to see if the event made or lost money, you have to compare numbers in an activity statement, just like you would do to see the balance of targeted funds in this technique.

This technique has no problem with overspending. Just use the same expense categories and don’t bother with splits. You’ll just have higher expenses than income, just like in an event that lost money. It doesn’t show up on your balance sheet, and eventually it will roll off the activity statements you regularly look at. And if you underspend earmarked funds and intend to retain them in general funds, you can just literally do nothing. Just stop looking at the targeted fund activity statement and the money is in general funds. Or, if you want to be really pedantic, post a transaction that moves money from the targeted expense account to an income account like “Retained Earmarked Income”. That way you can go back and see how much earmarked funds you retain over time, if you’re into that.

This technique is actually the one I’ve decided to use for restricted grants in the organization I’m currently treasurer of. Each year, we deal with just a handful of restricted grants, six at the most. We also run exactly one fundraising event where we earmark the funds, and we retain a portion of those earmarked funds into general funds upfront. I have an income account called “Restricted Grants” and an expense account called “Restricted Grant Spending”. For each restricted grant, I create subaccounts of both the income and expense accounts. I can set up custom activity reports for each grant and put them in the pile with other reports that I review monthly. When a restricted grant is spent and accounted for, I just remove it from my regular reports.

Ideally, I’d like a system where I can record transactions as if in income and expense accounts, but somehow group those accounts in a way that makes their difference show up as a liability on the balance sheet. I don’t know if that’s a thing, but it should be.

Simple Pintail Queues with mkfifo

I build a number of sites with Pintail, including and Some of these sites I build and upload manually. Others are hooked up to continuous deployment. I’ve been using python-github-webhooks on my server, which is a very simple tool to receive GitHub notifications and do stuff in response. What I was doing in response was building sites with Pintail.

The problem with this approach is that GitHub wants endpoints to respond within 30 seconds. And although building Mallard with Pintail is fast, there are things you don’t want to block on. In particular, you don’t want network operations to hold things up. At the very least, building requires updating one git repository, and possibly more. The seemingly simple configuration pulls in two more git repositories. (Yes, it’s that easy.)

So I needed a job queue. I don’t want to just background the build tasks, because then I could end up starting a new build before a previous build finished, and down that path lies madness. I looked into using AMQP queues or using a full-blown CI tool like Buildbot. But I wanted something simple that didn’t involve a lot of new software on my servers. (Side note: I’m building a handful of relatively small sites with fairly low traffic. If you’re doing more, go use a tool like Buildbot and ignore the rest of this post.)

What I finally decided to do was to manage a simple build queue with mkfifo. I have a program that creates a FIFO and reads from it indefinitely, triggering builds when it receives data. Slightly stripped down version:


rm -f "$wdir/queue"
mkfifo "$wdir/queue"
chmod a+w "$wdir/queue"

while read repo <"$wdir/queue"; do
    if [ "x$repo" = "" ]; then
    # Other sites get elif statements here.
    if [ ! -d "$wdir/$repo" ]; then
        (cd "$wdir" && git clone "$git")
        (cd "$wdir/$repo" && git pull -r)

    outdir="$repo"-$(date +%Y-%m-%d)-$(uuidgen)
    mkdir -p "/var/www/$outdir"
    (cd "$wdir/$repo" &&
        LANG=en_US.utf-8 scl enable python33 -- pintail build -v -o "/var/www/$outdir" &&
        cd "/var/www" &&
        ln -sf "$outdir" "$repo".new &&
        mv -T "$repo".new "$repo"
    ) 2>&1 >> "$wdir/$repo"-log

Now the only thing my hook endpoints actually do is write a line to the FIFO. Importantly, the build process only looks for known strings in the FIFO, and ignores any other input. It doesn’t, for example, execute arbitrary commands placed in the FIFO. So the worst an attacker could do is trigger builds (potentially resulting in a DoS).

This script has one other trick: It uses symlinks to atomically update sites. The actual built site is in a unique directory named with the actual site name, the date, and a uuid. The actual directory pointed to by my httpd config files is a symlink. Overwriting a symlink with mv -T is an atomic operation, so your site is never half-updated or half-broken. This is a trick I learned at a previous employer, where it was very very important that our very very large documentation site was updated exactly as our release announcement went out.

Build documentation sites with Pintail

Lately I’ve been working on Pintail, a documentation site generator built on top of Mallard, Yelp, and the various other tools we’ve developed over the years. Pintail grew out of the tool that used to build from Mallard sources. But it’s grown a lot to be able to handle general documentation sites. I want GNOME to be able to use Pintail for its documentation site. I want other projects to be able to use it too.

One of the more compelling features, and something many documentation site generators don’t handle, is that Pintail can pull in different git repositories for different directories. Small projects can get away with having all their docs in one repository. Large projects like GNOME can’t. Here’s a snippet of what the configuration for might look like:

git_repository = git://
git_branch = master
git_directory = gnome-help/C/

Pintail’s native format is Mallard, but you can add in support for other formats pretty easily. There’s Docbook support, for example, and I’d like to add AsciiDoc support using asciidoctor-mallard.

There are two major features I hope to have ready soon. Both of them are available as Summer of Code projects. First, documentation sites obviously need search. But it’s not enough to just search the whole site. You want to be able to search within specific documents, or specific versions of documents. I’ve actually already got some indexing code in Pintail using Elasticsearch as a backend.

Second, Pintail needs to be able to handle localizations. I’ve put a lot of work into documentation internationalization over the years. It’s important, and everything I work on will continue to support it. I have some ideas on how this will work.

If you need to build a documentation site, give Pintail a try. I’m building a few sites with it already, but I’d love to get input from people with different needs.

Help me improve Yelp’s conditional processing

Yelp has runtime conditional processing for both Mallard and DocBook, so you can show different content to users in different environments. For example:

<p if:test="platform:gnome-classic">We only show this in classic mode.</p>
<p if:test="platform:unity">We only show this in Unity.</p>
<p if:test="platform:fedora">We only show this in Fedora.</p>
<p if:test="platform:fedora-22">We only show this in Fedora 22.</p>

Read more about Yelp’s runtime conditional info and Mallard’s conditional tokens. To my knowledge, no other help system does this kind of automatic runtime conditional processing. After some conversations with Endless folks at GUADEC, I realized we’re still missing some cases. I want to make this better.

I’ve put together a short three-question survey. Please fill it out with information from each different kind of machine you have access to.

Builder Terminal Annoyance Workaround

If you’re running GNOME Builder with an unpatched vte, you may have noticed the annoying notification control sequence in Builder’s embedded terminal. Christian Hergert told me there’s a vte patch that fixes this, but it hasn’t been merged upstream. He also told me the workaround is to unset PROMPT_COMMAND. I’m too lazy to do this every time I open Builder, so this is what’s now in my .bashrc file:

parent=$(ps -ocommand= -p $PPID | awk -F/ '{print $NF}' | awk '{print $1}')
if [ "x$parent" = "xgnome-builder" ]; then

Come to Open Help. Have a Doc Sprint.

The Open Help Conference & Sprints is happening again this year. Open Help is the only event focused on open source documentation and support. It features a two-day conference with a mix of presentations, demos, and open discussions. This format has proven very popular, and really helps people find real solutions to improve their documentation. The conference this year is September 26-27. (But don’t miss the amazing reception September 25!)

After the conference, Open Help hosts three days of multiple-team sprints. The sprints are September 28-30 this year. Open Help has hosted doc sprints for teams like GNOME, Mozilla, FreeBSD, Wikipedia, WordPress, OpenMRS, and In many cases, Open Help was a team’s first exposure to having a sprint.

I wrote an article on about the Open Help doc sprints, along with five tips on holding a successful sprint.

Documentation is important. This is the fifth year that Open Help will be helping open source teams create better documentation. But we need your help. Please spread the word about Open Help to any communities you’re involved with. Send mail to mailing lists. Tweet. Tell your friends. Get the word out however you can.

Open Help is only great because of the people who come.

Mallard Documentation Sites With Pintail

When we first designed Mallard, we designed it around creating documents: non-linear collections of pages about a particular subject. Documents are manageable and maintainable, and we’re able to define all of Mallard’s automatic linking within the confines of a document.

If you wanted to publish a set of Mallard documents on the web, you could build each of them individually with a tool like yelp-build, then output some extra navigation pages to help people find the right document. But there was no simple way to create those extra pages. What’s more, you couldn’t link between documents except by using external href links. Mallard’s automatic links are confined to documents.

Enter Pintail. Pintail lets you build entire web sites from Mallard sources. Just lay out your pages in the directory structure you like, and let Pintail build the site for you. Put full Mallard documents in their own directories, then use Mallard to create the extra navigation pages between them. Better still, you can use an extended xref syntax to refer to pages in other directories. Just include the path to the target page with slashes, like so:

<link xref="/about/learn/svg"/>

This isn’t just a simple link. You can use this in topic links and seealso links and anywhere else that Mallard lets you put an xref attribute. Pintail makes Mallard’s automatic linking work across multiple documents.

Pintail is designed to allow other formats to be used, so you could use it to build all your documentation in an environment where not everything is in one format. It already supports Mallard Ducktype as well as XML. But Mallard is the primary format.

One of the really nice features is that it can pull it documents in other git repositories, so you don’t have to keep all your documentation in a single source tree. In fact, the site in your main repository might be little more than glue pages and the pintail.cfg file that specifies where all the actual documentation lives.

Pintail builds the web site right now, as well as a few other random sites I maintain. I hope it turns out to be useful for heavy Mallard users like GNOME, Ubuntu, and Endless. And I hope it makes Mallard easier for others who are considering using it.

No software is ever finished, but here are some of the top things I plan to add soon:

  •  Page merging: Mallard allows pages to be dropped into a document and seamlessly integrated into the navigation. Sometimes you want to publish a document with pages pulled from other places. For example, GNOME generally wants to publish GNOME Help with the optional Getting Started video pages merged in.
  • Translations: Mallard was designed from day one to be translator-friendly, and itstool ships with ITS rules for Mallard. I just need to hook the pieces together.
  • Search: An extensive documentation site needs configurable search. You often want to restrict search within a single document. Also, some documents (or versions of documents) shouldn’t appear in global search results.

What would you like to see a Mallard site tool do?