---
title: Organizing qutebrowser hyperlinks
date: 2022-07-18
---
## Actual state
Qutebrowser offers two strategies for saving an hyperlink [^1]:
- `bookmark`: a link relates to a title (without any format constraint).
- `quickmark`: a link relates to a tag (which must be unique).
I have always used the bookmark strategy, but it lacks a propper
quality-of-life update for sorting and formatting entries.
## Intended workflow
I have decided to pick the following workflow:
1. Do not use the quickmark strategy.
2. Store hyperlinks as bookmarks.
3. Format the bookmarks titles, but allow duplicates.
4. Sort the bookmarks per title.
5. Show the same ui of quickmark-add when adding a new bookmark.
A bookmark entry in `.config/qutebrowser/bookmarks/urls` would then look like
this:
```
[hyperlink] [tag+1] [tag+2] ... [tag+n]
https://beej.us/guide/bgnet/html/ +network +soft +wiki
http://cat-v.org/ +wiki
https://why-vi.rocks/ +wiki
```
While (1) and (2) are purely related to human behaviors, (3), (4) and (5)
requires the implementation of logic on qutebrowser side, which does not exist
on the upstream (yet).
I originally produced multiple hacky scripts for sorting and formatting my
bookmark file, but ended up doing Python injection from the qutebrowser
configuration file. Thanks to the functions injection, the sorting and
formatting operations are hooked to the `bookmark-add` keybind.
![qutebrowser-bookmark-popup](/img/b/qutebrowser-bookmark-popup.jpg)
## Implementation
1. Sort the tags of each bookmark entries, then sort the bookmarks entries by
their title:
```
from os import path
from io import SEEK_SET
from qutebrowser.browser.urlmarks import UrlMarkManager
def sort_bookmarks(url_filepath: str) -> None:
file = open(url_filepath, 'r+')
marks = (
(link, sorted(tags.split())) for link, tags in
(line.split(maxsplit=1) for line in file.readlines())
)
lines = (
f'{link} {" ".join(tags)}' for link, tags in
sorted(marks, key=lambda mark: mark[1])
)
file.seek(SEEK_SET)
file.writelines('\n'.join(lines))
file.close()
```
2. Hook the sort function when an new entry is added to the url file (Note: I
wanted to reuse the original `UrlMarkManager.save` method and extend it, but
pythonic shenanigans forced me to copy/paste two loc from the source code):
```
def urlmarkmanager_save(manager: UrlMarkManager, configdir: str) -> None:
"""Override Qutebrowser builtin method for saving bookmarks."""
manager._lineparser.data = [' '.join(tpl) for tpl in manager.marks.items()]
manager._lineparser.save()
sort_bookmarks(path.join(configdir, 'bookmarks/urls'))
from qutebrowser.browser.urlmarks import UrlMarkManager
UrlMarkManager.save = (
lambda manager: urlmarkmanager_save(manager, config.configdir)
)
```
3. Hook the save function with an homemade pop-up, that appears when adding a
new bookmark entry:
```
from qutebrowser.api import cmdutils
@cmdutils.register()
def bookmark_save(url: QUrl):
"""Save the current page as a bookmark."""
manager = objreg.get('bookmark-manager')
tags = message.ask(
title="Add bookmark:",
mode=PromptMode.text,
url=url.toString(QUrl.RemovePassword | QUrl.FullyEncoded),
text=(
"Please enter bookmark tags for<br/><b>"
f"{html.escape(url.toDisplayString())}</b>"
),
)
if not tags:
return
try:
manager.add(url, tags)
except UrlAlreadyExistsError:
message.warning("Bookmark already exists.")
```
Now all my bookmark entries are sorted by their titles automatically, and it
looks way better when I preview them!
![qutebrowser-bookmark-entries](/img/b/qutebrowser-bookmark-entries.jpg)
(Next step: integrate a fuzzy-finder for searching bookmarks)
[^1]: https://github.com/qutebrowser/qutebrowser/issues/882