Improved inline help for finger getter.
[elpher.git] / elpher.el
index e508c61..a1811c2 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,7 +4,7 @@
 
 ;; Author: Tim Vaughan <plugd@thelambdalab.xyz>
 ;; Created: 11 April 2019
-;; Version: 2.7.5
+;; Version: 2.7.8
 ;; Keywords: comm gopher
 ;; Homepage: http://thelambdalab.xyz/elpher
 ;; Package-Requires: ((emacs "26"))
@@ -70,7 +70,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "2.7.5"
+(defconst elpher-version "2.7.8"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
@@ -148,6 +148,15 @@ The actual width used is the minimum of this value and the window width at
 the time when the text is rendered."
   :type '(integer))
 
+(defcustom elpher-gemini-link-string "→ "
+  "Specify the string used to indicate links when rendering gemini maps.
+May be empty."
+  :type '(string))
+
+(defcustom elpher-gemini-bullet-string "•"
+  "Specify the string used for bullets when rendering gemini maps."
+  :type '(string))
+
 (defcustom elpher-bookmarks-file (locate-user-emacs-file "elpher-bookmarks")
   "Specify the name of the file where elpher bookmarks will be saved."
   :type '(file))
@@ -222,6 +231,10 @@ the time when the text is rendered."
   '((t :inherit bold :height 1.2))
   "Face used for gemini heading level 3.")
 
+(defface elpher-gemini-preformatted
+  '((t :inherit fixed-pitch))
+  "Face used for pre-formatted gemini text blocks.")
+
 ;;; Model
 ;;
 
@@ -354,7 +367,7 @@ If no address is defined, returns 0.  (This is for compatibility with the URL li
   "Retrieve gopher selector from ADDRESS object."
   (if (member (url-filename address) '("" "/"))
       ""
-    (substring (url-filename address) 2)))
+    (url-unhex-string (substring (url-filename address) 2))))
 
 
 ;; Cache
@@ -673,12 +686,19 @@ once they are retrieved from the gopher server."
         (insert " "))
     (insert (make-string elpher-margin-width ?\s))))
 
-(defun elpher-page-button-help (page)
-  "Return a string containing the help text for a button corresponding to PAGE."
-  (let ((address (elpher-page-address page)))
-    (format "mouse-1, RET: open '%s'" (if (elpher-address-special-p address)
-                                          address
-                                        (elpher-address-to-url address)))))
+(defun elpher--page-button-help (_window buffer pos)
+  "Function called by Emacs to generate mouse-over text.
+The arguments specify the BUFFER and the POS within the buffer of the item
+for which help is required.  The function returns the help to be
+displayed.  The _WINDOW argument is currently unused."
+  (with-current-buffer buffer
+    (let ((button (button-at pos)))
+      (when button
+        (let* ((page (button-get button 'elpher-page))
+               (address (elpher-page-address page)))
+          (format "mouse-1, RET: open '%s'" (if (elpher-address-special-p address)
+                                                address
+                                              (url-recreate-url address))))))))
 
 (defun elpher-insert-index-record (display-string &optional address)
   "Function to insert an index record into the current buffer.
@@ -698,7 +718,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
                               'elpher-page page
                               'action #'elpher-click-link
                               'follow-link t
-                              'help-echo (elpher-page-button-help page)))
+                              'help-echo #'elpher--page-button-help))
       (pcase type
         ('nil ;; Information
          (elpher-insert-margin)
@@ -744,7 +764,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
                             'elpher-page  page
                             'action #'elpher-click-link
                             'follow-link t
-                            'help-echo (elpher-page-button-help page)
+                            'help-echo #'elpher--page-button-help
                             'face 'button)))
     (buffer-string)))
 
@@ -874,6 +894,7 @@ to ADDRESS."
       (error "Cannot establish gemini connection: GnuTLS not available")
     (unless (< (elpher-address-port address) 65536)
       (error "Cannot establish gemini connection: port number > 65536"))
