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
" f"{html.escape(url.toDisplayString())}" ), ) 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