Merge remote-tracking branch 'origin/patch_gus'
authorTim Vaughan <plugd@thelambdalab.xyz>
Wed, 20 May 2020 07:26:46 +0000 (09:26 +0200)
committerTim Vaughan <plugd@thelambdalab.xyz>
Wed, 20 May 2020 07:26:46 +0000 (09:26 +0200)
1  2 
elpher.el

diff --combined elpher.el
+++ b/elpher.el
@@@ -2,9 -2,9 +2,9 @@@
  
  ;; Copyright (C) 2019 Tim Vaughan
  
 -;; Author: Tim Vaughan <timv@ughan.xyz>
 +;; Author: Tim Vaughan <plugd@thelambdalab.xyz>
  ;; Created: 11 April 2019
 -;; Version: 2.7.1
 +;; Version: 2.7.4
  ;; Keywords: comm gopher
  ;; Homepage: http://thelambdalab.xyz/elpher
  ;; Package-Requires: ((emacs "26"))
@@@ -26,8 -26,8 +26,8 @@@
  
  ;;; Commentary:
  
- ;; Elpher aims to provide a practical and friendly gopher client
- ;; for GNU Emacs.  It supports:
+ ;; Elpher aims to provide a practical and friendly gopher, gemini,
+ ;; and finger client for GNU Emacs.  It supports:
  
  ;; - intuitive keyboard and mouse-driven browsing,
  ;; - out-of-the-box compatibility with evil-mode,
