X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?a=blobdiff_plain;ds=sidebyside;f=elpher.el;h=8230377430bcd1627b6bbd7ebd23ed740aaea257;hb=42b70bca5bd23de3de35ec95a196d00cdf88dc61;hp=f2a8188d495178e4cd59248961bb01ddae267f5f;hpb=7c57225da0abd31b1c1da3814737410c49f2b868;p=elpher.git diff --git a/elpher.el b/elpher.el index f2a8188..8230377 100644 --- a/elpher.el +++ b/elpher.el @@ -4,7 +4,7 @@ ;; Author: Tim Vaughan ;; Created: 11 April 2019 -;; Version: 2.9.1 +;; Version: 2.10.0 ;; Keywords: comm gopher ;; Homepage: http://thelambdalab.xyz/elpher ;; Package-Requires: ((emacs "26.2")) @@ -71,7 +71,7 @@ ;;; Global constants ;; -(defconst elpher-version "2.9.1" +(defconst elpher-version "2.10.0" "Current version of elpher.") (defconst elpher-margin-width 6 @@ -100,6 +100,15 @@ "Association list from types to getters, renderers, margin codes and index faces.") +;;; Internal variables +;; + +(defvar elpher--gemini-page-links '() + "Internal variable containing list of links on page.") + +(defvar elpher--gemini-page-links-cache (make-hash-table :test 'equal) + "Internal variable containing hash of addresses and page links.") + ;;; Customization group ;; @@ -178,6 +187,11 @@ This can be useful when browsing from a computer that supports IPv6, because some servers which do not support IPv6 can take a long time to time-out." :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." + :type '(boolean)) + ;; Face customizations (defgroup elpher-faces nil @@ -457,12 +471,17 @@ unless NO-HISTORY is non-nil." (setq elpher-current-page page) (let* ((address (elpher-page-address page)) (type (elpher-address-type address)) - (type-record (cdr (assoc type elpher-type-map)))) + (type-record (cdr (assoc type elpher-type-map))) + (page-links nil)) (if type-record - (funcall (car type-record) - (if renderer - renderer - (cadr 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))) (elpher-visit-previous-page) (pcase type (`(gopher ,type-char) @@ -763,8 +782,8 @@ longer needed for this session." (cert-file (concat temporary-file-directory file-base ".crt"))) (elpher-generate-certificate file-base key-file cert-file t))) -(defun elpher-generate-permanent-certificate (file-base common-name) - "Generate and return details of a persistant certificate. +(defun elpher-generate-persistent-certificate (file-base common-name) + "Generate and return details of a persistent certificate. The argument FILE-BASE is used as the base for the key and certificate files, while COMMON-NAME specifies the common name field of the certificate. @@ -803,7 +822,7 @@ base for the installed key and certificate files." (expand-file-name cert-file)))) (defun elpher-list-existing-certificates () - "Return a list of the persistant certificates in `elpher-certificate-directory'." + "Return a list of the persistent certificates in `elpher-certificate-directory'." (mapcar (lambda (file) (file-name-sans-extension file)) @@ -1190,13 +1209,13 @@ that the response was malformed." (pcase (read-answer "What do you want to do? " '(("throwaway" ?t "generate and use throw-away certificate") - ("persistant" ?p - "generate new or use existing persistant certificate") + ("persistent" ?p + "generate new or use existing persistent certificate") ("abort" ?a "stop immediately"))) ("throwaway" (setq elpher-client-certificate (elpher-generate-throwaway-certificate))) - ("persistant" + ("persistent" (let* ((existing-certificates (elpher-list-existing-certificates)) (file-base (completing-read "Nickname for new or existing certificate (autocompletes, empty response aborts): " @@ -1218,7 +1237,7 @@ that the response was malformed." file-base))) (message "New key and self-signed certificate written to %s" elpher-certificate-directory) - (elpher-generate-permanent-certificate file-base common-name))) + (elpher-generate-persistent-certificate file-base common-name))) ("install" (let* ((cert-file (read-file-name "Certificate file: " nil nil t)) (key-file (read-file-name "Key file: " nil nil t))) @@ -1343,6 +1362,7 @@ 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 @@ -1397,21 +1417,36 @@ width defined by elpher-gemini-max-fill-width." (defun elpher-render-gemini-map (data _parameters) "Render DATA as a gemini map file, PARAMETERS is currently unused." (elpher-with-clean-buffer - (let ((preformatted nil)) + (let ((preformatted nil) + (link-counter 1)) (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))) (preformatted (insert (elpher-process-text-for-display (propertize line 'face 'elpher-gemini-preformatted)) "\n")) - ((string-prefix-p "=>" line) (elpher-gemini-insert-link line)) + ((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))) ((string-prefix-p "#" line) (elpher-gemini-insert-header line)) (t (elpher-gemini-insert-text line))))) (elpher-cache-content (elpher-page-address elpher-current-page) - (buffer-string)))) + (buffer-string)) + (puthash + (elpher-page-address elpher-current-page) + elpher--gemini-page-links + elpher--gemini-page-links-cache))) (defun elpher-render-gemini-plain-text (data _parameters) "Render DATA as plain text file. PARAMETERS is currently unused." @@ -1502,6 +1537,7 @@ The result is rendered using RENDERER." " - 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" @@ -1696,6 +1732,15 @@ When run interactively HOST-OR-URL is read from the minibuffer." (let ((url (read-string "Gopher or Gemini URL: " (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." + (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) @@ -1953,6 +1998,7 @@ When run interactively HOST-OR-URL is read from the minibuffer." (define-key map (kbd "B") 'elpher-bookmarks) (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 (kbd "TAB") 'elpher-next-link @@ -1980,7 +2026,8 @@ When run interactively HOST-OR-URL is read from the minibuffer." (kbd "X") 'elpher-unbookmark-current (kbd "B") 'elpher-bookmarks (kbd "S") 'elpher-set-gopher-coding-system - (kbd "F") 'elpher-forget-current-certificate)) + (kbd "F") 'elpher-forget-current-certificate + (kbd "v") 'elpher-visit-gemini-numbered-link)) map) "Keymap for gopher client.")