Updated notes.
[elpher.git] / elpher.el
index 6271090..531b2da 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -4,7 +4,7 @@
 
 ;; Author: Tim Vaughan <tgvaughan@gmail.com>
 ;; Created: 11 April 2019
-;; Version: 1.1.0
+;; Version: 1.2.0
 ;; Keywords: comm gopher
 ;; Homepage: https://github.com/tgvaughan/elpher
 ;; Package-Requires: ((emacs "25"))
@@ -55,7 +55,7 @@
 ;;; Global constants
 ;;
 
-(defconst elpher-version "1.1.0"
+(defconst elpher-version "1.2.0"
   "Current version of elpher.")
 
 (defconst elpher-margin-width 6
          "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"
-         "iPlaces to start exploring Gopherspace:\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"
@@ -213,6 +216,9 @@ special address types, such as 'start for the start page."
   "Retrieve port from ADDRESS."
   (elt address 3))
 
+(defun elpher-address-special-p (address)
+  (not (elpher-address-host address)))
+
 ;; Node
 
 (defun elpher-make-node (display-string parent address)
@@ -370,7 +376,7 @@ and PORT."
   (let ((address (elpher-make-address type selector host port))
         (type-map-entry (alist-get type elpher-type-map)))
     (if type-map-entry
-        (let* ((margin-code (cadr type-map-entry))
+        (let* ((margin-code (elt type-map-entry 1))
                (face (elt type-map-entry 2))
                (node (elpher-make-node display-string elpher-current-node address)))
           (elpher-insert-margin margin-code)
@@ -671,8 +677,7 @@ calls, as is necessary if the match is performed by `string-match'."
 (defun elpher-get-bookmarks-node ()
   "Getter which loads and displays the current bookmark list."
   (elpher-with-clean-buffer
-   (insert "Use 'u' to return to the previous page.\n\n"
-           "---- Bookmark list ----\n\n")
+   (insert "---- Bookmark list ----\n\n")
    (let ((bookmarks (elpher-load-bookmarks)))
      (if bookmarks
          (dolist (bookmark bookmarks)
@@ -684,7 +689,10 @@ calls, as is necessary if the match is performed by `string-match'."
                                          (elpher-address-host address)
                                          (elpher-address-port address))))
        (insert "No bookmarks found.\n")))
-   (insert "\n-----------------------")
+   (insert "\n-----------------------\n\n"
+           "u: return to previous page.\n"
+           "x: delete selected bookmark.\n"
+           "a: rename selected bookmark.\n")
    (elpher-restore-pos)))
   
 
@@ -701,6 +709,10 @@ bookmark list, while ADDRESS is the address of the entry."
   "Get the display string of BOOKMARK."
   (elt bookmark 0))
 
+(defun elpher-set-bookmark-display-string (bookmark display-string)
+  "Set the display string of BOOKMARK to DISPLAY-STRING."
+  (setcar bookmark display-string))
+
 (defun elpher-bookmark-address (bookmark)
   "Get the address for BOOKMARK."
   (elt bookmark 1))
@@ -720,23 +732,22 @@ Beware that this completely replaces the existing contents of the file."
       (goto-char (point-min))
       (read (current-buffer)))))
 
