Refactored and expanded link factoring to apply to non-gemini pages.
authorplugd <plugd@thelambdalab.xyz>
Wed, 21 Jul 2021 20:08:59 +0000 (22:08 +0200)
committerplugd <plugd@thelambdalab.xyz>
Wed, 21 Jul 2021 20:08:59 +0000 (22:08 +0200)
elpher.el

index 814f0fb..41d4a83 100644 (file)
--- a/elpher.el
+++ b/elpher.el
 (defvar elpher--gemini-page-headings nil
   "List of headings on the page.")
 
-(defvar elpher--gemini-page-links nil
-  "List of links on the page.")
-
-(defvar elpher--gemini-page-links-cache (make-hash-table :test 'equal)
-  "Hash of addresses and page links.")
 
 ;;; Customization group
 ;;
@@ -230,9 +225,9 @@ some servers which do not support IPv6 can take a long time to time-out."
 Otherwise, the SOCKS proxy is only used for connections to onion services."
   :type '(boolean))
 
-(defcustom elpher-gemini-number-links nil
-  "If non-nil, number links in gemini pages when rendering.
-Links can be accessed by pressing `v' ('visit') followed by the link number."
+(defcustom elpher-number-links nil
+  "If non-nil, number links in pages when rendering.
+Links can be followed numerically by pressing `M' and entering by the link number."
   :type '(boolean))
 
 ;; Face customizations
@@ -527,6 +522,7 @@ Additionally, push PAGE onto the stack of previously-visited pages,
 unless NO-HISTORY is non-nil."
   (elpher-save-pos)
   (elpher-process-cleanup)
+  (elpher-reset-link-number-counter)
   (unless (or no-history
               (equal (elpher-page-address elpher-current-page)
                      (elpher-page-address page)))
@@ -538,14 +534,10 @@ unless NO-HISTORY is non-nil."
          (type-record (cdr (assoc type elpher-type-map)))
          (page-links nil))
     (if type-record
-        (progn
-          (funcall (car type-record)
-                   (if renderer
-                       renderer
-                     (cadr type-record)))
-          (setq page-links (gethash address elpher--gemini-page-links-cache))
-          (if page-links
-              (setq elpher--gemini-page-links page-links)))
+        (funcall (car type-record)
+                 (if renderer
+                     renderer
+                   (cadr type-record)))
       (elpher-visit-previous-page)
       (pcase type
         (`(gopher ,type-char)
@@ -1015,6 +1007,22 @@ displayed.  The _WINDOW argument is currently unused."
                                                 address
                                               (elpher-address-to-url address))))))))
 
+(defvar elpher--link-number-counter 0)
+(defun elpher-reset-link-number-counter ()
+  (setq-local elpher--link-number-counter 0))
+
+(defun elpher--insert-text-button (label &rest properties)
+  "Inserts a potentially-numbered button into the current buffer.
+The text for the button is provided by LABEL, while the button
+properties in PROPERTIES are as per `insert-text-button'."
+
+  (if elpher-number-links
+      (setq-local elpher--link-number-counter (+ elpher--link-number-counter 1)))
+  (let ((pref (if elpher-number-links
+                  (concat "[" (number-to-string elpher--link-number-counter) "] ")
+                "")))
+    (apply #'insert-text-button (cons (concat pref label) properties))))
+
 (defun elpher-insert-index-record (display-string &optional address)
   "Function to insert an index record into the current buffer.
 The contents of the record are dictated by DISPLAY-STRING and ADDRESS.
@@ -1028,12 +1036,12 @@ If ADDRESS is not supplied or nil the record is rendered as an
                (filtered-display-string (elpher-color-filter-apply display-string))
                (page (elpher-make-page filtered-display-string address)))
           (elpher-insert-margin margin-code)
-          (insert-text-button filtered-display-string
-                              'face face
-                              'elpher-page page
-                              'action #'elpher-click-link
-                              'follow-link t
-                              'help-echo #'elpher--page-button-help))
+          (elpher--insert-text-button filtered-display-string
+                                      'face face
+                                      'elpher-page page
+                                      'action #'elpher-click-link
+                                      'follow-link t
+                                      'help-echo #'elpher--page-button-help))
       (pcase type
         ('nil ;; Information
          (elpher-insert-margin)
@@ -1444,19 +1452,18 @@ treatment that a separate function is warranted."
          (address (elpher-address-from-gemini-url url))
          (type (if address (elpher-address-type address) nil))
          (type-map-entry (cdr (assoc type elpher-type-map))))
-    (setq elpher--gemini-page-links (append elpher--gemini-page-links `(,address)))
     (when display-string
       (insert elpher-gemini-link-string)
       (if type-map-entry
           (let* ((face (elt type-map-entry 3))
                  (filtered-display-string (elpher-color-filter-apply display-string))
                  (page (elpher-make-page filtered-display-string address)))
-            (insert-text-button filtered-display-string
-                                'face face
-                                'elpher-page page
-                                'action #'elpher-click-link
-                                'follow-link t
-                                'help-echo #'elpher--page-button-help))
+            (elpher--insert-text-button filtered-display-string
+                                        'face face
+                                        'elpher-page page
+                                        'action #'elpher-click-link
+                                        'follow-link t
+                                        'help-echo #'elpher--page-button-help))
         (insert (propertize display-string 'face 'elpher-unknown)))
       (insert "\n"))))
 
@@ -1514,11 +1521,9 @@ width defined by `elpher-gemini-max-fill-width'."
   "Render DATA as a gemini map file, PARAMETERS is currently unused."
   (elpher-with-clean-buffer
    (setq elpher--gemini-page-headings nil)
-   (let ((preformatted nil)
-         (link-counter 1))
+   (let ((preformatted nil))
      (auto-fill-mode 1)
      (setq-local fill-column (min (window-width) elpher-gemini-max-fill-width))
-     (setq elpher--gemini-page-links '())
      (dolist (line (split-string data "\n"))
        (cond
         ((string-prefix-p "```" line) (setq preformatted (not preformatted)))
@@ -1526,25 +1531,13 @@ width defined by `elpher-gemini-max-fill-width'."
                                (propertize line 'face 'elpher-gemini-preformatted))
                               "\n"))
         ((string-prefix-p "=>" line)
-         (progn
-           (if elpher-gemini-number-links
-               (insert
-                (concat
-                 "["
-                 (number-to-string link-counter)
-                 "] ")))
-           (setq link-counter (1+ link-counter))
-           (elpher-gemini-insert-link line)))
+         (elpher-gemini-insert-link line))
         ((string-prefix-p "#" line) (elpher-gemini-insert-header line))
         (t (elpher-gemini-insert-text line)))))
    (setq elpher--gemini-page-headings (nreverse elpher--gemini-page-headings))
    (elpher-cache-content
     (elpher-page-address elpher-current-page)
-    (buffer-string))
-   (puthash
-    (elpher-page-address elpher-current-page)
-    elpher--gemini-page-links
-    elpher--gemini-page-links-cache)))
+    (buffer-string))))
 
 (defun elpher-render-gemini-plain-text (data _parameters)
   "Render DATA as plain text file.  PARAMETERS is currently unused."
@@ -1631,11 +1624,10 @@ The result is rendered using RENDERER."
            "\n"
            " - TAB/Shift-TAB: next/prev item on current page\n"
            " - RET/mouse-1: open item under cursor\n"
-           " - m: select an item on current page by name (autocompletes)\n"
+           " - m/M: select an item on current page by name (autocompletes) or number\n"
            " - u/mouse-3/U: return to previous page or to the start page\n"
            " - o/O: visit different selector or the root menu of the current server\n"
            " - g: go to a particular address (gopher, gemini, finger)\n"
-           " - v: visit a numbered link on a gemini page\n"
            " - d/D: download item under cursor or current page\n"
            " - i/I: info on item under cursor or current page\n"
            " - c/C: copy URL representation of item under cursor or current page\n"
@@ -1670,13 +1662,13 @@ The result is rendered using RENDERER."
    (insert "\n"
            "** Refer to the ")
    (let ((help-string "RET,mouse-1: Open Elpher info manual (if available)"))
-     (insert-text-button "Elpher info manual"
-                         'face 'link
-                         'action (lambda (_)
-                                   (interactive)
-                                   (info "(elpher)"))
-                         'follow-link t
-                         'help-echo help-string))
+     (elpher--insert-text-button "Elpher info manual"
+                                 'face 'link
+                                 'action (lambda (_)
+                                           (interactive)
+                                           (info "(elpher)"))
+                                 'follow-link t
+                                 'help-echo help-string))
    (insert " for the full documentation. **\n")
    (insert (propertize
             (concat "  (This should be available if you have installed Elpher using\n"
@@ -1958,15 +1950,6 @@ When run interactively HOST-OR-URL is read from the minibuffer."
                               (elpher-address-to-url address)))))
       (elpher-visit-page (elpher-make-page url (elpher-address-from-url url))))))
 
-(defun elpher-visit-gemini-numbered-link (n)
-  "Visit link designated by a number N."
-  (interactive "nLink number: ")
-  (if (or (> n (length elpher--gemini-page-links))
-          (< n 1))
-      (user-error "Invalid link number"))
-  (let ((address (nth (1- n) elpher--gemini-page-links)))
-    (elpher-go (url-recreate-url address))))
-
 (defun elpher-redraw ()
   "Redraw current page."
   (interactive)
@@ -2055,6 +2038,17 @@ When run interactively HOST-OR-URL is read from the minibuffer."
                 (goto-char (button-start b))
                 (button-activate b)))))))
 
+(defun elpher-jump-to-number (n)
+  "Jump to directory entry number N."
+  (interactive "nDirectory item/link number: ")
+  (let* ((link-map (reverse (elpher-build-link-map))))
+    (if link-map
+        (if (<= 1 n (length link-map))
+            (let ((b (cdr (elt link-map (- n 1)))))
+              (goto-char (button-start b))
+              (button-activate b))
+          (error "No link with that number.")))))
+
 (defun elpher-root-dir ()
   "Visit root of current server."
   (interactive)
@@ -2132,8 +2126,10 @@ When run interactively HOST-OR-URL is read from the minibuffer."
     (define-key map (kbd "<backtab>") 'elpher-prev-link)
     (define-key map (kbd "C-M-i") 'elpher-prev-link)
     (define-key map (kbd "u") 'elpher-back)
-    (define-key map (kbd "U") 'elpher-back-to-start)
+    (define-key map (kbd "-") 'elpher-back)
+    (define-key map (kbd "^") 'elpher-back)
     (define-key map [mouse-3] 'elpher-back)
+    (define-key map (kbd "U") 'elpher-back-to-start)
     (define-key map (kbd "O") 'elpher-root-dir)
     (define-key map (kbd "g") 'elpher-go)
     (define-key map (kbd "o") 'elpher-go-current)
@@ -2146,6 +2142,7 @@ When run interactively HOST-OR-URL is read from the minibuffer."
     (define-key map (kbd "d") 'elpher-download)
     (define-key map (kbd "D") 'elpher-download-current)
     (define-key map (kbd "m") 'elpher-jump)
+    (define-key map (kbd "M") 'elpher-jump-to-number)
     (define-key map (kbd "i") 'elpher-info-link)
     (define-key map (kbd "I") 'elpher-info-current)
     (define-key map (kbd "c") 'elpher-copy-link-url)
@@ -2155,7 +2152,6 @@ When run interactively HOST-OR-URL is read from the minibuffer."
     (define-key map (kbd "B") 'bookmark-bmenu-list)
     (define-key map (kbd "S") 'elpher-set-gopher-coding-system)
     (define-key map (kbd "F") 'elpher-forget-current-certificate)
-    (define-key map (kbd "v") 'elpher-visit-gemini-numbered-link)
     (when (fboundp 'evil-define-key*)
       (evil-define-key*
        'motion map
@@ -2165,8 +2161,8 @@ When run interactively HOST-OR-URL is read from the minibuffer."
        (kbd "u") 'elpher-back
        (kbd "-") 'elpher-back
        (kbd "^") 'elpher-back
-       (kbd "U") 'elpher-back-to-start
        [mouse-3] 'elpher-back
+       (kbd "U") 'elpher-back-to-start
        (kbd "o") 'elpher-go
        (kbd "O") 'elpher-go-current
        (kbd "r") 'elpher-redraw
@@ -2175,7 +2171,8 @@ When run interactively HOST-OR-URL is read from the minibuffer."
        (kbd ".") 'elpher-view-raw
        (kbd "d") 'elpher-download
        (kbd "D") 'elpher-download-current
-       (kbd "J") 'elpher-jump
+       (kbd "m") 'elpher-jump
+       (kbd "M") 'elpher-jump-to-number
        (kbd "i") 'elpher-info-link
        (kbd "I") 'elpher-info-current
        (kbd "c") 'elpher-copy-link-url
@@ -2184,8 +2181,7 @@ When run interactively HOST-OR-URL is read from the minibuffer."
        (kbd "A") 'bookmark-set-no-overwrite
        (kbd "B") 'bookmark-bmenu-list
        (kbd "S") 'elpher-set-gopher-coding-system
-       (kbd "F") 'elpher-forget-current-certificate
-       (kbd "v") 'elpher-visit-gemini-numbered-link))
+       (kbd "F") 'elpher-forget-current-certificate))
     map)
   "Keymap for gopher client.")