Improved Doxygen documentation and search

Whether you are brows­ing Mag­num docs or use Doxy­gen for your own C++ doc­u­men­ta­tion, there’s al­ways a way to im­prove your work­flow. This ar­ti­cle presents the most re­cent ad­di­tions to the m.css Doxy­gen theme.

A year ago I switched Mag­num docs to a theme writ­ten en­tire­ly from scratch, adding a nev­er-be­fore-seen search func­tion­al­i­ty a while lat­er. As the say­ing goes, eat­ing one’s own dog food is al­ways a good way to find ways to im­prove it, so dur­ing the whole year I col­lect­ed var­i­ous en­hance­ment ideas and not­ed rough cor­ners that need more pol­ish­ing, not to men­tion I re­ceived great feed­back from all the hap­py de­vel­op­ers that start­ed us­ing the theme for their own projects or were mere­ly the users of it through Mag­num doc­u­men­ta­tion.

A long-await­ed — and I would say al­so the most im­por­tant — ad­di­tion is in­for­ma­tion about what to #include to get giv­en sym­bol. But wait, that’s not all! While stock Doxy­gen shows in­clude paths on­ly for class­es — which made sense for clas­sic OOP-heavy code­bas­es — it cer­tain­ly doesn’t help much in Mag­num, which has a lot of func­tion­al­i­ty di­rect­ly in­side names­paces. While I’m try­ing hard to have the sym­bol ↔ file map­ping as in­tu­itive as pos­si­ble, it’s not al­ways clear and can be a lot of strug­gle es­pe­cial­ly for new­com­ers.

So I went one step fur­ther and the doc­u­men­ta­tion now shows #include in­for­ma­tion not on­ly for class­es, but al­so for names­paces (in case they are ful­ly con­tained in a sin­gle head­er, such as An­i­ma­tion::Eas­ing), for free func­tions, type­defs, vari­ables and enums. That makes a big dif­fer­ence es­pe­cial­ly in large names­paces such as Math or GL.

Include information for free namespace members
Additional keywords

The theme was al­ready han­dling ex­tra at­tributes like deleted and defaulted func­tions and noexcept — which Doxy­gen is not rec­og­niz­ing at all, if I re­mem­ber cor­rect­ly. To com­plete this, now al­so override, final and con­di­tion­al noexcept are parsed and shown. Be­sides that, final is rec­og­nized al­so for class­es, if you’d ev­er need that, and a few bugs re­lat­ed to pars­ing of those at­tributes were fixed.

You can see these in ac­tion for ex­am­ple in Con­tain­ers::Op­tion­al.

Documented private virtual functions

The clas­sic ar­ti­cle about Vir­tu­al­i­ty by Herb Sut­ter sug­gests that a class has nev­er any pub­lic virtual func­tions, but rather a non-vir­tu­al pub­lic in­ter­face and all vir­tu­al in­ter­faces pri­vate. That makes the in­ter­face de­sign much more flex­i­ble and you don’t run in­to weird is­sues with co­vari­ant re­turn types.

Mag­num fol­lows this rule since the very be­gin­ning in its ap­pli­ca­tion class­es (such as Plat­form::Sdl2Ap­pli­ca­tion) and all plug­in in­ter­faces like Trade::Ab­strac­tIm­porter, but un­til now Doxy­gen was not re­al­ly able to show doc­u­ment­ed pri­vate func­tions. To work around that, the pri­vate vir­tu­al func­tions used to be shown as protected, which was mis­lead­ing. Well, not any­more!

The ini­tial search im­ple­men­ta­tion as ex­plained in this ar­ti­cle was pick­ing up the re­sults in what­ev­er or­der the search da­ta had them in. This was al­ready mil­lion times bet­ter and faster than the clas­sic Doxy­gen search im­ple­men­ta­tion, but lat­er I re­al­ized it could be eas­i­ly im­proved to or­der the re­sults in a more use­ful way — in par­tic­u­lar pre­fer­ring class­es and names­paces over func­tions and tuck­ing away dep­re­cat­ed and deleted func­tion­al­i­ty, since you’re far less like­ly to need doc­u­men­ta­tion for these:

OpenSearch integration in Firefox
Search­ing for GL::Buf­fer be­fore
OpenSearch integration in Firefox
… and af­ter

For­tu­nate­ly, due to the way the search is im­ple­ment­ed, this was on­ly a mat­ter of sort­ing the re­sults while build­ing the search da­ta, it re­quired no com­plex al­go­rithm changes on the client side.

Be­sides the above, with fre­quent use it al­so be­came ap­par­ent that hav­ing to type long parts of sym­bol names to nar­row down the re­sults is … an­noy­ing. Again, a so­lu­tion was rather sim­ple to im­ple­ment, mak­ing use of a prop­er­ty of the Trie search struc­ture — it col­lects char­ac­ters un­til the first child node that has re­sults and then it of­fers them for au­to­com­ple­tion.

Search autocompletion

I’m a heavy us­er of brows­er’s search bar and search key short­cuts (for ex­am­ple, if I write cpp vector::emplace, my brows­er will search for std::vec­tor::em­place() di­rect­ly on cp­pref­er­ If you’re like me and want to have in-brows­er search avail­able al­so for Doxy­gen docs, it’s now dis­cov­er­able through OpenSearch on browsers that sup­port it. So, for ex­am­ple on Fire­fox, vis­it­ing doc.mag­num.graph­ics will of­fer you this:

OpenSearch integration in Firefox

Chrome sup­ports OpenSearch as well, but the dis­cov­ery is well-hid­den deep in the set­tings — if you are on the doc­u­men­ta­tion site and open the search en­gine set­tings, it will sug­gest adding a new search en­gine. An­oth­er way that works in many browsers (al­so in Vi­val­di, for ex­am­ple) is right-click­ing the search field and se­lect­ing Add search en­gine.

The un­der­ly­ing ca­pa­bil­i­ty that en­ables all this is recog­ni­tion of ?q={query}#search in GET pa­ram­e­ters. Ap­pend­ing it to the doc­u­men­ta­tion URL will di­rect­ly open a search pop­up with re­sults for {query}.

Among oth­er things there’s now a sup­port for C++14 vari­able tem­plates and var­i­ous oth­er im­prove­ments, main­ly re­lat­ed to UX of the search pop­up. See the m.css com­mit his­to­ry if you want to know more.

The theme is al­ways im­prov­ing, for a hint on what could come next, see for ex­am­ple mosra/m.css#79. If you want to get in­volved, there are var­i­ous is­sues marked help want­ed. I’m al­ways very hap­py to ac­cept con­tri­bu­tions, bu­gre­ports and sug­ges­tions for im­prove­ment — in par­tic­u­lar, it’s very pos­si­ble that the new fea­tures are bro­ken for cor­ner cas­es that I didn’t think about or that some things are not work­ing con­sis­tent­ly across all browsers. Com­ments on that very wel­come.

If you are a Mag­num us­er, there’s a chance you al­ready spot­ted some of these im­prove­ments on doc.mag­num.graph­ics as I was grad­u­al­ly push­ing them out. If you are us­ing Doxy­gen for your C++ docs, give the m.css Doxy­gen theme a go — I’m sure you’ll like the re­sult both as a li­brary de­vel­op­er and as an us­er of the doc­u­men­ta­tion 😊