-(defun elpher-add-node-bookmark (node)
-  "Add bookmark to NODE to the saved list of bookmarks."
-  (let ((bookmark (elpher-make-bookmark (elpher-node-display-string node)
-                                        (elpher-node-address node)))
-        (bookmarks (elpher-load-bookmarks)))
-    (add-to-list 'bookmarks bookmark)
+(defun elpher-add-address-bookmark (address display-string)
+  "Save a bookmark for ADDRESS with label DISPLAY-STRING.
+If ADDRESS is already bookmarked, update the label only."
+  (let ((bookmarks (elpher-load-bookmarks)))
+    (let ((existing-bookmark (rassoc (list address) bookmarks)))
+      (if existing-bookmark
+          (elpher-set-bookmark-display-string existing-bookmark display-string)
+        (add-to-list 'bookmarks (elpher-make-bookmark display-string address))))
     (elpher-save-bookmarks bookmarks)))
 
-(defun elpher-remove-node-bookmark (node)
-  "Remove bookmark to NODE from the saved list of bookmarks."
-  (let ((bookmark (elpher-make-bookmark (elpher-node-display-string node)
-                                        (elpher-node-address node))))
+(defun elpher-remove-address-bookmark (address)
+  "Remove any bookmark to ADDRESS."
     (elpher-save-bookmarks
-     (seq-filter (lambda (this-bookmark)
-                   (not (equal bookmark this-bookmark)))
-                 (elpher-load-bookmarks)))))
-     
+     (seq-filter (lambda (bookmark)
+                   (not (equal (elpher-bookmark-address bookmark) address)))
+                 (elpher-load-bookmarks))))
 
 ;;; Interactive procedures
 ;;
@@ -757,7 +768,9 @@ Beware that this completely replaces the existing contents of the file."
   (push-button))
 
 (defun elpher-go ()
-  "Go to a particular gopher site."
+  "Go to a particular gopher site read from the minibuffer.
+The site may be specified via a URL or explicitly in terms of
+host, selector and port."
   (interactive)
   (let ((node
          (let ((host-or-url (read-string "Gopher host or URL: ")))
@@ -790,11 +803,13 @@ Beware that this completely replaces the existing contents of the file."
     (message "No current site.")))
 
 (defun elpher-view-raw ()
-  "View current page as plain text."
+  "View raw server response for current page."
   (interactive)
   (if elpher-current-node
-      (elpher-visit-node elpher-current-node
-                         #'elpher-get-node-raw)
+      (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))
     (message "No current site.")))
 
 (defun elpher-back ()
@@ -810,10 +825,10 @@ Beware that this completely replaces the existing contents of the file."
   (let ((button (button-at (point))))
     (if button
         (let ((node (button-get button 'elpher-node)))
-          (if node
-              (elpher-visit-node (button-get button 'elpher-node)
-                                 #'elpher-get-node-download)
-            (error "Can only download gopher links, not general URLs")))
+          (if (elpher-address-special-p (elpher-node-address node))
+              (error "Cannot download this link")
+            (elpher-visit-node (button-get button 'elpher-node)
+                               #'elpher-get-node-download)))
       (error "No link selected"))))
 
 (defun elpher-build-link-map ()
@@ -831,7 +846,7 @@ Beware that this completely replaces the existing contents of the file."
   (let* ((link-map (elpher-build-link-map)))
     (if link-map
         (let ((key (let ((completion-ignore-case t))
-                     (completing-read "Directory entry/link (tab to autocomplete): "
+                     (completing-read "Directory item/link: "
                                       link-map nil t))))
           (if (and key (> (length key) 0))
               (let ((b (cdr (assoc key link-map))))
@@ -859,7 +874,7 @@ Beware that this completely replaces the existing contents of the file."
       (error "Command invalid for this page"))))
 
 (defun elpher-bookmarks-current-p ()
-  "Return true if current node is a bookmarks page."
+  "Return non-nil if current node is a bookmarks page."
   (eq (elpher-address-type (elpher-node-address elpher-current-node)) 'bookmarks))
 
 (defun elpher-reload-bookmarks ()
@@ -870,33 +885,43 @@ Beware that this completely replaces the existing contents of the file."
 (defun elpher-bookmark-current ()
   "Bookmark the current node."
   (interactive)
-  (if (not (elpher-bookmarks-current-p))
-      (elpher-add-node-bookmark elpher-current-node)))
+  (unless (elpher-bookmarks-current-p)
+      (let ((address (elpher-node-address elpher-current-node))
+            (display-string (read-string "Bookmark display string: "
+                                         (elpher-node-display-string elpher-current-node))))
+        (elpher-add-address-bookmark address display-string)
+        (message "Bookmark added."))))
 
 (defun elpher-bookmark-link ()
   "Bookmark the link at point."
   (interactive)
   (let ((button (button-at (point))))
     (if button
-        (progn
-          (elpher-add-node-bookmark (button-get button 'elpher-node))
-          (elpher-reload-bookmarks))
+        (let* ((node (button-get button 'elpher-node))
+               (address (elpher-node-address node))
+               (display-string (read-string "Bookmark display string: "
+                                            (elpher-node-display-string node))))
+          (elpher-add-address-bookmark address display-string)
+          (elpher-reload-bookmarks)
+          (message "Bookmark added."))
       (error "No link selected"))))
 
 (defun elpher-unbookmark-current ()
   "Remove bookmark for the current node."
   (interactive)
-  (if (not (elpher-bookmarks-current-p))
-      (elpher-remove-node-bookmark elpher-current-node)))
+  (unless (elpher-bookmarks-current-p)
+    (elpher-remove-address-bookmark (elpher-node-address elpher-current-node))
+    (message "Bookmark removed.")))
 
 (defun elpher-unbookmark-link ()
   "Remove bookmark for the link at point."
   (interactive)
   (let ((button (button-at (point))))
     (if button
-        (progn
-          (elpher-remove-node-bookmark (button-get button 'elpher-node))
-          (elpher-reload-bookmarks))
+        (let ((node (button-get button 'elpher-node)))
+          (elpher-remove-address-bookmark (elpher-node-address node))
+          (elpher-reload-bookmarks)
+          (message "Bookmark removed."))
       (error "No link selected"))))
 
 (defun elpher-bookmarks ()