I have a personal goal of getting top-notch docs CI in place for GNOME 40, and part of that is having checks for merge requests. We have a number of great checks already in yelp-check, like checking for broken links and media references, but for large documents sets, you want to be able to enforce your own rules for style and consistency. The most common tool for this is Schematron, and in fact we do have a Schematron file in gnome-help. But it has a lot of boilerplate, most people don’t know how to write it, and the command to run it is unpleasant.

I’ve been rewriting yelp-check in Python. This has made it easy to implement things that were just not fun with /bin/sh. (It’s also way faster, which is great.) Today I added the ability to define custom checkers in a config file. These checkers use the same idea as Schematron: assert something with XPath and bomb if it’s false. But they’re way easier to write. From my commit message, here’s one of the checks from the gnome-help Schematron file, reworked as a yelp-check checker:

[namespaces]
mal = http://projectmallard.org/1.0/

[check:gnome-desc]
select = /mal:page/mal:info
assert = normalize-space(mal:desc) != ''
message = Must have non-empty desc

With the new yelp-check (as yet not released), you’ll be able to just run this command:

yelp-check gnome-desc

All of the custom checkers show up the the yelp-check usage blurb, alongside the builtin checks, so you can easily see what’s available.

The next steps are to let you define sets of commands in the config file, so you can define what’s important for you on merge requests, and then to provide some boilerplate GitLab CI YAML so all docs contributions get checked automatically.

I spent a lot of time making Ducktype into a lightweight syntax that I would really enjoy using. I had a list of design goals, and I feel like I hit them pretty well. In this post, I want to outline some of the things I hope you’ll love about the syntax.

1. Ducktype has a spec

I know not everybody nerds out over reading a spec the way I do. But whether or not you like reading them, specs are important for setting expectations and ensuring interoperable implementations. Probably my biggest gripe with Markdown is that there are just so many different flavors. Couple that with the multiple wiki flavors I regularly deal with, and my days become a game of guess-and-check.

Even in languages that haven’t seen flavor proliferation, you can still run into issues with new versions of the language. In a sense, every version of a parser that adds features creates a new flavor. Without a way to specify the version or do any sort of introspection or conditionals, you can run into situations where what you type is silently formatted very differently on different systems.

In Ducktype, you can declare the language version and any extensions (more on those later) right at the top of the file.

@ducktype/1.0
= Header

This is a paragraph.

2. Ducktype keeps semantics

I’ve been a strong proponent of semantic markup for a long time, since I started working with LaTeX over 20 years ago. Presentational markup has short-term gains in ease-of-use, but the long-term benefits of semantic markup are clear. Not only can you more easily adapt presentation over time, but you can do a lot with semantic markup other than display it. Semantic markup is real data.

Different lightweight languages support semantic markup to different degrees. Markdown is entirely presentational, and if you want to do anything even remotely sophisticated, you have to break out to HTML+CSS. AsciiDoc and reStructuredText both give you some semantic markup at the block level, and give you the tools to create inline semantic markup, but out of the box they still encourage doing semantic-free bold, italic, and monospace.

My goal was to completely capture the semantics of Mallard with less markup, not to just create yet another syntax. Ducktype does that. What I found along the way is that it can fairly nicely capture the semantics of other formats like DocBook too, but that’s a story for another day.

Ducktype uses a square bracket syntax to introduce semantic block elements, inspired by the group syntax in key files. So if you want a note, you type:

[note]
This text is an implicit paragraph in the note.

If you want a plain old bullet list, you type it just like in any other lightweight syntax:

* First item
* Second item
* Third item

But if you want a semantic steps list, you add the block declaration:

[steps]
* First step
* Second step
* Third step

Ducktype keeps the semantics in inline markup too. Rather than try to introduce special characters for each semantic inline element, Ducktype just lets you use the element name, but with a syntax that’s much less verbose than XML. For example:

Click $gui(File).
Call the $code(frobnicate) function.
Name the file $file(index.duck).

Both AsciiDoc and reStructuredText let you do something similar, although sometimes you have to cook up the elements yourself. With Ducktype, you just automatically get everything Mallard can do. You can even include attributes:

Press the $key[xref=superkey](Super) key.
Call the $code[style=function](frobnicate) function.

3. Ducktype keeps metadata

In designing Ducktype, it was absolutely critical that we have a consistent way to provide all metadata. Not only is metadata important for the maintenance of large document sets, but linking metadata is how documents get structure in Mallard. Without metadata, you can’t really write more than a single page.

