2025-10-29
I put my fish functions on github, and over the years, the repository has attracted users and contributors.
As I started to notice the interest, I started putting more effort into the README, which serves as the documentation.
Once I got enough functions, I added a table of contents. The functions names in the table of contents link to the full documentation for each function, and the documentation header for each function includes a link to the source for each function.
I put the arguments in the heading for each function, which became part of the autogenerated heading IDs on github. In order to make the links go to the header, I’d put a link like so:
* [coln](#coln-number-source)
...
`coln <number>` [(source)](functions/coln.fish)
This worked fine for a while. If there was ever an ID link not working, I’d just see what github rendered by going to the web page and hovering, hovering over the heading, and copying the id link when the it appeared.
Over the years, I grew disenchanted with github, and eventually I started putting my latest code on Sourcehut. However the autogenerated links were different!
I figured that more users were visiting github than sourcehut, so I let it be with a partially-functional table of contents.
Recently I added my fish functions to Codeberg as well, which has some appeal to me since it’s open source but has a bit more visual appeal than sourcehut.
It has yet another way it autogenerates heading links, so I figured it was time to standardize the links once and for all!
Recognizing that all html is valid markdown, I first tried to rewrite the headings in html.
This markdown:
### `abbr-add <name> <expansion> [<args>]` [(source)](functions/abbr-add.fish)
became:
<h3 id="abbr-add">
<code>abbr-add <name> <expansion> [<args>]</code>
<a href="functions/abbr-add.fish">(source)</a>
</h3>
There were 2 issues, one tractable, one intractable.
The tractable issue was that it was a lot of visual noise over the markdown. Generally plain html wouldn’t be so bad, but having to escape the < and > symbols which have a special meaning in html (though luckily not markdown) caused a lot of visual noise.
The intractable problem is that the relative links such as the one to functions/abbr-add.fish
actually go to different places in each of the forges.
blob/main/functions/abbr-add.fishtree/HEAD/functions/abbr-add.fishsrc/branch/main/functions/abbr-add.fishWhen it was being parsed in markdown, it was generating the correct relative link for each platform.
When I put the link in html, it got rid of the blob/main or tree/HEAD or src/branch/main part,
which I couldn’t hardcode since they are different for each one.
According to this answer on Stack Overflow, markdown implementations implementing CommonMark allow you to start with an html tag, put a blank line, write some markdown, then put another blank line and close your original tag.
The following works:
<h3 id="abbr-add">
`abbr-add <name> <expansion> [<args>]` [(source)](functions/abbr-add.fish)
</h3>
I first tried to do this with <div>s and s, however the implementation was inconsistent. I think I would have been able to get this working with enough tweaking, and possibly opening issues on any of the forges that were not behaving as I’d expect, however I’m in a bit of a gray area of markdown + git forge + markdown implementation agreement, and it’s possible this whole thing would be intractable and I’d have to reformat my README to conform to a working subset of markdown all the forges supported, or just give up on supporting other forges.
Fortunately, I realized that everything I was linking to was a heading already, and I tried just adding the id to an html header.
Using this, I have links working on all 3 forges, and since I control the id,
I can make my table of contents just link to #abbr-add.
I was getting pretty close to making the README generate from the code, which could then do any arbitrary formatting of the table of contents and the functions.
Alternatively I could have used a dedicated documentation solution, such as RST, and I might still; there are definitely advantages to using documentation software over just writing a huge README. README.rst even renders on Github; I’m not sure about the other ones.
It’s a pretty interesting case of software interoperability, since all these forges have to work with source code which wasn’t necessarily written with them in mind.
The linux kernel, which predates Github and probably all the rest (linux was written before Linus Torvalds invented git to manage the source of linux) has a plain README on github, for example :)
I hope this information is useful for anybody who is dealing with markdown or git forges.