aboutsummaryrefslogtreecommitdiffstats
path: root/src/b/2022-07-organizing-qutebrowser-hyperlinks.md
blob: 897db0808aa042924c97e87a2e70032dd34ef1e6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
---
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](/static/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](/static/b/qutebrowser-bookmark-entries.jpg)

(Next step: integrate a fuzzy-finder for searching bookmarks)

[^1]: https://github.com/qutebrowser/qutebrowser/issues/882
remember that computers suck.