@@@ -70,7 -70,7 +70,7 @@@
  ;;; Global constants
  ;;
  
 -(defconst elpher-version "2.7.1"
 +(defconst elpher-version "2.7.4"
    "Current version of elpher.")
  
  (defconst elpher-margin-width 6
@@@ -148,10 -148,6 +148,10 @@@ The actual width used is the minimum o
  the time when the text is rendered."
    :type '(integer))
  
 +(defcustom elpher-bookmarks-file (locate-user-emacs-file "elpher-bookmarks")
 +  "Specify the name of the file where elpher bookmarks will be saved."
 +  :type '(file))
 +
  ;; Face customizations
  
  (defgroup elpher-faces nil
  
  (defface elpher-gemini-heading1
    '((t :inherit bold :height 1.8))
 -  "Face used for brackets around directory margin key.")
 +  "Face used for gemini heading level 1.")
  
  (defface elpher-gemini-heading2
    '((t :inherit bold :height 1.5))
 -  "Face used for brackets around directory margin key.")
 +  "Face used for gemini heading level 2.")
  
  (defface elpher-gemini-heading3
    '((t :inherit bold :height 1.2))
 -  "Face used for brackets around directory margin key.")
 +  "Face used for gemini heading level 3.")
  
  ;;; Model
  ;;
@@@ -265,7 -261,6 +265,7 @@@ The basic attributes include: TYPE, SEL
  If the optional attribute TLS is non-nil, the address will be marked as
  requiring gopher-over-TLS."
    (cond
 +   ((equal type ?i) nil)
     ((and (equal type ?h)
           (string-prefix-p "URL:" selector))
      (elpher-address-from-url (elt (split-string selector "URL:") 1)))
@@@ -550,8 -545,6 +550,8 @@@ to ADDRESS.
               (port (elpher-address-port address))
               (host (elpher-address-host address))
               (selector-string-parts nil)
 +             (bytes-received 0)
 +             (hkbytes-received 0)
               (proc (open-network-stream "elpher-process"
                                          nil
                                          (if force-ipv4 (dns-query host) host)
          (set-process-coding-system proc 'binary)
          (set-process-filter proc
                              (lambda (_proc string)
 -                              (cancel-timer timer)
 +                              (when timer
 +                                (cancel-timer timer)
 +                                (setq timer nil))
 +                              (setq bytes-received (+ bytes-received (length string)))
 +                              (let ((new-hkbytes-received (/ bytes-received 102400)))
 +                                (when (> new-hkbytes-received hkbytes-received)
 +                                  (setq hkbytes-received new-hkbytes-received)
 +                                  (with-current-buffer "*elpher*"
 +                                    (let ((inhibit-read-only t))
 +                                      (goto-char (point-min))
 +                                      (beginning-of-line 2)
 +                                      (delete-region (point) (point-max))
 +                                      (insert "("
 +                                              (number-to-string (/ hkbytes-received 10.0))
 +                                              " MB read)")))))
                                (setq selector-string-parts
                                      (cons string selector-string-parts))))
          (set-process-sentinel proc
                                           (concat (elpher-gopher-address-selector address)
                                                   "\r\n"))))
                                       (t
 -                                      (cancel-timer timer)
 +                                      (when timer
 +                                        (cancel-timer timer)
 +                                        (setq timer nil))
                                        (funcall renderer (apply #'concat
                                                                 (reverse selector-string-parts)))
                                        (elpher-restore-pos)))
@@@ -633,7 -610,7 +633,7 @@@ once they are retrieved from the gophe
           (insert content)
           (elpher-restore-pos))
        (elpher-with-clean-buffer
 -       (insert "LOADING... (use 'u' to cancel)"))
 +       (insert "LOADING... (use 'u' to cancel)\n"))
        (condition-case the-error
            (elpher-get-selector address renderer)
          (error
@@@ -699,7 -676,7 +699,7 @@@ If ADDRESS is not supplied or nil the r
                                'follow-link t
                                'help-echo (elpher-page-button-help page)))
        (pcase type
 -        ((or '(gopher ?i) 'nil) ;; Information
 +        ('nil ;; Information
           (elpher-insert-margin)
           (let ((propertized-display-string
                  (propertize display-string 'face 'elpher-info)))
@@@ -832,9 -809,7 +832,9 @@@ The response is rendered using the rend
    (if (not data)
        nil
      (let* ((address (elpher-page-address elpher-current-page))
 -           (selector (elpher-gopher-address-selector address)))
 +           (selector (if (elpher-address-gopher-p address)
 +                         (elpher-gopher-address-selector address)
 +                       (elpher-address-filename address))))
        (elpher-visit-previous-page) ; Do first in case of non-local exits.
        (let* ((filename-proposal (file-name-nondirectory selector))
               (filename (read-file-name "Download complete. Save file as: "
@@@ -878,8 -853,6 +878,8 @@@ to ADDRESS.
                 (port (elpher-address-port address))
                 (host (elpher-address-host address))
                 (response-string-parts nil)
 +               (bytes-received 0)
 +               (hkbytes-received 0)
                 (proc (open-network-stream "elpher-process"
                                            nil
                                            (if force-ipv4 (dns-query host) host)
                                  (when timer
                                    (cancel-timer timer)
                                    (setq timer nil))
 +                                (setq bytes-received (+ bytes-received (length string)))
 +                                (let ((new-hkbytes-received (/ bytes-received 102400)))
 +                                  (when (> new-hkbytes-received hkbytes-received)
 +                                    (setq hkbytes-received new-hkbytes-received)
 +                                    (with-current-buffer "*elpher*"
 +                                      (let ((inhibit-read-only t))
 +                                        (goto-char (point-min))
 +                                        (beginning-of-line 2)
 +                                        (delete-region (point) (point-max))
 +                                        (insert "("
 +                                                (number-to-string (/ hkbytes-received 10.0))
 +                                                " MB read)")))))
                                  (setq response-string-parts
                                        (cons string response-string-parts))))
            (set-process-sentinel proc
                                                   renderer)
                                          (elpher-restore-pos)))
                                      (error
 -                                           (elpher-network-error address the-error))))))
 +                                     (elpher-network-error address the-error))))))
        (error
         (error "Error initiating connection to server")))))
  
