X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=elpher.git;a=blobdiff_plain;f=elpher.el;h=f94b33f22c3b42cf6d3540da8a20ce60a2752872;hp=662e451ff7638fb23178788db045fde42338aa86;hb=e3fca3512d458c4a78ef9b3d87b66e952f2b5f94;hpb=3cb3110e90a8f9f71bd49e6bbd2781f2e9a50285 diff --git a/elpher.el b/elpher.el index 662e451..f94b33f 100644 --- a/elpher.el +++ b/elpher.el @@ -81,7 +81,6 @@ (require 'nsm) (require 'gnutls) (require 'socks) -(require 'ol) ;;; ANSI colors or XTerm colors @@ -91,8 +90,8 @@ (defalias 'elpher-color-filter-apply (if (fboundp 'xterm-color-filter) (lambda (s) - (let ((xterm-color-render nil)) - (xterm-color-filter s))) + (let ((xterm-color-render nil)) + (xterm-color-filter s))) 'ansi-color-filter-apply) "A function to filter out ANSI escape sequences.") (defalias 'elpher-color-apply @@ -157,7 +156,7 @@ (defcustom elpher-open-urls-with-eww nil "If non-nil, open URL selectors using eww. -Otherwise, use the system browser via the BROWSE-URL function." +Otherwise, use the system browser via the `browse-url' function." :type '(boolean)) (defcustom elpher-use-header t @@ -511,8 +510,8 @@ If no address is defined, returns 0. (This is for compatibility with the URL li "Set the address corresponding to PAGE to NEW-ADDRESS." (setcar (cdr page) new-address)) -(defvar elpher-current-page nil) ; buffer local -(defvar elpher-history nil) ; buffer local +(defvar elpher-current-page nil) ; buffer local +(defvar elpher-history nil) ; buffer local (defun elpher-visit-page (page &optional renderer no-history) "Visit PAGE using its own renderer or RENDERER, if non-nil. @@ -759,10 +758,10 @@ the host operating system and the local network capabilities." (when (> new-hkbytes-received hkbytes-received) (setq hkbytes-received new-hkbytes-received) (elpher-buffer-message - (concat "(" - (number-to-string (/ hkbytes-received 10.0)) - " MB read)") - 1))) + (concat "(" + (number-to-string (/ hkbytes-received 10.0)) + " MB read)") + 1))) (setq response-string-parts (cons string response-string-parts)))) (set-process-sentinel proc @@ -1066,13 +1065,13 @@ If ADDRESS is not supplied or nil the record is rendered as an (while (re-search-forward elpher-url-regex nil t) (let ((page (elpher-make-page (substring-no-properties (match-string 0)) (elpher-address-from-url (match-string 0))))) - (make-text-button (match-beginning 0) - (match-end 0) - 'elpher-page page - 'action #'elpher-click-link - 'follow-link t - 'help-echo #'elpher--page-button-help - 'face 'button))) + (make-text-button (match-beginning 0) + (match-end 0) + 'elpher-page page + 'action #'elpher-click-link + 'follow-link t + 'help-echo #'elpher--page-button-help + 'face 'button))) (buffer-string))) (defconst elpher-ansi-regex "\x1b\\[[^m]*m" @@ -1116,9 +1115,9 @@ Currently includes buttonifying URLs and processing ANSI escape codes." (defun elpher-get-gopher-query-page (renderer) "Getter for gopher addresses requiring input. The response is rendered using the rendering function RENDERER." - (let* ((address (elpher-page-address elpher-current-page)) - (content (elpher-get-cached-content address)) - (aborted t)) + (let* ((address (elpher-page-address elpher-current-page)) + (content (elpher-get-cached-content address)) + (aborted t)) (if (and content (funcall renderer nil)) (elpher-with-clean-buffer (insert content) @@ -1272,6 +1271,11 @@ that the response was malformed." (error "Gemini server response unknown: %s %s" response-code response-meta)))))) +(unless (fboundp 'read-answer) + (defun read-answer (question answers) + "Backfill for the new read-answer code." + (completing-read question (mapcar 'identity answers)))) + (defun elpher-choose-client-certificate () "Prompt for a client certificate to use to establish a TLS connection." (let* ((read-answer-short t)) @@ -1325,8 +1329,8 @@ that the response was malformed." (condition-case the-error (if (and content (funcall renderer nil)) (elpher-with-clean-buffer - (insert content) - (elpher-restore-pos)) + (insert content) + (elpher-restore-pos)) (elpher-with-clean-buffer (insert "LOADING GEMINI... (use 'u' to cancel)\n")) (setq elpher-gemini-redirect-chain nil) @@ -1459,10 +1463,10 @@ by HEADER-LINE." (2 'elpher-gemini-heading2) (3 'elpher-gemini-heading3) (_ 'default))) - (fill-column (if (display-graphic-p) - (/ (* fill-column - (font-get (font-spec :name (face-font 'default)) :size)) - (font-get (font-spec :name (face-font face)) :size)) fill-column))) + (fill-column (if (display-graphic-p) + (/ (* fill-column + (font-get (font-spec :name (face-font 'default)) :size)) + (font-get (font-spec :name (face-font face)) :size)) fill-column))) (setq elpher--gemini-page-headings (cons (cons header (point)) elpher--gemini-page-headings)) (unless (display-graphic-p) @@ -1488,7 +1492,7 @@ width defined by `elpher-gemini-max-fill-width'." (propertize text-line 'face 'elpher-gemini-quoted)) (t text-line)) text-line)) - (adaptive-fill-mode nil)) + (adaptive-fill-mode t)) (insert (elpher-process-text-for-display processed-text-line)) (newline))) @@ -1780,35 +1784,84 @@ If ADDRESS is already bookmarked, update the label only." ;;; Integrations ;; +;;; Org + ;; Avoid byte compilation warnings. (eval-when-compile (declare-function org-link-store-props "ol") - (declare-function org-link-set-parameters "ol") - (defvar thing-at-point-uri-schemes)) - -(defun elpher-org-link-store () - "Store link to an `elpher' page in `org'." + (declare-function org-link-set-parameters "ol")) + +(defun elpher-org-export-link (link description format protocol) + "Export a LINK with DESCRIPTION for the given PROTOCOL and FORMAT. + +FORMAT is an Org export backend. DESCRIPTION may be nil. PROTOCOL may be one +of gemini, gopher or finger." + (let* ((url (if (equal protocol "elpher") + (string-remove-prefix "elpher:" link) + (format "%s:%s" protocol link))) + (desc (or description url))) + (pcase format + (`gemini (format "=> %s %s" url desc)) + (`html (format "%s" url desc)) + (`latex (format "\\href{%s}{%s}" url desc)) + (_ (if (not description) + url + (format "%s (%s)" desc url)))))) + +(defun elpher-org-store-link () + "Store link to an `elpher' page in Org." (when (eq major-mode 'elpher-mode) - (let ((link (concat "elpher:" (elpher-info-current))) - (desc (car elpher-current-page))) - (org-link-store-props :type "elpher" - :link link - :description desc) + (let* ((url (elpher-info-current)) + (desc (car elpher-current-page)) + (protocol (cond + ((string-prefix-p "gemini:" url) "gemini") + ((string-prefix-p "gopher:" url) "gopher") + ((string-prefix-p "finger:" url) "finger") + (t "elpher")))) + (when (equal "elpher" protocol) + ;; Weird link. Or special inner link? + (setq url (concat "elpher:" url))) + (org-link-store-props :type protocol :link url :description desc) t))) -(defun elpher-org-link-follow (link _args) - "Follow an `elpher' LINK in an `org' buffer." - (require 'elpher) - (message (concat "Got link: " link)) - (when (or - (string-match-p "^gemini://.+" link) - (string-match-p "^gopher://.+" link) - (string-match-p "^finger://.+" link)) - (elpher-go (string-remove-prefix "elpher:" link)))) +(defun elpher-org-follow-link (link protocol) + "Visit a LINK for the given PROTOCOL. + +PROTOCOL may be one of gemini, gopher or finger. This method also +supports the old protocol elpher, where the link is self-contained." + (let ((url (if (equal protocol "elpher") + (string-remove-prefix "elpher:" link) + (format "%s:%s" protocol link)))) + (elpher-go url))) + +(with-eval-after-load 'org + (org-link-set-parameters + "elpher" + :store #'elpher-org-store-link + :export (lambda (link description format _plist) + (elpher-org-export-link link description format "elpher")) + :follow (lambda (link _arg) (elpher-org-follow-link link "elpher"))) + (org-link-set-parameters + "gemini" + :export (lambda (link description format _plist) + (elpher-org-export-link link description format "gemini")) + :follow (lambda (link _arg) (elpher-org-follow-link link "gemini"))) + (org-link-set-parameters + "gopher" + :export (lambda (link description format _plist) + (elpher-org-export-link link description format "gopher")) + :follow (lambda (link _arg) (elpher-org-follow-link link "gopher"))) + (org-link-set-parameters + "finger" + :export (lambda (link description format _plist) + (elpher-org-export-link link description format "finger")) + :follow (lambda (link _arg) (elpher-org-follow-link link "finger")))) + +;;; Browse URL -(org-link-set-parameters "elpher" - :store #'elpher-org-link-store - :follow #'elpher-org-link-follow) +;; Avoid byte compilation warnings. +(eval-when-compile + (defvar thing-at-point-uri-schemes)) ;;;###autoload (defun elpher-browse-url-elpher (url &rest _args) @@ -1816,12 +1869,39 @@ If ADDRESS is already bookmarked, update the label only." (interactive (browse-url-interactive-arg "Elpher URL: ")) (elpher-go url)) -(add-to-list - 'browse-url-default-handlers - '("^\\(gopher\\|finger\\|gemini\\)://" . elpher-browse-url-elpher)) +;; Use elpher to open gopher, finger and gemini links +;; For recent version of `browse-url' package +(if (boundp 'browse-url-default-handlers) + (add-to-list + 'browse-url-default-handlers + '("^\\(gopher\\|finger\\|gemini\\)://" . elpher-browse-url-elpher)) + ;; Patch `browse-url-browser-function' for older ones. The value of + ;; that variable is `browse-url-default-browser' by default, so + ;; that's the function that gets advised. + (advice-add browse-url-browser-function :before-while + (lambda (url &rest _args) + "Handle gemini, gopher, and finger schemes using Elpher." + (let ((scheme (downcase (car (split-string url ":" t))))) + (if (member scheme '("gemini" "gopher" "finger")) + ;; `elpher-go' always returns nil, which will stop the + ;; advice chain here in a before-while + (elpher-go url) + ;; chain must continue, then return t. + t))))) ;; Register "gemini://" as a URI scheme so `browse-url' does the right thing -(add-to-list 'thing-at-point-uri-schemes "gemini://") +(with-eval-after-load 'thingatpt + (add-to-list 'thing-at-point-uri-schemes "gemini://")) + +;;; Mu4e: + +(eval-when-compile + (defvar mu4e~view-beginning-of-url-regexp)) + +(with-eval-after-load 'mu4e-view + ;; Make mu4e aware of the gemini world + (setq mu4e~view-beginning-of-url-regexp + "\\(?:https?\\|gopher\\|finger\\|gemini\\)://\\|mailto:")) ;;; Interactive procedures ;; @@ -2131,36 +2211,37 @@ When run interactively HOST-OR-URL is read from the minibuffer." (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 - (kbd "C-") 'elpher-follow-current-link - (kbd "C-t") 'elpher-back - (kbd "u") 'elpher-back - (kbd "-") 'elpher-back - (kbd "^") 'elpher-back - (kbd "U") 'elpher-back-to-start - [mouse-3] 'elpher-back - (kbd "o") 'elpher-go - (kbd "O") 'elpher-go-current - (kbd "r") 'elpher-redraw - (kbd "R") 'elpher-reload - (kbd "T") 'elpher-toggle-tls - (kbd ".") 'elpher-view-raw - (kbd "d") 'elpher-download - (kbd "D") 'elpher-download-current - (kbd "J") 'elpher-jump - (kbd "i") 'elpher-info-link - (kbd "I") 'elpher-info-current - (kbd "c") 'elpher-copy-link-url - (kbd "C") 'elpher-copy-current-url - (kbd "a") 'elpher-bookmark-link - (kbd "A") 'elpher-bookmark-current - (kbd "x") 'elpher-unbookmark-link - (kbd "X") 'elpher-unbookmark-current - (kbd "B") 'elpher-bookmarks - (kbd "S") 'elpher-set-gopher-coding-system - (kbd "F") 'elpher-forget-current-certificate - (kbd "v") 'elpher-visit-gemini-numbered-link)) + (evil-define-key* + 'motion map + (kbd "TAB") 'elpher-next-link + (kbd "C-") 'elpher-follow-current-link + (kbd "C-t") 'elpher-back + (kbd "u") 'elpher-back + (kbd "-") 'elpher-back + (kbd "^") 'elpher-back + (kbd "U") 'elpher-back-to-start + [mouse-3] 'elpher-back + (kbd "o") 'elpher-go + (kbd "O") 'elpher-go-current + (kbd "r") 'elpher-redraw + (kbd "R") 'elpher-reload + (kbd "T") 'elpher-toggle-tls + (kbd ".") 'elpher-view-raw + (kbd "d") 'elpher-download + (kbd "D") 'elpher-download-current + (kbd "J") 'elpher-jump + (kbd "i") 'elpher-info-link + (kbd "I") 'elpher-info-current + (kbd "c") 'elpher-copy-link-url + (kbd "C") 'elpher-copy-current-url + (kbd "a") 'elpher-bookmark-link + (kbd "A") 'elpher-bookmark-current + (kbd "x") 'elpher-unbookmark-link + (kbd "X") 'elpher-unbookmark-current + (kbd "B") 'elpher-bookmarks + (kbd "S") 'elpher-set-gopher-coding-system + (kbd "F") 'elpher-forget-current-certificate + (kbd "v") 'elpher-visit-gemini-numbered-link)) map) "Keymap for gopher client.") @@ -2198,18 +2279,19 @@ that number, creating it if necessary. A non numeric prefix ARG means to create a new session. Returns the buffer selected (or created)." (interactive "P") (let* ((name (default-value 'elpher-buffer-name)) - (buf (cond ((numberp arg) - (get-buffer-create (format "%s<%d>" name arg))) - (arg - (generate-new-buffer name)) - (t - (get-buffer-create name))))) + (buf (cond ((numberp arg) + (get-buffer-create (format "%s<%d>" name arg))) + (arg + (generate-new-buffer name)) + (t + (get-buffer-create name))))) (pop-to-buffer-same-window buf) (unless (buffer-modified-p) (elpher-mode) - (let ((start-page (elpher-make-page "Elpher Start Page" - (elpher-make-special-address 'start)))) - (elpher-visit-page start-page)) + (let ((start-page (elpher-make-page + "Elpher Start Page" + (elpher-make-special-address 'start)))) + (elpher-visit-page start-page)) "Started Elpher."))); Otherwise (elpher) evaluates to start page string. ;;; elpher.el ends here