Debugging after refactor: now functional.
authorTim Vaughan <tgvaughan@gmail.com>
Wed, 11 Sep 2019 11:48:20 +0000 (13:48 +0200)
committerTim Vaughan <tgvaughan@gmail.com>
Wed, 11 Sep 2019 11:48:20 +0000 (13:48 +0200)
elpher.el

index c22f7b8..3735345 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -1,4 +1,4 @@
-;;; elpher.el --- A friendly gopher client.
+;;; elpher.el --- A friendly gopher client.  -*- lexical-binding:t -*-
 
 ;; Copyright (C) 2019 Tim Vaughan
 
 
 ;; Copyright (C) 2019 Tim Vaughan
 
@@ -428,9 +428,6 @@ away CRs and any terminating period."
                                            (replace-regexp-in-string "\r" "" string))))
 
 
                                            (replace-regexp-in-string "\r" "" string))))
 
 
-;;; Index rendering
-;;
-
 ;;; Network error reporting
 ;;
 
 ;;; Network error reporting
 ;;
 
@@ -438,12 +435,12 @@ away CRs and any terminating period."
   (elpher-with-clean-buffer
    (insert (propertize "\n---- ERROR -----\n\n" 'face 'error)
            "When attempting to retrieve " (elpher-address-to-url address) ":\n"
   (elpher-with-clean-buffer
    (insert (propertize "\n---- ERROR -----\n\n" 'face 'error)
            "When attempting to retrieve " (elpher-address-to-url address) ":\n"
-           (error-message-string the-error) ".\n"
+           (error-message-string error) ".\n"
            (propertize "\n----------------\n\n" 'face 'error)
            "Press 'u' to return to the previous page.")))
 
 
            (propertize "\n----------------\n\n" 'face 'error)
            "Press 'u' to return to the previous page.")))
 
 