You can do very good page-level metadata in both AsciiDoc and reStructuredText, but for Mallard we need to do metadata at the section and even block levels as well. Markdown, once again, is the poor format here with no support for any metadata at all. Some tools support a YAML header in Markdown, which would be pretty great if it were any sort of standard.

Ducktype uses a single, consistent syntax for metadata, always referencing the element name, and coming after the page header, section header, or block declaration that it’s providing info for.

= My First Topic
@link[type=guide xref=index]
@desc This is the first topic page I ever wrote.

== My First Section
@keywords first section, initial section

Do you like my first topic page with a section?

We can even do this with block elements:

[listing]
  @credit
    @name Shaun McCance
  . A code listing by Shaun
  [code]
    here_is_some_code()

There’s a lot going on in that little example, from nesting to shorthand block titles. The important part is that we can provide metadata for the code listing.

(Side note: I thought a lot about a shorthand for credits, and I have ideas on an extension to provide one. But in the end, I decided that the core syntax should have less magic. Explicit is better than implicit.)

4. Ducktype allows nesting

I do quite a bit of semi-manual format conversion fairly frequently. It’s not something I enjoy, and it’s not the best use of my time, but it just keep having to be done, and I’m kind of good at it. If you do that a lot, you’ll often run into cases where something just can’t be represented in another format, and that usually comes down to nesting. Can you put lists inside table cells? Can you put tables inside list items? Can lists nest? Can list items have multiple paragraphs?

Both reStructuredText and (shockingly) most flavors of Markdown allow some amount of nesting using indentation. This sometimes breaks down when tables or code blocks are involved, but it’s not terrible. This is the one place where AsciiDoc is my least favorite. In fact, this is my single least favorite thing about AsciiDoc. It uses a plus on its own line to indicate a continuation. It’s hard to visualize, and it has severely limited functionality.

Ducktype very explicitly allows nesting with indentation, whether you’re nesting lists, tables, code blocks, notes, or anything else. It allows you to skip some indentation in some common cases, but you can always add indentation to stay in a block. Ducktype specifies the exact nesting behavior.

[note style=important]
  This is very important note about these three things:

  * The first thing is hard to explain.

    It takes two paragraphs.

  * The second thing can really be broken in two:

    * First subthing
    * Second subthing

  * The third thing involves some code:

    [code]
      here_is_some_code()

You can use any number of spaces you like. I prefer two, in part because it fits in with the shorthand syntax for things like lists. Using indentation and block declarations, there is absolutely no limit on what or how deeply you can nest.

5. Ducktype tables are sane

I have never met a lightweight table syntax I liked. Trying to represent real tables with some sort of pseudo-ascii-art works ok for very simple tables, but it falls apart real fast with anything remotely non-trivial. Add to this the extra syntax required for for all the non-trivial stuff, and a substantial portion of your parser is devoted to tables. As a user, I have a hard time keeping all those workarounds in my head.

As I said above, the ability to nest things was very important, so most of these tables syntaxes were just a non-starter. How do you add extra blocks in a table cell when the whole table row is expected to fit on one line? In the end, I decided not to have a table syntax. Or more accurately, to treat tables as a sort of list.

Without any special treatment, tables, rows, and columns can already be written in Ducktype using the block declarations you’ve already seen:

[table]
  [tr]
    [td]
      One
    [td]
      Two
  [tr]
    [td]
      Three
    [td]
      Four

There’s no magic happening here. This is just standard block nesting. But it’s pretty verbose, not as verbose as XML, but still. Can’t we make it a bit easier, like we do for lists? Well yes, we make it easier exactly like we do for lists.

[table]
[tr]
* One
* Two
[tr]
* Three
* Four

There are a few things going on here. Most importantly, the asterisk is shorthand for a table cell, not a list item. What that shorthand does depends on context. But also, we’ve removed quite a lot of indentation. Earlier I wrote that there are some special rules that allow you to trim indentation in certain common cases. This is one of them.

Using a vertical table syntax means that you can do the same kind of nesting you can do with other elements. Here’s a list in a table cell:

[table]
[tr]
* Here's a list:
  * First
  * Second
* Another table cell

Ducktype isn’t the first format to allow a vertical table syntax. MediaWiki allows tables to be written vertically, and it works really well. But Ducktype is the only one to my knowledge not to introduce any new syntactical constructs at all. I have a hard time keeping all those dots and dashes in my head. Using a single, consistent syntax makes it easier to remember, and it reduces the number of special rules you need.

6. Ducktype supports extensions

