Migrating to stack-based history.
[elpher.git] / elpher.el
index 8afdc0a..95c3057 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,7 +4,7 @@
 
 ;; Author: Tim Vaughan <tgvaughan@gmail.com>
 ;; Created: 11 April 2019
-;; Version: 2.4.2
+;; Version: 2.4.4
 ;; Keywords: comm gopher
 ;; Homepage: http://thelambdalab.xyz/elpher
 ;; Package-Requires: ((emacs "26"))
@@ -37,7 +37,7 @@
 ;; - direct visualisation of image files,
 ;; - a simple bookmark management system,
 ;; - connections using TLS encryption,
-;; - basic support for the fledgling Gemini protocol.
+;; - support for the fledgling Gemini protocol.
 
 ;; To launch Elpher, simply use 'M-x elpher'.  This will open a start
 ;; page containing information on key bindings and suggested starting
@@ -46,7 +46,8 @@
 ;; Full instructions can be found in the Elpher info manual.
 
 ;; Elpher is under active development.  Any suggestions for
-;; improvements are welcome!
+;; improvements are welcome, and can be made on the official
+;; project page, gopher://thelambdalab.xyz/1/projects/elpher/.
 
 ;;; Code:
 
@@ -66,7 +67,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "2.4.2"
+(defconst elpher-version "2.4.4"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
@@ -307,36 +308,8 @@ If no address is defined, returns 0.  (This is for compatibility with the URL li
       ""
     (substring (url-filename address) 2)))
 
-;; Node
+;; Page
 
-(defun elpher-make-node (display-string address &optional parent)
-  "Create a node in the page hierarchy.
-
-DISPLAY-STRING records the display string used for the page.
-
-ADDRESS specifies the address object of the page.
-
-The optional PARENT specifies the parent node in the hierarchy.
-This is set every time the node is visited, so while it forms
-an important part of the node data there is no need to set it
-initially."
-  (list display-string address parent))
-
-(defun elpher-node-display-string (node)
-  "Retrieve the display string of NODE."
-  (elt node 0))
-
-(defun elpher-node-address (node)
-  "Retrieve the ADDRESS object of NODE."
-  (elt node 1))
-
-(defun elpher-node-parent (node)
-  "Retrieve the parent node of NODE."
-  (elt node 2))
-
-(defun elpher-set-node-parent (node parent)
-  "Set the parent node of NODE to be PARENT."
-  (setcar (cdr (cdr node)) parent))
 
 ;; Cache
 
@@ -359,24 +332,31 @@ initially."
   "Set the cursor position cache for ADDRESS to POS."
   (puthash address pos elpher-pos-cache))
 
-;; Node graph traversal
+;; Page
+
+(defun elpher-make-page (address display-string)
+  (list address display-string))
+
+(defun elpher-page-address (page)
+  (elt page 0))
 
-(defvar elpher-current-node nil)
+(defun elpher-page-display-string (page)
+  (elt page 1))
 
-(defun elpher-visit-node (node &optional renderer preserve-parent)
-  "Visit NODE using its own renderer or RENDERER, if non-nil.
-Additionally, set the parent of NODE to `elpher-current-node',
-unless PRESERVE-PARENT is non-nil."
+
+(defvar elpher-current-page nil)
+(defvar elpher-history nil)
+
+(defun elpher-visit-page (page &optional renderer no-history)
+  "Visit PAGE using its own renderer or RENDERER, if non-nil.
+Additionally, push PAGE onto the stack of previously-visited pages,
+unless NO-HISTORY is non-nil."
   (elpher-save-pos)
   (elpher-process-cleanup)
-  (unless preserve-parent
-    (if (and (elpher-node-parent elpher-current-node)
-             (equal (elpher-node-address elpher-current-node)
-                    (elpher-node-address node)))
-        (elpher-set-node-parent node (elpher-node-parent elpher-current-node))
-      (elpher-set-node-parent node elpher-current-node)))
-  (setq elpher-current-node node)
-  (let* ((address (elpher-node-address node))
+  (unless no-history
+    (push page elpher-history))
+  (setq elpher-current-page page)
+  (let* ((address (elpher-page-address node))
          (type (elpher-address-type address))
          (type-record (cdr (assoc type elpher-type-map))))
     (if type-record
@@ -384,7 +364,7 @@ unless PRESERVE-PARENT is non-nil."
                  (if renderer
                      renderer
                    (cadr type-record)))
-      (elpher-visit-parent-node)
+      (elpher-visit-previous-page)
       (pcase type
         (`(gopher ,type-char)
          (error "Unsupported gopher selector type '%c' for '%s'"
@@ -393,13 +373,13 @@ unless PRESERVE-PARENT is non-nil."
          (error "Unsupported address type '%S' for '%s'"
                 other (elpher-address-to-url address)))))))
 
-(defun elpher-visit-parent-node ()
+(defun elpher-visit-previous-page ()
   "Visit the parent of the current node."
-  (let ((parent-node (elpher-node-parent elpher-current-node)))
-    (when parent-node
-      (elpher-visit-node parent-node nil t))))
+  (let ((previous-page (pop elpher-history)))
+    (when previous-page
+      (elpher-visit-node previous-page nil t))))
       
-(defun elpher-reload-current-node ()
+(defun elpher-reload-current-page ()
   "Reload the current node, discarding any existing cached content."
   (elpher-cache-content (elpher-node-address elpher-current-node) nil)
   (elpher-visit-node elpher-current-node))
@@ -671,7 +651,7 @@ If ADDRESS is not supplied or nil the record is rendered as an
 ;; Text rendering
 
 (defconst elpher-url-regex
-  "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]*[a-zA-Z0-9\-]\\|\[[a-zA-Z0-9:]+\]\\)\\(:[0-9]+\\)?\\(/\\([0-9a-zA-Z\-_~?/@|:.%]*[0-9a-zA-Z\-_~?/@|]\\)?\\)?"
+  "\\([a-zA-Z]+\\)://\\([a-zA-Z0-9.\-]*[a-zA-Z0-9\-]\\|\[[a-zA-Z0-9:]+\]\\)\\(:[0-9]+\\)?\\(/\\([0-9a-zA-Z\-_~?/@|:.%#]*[0-9a-zA-Z\-_~?/@|#]\\)?\\)?"
   "Regexp used to locate and buttniofy URLs in text files loaded by elpher.")
 
 (defun elpher-buttonify-urls (string)