-;;; Gopher selector retrieval (all kinds)
+;;; Gopher selector retrieval
 ;;
 
 (defun elpher-process-cleanup ()
 ;;
 
 (defun elpher-process-cleanup ()
@@ -511,10 +508,9 @@ up to the calling function."
   (let* ((address (elpher-node-address elpher-current-node))
          (content (elpher-get-cached-content address)))
     (if (and content (funcall renderer nil))
   (let* ((address (elpher-node-address elpher-current-node))
          (content (elpher-get-cached-content address)))
     (if (and content (funcall renderer nil))
-        (progn
-          (elpher-with-clean-buffer)
-          (insert content)
-          (elpher-restore-pos))
+        (elpher-with-clean-buffer
+         (insert content)
+         (elpher-restore-pos))
       (elpher-with-clean-buffer
        (insert "LOADING... (use 'u' to cancel)"))
       (elpher-get-selector address
       (elpher-with-clean-buffer
        (insert "LOADING... (use 'u' to cancel)"))
       (elpher-get-selector address
@@ -568,8 +564,8 @@ If ADDRESS is not supplied or nil the record is rendered as an
   (let* ((type (if address (elpher-address-type address) nil))
          (type-map-entry (cdr (assoc type elpher-type-map))))
     (if type-map-entry
   (let* ((type (if address (elpher-address-type address) nil))
          (type-map-entry (cdr (assoc type elpher-type-map))))
     (if type-map-entry
-        (let* ((margin-code (elt type-map-entry 1))
-               (face (elt type-map-entry 2))
+        (let* ((margin-code (elt type-map-entry 2))
+               (face (elt type-map-entry 3))
                (node (elpher-make-node display-string address)))
           (elpher-insert-margin margin-code)
           (insert-text-button display-string
                (node (elpher-make-node display-string address)))
           (elpher-insert-margin margin-code)
           (insert-text-button display-string
@@ -597,8 +593,8 @@ If ADDRESS is not supplied or nil the record is rendered as an
   (let ((node (button-get button 'elpher-node)))
     (elpher-visit-node node)))
 
   (let ((node (button-get button 'elpher-node)))
     (elpher-visit-node node)))
 
-(defun elpher-render-index (data)
-  "Render DATA as an index."
+(defun elpher-render-index (data &optional mime-type-string)
+  "Render DATA as an index, MIME-TYPE-STRING is unused"
   (elpher-with-clean-buffer
    (if (not data)
        t
   (elpher-with-clean-buffer
    (if (not data)
        t
@@ -628,24 +624,22 @@ If ADDRESS is not supplied or nil the record is rendered as an
                             'help-echo (elpher-node-button-help node))))
     (buffer-string)))
 
                             'help-echo (elpher-node-button-help node))))
     (buffer-string)))
 
-(defun elpher-render-text (data)
-  "Render DATA as text."
+(defun elpher-render-text (data &optional mime-type-string)
+  "Render DATA as text, MIME-TYPE-STRING is unused."
   (elpher-with-clean-buffer
    (if (not data)
        t
   (elpher-with-clean-buffer
    (if (not data)
        t
-     (insert (elpher-buttonify-urls
-              (elpher-preprocess-text-response)
-              elpher-selector-string))
+     (insert (elpher-buttonify-urls (elpher-preprocess-text-response data)))
      (elpher-cache-content
       (elpher-node-address elpher-current-node)
       (buffer-string)))))
 
 ;; Image retrieval
 
      (elpher-cache-content
       (elpher-node-address elpher-current-node)
       (buffer-string)))))
 
 ;; Image retrieval
 
-(defun elpher-render-image (data)
-  "Display DATA as image."
+(defun elpher-render-image (data &optional mime-type-string)
+  "Display DATA as image, MIME-TYPE-STRING is unused."
   (if (not data)
   (if (not data)
-      f
+      nil
     (if (display-images-p)
         (progn
           (let ((image (create-image
     (if (display-images-p)
         (progn
           (let ((image (create-image
@@ -689,10 +683,10 @@ If ADDRESS is not supplied or nil the record is rendered as an
  
 ;; Raw server response rendering
 
  
 ;; Raw server response rendering
 
-(defun elpher-render-raw (data)
-  "Display raw DATA in buffer."
+(defun elpher-render-raw (data &optional mime-type-string)
+  "Display raw DATA in buffer, MIME-TYPE-STRING is unused."
   (if (not data)
   (if (not data)
-      f
+      nil
     (elpher-with-clean-buffer
      (insert data)
      (goto-char (point-min)))
     (elpher-with-clean-buffer
      (insert data)
      (goto-char (point-min)))
@@ -700,10 +694,10 @@ If ADDRESS is not supplied or nil the record is rendered as an
 
 ;; File save "rendering"
 
 
 ;; File save "rendering"
 
-(defun elpher-render-download (data)
-  "Save DATA to file."
+(defun elpher-render-download (data &optional mime-type-string)
+  "Save DATA to file, MIME-TYPE-STRING is unused."
   (if (not data)
   (if (not data)
-      f
+      nil
     (let* ((address (elpher-node-address elpher-current-node))
            (selector (elpher-gopher-address-selector address)))
       (elpher-visit-parent-node) ; Do first in case of non-local exits.
     (let* ((address (elpher-node-address elpher-current-node))
            (selector (elpher-gopher-address-selector address)))
       (elpher-visit-parent-node) ; Do first in case of non-local exits.
@@ -712,16 +706,16 @@ If ADDRESS is not supplied or nil the record is rendered as an
                                        nil nil nil
                                        (if (> (length filename-proposal) 0)
                                            filename-proposal
                                        nil nil nil
                                        (if (> (length filename-proposal) 0)
                                            filename-proposal
-                                         "gopher.file"))))
-        (with-temp-file filename
-          (insert elpher-selector-string)
-          (message (format "Saved to file %s."
-                           elpher-download-filename)))))))
+                                         "download.file"))))
+        (let ((coding-system-for-write 'binary))
+          (with-temp-file filename
+            (insert data)))
+        (message (format "Saved to file %s." filename))))))
 
 ;; HTML rendering
 
 
 ;; HTML rendering
 
-(defun elpher-render-html (data)
-  "Render DATA as HTML using shr."
+(defun elpher-render-html (data &optional mime-type-string)
+  "Render DATA as HTML using shr, MIME-TYPE-STRING is unused."
   (elpher-with-clean-buffer
    (if (not data)
        t
   (elpher-with-clean-buffer
    (if (not data)
        t
@@ -734,9 +728,8 @@ If ADDRESS is not supplied or nil the record is rendered as an
 
 (defvar elpher-gemini-response)
 
 
 (defvar elpher-gemini-response)
 
-
-(defun elpher-get-gemini-response (address renderer)
-  "Retrieve gemini ADDRESS, then execute RENDERER on the result.
+(defun elpher-get-gemini-response (address after)
+  "Retrieve gemini ADDRESS, then execute AFTER.
 The response is stored in the variable ‘elpher-gemini-response’."
   (setq elpher-gemini-response "")
   (if (not (gnutls-available-p))
 The response is stored in the variable ‘elpher-gemini-response’."
   (setq elpher-gemini-response "")
   (if (not (gnutls-available-p))
@@ -754,50 +747,56 @@ The response is stored in the variable ‘elpher-gemini-response’."
                           (lambda (proc string)
                             (setq elpher-gemini-response
                                   (concat elpher-gemini-response string))))
                           (lambda (proc string)
                             (setq elpher-gemini-response
                                   (concat elpher-gemini-response string))))
-      (set-process-sentinel proc
-                            (lambda (proc event)
-                              (unless (string-prefix-p "deleted" event)
-                                (elpher-process-gemini-response #'after))))
+      (set-process-sentinel proc after)
       (process-send-string proc
                            (concat (elpher-address-to-url address) "\r\n")))))
 
 
 (defun elpher-process-gemini-response (renderer)
       (process-send-string proc
                            (concat (elpher-address-to-url address) "\r\n")))))
 
 
 (defun elpher-process-gemini-response (renderer)
-  "Process the gemini response found in the variable elpher-gemini-response and
+  "Process the gemini response found in the variable `elpher-gemini-response' and
 pass the result to RENDERER."
   (condition-case the-error
 pass the result to RENDERER."
   (condition-case the-error
-      (unless (string-prefix-p "deleted" event)
-        (let* ((response-header (car (split-string elpher-gemini-response "\r\n")))
-               (response-body (substring elpher-gemini-response
-                                         (+ (string-match "\r\n" elpher-gemini-response) 2)))
-               (response-code (car (split-string response-header)))
-               (response-meta (string-trim
-                               (substring response-header
-                                          (string-match "[ \t]+" response-header)))))
-          (pcase (elt response-code 0)
-            (?1 ; Input required
-             (elpher-with-clean-buffer
-              (insert "Gemini server is requesting input."))
-             (let* ((query-string (read-string (concat response-meta ": ")))
-                    (url (elpher-address-to-url (elpher-node-address elpher-current-node)))
-                    (query-address (elpher-address-from-url (concat url "?" query-string))))
-               (elpher-get-gemini query-address #'renderer)))
-            (?2 ; Normal response
-             (message response-header)
-             (funcall #'renderer elpher-gemini-response))
-            (?3 ; Redirect
-             (message "Following redirect to %s" meta)
-             (let ((redirect-address (elpher-address-from-gemini-url meta)))
-               (elpher-get-gemini redirect-address #'renderer)))
-            (?4 ; Temporary failure
-             (error "Gemini server reports TEMPORARY FAILURE for this request"))
-            (?5 ; Permanent failure
-             (error "Gemini server reports PERMANENT FAILURE for this request"))
-            (?6 ; Client certificate required
-             (error "Gemini server requires client certificate (unsupported at this time)"))
-            (other
-             (error "Gemini server responded with unknown response code %S"
-                    response-code)))))
+      (let* ((response-header (car (split-string elpher-gemini-response "\r\n")))
+             (response-body (substring elpher-gemini-response
+                                       (+ (string-match "\r\n" elpher-gemini-response) 2)))
+             (response-code (car (split-string response-header)))
+             (response-meta (string-trim
+                             (substring response-header
+                                        (string-match "[ \t]+" response-header)))))
+        (pcase (elt response-code 0)
+          (?1 ; Input required
+           (elpher-with-clean-buffer
+            (insert "Gemini server is requesting input."))
+           (let* ((query-string (read-string (concat response-meta ": ")))
+                  (url (elpher-address-to-url (elpher-node-address elpher-current-node)))
+                  (query-address (elpher-address-from-url (concat url "?" query-string))))
+             (elpher-get-gemini-response query-address
+                                         (lambda (proc event)
+                                           (unless (string-prefix-p "deleted" event)
+                                             (funcall #'elpher-process-gemini-response
+                                                      renderer)
+                                             (elpher-restore-pos))))))
+          (?2 ; Normal response
+           (message response-header)
+           (funcall renderer response-body response-meta))
+          (?3 ; Redirect
+           (message "Following redirect to %s" response-meta)
+           (let ((redirect-address (elpher-address-from-gemini-url response-meta)))
+             (elpher-get-gemini-response redirect-address
+                                         (lambda (proc event)
+                                           (unless (string-prefix-p "deleted" event)
+                                             (funcall #'elpher-process-gemini-response
+                                                      renderer)
+                                             (elpher-restore-pos))))))
+          (?4 ; Temporary failure
+           (error "Gemini server reports TEMPORARY FAILURE for this request"))
+          (?5 ; Permanent failure
+           (error "Gemini server reports PERMANENT FAILURE for this request"))
+          (?6 ; Client certificate required
+           (error "Gemini server requires client certificate (unsupported at this time)"))
+          (other
+           (error "Gemini server responded with unknown response code %S"
+                  response-code))))
     (error
      (elpher-network-error (elpher-node-address elpher-current-node) the-error))))
 
     (error
      (elpher-network-error (elpher-node-address elpher-current-node) the-error))))
 
@@ -807,28 +806,27 @@ pass the result to RENDERER."
          (content (elpher-get-cached-content address)))
     (condition-case the-error
         (if (and content (funcall renderer nil))
          (content (elpher-get-cached-content address)))
     (condition-case the-error
         (if (and content (funcall renderer nil))
-            (progn
+            (elpher-with-clean-buffer
               (insert content)
               (elpher-restore-pos))
           (elpher-with-clean-buffer
            (insert "LOADING GEMINI... (use 'u' to cancel)"))
               (insert content)
               (elpher-restore-pos))
           (elpher-with-clean-buffer
            (insert "LOADING GEMINI... (use 'u' to cancel)"))
-          (elpher-get-gemini address
-                             (lambda (proc event)
-                               (unless (string-prefix-p "deleted" event)
-                                 (funcall renderer elpher-gemini-response)
-                                 (elpher-restore-pos)))))
+          (elpher-get-gemini-response address
+                                      (lambda (proc event)
+                                        (unless (string-prefix-p "deleted" event)
+                                          (funcall #'elpher-process-gemini-response
+                                                   renderer)
+                                          (elpher-restore-pos)))))
       (error
        (elpher-network-error address the-error)))))
 
 
       (error
        (elpher-network-error address the-error)))))
 
 
-(defun elpher-render-gemini (data)
-  "Render gemini response DATA."
-  (if (not data)
+(defun elpher-render-gemini (body &optional mime-type-string)
+  "Render gemini response BODY with rendering hints in META."
+  (if (not body)
       t
       t
-    (let* ((response-header (car (split-string data "\r\n")))
-           (response-body (substring data (+ (string-match "\r\n" data) 2)))
-           (mime-type-string (string-trim (substring response-header 2)))
-           (mime-type-string* (if (string-empty-p mime-type-string)
+    (let* ((mime-type-string* (if (or (not mime-type-string)
+                                      (string-empty-p mime-type-string))
                                   "text/gemini; charset=utf-8"
                                 mime-type-string))
            (mime-type-split (split-string mime-type-string* ";"))
                                   "text/gemini; charset=utf-8"
                                 mime-type-string))
            (mime-type-split (split-string mime-type-string* ";"))
@@ -850,11 +848,11 @@ pass the result to RENDERER."
               (replace-regexp-in-string "\r" "" elpher-gemini-response)))
       (pcase mime-type
         ((or "text/gemini" "")
               (replace-regexp-in-string "\r" "" elpher-gemini-response)))
       (pcase mime-type
         ((or "text/gemini" "")
-         (elpher-render-gemini-text/gemini response-body parameters))
+         (elpher-render-gemini-text/gemini body parameters))
         ((pred (string-prefix-p "text/"))
         ((pred (string-prefix-p "text/"))
-         (elpher-render-gemini-text/plain response-body parameters))
+         (elpher-render-gemini-text/plain body parameters))
         ((pred (string-prefix-p "image/"))
         ((pred (string-prefix-p "image/"))
-         (elpher-render-image response-body))
+         (elpher-render-image body))
         (other
          (error "Unsupported MIME type %S" mime-type))))))
 
         (other
          (error "Unsupported MIME type %S" mime-type))))))
 
@@ -1110,7 +1108,7 @@ If ADDRESS is already bookmarked, update the label only."
   (interactive)
   (let ((address (elpher-node-address elpher-current-node)))
     (if (elpher-address-special-p address)
   (interactive)
   (let ((address (elpher-node-address elpher-current-node)))
     (if (elpher-address-special-p address)
-        (error "Command not valid for this page")
+        (error "Command invalid for this page")
       (let ((url (read-string "Gopher or Gemini URL: " (elpher-address-to-url address))))
         (elpher-visit-node (elpher-make-node url (elpher-address-from-url url)))))))
 
       (let ((url (read-string "Gopher or Gemini URL: " (elpher-address-to-url address))))
         (elpher-visit-node (elpher-make-node url (elpher-address-from-url url)))))))
 
@@ -1146,7 +1144,7 @@ If ADDRESS is already bookmarked, update the label only."
       (if (elpher-address-special-p (elpher-node-address elpher-current-node))
           (error "This page was not generated by a server")
         (elpher-visit-node elpher-current-node
       (if (elpher-address-special-p (elpher-node-address elpher-current-node))
           (error "This page was not generated by a server")
         (elpher-visit-node elpher-current-node
-                           #'elpher-get-node-raw))
+                           #'elpher-render-raw))
     (message "No current site.")))
 
 (defun elpher-back ()
     (message "No current site.")))
 
 (defun elpher-back ()
@@ -1163,21 +1161,23 @@ If ADDRESS is already bookmarked, update the label only."
     (if button
         (let ((node (button-get button 'elpher-node)))
           (if (elpher-address-special-p (elpher-node-address node))
     (if button
         (let ((node (button-get button 'elpher-node)))
           (if (elpher-address-special-p (elpher-node-address node))
-              (error "Cannot download this link")
+              (error "Cannot download %s"
+                     (elpher-node-display-string node))
             (elpher-visit-node (button-get button 'elpher-node)
             (elpher-visit-node (button-get button 'elpher-node)
-                               #'elpher-get-node-download)))
+                               #'elpher-render-download)))
       (error "No link selected"))))
 
 (defun elpher-download-current ()
   "Download the current page."
   (interactive)
   (if (elpher-address-special-p (elpher-node-address elpher-current-node))
       (error "No link selected"))))
 
 (defun elpher-download-current ()
   "Download the current page."
   (interactive)
   (if (elpher-address-special-p (elpher-node-address elpher-current-node))
-      (error "Cannot download this page")
+      (error "Cannot download %s"
+             (elpher-node-display-string elpher-current-node))
     (elpher-visit-node (elpher-make-node
                         (elpher-node-display-string elpher-current-node)
                         (elpher-node-address elpher-current-node)
                         elpher-current-node)
     (elpher-visit-node (elpher-make-node
                         (elpher-node-display-string elpher-current-node)
                         (elpher-node-address elpher-current-node)
                         elpher-current-node)
-                       #'elpher-get-node-download
+                       #'elpher-render-download
                        t)))
 
 (defun elpher-build-link-map ()
                        t)))
 
 (defun elpher-build-link-map ()
@@ -1217,7 +1217,7 @@ If ADDRESS is already bookmarked, update the label only."
             (elpher-visit-node
              (elpher-make-node (elpher-address-to-url address-copy)
                                address-copy))))
             (elpher-visit-node
              (elpher-make-node (elpher-address-to-url address-copy)
                                address-copy))))
-      (error "Command invalid for this page"))))
+      (error "Command invalid for %s" (elpher-node-display-string elpher-current-node)))))
 
 (defun elpher-bookmarks-current-p ()
   "Return non-nil if current node is a bookmarks page."
 
 (defun elpher-bookmarks-current-p ()
   "Return non-nil if current node is a bookmarks page."
@@ -1365,8 +1365,8 @@ If ADDRESS is already bookmarked, update the label only."
     (define-key map (kbd "X") 'elpher-unbookmark-current)
     (define-key map (kbd "B") 'elpher-bookmarks)
     (define-key map (kbd "S") 'elpher-set-coding-system)
     (define-key map (kbd "X") 'elpher-unbookmark-current)
     (define-key map (kbd "B") 'elpher-bookmarks)
     (define-key map (kbd "S") 'elpher-set-coding-system)
-    (when (fboundp 'evil-define-key)
-      (evil-define-key 'motion map
+    (when (fboundp 'evil-mode)
+      (evil-define-key* 'motion map
         (kbd "TAB") 'elpher-next-link
         (kbd "C-") 'elpher-follow-current-link
         (kbd "C-t") 'elpher-back
         (kbd "TAB") 'elpher-next-link
         (kbd "C-") 'elpher-follow-current-link
         (kbd "C-t") 'elpher-back
@@ -1394,7 +1394,7 @@ If ADDRESS is already bookmarked, update the label only."
   "Keymap for gopher client.")
 
 (define-derived-mode elpher-mode special-mode "elpher"
   "Keymap for gopher client.")
 
 (define-derived-mode elpher-mode special-mode "elpher"
-  "Major mode for elpher, an elisp gopher client.)
+  "Major mode for elpher, an elisp gopher client.)))))))
 
 This mode is automatically enabled by the interactive
 functions which initialize the gopher client, namely
 
 This mode is automatically enabled by the interactive
 functions which initialize the gopher client, namely