I’ve mentioned extensions a couple of times already in this post, but I haven’t elaborated on them. In addition to being able to use Mallard extensions (which you can do without any extra syntax), I wanted  a way to add and experiment with syntax without shoving everything in the core. And, importantly, I wanted pages to have to declare what extensions they use, so we don’t have a guess-and-check mess of incompatible flavors. I live in a world where files are frequently cherry-picked and pushed through different tool chains. Being explicit is important to me.

So in Ducktype, you declare your extensions at the top, just like you do with the version of Ducktype you’re using:

@ducktype/1.0 if/experimental
= Page with Experimental Extension

What kinds of things can an extension do? Well, according to the spec, extensions can do literally anything. Realistically, what extensions can do depends on the extension points in the implementation. The reference implementation has two rough classes of extensions. There are the methods in the ParserExtension class, which let you plug in at the line level to affect how things are parsed. And there’s the NodeFactory, which lets you affect how parsed things are interpreted.

All of the ParserExtension methods are implemented and pretty well commented in the _test extension. One interesting use of this is the experimental csv extension. Remember how tables are always written vertically with a list-like syntax. That’s very powerful, but sometimes you really do just want something super simple. The csv extension lets you do simple comma-separated tables, like this:

@ducktype/1.0 csv/experimental
= CSV Table Example
[csv:table]
one,two,three
four,five,six
seven,eight,nine

That’s in about 40 lines of extension code. Another example of line parser extensions is a special syntax for conditionals, which I blogged about before.

The NodeFactory extension point, on the other hand, lets you control what the various bits of special syntax do. If you want to want to use Ducktype to write DocBook, you would use DocBook element names in explicit block declarations and inline notation, but what about the elements that get implicitly created for paragraphs, headers, list items, etc? That’s what NodeFactory does.

Et cetera

That’s six things to love about Ducktype. I’ve been really happy with Mallard’s unique approach to structuring docs. I hope Ducktype can make the power of Mallard available without all the extra typing that comes along with XML. If you want to learn more, get in touch.

 

 

 

Ducktype 1.0

2019-04-13

Earlier this week, I released the final specification for Ducktype 1.0. If you’d like a quick primer on what Ducktype looks like, check out our Learn Ducktype tutorial.

Ducktype is a lightweight syntax designed to be able to do everything Mallard can do. It doesn’t sacrifice semantics. It doesn’t sacrifice metadata. It doesn’t sacrifice nesting and content model.

It took a long time to reach 1.0, because I take compatibility and correctness very seriously. Syntax is API. I work with multiple documentation formats on an almost daily basis. It is frustrating to constantly run into language quirks, especially quirks that vary between implementations. I realize quirks tend to accumulate over time, but I think we’ve prevented  a lot of them by writing an actual specification, developing a regression test suite, and focusing on the cohesiveness of the language.

I’m really happy with how it’s turned out so far. I hope you enjoy it too.

We’ve just released Mallard 1.1. Let’s take a look at what’s new. All of these features are already supported in tools like Yelp and Pintail.

This is part 3 in a 3-part series. Read part 1 and part 2.

Keywords

MEP-0014: Informational keywords element

One of the most visible additions in Mallard 1.1 is the introduction of the keywords element. If you look through a lot of real-world documents, you’ll find some that stuff synonyms into the desc element just to match things that users search for. This is not ideal.

In Mallard 1.1, we’ve introduced the keywords element to aid search systems built into tools like Yelp and Pintail. We decided to keep the element simple, using just a comma-separated list of terms instead of any nested keyword elements.

External info links

MEP-0007: External Info Links

Mallard features a number of types of automatic links, like seealso links. These links tend to automatically link in both directions, hence why we call them automatic links. The exact way they automatically link back depends on the link type. They all use informational link elements with an xref attribute, and the link text for these links is taken from informational elements like title and desc of the target node.

Since automatic links use the xref attribute, they were only able to link to pages within the same document, not to anything on the web. This makes some sense. After all, we can’t very well make random web pages automatically link back to our page. But sometimes you really do just want to have a seealso link to an external web page, even if it can’t automatically link back.

In Mallard 1.1, you can use the href attribute on informational links. Exactly how this works depends on the link type, but in general for things like seealso links, the link will appear in the list with your internal links. Because we can’t reliably fetch external resources for the link text every time we build a document, the informational link element can now have a title and desc element to specify the title and desc of the target.

Inline highlights

MEP-0009: Inline Highlight Element

Another highly visible change is the new hi element. This element can be used to highlight text that you’ve added or changed when progressively building up code examples. You can see it in action in the Mallard Ten Minute Tour.

In fact, we’ve been using this element (in the experimental namespace) in the Ten Minute Tour and other places since before Mallard 1.0 was even released. It was (I think) the first thing we added to the experimental namespace, and certainly the longest element in continual use without being in an actual spec. I wasn’t sure back then if we really wanted to add it, because it was unlike any inline element in any other semantic format. Years and years of usage have proven it’s worth having.

