TL;DR: Build a wiki, but far more powerful

  • I would like to be reminded of things as I read, like a wiki, but better.
  • I would like to see a glossary of the words I have learned.
  • I would like to know at a moment’s glance what I have already read and understood.
  • I would like to be reminded of important things as I’m having a conversation.

Great for

  • Reading books
  • Keeping and navigating a glossary extremely easily
  • Procedurally/generatively navigating text (the applications are endless)

Enter, emacs

Demonstration

The shell

Grep will return the byte positions of the definitions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
export TTY
# shopt -s nullglob # use for 'for' loops but not for 'ls', 'grep'

( hs "$(basename "$0")" "$@" "#" "<==" "$(ps -o comm= $PPID)" 0</dev/null ) &>/dev/null

grep() {
    /bin/grep --byte-offset --with-filename -oP "^[a-zA-Z0-9]+[/a-zA-Z0-9 ]+$" "$@"
}

{
    if test "$#" -eq 0; then
        grep $HOME/glossaries/*
    else
        grep "$@"
    fi
} | {
    echo "'("
 # -e 's/\(" \)\([0-9]*\)/\1"\2"/'
    sed -e 's/\([^:]*\):/"\1" /' -e "s/:\(.*\)$/ \"\1\"/" -e 's/.*/(&)/'
    echo ")"
} | pavs

The lisp

I generate buttons for all the byte positions

  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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
(define-button-type 'glossary-button 'follow-link t 'help-echo "Click to go to definition")

(defset glossary-term-3tuples (sort (my-eval-string (sn "list-glossary-terms-for-elisp")) (lambda (e1 e2) (< (length (third e1))
                                                                                                             (length (third e2))))))
(defun make-buttons-for-glossary-terms (beg end)
  "Makes buttons for glossary terms found in this buffer."
  (interactive "r")
  (cl-loop for termtuple in glossary-term-3tuples do (create-buttons-for-term (third termtuple) beg end
                                                                              (first termtuple)
                                                                              (second termtuple))))

(defun create-buttons-for-term (term beg end &optional glossarypath byteoffset)
  "Adds glossary buttons for Term in in beg/end region.
Go through a buffer in search of a term and create buttons for all instances
Use my position list code. Make it use rosie lang and external software."
  (interactive "sTerm: \nr")
  (if mark-active
      (setq deactivate-mark t)
    (setq beg
          (point-min)
          end
          (point-max)))
  (goto-char beg)
  (while (re-search-forward
          (concat
           "\\b"
           (regexp-quote term)
           "\\b")
          end
          t)
    (progn
      ;; Remove the regex at point here
      ;; Put the button here
      ; (replace-match "" t t)
      ;; (insert-text-button
      ;; It's modifying the buffer, which is bad.
      ;; It operates on org-mode, which is bad.
      ; (insert-text-button
      (make-button
        (match-beginning 0)
        (match-end 0)
       ;; (propertize term 'face 'bold)
       'term   term
       'glossarypath   glossarypath
       'byteoffset   byteoffset
       'action 'glossary-button-pressed
       'type 'glossary-button))))

(defun goto-byte (byteoffset)
  (goto-char (byte-to-position (+ byteoffset 1))))

(defun glossary-button-pressed (button)
  "When I press a glossary button, it should take me to the definition"
  (let* ((term (button-get button 'term))
         (byteoffset (button-get button 'byteoffset))
         (glossarypath (button-get button 'glossarypath)))
    (with-current-buffer
        (find-file glossarypath)
      (goto-byte byteoffset))))

(defun generate-glossary-buttons-over-buffer ()
  (interactive)
  (if (not (major-mode-p 'org-mode))
      (make-buttons-for-glossary-terms
       (point-min)
       (point-max))))
(add-hook 'text-mode-hook 'generate-glossary-buttons-over-buffer)


(defun glossary-next-button-fast (pos)
  (let* ((nextpos (next-single-char-property-change pos 'button))
         (nextbutton (button-at nextpos)))
    ;; (message (concat (str pos) " " (str nextpos)))
    (if (not (and (not nextbutton) (= (button-start nextbutton) pos)))
        (progn
          (while (or (not nextbutton) (= (button-start nextbutton) pos))
            ;; (message (concat (str pos) " " (str nextpos)))
            (progn
              (setq nextpos (next-single-char-property-change nextpos 'button))
              (setq nextbutton (button-at nextpos))))
          nextbutton))))

(defun glossary-buttons-collect ()
  "Collect the positions of visible links in the current `help-mode' buffer."

  (let* ((candidates)
        (p (window-start))
        (b (button-at p))
        (e (or (and b (button-end b)) p)))
    (if b
        (push (cons (button-label b) p) candidates))
    (while (and (setq b ;; (glossary-next-button-fast p)
                      (next-button e))
                (setq p (button-start b))
                (setq e (button-end b))
                (< p (window-end)))
      ;; (message (str p))
      (push (cons (button-label b) p) candidates))
    (nreverse candidates)))

(defun ace-link-glossary ()
  (interactive)
  (let ((pt (avy-with ace-link-help
              (avy-process
               (mapcar #'cdr (glossary-buttons-collect))
               (avy--style-fn avy-style)))))
    (ace-link--help-action pt)))

(defun ace-link ()
  "Call the ace link function for the current `major-mode'"
  (interactive)
  (cond ((eq major-mode 'Info-mode)
         (ace-link-info))
        ((member major-mode '(help-mode package-menu-mode geiser-doc-mode elbank-report-mode
                                        org-brain-visualize-mode
                                        elbank-overview-mode slime-trace-dialog-mode helpful-mode))
         (ace-link-help))
        ((eq major-mode 'text-mode)
         (or
          (ace-link-glossary)
          (ace-link-help)
          (ace-link-org)))
        ((eq major-mode 'woman-mode)
         (ace-link-woman))
        ((eq major-mode 'eww-mode)
         (ace-link-eww))
        ((eq major-mode 'w3m-mode)
         (ace-link-w3m))
        ((or (member major-mode '(compilation-mode grep-mode))
             (bound-and-true-p compilation-shell-minor-mode))
         (ace-link-compilation))
        ((eq major-mode 'gnus-article-mode)
         (ace-link-gnus))
        ((or (memq major-mode '(org-mode erc-mode elfeed-show-mode term-mode))
             (derived-mode-p '(org-mode erc-mode elfeed-show-mode term-mode)))
         (ace-link-org))
        ((eq major-mode 'org-agenda-mode)
         (ace-link-org-agenda))
        ((eq major-mode 'Custom-mode)
         (ace-link-custom))
        ((eq major-mode 'sldb-mode)
         (ace-link-sldb))
        ((eq major-mode 'slime-xref-mode)
         (ace-link-slime-xref))
        ((eq major-mode 'slime-inspector-mode)
         (ace-link-slime-inspector))
        ((eq major-mode 'indium-inspector-mode)
         (ace-link-indium-inspector))
        ((eq major-mode 'indium-debugger-frames-mode)
         (ace-link-indium-debugger-frames))
        ((and ace-link-fallback-function
              (funcall ace-link-fallback-function)))
        (t
         (ace-link-help)
         ;; (error
         ;;  "%S isn't supported"
         ;;  major-mode)
         )))

(provide 'my-glossary)