;; Copyright (C) 2021 Christopher Brannon <chris@the-brannons.com>
;; Copyright (C) 2021 Omar Polo <op@omarpolo.com>
;; Copyright (C) 2021 Noodles! <nnoodle@chiru.no>
+;; Copyright (C) 2021 Abhiseck Paira <abhiseckpaira@disroot.org>
;; Copyright (C) 2020-2021 Alex Schroeder <alex@gnu.org>
;; Copyright (C) 2020 Zhiwei Chen <chenzhiwei03@kuaishou.com>
;; Copyright (C) 2020 condy0919 <condy0919@gmail.com>
(finger elpher-get-finger-page elpher-render-text "txt" elpher-text)
(telnet elpher-get-telnet-page nil "tel" elpher-telnet)
(other-url elpher-get-other-url-page nil "url" elpher-other-url)
- ((special start) elpher-get-start-page nil "E" elpher-index)
+ (file elpher-get-file-page nil "~" elpher-gemini)
+ ((special welcome) elpher-get-welcome-page nil "E" elpher-index)
((special bookmarks) elpher-get-bookmarks-page nil "E" elpher-index)
((special history) elpher-get-history-page nil "E" elpher-index)
((special visited-pages) elpher-get-visited-pages-page nil "E" elpher-index))
(defvar ansi-color-context)
(defvar bookmark-make-record-function)
(defvar mu4e~view-beginning-of-url-regexp)
+ (defvar eww-use-browse-url)
(defvar thing-at-point-uri-schemes)
(defvar xterm-color-preserve-properties))
page within which all of the standard elpher keybindings are active."
:type '(boolean))
+(defcustom elpher-start-page "about:welcome"
+ "Specify the page displayed initially by elpher.
+The default welcome screen \"about:welcome\", while the bookmarks list
+is \"about:bookmarks\". You can also specify local files via \"file:\".")
+
;; Face customizations
(defgroup elpher-faces nil
(defun elpher-make-special-address (type)
"Create an ADDRESS object corresponding to the given special address symbol TYPE."
- type)
+ (elpher-address-from-url (concat "about:" (symbol-name type))))
(defun elpher-address-to-url (address)
"Get string representation of ADDRESS, or nil if ADDRESS is special."
- (if (elpher-address-special-p address)
- nil
- (url-encode-url (url-recreate-url address))))
+ (url-encode-url (url-recreate-url address)))
(defun elpher-address-type (address)
"Retrieve type of ADDRESS object.
This is used to determine how to retrieve and render the document the
address refers to, via the table `elpher-type-map'."
- (if (symbolp address)
- (list 'special address)
- (let ((protocol (url-type address)))
- (cond ((or (equal protocol "gopher")
- (equal protocol "gophers"))
- (list 'gopher
- (if (member (url-filename address) '("" "/"))
- ?1
- (string-to-char (substring (url-filename address) 1)))))
- ((equal protocol "gemini")
- 'gemini)
- ((equal protocol "telnet")
- 'telnet)
- ((equal protocol "finger")
- 'finger)
- (t 'other-url)))))
+ (let ((protocol (url-type address)))
+ (pcase (url-type address)
+ ("about"
+ (list 'special (intern (url-filename address))))
+ ((or "gopher" "gophers")
+ (list 'gopher
+ (if (member (url-filename address) '("" "/"))
+ ?1
+ (string-to-char (substring (url-filename address) 1)))))
+ ("gemini" 'gemini)
+ ("telnet" 'telnet)
+ ("finger" 'finger)
+ ("file" 'file)
+ (_ 'other-url))))
+
+(defun elpher-address-special-p (address)
+ "Return non-nil if ADDRESS is a special address."
+ (pcase (elpher-address-type address) (`(special ,subtype) t)))
(defun elpher-address-protocol (address)
"Retrieve the transport protocol for ADDRESS. This is nil for special addresses."
- (if (symbolp address)
+ (if (elpher-address-special-p address)
nil
(url-type address)))
(defun elpher-address-port (address)
"Retrieve port from ADDRESS object.
If no address is defined, returns 0. (This is for compatibility with the URL library.)"
- (if (symbolp address)
+ (if (elpher-address-special-p address)
0
(url-port address)))
-(defun elpher-address-special-p (address)
- "Return non-nil if ADDRESS object is special (e.g. start page page)."
- (symbolp address))
-
(defun elpher-address-gopher-p (address)
"Return non-nill if ADDRESS object is a gopher address."
- (and (not (elpher-address-special-p address))
- (member (elpher-address-protocol address) '("gopher" "gophers"))))
+ (eq 'gopher (elpher-address-type address)))
(defun elpher-gopher-address-selector (address)
"Retrieve gopher selector from ADDRESS object."
(list display-string address))
(defun elpher-make-start-page ()
- "Create the start page."
- (elpher-make-page "Elpher Start Page"
- (elpher-make-special-address 'start)))
+ "Create the welcome page."
+ (elpher-make-page "Start Page"
+ (elpher-address-from-url elpher-start-page)))
(defun elpher-page-display-string (page)
"Retrieve the display string corresponding to PAGE."
(if (not data)
nil
(if (display-images-p)
- (progn
- (let ((image (create-image
- data
- nil t)))
- (elpher-with-clean-buffer
- (insert-image image)
- (elpher-restore-pos))))
+ (let* ((image (create-image
+ data
+ nil t))
+ (window (get-buffer-window elpher-buffer-name)))
+ (when window
+ (setf (image-property image :max-width) (window-pixel-width window))
+ (setf (image-property image :max-height) (window-pixel-height window)))
+ (elpher-with-clean-buffer
+ (insert-image image)
+ (elpher-restore-pos)))
(elpher-render-download data))))
;; Search retrieval and rendering
(let ((redirect-address (elpher-address-from-gemini-url response-meta)))
(if (member redirect-address elpher-gemini-redirect-chain)
(error "Redirect loop detected"))
- (if (not (string= (elpher-address-protocol redirect-address)
- "gemini"))
+ (if (not (eq (elpher-address-type redirect-address) 'gemini))
(error "Server tried to automatically redirect to non-gemini URL: %s"
response-meta))
(elpher-page-set-address elpher-current-page redirect-address)
(if (string-empty-p (url-filename address))
(setf (url-filename address) "/")) ;ensure empty filename is marked as absolute
(setf (url-host address) (url-host current-address))
- (setf (url-port address) (url-port current-address))
+ (setf (url-portspec address) (url-portspec current-address)) ; (url-port) too slow!
(unless (string-prefix-p "/" (url-filename address)) ;deal with relative links
(setf (url-filename address)
(concat (file-name-directory (url-filename current-address))
(error "Command not supported for general URLs"))
(let* ((address (elpher-page-address elpher-current-page))
(url (elpher-address-to-url address)))
- (progn
- (elpher-visit-previous-page) ; Do first in case of non-local exits.
- (message "Opening URL...")
- (if elpher-open-urls-with-eww
- (browse-web url)
- (browse-url url)))))
-
+ (elpher-visit-previous-page) ; Do first in case of non-local exits.
+ (message "Opening URL...")
+ (if elpher-open-urls-with-eww
+ (browse-web url)
+ (browse-url url))))
-;; Start page retrieval
+;; File page
-(defun elpher-get-start-page (renderer)
- "Getter which displays the start page (RENDERER must be nil)."
+(defun elpher-get-file-page (renderer)
+ "Getter which retrieves the contents of a local file and renders it using RENDERER."
+ (let* ((address (elpher-page-address elpher-current-page))
+ (filename (elpher-address-filename address)))
+ (unless (file-exists-p filename)
+ (elpher-visit-previous-page)
+ (error "File not found"))
+ (unless (file-readable-p filename)
+ (elpher-visit-previous-page)
+ (error "Could not read from file"))
+ (funcall
+ (if renderer
+ renderer
+ (pcase (file-name-extension filename)
+ ((or "gmi" "gemini") #'elpher-render-gemini-map)
+ ((or "htm" "html") #'elpher-render-html)
+ ((or "jpg" "jpeg" "gif" "png" "bmp" "tif" "tiff")
+ #'elpher-render-image)
+ ((or "txt" "") #'elpher-render-text)
+ (t
+ #'elpher-render-download)))
+ (with-temp-buffer
+ (let ((coding-system-for-read 'binary)
+ (coding-system-for-write 'binary))
+ (insert-file-contents-literally filename)
+ (string-as-unibyte (buffer-string))))
+ nil)))
+
+
+;; Welcome page retrieval
+
+(defun elpher-get-welcome-page (renderer)
+ "Getter which displays the welcome page (RENDERER must be nil)."
(when renderer
(elpher-visit-previous-page)
- (error "Command not supported for start page"))
+ (error "Command not supported for welcome page"))
(elpher-with-clean-buffer
(insert " --------------------------------------------\n"
" Elpher Gopher and Gemini Client \n"
(setq mu4e~view-beginning-of-url-regexp
"\\(?:https?\\|gopher\\|finger\\|gemini\\)://\\|mailto:")
+;;; eww:
+
+;; Let elpher handle gemini, gopher links in eww buffer.
+(setq eww-use-browse-url
+ "\\`mailto:\\|\\(\\`gemini\\|\\`gopher\\|\\`finger\\)://")
+
;;; Interactive procedures
;;
"Go to a particular gopher site HOST-OR-URL.
When run interactively HOST-OR-URL is read from the minibuffer."
(interactive "sGopher or Gemini URL: ")
- (let* ((cleaned-host-or-url (string-trim host-or-url))
- (address (elpher-address-from-url cleaned-host-or-url))
- (page (elpher-make-page cleaned-host-or-url address)))
- (switch-to-buffer elpher-buffer-name)
- (elpher-with-clean-buffer
- (elpher-visit-page page))
- nil))
+ (let ((trimmed-host-or-url (string-trim host-or-url)))
+ (unless (string-empty-p trimmed-host-or-url)
+ (let* ((address (elpher-address-from-url trimmed-host-or-url))
+ (page (elpher-make-page trimmed-host-or-url address)))
+ (switch-to-buffer elpher-buffer-name)
+ (elpher-with-clean-buffer
+ (elpher-visit-page page))
+ nil)))) ; non-nil value is displayed by eshell
(defun elpher-go-current ()
"Go to a particular site read from the minibuffer, initialized with the current URL."
(interactive)
- (let ((address (elpher-page-address elpher-current-page)))
- (let ((url (read-string "Gopher or Gemini URL: "
- (unless (elpher-address-special-p address)
- (elpher-address-to-url address)))))
+ (let* ((address (elpher-page-address elpher-current-page))
+ (url (read-string "Gopher or Gemini URL: "
+ (unless (elpher-address-special-p address)
+ (elpher-address-to-url address)))))
+ (unless (string-empty-p (string-trim url))
(elpher-visit-page (elpher-make-page url (elpher-address-from-url url))))))
(defun elpher-redraw ()
(let ((button (button-at (point))))
(if button
(let ((page (button-get button 'elpher-page)))
- (if (elpher-address-special-p (elpher-page-address page))
- (error "Cannot download %s"
- (elpher-page-display-string page))
- (elpher-visit-page (button-get button 'elpher-page)
- #'elpher-render-download)))
+ (unless page
+ (error "Not an elpher page"))
+ (when (elpher-address-special-p (elpher-page-address page))
+ (error "Cannot download %s" (elpher-page-display-string page)))
+ (elpher-visit-page (button-get button 'elpher-page)
+ #'elpher-render-download))
(error "No link selected"))))
(defun elpher-download-current ()
"Display information on PAGE."
(let ((display-string (elpher-page-display-string page))
(address (elpher-page-address page)))
- (if (elpher-address-special-p address)
- (message "Special page: %s" display-string)
- (message "%s" (elpher-address-to-url address)))))
+ (message "%s" (elpher-address-to-url address))))
(defun elpher-info-link ()
"Display information on page corresponding to link at point."
(interactive)
(let ((button (button-at (point))))
- (if button
- (elpher-info-page (button-get button 'elpher-page))
- (error "No item selected"))))
+ (unless button
+ (error "No item selected"))
+ (let ((page (button-get button 'elpher-page)))
+ (unless page
+ (error "Not an elpher page"))
+ (elpher-info-page page))))
(defun elpher-info-current ()
"Display information on current page."
(defun elpher-copy-page-url (page)
"Copy URL representation of address of PAGE to `kill-ring'."
- (let ((address (elpher-page-address page)))
- (if (elpher-address-special-p address)
- (error (format "Cannot represent %s as URL" (elpher-page-display-string page)))
- (let ((url (elpher-address-to-url address)))
- (message "Copied \"%s\" to kill-ring/clipboard." url)
- (kill-new url)))))
+ (let* ((address (elpher-page-address page))
+ (url (elpher-address-to-url address)))
+ (message "Copied \"%s\" to kill-ring/clipboard." url)
+ (kill-new url)))
(defun elpher-copy-link-url ()
"Copy URL of item at point to `kill-ring'."
(interactive)
(let ((button (button-at (point))))
- (if button
- (elpher-copy-page-url (button-get button 'elpher-page))
- (error "No item selected"))))
+ (unless button
+ (error "No item selected"))
+ (let ((page (button-get button 'elpher-page)))
+ (unless page
+ (error "Not an elpher page"))
+ (elpher-copy-page-url page))))
(defun elpher-copy-current-url ()
"Copy URL of current page to `kill-ring'."