You can also use the ins and del style hints when including a diff in a Mallard page. These will do what you expect: green background, and red background with a strikethrough, respectively. Also, the yelp-xsl stylesheets let you use any of red, orange, yellow, green, blue, or purple as style hints to set the background color. Have fun.

Linkable sequences

Mallard features ubiquitous linking, meaning that any of the three linking attribute can be used on any inline element, so you don’t have to use two separate element to make a function name be both code and a link. Well, almost any inline element. In Mallard 1.0, you couldn’t put linking attributes on the guiseq and keyseq elements. I have completely forgotten whatever reason I might have had for doing that, and multiple people have since convinced me that was a mistake. Mallard 1.1 fixes this. You can now do ubiquitous linking on guiseq and keyseq.

Speaking of guiseq and keyseq, we’re considering creating a shorthand syntax for them so you can easily write simple GUI paths and key sequences without nested gui and key elements. Comment on the issue to let us know your thoughts.

And more

That’s it for part 3 of 3. If you haven’t already, read part 1 and part 2. For more information, you can read the Mallard 1.1 changes, the Mallard 1.1 enhancement proposals, and the 1.1 milestone issues.

Want to get involved? Take a look at our 1.2 milestone issues. Or check out the Documentation issue label and help us write tutorials. Keep in touch on the Mallard mailing list.

We’ve just released Mallard 1.1. Let’s take a look at what’s new. All of these features are already supported in tools like Yelp and Pintail.

This is part 2 in a 3-part series. Read part 1.

Table header cells

MEP-0012: Table Header Cells

Mallard mostly follows the HTML table model, with some simplifications of how things are styled. One element that was notably absent from Mallard tables, however, was the th element. The th element allows you to mark a cell as being a header for a row or column, even if it’s not in a thead element (as row headers wouldn’t be).

This was a pretty obvious and easy addition. I was so confident Mallard 1.1 would get a th element that I added a Ducktype shorthand for it well before Mallard 1.1 was released.

Custom roles for links

MEP-0003: The role Attribute on the links Element

This one’s pretty exciting, though a bit advanced. In Mallard, links can have roles which affect things like link text. These work with the multiple informational titles you can provide for a page or section. So, for example, if your language needs to do declensions for different parts of speech, you could write your links like this:

<link role="subject" xref="useful"/> is really useful.
For useful info, check out <link role="object" xref="useful"/>.

Then in useful.page, you would have something like this:

<info>
  <title type="link" role="subject">Subject Title</title>
  <title type="link" role="object">Object Title</title>
</info>
<title>Normal Title</title>

Mallard will pick up the right title. More often, however, you don’t write your links inline, but instead you do automatic linking with informational links and possibly the links element. What happens then?

In Mallard 1.0, different types of automatic links have implicit roles. So the topic links on a guide page will automatically use the "topic" role to select link text from titles, for example. There are implicit roles for topic, guide, seealso, and series links.

So far, so good. But what if you want to use different titles for link text when coming from different guide pages with different topic link styles? This is where the new Mallard 1.1 feature comes in. In Mallard 1.1, you can add a role attribute to a links element to set the primary role to use for all the links it generates. The implicit role for that links type will still be used as a secondary role.

<links type="topic" role="fancyrole"/>

Boring schema changes

In Mallard 1.0, sections were required to have id. There were a couple of reasons I made that decision, but in the end it turned out to annoy more people than it helped. So in Mallard 1.1, section IDs are optional.

We also made a perfectly boring schema change to make it easier for the Cache Files schema to extend the Mallard schema. (There’s also a new Cache Files 1.1 release.) Although RELAX NG is a mostly great schema language for extensible schema design, it does take some effort to design schemas that can be extended. Mallard got a lot of things right, but sometimes we find something to improve.

And more

That’s it for part 2. If you haven’t already, go read part 1, and keep your eye out for part 3. For more information, you can read the Mallard 1.1 changes, the Mallard 1.1 enhancement proposals, and the 1.1 milestone issues.

Want to get involved? Take a look at our 1.2 milestone issues. Or check out the Documentation issue label and help us write tutorials. Keep in touch on the Mallard mailing list.

We’ve just released Mallard 1.1. Let’s take a look at what’s new. All of these features are already supported in tools like Yelp and Pintail.

This is part 1 in a (probably) 3-part series.

New type attribute for code blocks

MEP-0010: The type Attribute on the code Element