+    (defvar gnutls-verify-error)
     (condition-case nil
         (let* ((kill-buffer-query-functions nil)
                (gnutls-verify-error nil) ; We use the NSM for verification
@@ -1111,7 +1132,7 @@ For instance, the filename /a/b/../c/./d will reduce to /a/c/d"
          (type (if address (elpher-address-type address) nil))
          (type-map-entry (cdr (assoc type elpher-type-map))))
     (when display-string
-      (insert "→ ")
+      (insert elpher-gemini-link-string)
       (if type-map-entry
           (let* ((face (elt type-map-entry 3))
                  (filtered-display-string (ansi-color-filter-apply display-string))
@@ -1121,7 +1142,7 @@ For instance, the filename /a/b/../c/./d will reduce to /a/c/d"
                                 'elpher-page page
                                 'action #'elpher-click-link
                                 'follow-link t
-                                'help-echo (elpher-page-button-help page)))
+                                'help-echo #'elpher--page-button-help))
         (insert (propertize display-string 'face 'elpher-unknown)))
       (insert "\n"))))
   
@@ -1142,6 +1163,24 @@ by HEADER-LINE."
                             (_ 'default)))
               "\n"))))
 
+(defun elpher-gemini-insert-text (text-line)
+  "Insert a plain non-preformatted TEXT-LINE into a text/gemini document.
+This function uses Emacs' auto-fill to wrap text sensibly to a maximum
+width defined by elpher-gemini-max-fill-width."
+  (string-match "\\(^[ \t]*\\)\\(\*[ \t]\\)?" text-line)
+  (let* ((processed-text-line (if (match-string 2 text-line)
+                                  (concat
+                                   (replace-regexp-in-string "\*"
+                                                             elpher-gemini-bullet-string
+                                                             (match-string 0 text-line))
+                                   (substring text-line (match-end 0)))
+                                text-line))
+         (fill-prefix (if (match-string 1 text-line)
+                          (replace-regexp-in-string "\*" " " (match-string 0 text-line))
+                        nil)))
+    (insert (elpher-process-text-for-display processed-text-line))
+    (newline)))
+
 (defun elpher-render-gemini-map (data _parameters)
   "Render DATA as a gemini map file, PARAMETERS is currently unused."
   (elpher-with-clean-buffer
@@ -1151,10 +1190,12 @@ by HEADER-LINE."
      (dolist (line (split-string data "\n"))
        (cond
         ((string-prefix-p "```" line) (setq preformatted (not preformatted)))
-        (preformatted (insert (elpher-process-text-for-display line) "\n"))
+        (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) (elpher-gemini-insert-header line))
-        (t (insert (elpher-process-text-for-display line)) (newline)))))
+        (t (elpher-gemini-insert-text line)))))
    (elpher-cache-content
     (elpher-page-address elpher-current-page)
     (buffer-string))))
@@ -1170,7 +1211,10 @@ by HEADER-LINE."
 ;; Finger page connection
 
 (defun elpher-get-finger-page (renderer &optional force-ipv4)
-  "Opens a finger connection to the current page address and renders it using RENDERER."
+  "Opens a finger connection to the current page address.
+The result is rendered using RENDERER.  When the optional argument
+FORCE-IPV4 is non-nil, the IPv4 address returned by a DNS lookup will
+be used explicitly in making the connection."
   (let* ((address (elpher-page-address elpher-current-page))
          (content (elpher-get-cached-content address)))
     (if (and content (funcall renderer nil))
@@ -1215,7 +1259,7 @@ by HEADER-LINE."
                                         (cons string selector-string-parts))))
             (set-process-sentinel proc
                                   (lambda (_proc event)
-                                    (condition-case the-error
+                                    (condition-case _the-error
                                         (cond
                                          ((string-prefix-p "deleted" event))
                                          ((string-prefix-p "open" event)
@@ -1773,6 +1817,7 @@ functions which initialize the gopher client, namely
       (switch-to-buffer "*elpher*")
     (switch-to-buffer "*elpher*")
     (setq elpher-current-page nil)
+    (setq elpher-history nil)
     (let ((start-page (elpher-make-page "Elpher Start Page"
                                         (elpher-make-special-address 'start))))
       (elpher-visit-page start-page)))