X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=elpher.git;a=blobdiff_plain;f=elpher.el;h=ea95aee605c5311b01ae99d9bd02631a7037a1ac;hp=33804cfd14e8c07fc1d3f7b601e55bff54f0c9c3;hb=986d88c39d06c58fb23a7dfad037fec8235164b0;hpb=63e0f6df1856b9298b90d27ac2ad1ddc1e761590 diff --git a/elpher.el b/elpher.el index 33804cf..ea95aee 100644 --- a/elpher.el +++ b/elpher.el @@ -4,10 +4,10 @@ ;; Author: Tim Vaughan ;; Created: 11 April 2019 -;; Version: 1.2.4 +;; Version: 1.4.6 ;; Keywords: comm gopher ;; Homepage: https://github.com/tgvaughan/elpher -;; Package-Requires: ((emacs "25")) +;; Package-Requires: ((emacs "26")) ;; This file is not part of GNU Emacs. @@ -26,81 +26,49 @@ ;;; Commentary: -;; Elpher aims to provide a practical gopher client for GNU Emacs. -;; It supports: +;; Elpher aims to provide a practical and friendly gopher client +;; for GNU Emacs. It supports: -;; - intuitive keyboard and mouse-driven interface, -;; - caching of visited sites (both content and cursor position), +;; - intuitive keyboard and mouse-driven browsing, +;; - out-of-the-box compatibility with evil-mode, +;; - clickable web and gopher links *in plain text*, +;; - caching of visited sites, ;; - pleasant and configurable colouring of Gopher directories, ;; - direct visualisation of image files, -;; - (m)enu key support, similar to Emacs' info browser, -;; - clickable web and gopher links in plain text, -;; - a simple bookmark management system. - -;; Visited pages are stored as a hierarchy rather than a linear history, -;; meaning that navigation between these pages is quick and easy. +;; - a simple bookmark management system, +;; - connections using TLS encryption. ;; To launch Elpher, simply use 'M-x elpher'. This will open a start ;; page containing information on key bindings and suggested starting ;; points for your gopher exploration. -;; Faces, caching and other options can be configured via -;; the Elpher customization group in Applications. +;; Full instructions can be found in the Elpher info manual. + +;; Elpher is under active development. Any suggestions for +;; improvements are welcome! ;;; Code: (provide 'elpher) + +;;; Dependencies +;; + (require 'seq) (require 'pp) (require 'shr) +(require 'url-util) + ;;; Global constants ;; -(defconst elpher-version "1.2.4" +(defconst elpher-version "1.4.6" "Current version of elpher.") (defconst elpher-margin-width 6 "Width of left-hand margin used when rendering indicies.") -(defconst elpher-start-index - (mapconcat - 'identity - (list "i\tfake\tfake\t1" - "i --------------------------------------------\tfake\tfake\t1" - "i Elpher Gopher Client \tfake\tfake\t1" - (format "i version %s\tfake\tfake\t1" elpher-version) - "i --------------------------------------------\tfake\tfake\t1" - "i\tfake\tfake\t1" - "iUsage:\tfake\tfake\t1" - "i\tfake\tfake\t1" - "i - tab/shift-tab: next/prev item on current page\tfake\tfake\t1" - "i - RET/mouse-1: open item under cursor\tfake\tfake\t1" - "i - m: select an item on current page by name (autocompletes)\tfake\tfake\t1" - "i - u: return to parent\tfake\tfake\t1" - "i - O: visit the root menu of the current server\tfake\tfake\t1" - "i - g: go to a particular menu or item\tfake\tfake\t1" - "i - i/I: info on item under cursor or current page\tfake\tfake\t1" - "i - c/C: copy URL representation of item under cursor or current page\tfake\tfake\t1" - "i - a/A: bookmark the item under cursor or current page\tfake\tfake\t1" - "i - x/X: remove bookmark for item under cursor or current page\tfake\tfake\t1" - "i - B: visit the bookmarks page\tfake\tfake\t1" - "i - r: redraw current page (using cached contents if available)\tfake\tfake\t1" - "i - R: reload current page (regenerates cache)\tfake\tfake\t1" - "i - d: download directory entry under cursor\tfake\tfake\t1" - "i - w: display the raw server response for the current page\tfake\tfake\t1" - "i\tfake\tfake\t1" - "iWhere to start exploring Gopherspace:\tfake\tfake\t1" - "i\tfake\tfake\t1" - "1Floodgap Systems Gopher Server\t/\tgopher.floodgap.com\t70" - "i\tfake\tfake\t1" - "iAlternatively, select the following item and enter some\tfake\tfake\t1" - "isearch terms:\tfake\tfake\t1" - "i\tfake\tfake\t1" - "7Veronica-2 Gopher Search Engine\t/v2/vs\tgopher.floodgap.com\t70" - ".\r\n") - "\r\n") - "Source for elpher start page.") (defconst elpher-type-map '((?0 elpher-get-text-node "txt" elpher-text) @@ -114,7 +82,9 @@ (?p elpher-get-image-node "img" elpher-image) (?I elpher-get-image-node "img" elpher-image) (?d elpher-get-node-download "doc" elpher-binary) - (?h elpher-get-url-node "web" elpher-url) + (?P elpher-get-node-download "doc" elpher-binary) + (?s elpher-get-node-download "snd" elpher-binary) + (?h elpher-get-url-node "url" elpher-url) (bookmarks elpher-get-bookmarks-node "#" elpher-index) (start elpher-get-start-node "#" elpher-index)) "Association list from types to getters, margin codes and index faces.") @@ -192,16 +162,28 @@ Otherwise, use the system browser via the BROWSE-URL function." "If non-nil, display current node information in buffer header." :type '(boolean)) +(defcustom elpher-auto-disengage-TLS nil + "If non-nil, automatically disengage TLS following an unsuccessful connection. +While enabling this may seem convenient, it is also potentially dangerous as it +allows switching from an encrypted channel back to plain text without user input." + :type '(boolean)) + + ;;; Model ;; ;; Address -(defun elpher-make-address (type &optional selector host port) +(defun elpher-make-address (type &optional selector host port use-tls) "Create an address of a gopher object with TYPE, SELECTOR, HOST and PORT. Although selector host and port are optional, they are only omitted for -special address types, such as 'start for the start page." - (list type selector host port)) +special address types, such as 'start for the start page. + +Setting the USE-TLS parameter to non-nil causes Elpher to engage TLS mode +before attempting to connect to the server." + (if use-tls + (list type selector host port 'tls) + (list type selector host port))) (defun elpher-address-type (address) "Retrieve type from ADDRESS." @@ -219,10 +201,37 @@ special address types, such as 'start for the start page." "Retrieve port from ADDRESS." (elt address 3)) +(defun elpher-address-use-tls-p (address) + "Return non-nil if ADDRESS is marked as needing TLS." + (elt address 4)) + (defun elpher-address-special-p (address) "Return non-nil if ADDRESS is special (e.g. start page, bookmarks page)." (not (elpher-address-host address))) +(defun elpher-get-address-url (address) + "Get URL representation of ADDRESS." + (let ((type (elpher-address-type address)) + (selector (elpher-address-selector address)) + (bare-host (elpher-address-host address)) + (port (elpher-address-port address))) + (url-encode-url + (let ((host (if (string-match-p ":" bare-host) + (concat "[" bare-host "]") + bare-host))) + (if (and (equal type ?h) + (string-prefix-p "URL:" selector)) + (elt (split-string selector "URL:") 1) + (concat "gopher" + (if (elpher-address-use-tls-p address) "s" "") + "://" + host + (if (equal port 70) + "" + (format ":%d" port)) + "/" (string type) + selector)))))) + ;; Node (defun elpher-make-node (display-string address &optional parent) @@ -295,8 +304,12 @@ unless PRESERVE-PARENT is non-nil." (if getter (funcall getter) (let* ((address (elpher-node-address node)) - (type (elpher-address-type address))) - (funcall (car (alist-get type elpher-type-map)))))) + (type (elpher-address-type address)) + (type-record (alist-get type elpher-type-map))) + (if (listp type-record) + (funcall (car type-record)) + (elpher-visit-parent-node) + (error "Unsupported gopher selector type '%c'" type))))) (defun elpher-visit-parent-node () "Visit the parent of the current node." @@ -340,14 +353,28 @@ unless PRESERVE-PARENT is non-nil." args))) -;;; Index rendering +;;; Text Processing ;; +(defvar elpher-user-coding-system nil + "User-specified coding system to use for decoding text responses.") + +(defun elpher-decode (string) + "Decode STRING using autodetected or user-specified coding system." + (decode-coding-string string + (if elpher-user-coding-system + elpher-user-coding-system + (detect-coding-string string t)))) + (defun elpher-preprocess-text-response (string) - "Clear away CRs and terminating period from STRING." - (replace-regexp-in-string "\n\.\n$" "\n" - (replace-regexp-in-string "\r" "" - string))) + "Preprocess text selector response contained in STRING. +This involes decoding the character representation, and clearing +away CRs and any terminating period." + (elpher-decode (replace-regexp-in-string "\n\.\n$" "\n" + (replace-regexp-in-string "\r" "" string)))) + +;;; Index rendering +;; (defun elpher-insert-index (string) "Insert the index corresponding to STRING into the current buffer." @@ -363,8 +390,9 @@ unless PRESERVE-PARENT is non-nil." (host (elt fields 2)) (port (if (elt fields 3) (string-to-number (elt fields 3)) - nil))) - (elpher-insert-index-record display-string type selector host port)))))) + nil)) + (address (elpher-make-address type selector host port))) + (elpher-insert-index-record display-string address)))))) (defun elpher-insert-margin (&optional type-name) "Insert index margin, optionally containing the TYPE-NAME, into the current buffer." @@ -389,12 +417,12 @@ unless PRESERVE-PARENT is non-nil." (elpher-address-host address) (elpher-address-port address))))) -(defun elpher-insert-index-record (display-string type selector host port) + +(defun elpher-insert-index-record (display-string address) "Function to insert an index record into the current buffer. -The contents of the record are dictated by TYPE, DISPLAY-STRING, SELECTOR, HOST -and PORT." - (let ((address (elpher-make-address type selector host port)) - (type-map-entry (alist-get type elpher-type-map))) +The contents of the record are dictated by DISPLAY-STRING and ADDRESS." + (let* ((type (elpher-address-type address)) + (type-map-entry (alist-get type elpher-type-map))) (if type-map-entry (let* ((margin-code (elt type-map-entry 1)) (face (elt type-map-entry 2)) @@ -434,29 +462,59 @@ and PORT." (let ((p (get-process "elpher-process"))) (if p (delete-process p)))) +(defvar elpher-use-tls nil + "If non-nil, use TLS to communicate with gopher servers.") + (defvar elpher-selector-string) -(defun elpher-get-selector (address after) +(defun elpher-get-selector (address after &optional propagate-error) "Retrieve selector specified by ADDRESS, then execute AFTER. -The result is stored as a string in the variable ‘elpher-selector-string’." +The result is stored as a string in the variable ‘elpher-selector-string’. + +Usually errors result in an error page being displayed. This is only +appropriate if the selector is to be directly viewed. If PROPAGATE-ERROR +is non-nil, this message is not displayed. Instead, the error propagates +up to the calling function." (setq elpher-selector-string "") - (condition-case nil - (progn - (make-network-process :name "elpher-process" - :host (elpher-address-host address) - :service (elpher-address-port address) - :filter (lambda (proc string) - (setq elpher-selector-string - (concat elpher-selector-string string))) - :sentinel after) - (process-send-string "elpher-process" + (when (elpher-address-use-tls-p address) + (if (gnutls-available-p) + (when (not elpher-use-tls) + (setq elpher-use-tls t) + (message "Engaging TLS mode.")) + (error "Cannot retrieve TLS selector: GnuTLS not available"))) + (condition-case the-error + (let* ((kill-buffer-query-functions nil) + (proc (open-network-stream "elpher-process" + nil + (elpher-address-host address) + (elpher-address-port address) + :type (if elpher-use-tls 'tls 'plain)))) + (set-process-coding-system proc 'binary) + (set-process-filter proc + (lambda (proc string) + (setq elpher-selector-string + (concat elpher-selector-string string)))) + (set-process-sentinel proc after) + (process-send-string proc (concat (elpher-address-selector address) "\n"))) (error - (elpher-with-clean-buffer - (insert (propertize "\n---- ERROR -----\n\n" 'face 'error) - "Failed to connect to " (elpher-get-address-url address) ".\n" - (propertize "\n----------------\n\n" 'face 'error) - "Press 'u' to return to the previous page."))))) + (if (and (consp the-error) + (eq (car the-error) 'gnutls-error) + (not (elpher-address-use-tls-p address)) + (or elpher-auto-disengage-TLS + (yes-or-no-p "Could not establish encrypted connection. Disable TLS mode? "))) + (progn + (message "Disengaging TLS mode.") + (setq elpher-use-tls nil) + (elpher-get-selector address after)) + (elpher-process-cleanup) + (if propagate-error + (error the-error) + (elpher-with-clean-buffer + (insert (propertize "\n---- ERROR -----\n\n" 'face 'error) + "Failed to connect to " (elpher-get-address-url address) ".\n" + (propertize "\n----------------\n\n" 'face 'error) + "Press 'u' to return to the previous page."))))))) ;; Index retrieval @@ -484,7 +542,7 @@ The result is stored as a string in the variable ‘elpher-selector-string’." ;; Text retrieval (defconst elpher-url-regex - "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]+\\)\\(?3::[0-9]+\\)?\\(?4:/[^ \r\n\t(),]*\\)?" + "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]+\\|\[[a-zA-Z0-9:]+\]\\)\\(?3::[0-9]+\\)?\\(?4:/[^ \r\n\t(),]*\\)?" "Regexp used to locate and buttinofy URLs in text files loaded by elpher.") (defun elpher-make-node-from-matched-url (&optional string) @@ -494,8 +552,12 @@ If STRING is non-nil, this is given as an argument to all `match-string' calls, as is necessary if the match is performed by `string-match'." (let ((url (match-string 0 string)) (protocol (downcase (match-string 1 string)))) - (if (string= protocol "gopher") - (let* ((host (match-string 2 string)) + (if (or (string= protocol "gopher") + (string= protocol "gophers")) + (let* ((bare-host (match-string 2 string)) + (host (if (string-prefix-p "[" bare-host) + (substring bare-host 1 (- (length bare-host) 1)) + bare-host)) (port (if (> (length (match-string 3 string)) 1) (string-to-number (substring (match-string 3 string) 1)) 70)) @@ -503,10 +565,13 @@ calls, as is necessary if the match is performed by `string-match'." (type (if (> (length type-and-selector) 1) (elt type-and-selector 1) ?1)) - (selector (if (> (length type-and-selector) 1) - (substring type-and-selector 2) - "")) - (address (elpher-make-address type selector host port))) + (selector (decode-coding-string + (url-unhex-string + (if (> (length type-and-selector) 1) + (substring type-and-selector 2) + "")) 'utf-8)) + (use-tls (string= protocol "gophers")) + (address (elpher-make-address type selector host port use-tls))) (elpher-make-node url address)) (let* ((host (match-string 2 string)) (port (if (> (length (match-string 3 string)) 1) @@ -575,9 +640,7 @@ calls, as is necessary if the match is performed by `string-match'." (lambda (proc event) (unless (string-prefix-p "deleted" event) (let ((image (create-image - (encode-coding-string - elpher-selector-string - 'no-conversion) + elpher-selector-string nil t))) (elpher-with-clean-buffer (insert-image image) @@ -630,17 +693,12 @@ calls, as is necessary if the match is performed by `string-match'." (let ((address (elpher-node-address elpher-current-node))) (elpher-with-clean-buffer (insert "LOADING RAW SERVER RESPONSE... (use 'u' to cancel)")) - (if address - (elpher-get-selector address - (lambda (proc event) - (unless (string-prefix-p "deleted" event) - (elpher-with-clean-buffer - (insert elpher-selector-string) - (goto-char (point-min)))))) - (progn - (elpher-with-clean-buffer - (insert elpher-start-index)) - (goto-char (point-min))))) + (elpher-get-selector address + (lambda (proc event) + (unless (string-prefix-p "deleted" event) + (elpher-with-clean-buffer + (insert elpher-selector-string) + (goto-char (point-min))))))) (message "Displaying raw server response. Reload or redraw to return to standard view.")) ;; File export retrieval @@ -660,13 +718,17 @@ calls, as is necessary if the match is performed by `string-match'." "gopher.file")))) (message "Downloading...") (setq elpher-download-filename filename) - (elpher-get-selector address - (lambda (proc event) - (let ((coding-system-for-write 'binary)) - (with-temp-file elpher-download-filename - (insert elpher-selector-string) - (message (format "Download complate, saved to file %s." - elpher-download-filename))))))))) + (condition-case the-error + (elpher-get-selector address + (lambda (proc event) + (let ((coding-system-for-write 'binary)) + (with-temp-file elpher-download-filename + (insert elpher-selector-string) + (message (format "Download complate, saved to file %s." + elpher-download-filename))))) + t) + (error + (error "Error downloading %s" elpher-download-filename)))))) ;; URL retrieval @@ -722,7 +784,53 @@ calls, as is necessary if the match is performed by `string-match'." (defun elpher-get-start-node () "Getter which displays the start page." (elpher-with-clean-buffer - (elpher-insert-index elpher-start-index) + (insert " --------------------------------------------\n" + " Elpher Gopher Client \n" + " version " elpher-version "\n" + " --------------------------------------------\n" + "\n" + "Default bindings:\n" + "\n" + " - TAB/Shift-TAB: next/prev item on current page\n" + " - RET/mouse-1: open item under cursor\n" + " - m: select an item on current page by name (autocompletes)\n" + " - u: 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" + " - i/I: info on item under cursor or current page\n" + " - c/C: copy URL representation of item under cursor or current page\n" + " - a/A: bookmark the item under cursor or current page\n" + " - x/X: remove bookmark for item under cursor or current page\n" + " - B: visit the bookmarks page\n" + " - r: redraw current page (using cached contents if available)\n" + " - R: reload current page (regenerates cache)\n" + " - T: toggle TLS mode\n" + " - d/D: download item under cursor or current page\n" + " - w: display the raw server response for the current page\n" + " - S: set an explicit character coding system (default is to autodetect)\n" + "\n" + "Start your exploration of gopher space:\n") + (elpher-insert-index-record "Floodgap Systems Gopher Server" + (elpher-make-address ?1 "" "gopher.floodgap.com" 70)) + (insert "\n" + "Alternatively, select the following item and enter some search terms:\n") + (elpher-insert-index-record "Veronica-2 Gopher Search Engine" + (elpher-make-address ?7 "/v2/vs" "gopher.floodgap.com" 70)) + (insert "\n" + "** Refer to the ") + (let ((help-string "RET,mouse-1: Open Elpher info manual (if available)")) + (insert-text-button "Elpher info manual" + 'face 'link + 'action (lambda (button) + (interactive) + (info "(elpher)")) + 'follow-link t + 'help-echo help-string)) + (insert " for the full documentation. **\n") + (insert (propertize + (concat " (This should be available if you have installed Elpher using\n" + " MELPA. Otherwise you will have to install the manual yourself.)") + 'face 'shadow)) (elpher-restore-pos))) ;; Bookmarks page node retrieval @@ -736,11 +844,7 @@ calls, as is necessary if the match is performed by `string-match'." (dolist (bookmark bookmarks) (let ((display-string (elpher-bookmark-display-string bookmark)) (address (elpher-bookmark-address bookmark))) - (elpher-insert-index-record display-string - (elpher-address-type address) - (elpher-address-selector address) - (elpher-address-host address) - (elpher-address-port address)))) + (elpher-insert-index-record display-string address))) (insert "No bookmarks found.\n"))) (insert "\n-----------------------\n\n" "- u: return to previous page\n" @@ -808,6 +912,7 @@ If ADDRESS is already bookmarked, update the label only." (not (equal (elpher-bookmark-address bookmark) address))) (elpher-load-bookmarks)))) + ;;; Interactive procedures ;; @@ -845,20 +950,44 @@ host, selector and port." (switch-to-buffer "*elpher*") (elpher-visit-node node))) -(defun elpher-redraw () +(defun elpher-go-current () + "Go to a particular site read from the minibuffer, initialized with the current URL." + (interactive) + (let ((address (elpher-node-address elpher-current-node))) + (if (elpher-address-special-p address) + (error "Command not valid for this page") + (let ((url (read-string "URL: " (elpher-get-address-url address)))) + (if (string-match elpher-url-regex url) + (let ((new-node (elpher-make-node-from-matched-url url))) + (unless (equal (elpher-node-address new-node) address) + (elpher-visit-node new-node))) + (error "Could not parse URL %s" url)))))) + +(defun elpher-redraw () "Redraw current page." (interactive) (if elpher-current-node (elpher-visit-node elpher-current-node) (message "No current site."))) -(defun elpher-reload () +(defun elpher-reload () "Reload current page." (interactive) (if elpher-current-node (elpher-reload-current-node) (message "No current site."))) +(defun elpher-toggle-tls () + "Toggle TLS encryption mode." + (interactive) + (setq elpher-use-tls (not elpher-use-tls)) + (if elpher-use-tls + (if (gnutls-available-p) + (message "TLS mode enabled. (Will not affect current page until reload.)") + (setq elpher-use-tls nil) + (error "Cannot enable TLS mode: GnuTLS not available")) + (message "TLS mode disabled. (Will not affect current page until reload.)"))) + (defun elpher-view-raw () "View raw server response for current page." (interactive) @@ -888,6 +1017,18 @@ host, selector and port." #'elpher-get-node-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 "Cannot download this page") + (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 + t))) + (defun elpher-build-link-map () "Build alist mapping link names to destination nodes in current buffer." (let ((link-map nil) @@ -987,7 +1128,7 @@ host, selector and port." (error "No link selected")))) (defun elpher-bookmarks () - "Visit bookmarks." + "Visit bookmarks page." (interactive) (switch-to-buffer "*elpher*") (elpher-visit-node @@ -1017,23 +1158,6 @@ host, selector and port." (interactive) (elpher-info-node elpher-current-node)) -(defun elpher-get-address-url (address) - "Get URL representation of ADDRESS." - (let ((type (elpher-address-type address)) - (selector (elpher-address-selector address)) - (host (elpher-address-host address)) - (port (elpher-address-port address))) - (if (and (equal type ?h) - (string-prefix-p "URL:" selector)) - (elt (split-string selector "URL:") 1) - (concat "gopher://" - host - (if (equal port 70) - "" - (format ":%d" port)) - "/" (string type) - selector)))) - (defun elpher-copy-node-url (node) "Copy URL representation of address of NODE to `kill-ring'." (let ((address (elpher-node-address node))) @@ -1056,6 +1180,16 @@ host, selector and port." (interactive) (elpher-copy-node-url elpher-current-node)) +(defun elpher-set-coding-system () + "Specify an explicit character coding system." + (interactive) + (let ((system (read-coding-system "Set coding system to use (default is to autodetect): " nil))) + (setq elpher-user-coding-system system) + (if system + (message "Coding system fixed to %s. (Reload to see effect)." system) + (message "Coding system set to autodetect. (Reload to see effect).")))) + + ;;; Mode and keymap ;; @@ -1066,10 +1200,13 @@ host, selector and port." (define-key map (kbd "u") 'elpher-back) (define-key map (kbd "O") 'elpher-root-dir) (define-key map (kbd "g") 'elpher-go) + (define-key map (kbd "o") 'elpher-go-current) (define-key map (kbd "r") 'elpher-redraw) (define-key map (kbd "R") 'elpher-reload) + (define-key map (kbd "T") 'elpher-toggle-tls) (define-key map (kbd "w") 'elpher-view-raw) (define-key map (kbd "d") 'elpher-download) + (define-key map (kbd "D") 'elpher-download-current) (define-key map (kbd "m") 'elpher-jump) (define-key map (kbd "i") 'elpher-info-link) (define-key map (kbd "I") 'elpher-info-current) @@ -1080,6 +1217,7 @@ host, selector and port." (define-key map (kbd "x") 'elpher-unbookmark-link) (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 (kbd "TAB") 'elpher-next-link @@ -1088,10 +1226,13 @@ host, selector and port." (kbd "u") 'elpher-back (kbd "O") 'elpher-root-dir (kbd "g") 'elpher-go + (kbd "o") 'elpher-go-current (kbd "r") 'elpher-redraw (kbd "R") 'elpher-reload + (kbd "T") 'elpher-toggle-tls (kbd "w") 'elpher-view-raw (kbd "d") 'elpher-download + (kbd "D") 'elpher-download-current (kbd "m") 'elpher-jump (kbd "i") 'elpher-info-link (kbd "I") 'elpher-info-current @@ -1101,16 +1242,22 @@ host, selector and port." (kbd "A") 'elpher-bookmark-current (kbd "x") 'elpher-unbookmark-link (kbd "X") 'elpher-unbookmark-current - (kbd "B") 'elpher-bookmarks)) + (kbd "B") 'elpher-bookmarks + (kbd "S") 'elpher-set-coding-system)) map) "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 +`elpher', `elpher-go' and `elpher-bookmarks'.") (when (fboundp 'evil-set-initial-state) (evil-set-initial-state 'elpher-mode 'motion)) + ;;; Main start procedure ;;