In Mallard 1.0, we used the mime attribute to specify the content type of elements like code and screen. The mime attribute took a MIME type, of course, which seemed like a good idea at the time. Unfortunately, there are very few registered MIME types for the types of content we put in code blocks. So you had to memorize arbitrary long strings.

<code mime="text/x-python">

In Mallard 1.1, we’ve deprecated the mime attribute in favor of the new type attribute. The type attribute takes simple strings, like "xml", "py", and "c". It can also take a space-separated list, so you can provide specific types as well as more generic types to catch different syntax highlighters, like "xml mallard".

<code type="py">

As an added bonus, the type attribute has a shorthand syntax in Ducktype. So instead of typing this:

[code type="py"]

You can just type this:

[code py]

New div element

MEP-0005: Generic div Element

Mallard gives you a handful of semantic block elements, but sometimes you just need to group some block elements together without semantics. This is useful for extensions like Conditionals that operate on block elements. It can make it easier to control things like translatability with ITS. And it can help with transclusions using XInclude.

Mallard 1.1 introduces the new div element as a generic block element. The div element is a formal element, accepting optional title and desc elements. One extra bonus of this addition is that it provides better fallback behavior for extensions that have formal block elements.

Making example formal

Speaking of formal block elements, we made the example element formal, accepting optional title and desc elements. The example element was the only block container element that didn’t allow any sort of title, and that just seemed silly.

Giving blocks info

MEP-0002: Block info Element

All formal block elements now take an optional info element. This can be used, for example, to provide credits and license information for people who drew figures or wrote code listings. Or you can use it to provide alternate titles for UI expanders.

More importantly, this is a crucial first step in making block elements participate in both inline and automatic links, tentatively slated for Mallard 1.2

And more

That’s it for part 1. Keep your eye out for parts 2 and 3. For more information, you can read the Mallard 1.1 changes, the Mallard 1.1 enhancement proposals, and the 1.1 milestone issues.

Want to get involved? Take a look at our 1.2 milestone issues. Or check out the Documentation issue label and help us write tutorials. Keep in touch on the Mallard mailing list.

Mallard allows you to declare a content type for code blocks, which can then be used for syntax highlighting. We ship a copy of the excellent highlight.js with yelp-xsl, which in turn is used by tools like Yelp, yelp-build, and Pintail. And we colorize what highlight.js gives us using the same color templates used in the rest of the stack.

With Mallard 1.0, the way to declare the content type of a code block is using a mime attribute, which takes a MIME type.

<code mime="text/x-csrc">

This seemed like a good idea at the time. MIME types are the most universal standard for identifying stuff like this, for better or for worse. In practice, it’s been pretty cumbersome. Almost none of the kinds of things you’d put in a code block have registered MIME types. You just have to look up and remember arbitrary long strings. Sad face.

In Mallard 1.1, we’re introducing the type attribute and deprecating the mime attribute. The type attribute can take a simple, short string identifier, just like other documentation formats use.

<code type="c">

The strings are shorter than ad hoc MIME types, and generally easier to remember, or even to guess. For most programming languages, both the file extension and the lowercase name of the language probably work. If they don’t, tell us. The type attribute can also take a space-separated list. So you can list a more specific type alongside a generic type you know will match. This is useful for XML vocabularies.

<code type="xml mallard">

This is also particularly useful when you’re also doing things besides syntax highlighting with code blocks of a certain type. The type attribute (and mime before it) is useful for syntax highlighting, but that’s not its only purpose. You could use it to extract code samples for linting, for example. On projectmallard.org, we automatically extract the RELAX NG schemas from the specifications using (currently) the mime attribute. So it can be useful to list multiple types.

One other really nice thing about using the type attribute is that it takes advantage of a syntax shorthand in Ducktype. Ducktype is a compact syntax designed to do everything Mallard can do. Attributes like type, style, and xref are so common that Ducktype gives you a shorthand way of writing them. For the type attribute, you just put the value there on its own. So in Ducktype, it’s as easy as this:

[code c]

Or this:

[code xml mallard]

We could always use help with testing and with updating the documentation. See my post to mallard-list for more information.

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:

@ducktype/1.0
@namespace if http://projectmallard.org/if/1.0/

= 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:

[_test:foo]

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:

$gnome:issue(yelp#138)

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.

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:

<xsl:template
     mode="html.sidebar.mode"
     match="token[. = 'googlead']">
  <!-- Put Google's stuff here -->
</xsl:template>

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.

[pintail]
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:

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

Then your pintail.cfg would look like this:

[pintail]
sidebar_left = contents
sidebar_right = search languages

[/path/to/nightly/]
sidebar_left = contents nightly

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.