@@@ -1008,7 -969,7 +1008,7 @@@ that the response was malformed.
                (insert content)
                (elpher-restore-pos))
            (elpher-with-clean-buffer
 -           (insert "LOADING GEMINI... (use 'u' to cancel)"))
 +           (insert "LOADING GEMINI... (use 'u' to cancel)\n"))
            (setq elpher-gemini-redirect-chain nil)
            (elpher-get-gemini-response address renderer))
        (error
          ((pred (string-prefix-p "image/"))
           (elpher-render-image body))
          (_other
 -         (error "Unsupported MIME type %S" mime-type))))))
 +         (elpher-render-download body))))))
  
  (defun elpher-gemini-get-link-url (link-line)
    "Extract the url portion of LINK-LINE, a gemini map file link line.
@@@ -1176,7 -1137,7 +1176,7 @@@ by HEADER-LINE.
           (insert content)
           (elpher-restore-pos))
        (elpher-with-clean-buffer
 -       (insert "LOADING... (use 'u' to cancel)"))
 +       (insert "LOADING... (use 'u' to cancel)\n"))
        (condition-case the-error
            (let* ((kill-buffer-query-functions nil)
                   (user (let ((filename (elpher-address-filename address)))
              (set-process-coding-system proc 'binary)
              (set-process-filter proc
                                  (lambda (_proc string)
 -                                  (cancel-timer timer)
 +                                  (when timer
 +                                    (cancel-timer timer)
 +                                    (setq timer nil))
                                    (setq selector-string-parts
                                          (cons string selector-string-parts))))
              (set-process-sentinel proc
                                               proc
                                               (concat user "\r\n"))))
                                           (t
 -                                          (cancel-timer timer)
 +                                          (when timer
 +                                            (cancel-timer timer)
 +                                            (setq timer nil))
                                            (funcall renderer (apply #'concat
                                                                     (reverse selector-string-parts)))
                                            (elpher-restore-pos)))))))
             " - m: select an item on current page by name (autocompletes)\n"
             " - u/mouse-3: return to previous page\n"
             " - o/O: visit different selector or the root menu of the current server\n"
-            " - g: go to a particular gopher address\n"
+            " - g: go to a particular address (gopher, gemini, finger)\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"
     (elpher-insert-index-record "Floodgap Systems Gopher Server"
                                 (elpher-make-gopher-address ?1 "" "gopher.floodgap.com" 70))
     (insert "\n"
-            "Alternatively, select the following item and enter some search terms:\n")
+            "Alternatively, select a search engine and enter some search terms:\n")
     (elpher-insert-index-record "Veronica-2 Gopher Search Engine"
                                 (elpher-make-gopher-address ?7 "/v2/vs" "gopher.floodgap.com" 70))
+    (elpher-insert-index-record "GUS Gemini Search Engine"
+                                (elpher-address-from-url "gemini://gus.guru/search"))
     (insert "\n"
             "This page contains your bookmarked sites (also visit with B):\n")
     (elpher-insert-index-record "Your Bookmarks" 'bookmarks)
             "- a: rename selected bookmark\n"
             "\n"
             "Bookmarks are stored in the file ")
 -   (let ((filename (locate-user-emacs-file "elpher-bookmarks"))
 +   (let ((filename elpher-bookmarks-file)
           (help-string "RET,mouse-1: Open bookmarks file in new buffer for editing."))
       (insert-text-button filename
                           'face 'link
@@@ -1390,7 -1349,7 +1392,7 @@@ bookmark list, while URL is the url of 
  (defun elpher-save-bookmarks (bookmarks)
    "Record the bookmark list BOOKMARKS to the user's bookmark file.
  Beware that this completely replaces the existing contents of the file."
 -  (with-temp-file (locate-user-emacs-file "elpher-bookmarks")
 +  (with-temp-file elpher-bookmarks-file
      (erase-buffer)
      (insert "; Elpher bookmarks file\n\n"
              "; Bookmarks are stored as a list of (label URL) items.\n"
    (let ((bookmarks
           (with-temp-buffer
             (ignore-errors
 -             (insert-file-contents (locate-user-emacs-file "elpher-bookmarks"))
 +             (insert-file-contents elpher-bookmarks-file)
               (goto-char (point-min))
               (read (current-buffer))))))
      (if (and bookmarks (listp (cadar bookmarks)))
@@@ -1693,7 -1652,6 +1695,7 @@@ When run interactively HOST-OR-URL is r
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "TAB") 'elpher-next-link)
      (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 [mouse-3] 'elpher-back)
      (define-key map (kbd "O") 'elpher-root-dir)