In Google Docs, Confluence, and other heavy document editors, you can just paste a URL and it’ll fetch the page title and make a nice link out of it, at the cost of some tick, tick, tick while it fetches the title from the other page.
I realized some links actually contain enough information
to implement an instant (though limited) version of this feature in a Hugo site.
All I have to do is paste it as a Markdown autolink,
i.e. between angle brackets like <https://example.com>, without specifying a title.
The result:
| Markdown | Rendered |
|---|---|
<https://github.com/mrled/dhd> |
[GH] mrled/dhd |
<https://example.atlassian.net/browse/EXAMPLE-12345> |
[Jira] EXAMPLE-12345 |
<https://en.wikipedia.org/wiki/Oblique_Strategies> |
[WP] Oblique Strategies |
More examples
| Markdown | Rendered |
|---|---|
<https://github.com/mrled/suns/pull/2> |
[GH] mrled/suns#2 |
<https://github.com/mrled/hedgerules/issues/1> |
[GH] mrled/hedgerules#1 |
<https://github.com/mrled/psyops/blob/master/ansible/cloudformation/MicahrlDotCom.cfn.yml> |
[GH] mrled/psyops@master MicahrlDotCom.cfn.yml |
<https://example.atlassian.net/wiki/spaces/EXAMPLE/pages/12345/Important+Company+Announcement> |
[Confluence: EXAMPLE] Important Company Announcement |
<https://x.com/mrled> |
[Twitter] @mrled |
<https://x.com/mrled/status/1761059606502031502> |
[Twitter] tweet from @mrled |
<https://example.slack.com/archives/C0WCVFB5X/p1772997481266379> |
[Slack] p1772997481266379 |
Some notes:
- GitHub happens to use the same namespace for both issue and PR IDs,
so if there is a /pull/1 there will not be an /issues/1, and vice versa.
It uses the
#idsyntax for both in its own UI, so we can too.
The secret is Hugo’s render hooks, which can run string matching and munging code on every Markdown link, including autolinks. Here’s source code for an example link render hook:
_markup/render-link.html
{{- /*
Compute display text for <https://example.com> -style "autolinks".
For recognized services, replace the bare URL with a human-readable label.
*/ -}}
{{- $linkText := .Text }}
{{- $useCite := false }}
{{- if and $u.IsAbs (eq .Text .Destination) }}
{{- /* GitHub: shorten to org/repo, org/repo#number, or org/repo@commit/path */}}
{{- if hasPrefix .Destination "https://github.com/" }}
{{- $path := strings.TrimPrefix "https://github.com/" .Destination }}
{{- $parts := split $path "/" }}
{{- $org := index $parts 0 }}
{{- $repo := index $parts 1 }}
{{- $text := printf "%s/%s" $org $repo }}
{{- if and (ge (len $parts) 4) (or (eq (index $parts 2) "issues") (eq (index $parts 2) "pull")) }}
{{- /* Issue or PR URL: append #number */}}
{{- $text = printf "%s/%s#%s" $org $repo (index $parts 3) }}
{{- else if and (ge (len $parts) 4) (or (eq (index $parts 2) "tree") (eq (index $parts 2) "blob")) }}
{{- /* Tree or blob URL: show @ref and last path segment only */}}
{{- $hash := index $parts 3 }}
{{- $short := substr $hash 0 7 }}
{{- $rest := after 4 $parts }}
{{- if $rest }}
{{- $last := index $rest (sub (len $rest) 1) }}
{{- $text = printf "%s/%s@%s %s" $org $repo $short $last }}
{{- else }}
{{- $text = printf "%s/%s@%s" $org $repo $short }}
{{- end }}
{{- end }}
{{- $linkText = printf "[GH] %s" $text }}
{{- /* Confluence wiki: show space key and page title */}}
{{- else if findRE `^https://example\.atlassian\.net/wiki/spaces/` .Destination }}
{{- $path := replaceRE `^https://example\.atlassian\.net/wiki/spaces/` "" .Destination }}
{{- $parts := split $path "/" }}
{{- $space := index $parts 0 }}
{{- /* Page title is the 4th path segment; + encodes spaces in Confluence URLs */}}
{{- $title := index $parts 3 | replaceRE `\+` " " }}
{{- $linkText = printf "[Confluence: %s] %s" $space $title }}
{{- $useCite = true }}
{{- /* Twitter/X profile or tweet; both twitter.com and x.com are supported */}}
{{- else if or (hasPrefix .Destination "https://twitter.com/") (hasPrefix .Destination "https://x.com/") }}
{{- $twitterBase := cond (hasPrefix .Destination "https://twitter.com/") "https://twitter.com/" "https://x.com/" }}
{{- $path := strings.TrimPrefix $twitterBase .Destination }}
{{- $parts := split $path "/" }}
{{- $user := index $parts 0 }}
{{- if and (ge (len $parts) 3) (eq (index $parts 1) "status") }}
{{- /* Tweet URL: twitter.com/user/status/id */}}
{{- $linkText = printf "[Twitter] tweet from @%s" $user }}
{{- else }}
{{- /* Profile URL: twitter.com/user */}}
{{- $linkText = printf "[Twitter] @%s" $user }}
{{- end }}
{{- /* Wikipedia: convert article slug to title (underscores to spaces) */}}
{{- else if findRE `^https://[a-z]+\.wikipedia\.org/wiki/` .Destination }}
{{- $title := replaceRE `^https://[a-z]+\.wikipedia\.org/wiki/` "" .Destination | replaceRE `_` " " }}
{{- $linkText = printf "[WP] %s" $title }}
{{- $useCite = true }}
{{- /* Jira ticket: extract and show the ticket ID (e.g. PROJ-123) */}}
{{- else if findRE `^https://example\.atlassian\.net/.*[A-Z]+-\d+` .Destination }}
{{- $ticket := replaceRE `.*?([A-Z]+-\d+).*` "$1" .Destination }}
{{- $linkText = printf "[Jira] %s" $ticket }}
{{- /* Slack: show message ID (p-number) from archives URL; query params ignored via $u.Path */}}
{{- else if findRE `^https://example\.slack\.com/archives/` .Destination }}
{{- $pathParts := split (strings.TrimPrefix "/" $u.Path) "/" }}
{{- $msgId := index $pathParts (sub (len $pathParts) 1) }}
{{- $linkText = printf "[Slack] %s" $msgId }}
{{- end }}
{{- end }}On this public site, I use this pretty conservatively, because I have plenty of time and a hand-written title is often better. But on a private site that I use for notes, probably 90% of my outbound links are autolinks. The friction of deciding on a title and writing it out the Markdown way matters sometimes, like when taking notes real time during a meeting, and a title adapted from a URL ends up looking good enough, sometimes even great.
I also like that, unlike Confluence and Google docs, there is zero waiting when authoring the document. And furthermore, there’s no nondeterminism or delay at build time either.
If you’re building a web application: please, use nice human-readable URLs!