Switch URL and Name position in the menu
[elpher.git] / elpher.el
index a4c8831..29687f1 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -2010,12 +2010,13 @@ functions which initialize the gopher client, namely
 ;;; Menu
 ;;
 
-(defun elpher-menu ()
-  "Show a list of all your `elpher' buffers."
-  (interactive)
+(defun elpher-menu (&optional arg)
+  "Show a list of all your `elpher' buffers.
+With an optional argument, add all the history items, too."
+  (interactive "P")
   (switch-to-buffer (get-buffer-create "*Elpher Menu*"))
   (elpher-menu-mode)
-  (elpher-menu-refresh)
+  (elpher-menu-refresh arg)
   (tabulated-list-print))
 
 (defvar elpher-menu-mode-map
@@ -2027,9 +2028,9 @@ functions which initialize the gopher client, namely
     (define-key map "1" 'Buffer-menu-1-window)
     (define-key map "f" 'Buffer-menu-this-window)
     (define-key map "e" 'Buffer-menu-this-window)
-    (define-key map "\C-m" 'Buffer-menu-this-window)
-    (define-key map "o" 'Buffer-menu-other-window)
-    (define-key map "\C-o" 'Buffer-menu-switch-other-window)
+    (define-key map "\C-m" 'elpher-menu-this-window)
+    (define-key map "o" 'elpher-menu-other-window)
+    (define-key map "\C-o" 'elpher-menu-switch-other-window)
     (define-key map "d" 'Buffer-menu-delete)
     (define-key map "k" 'Buffer-menu-delete)
     (define-key map "\C-k" 'Buffer-menu-delete)
@@ -2063,10 +2064,10 @@ functions which initialize the gopher client, namely
       '(menu-item "Select Current" Buffer-menu-1-window
                 :help "Select this line's buffer, alone, in full frame"))
     (bindings--define-key menu-map [ow]
-      '(menu-item "Select in Other Window" Buffer-menu-other-window
+      '(menu-item "Select in Other Window" elpher-menu-other-window
                 :help "Select this line's buffer in other window, leaving buffer menu visible"))
     (bindings--define-key menu-map [tw]
-      '(menu-item "Select in Current Window" Buffer-menu-this-window
+      '(menu-item "Select in Current Window" elpher-menu-this-window
                 :help "Select this line's buffer in this window"))
     (bindings--define-key menu-map [s2] menu-bar-separator)
     (bindings--define-key menu-map [is]
@@ -2103,17 +2104,20 @@ functions which initialize the gopher client, namely
 
 (define-derived-mode elpher-menu-mode tabulated-list-mode "Elpher Menu"
   "Major mode for Elpher Menu buffers.
-The Elpher Menu is invoked by the command \\[elpher-menu].
+The Elpher Menu is invoked by the command \\[elpher-menu]. When
+invoked with a prefix, the command also shows history items.
+Since history items are no longer showing in a buffer, many of
+the commands shown below will not work on them.
 
 In Elpher Menu mode, the following commands are defined:
 \\<elpher-menu-mode-map>
 \\[quit-window]    Remove the Buffer Menu from the display.
-\\[tabulated-list-sort]    sorts buffers according to the current
+\\[tabulated-list-sort]    Sorts buffers according to the current
      column. With a numerical argument, sort by that column.
-\\[Buffer-menu-this-window]  Select current line's buffer in place of the buffer menu.
-\\[Buffer-menu-other-window]    Select that buffer in another window,
+\\[elpher-menu-this-window]  Select current line's buffer in place of the buffer menu.
+\\[elpher-menu-other-window]    Select that buffer in another window,
      so the Buffer Menu remains visible in its window.
-\\[Buffer-menu-switch-other-window]  Make another window display that buffer.
+\\[elpher-menu-switch-other-window]  Make another window display that buffer.
 \\[Buffer-menu-mark]    Mark current line's buffer to be displayed.
 \\[Buffer-menu-select]    Select current line's buffer.
      Also show buffers marked with m, in other windows.
@@ -2133,6 +2137,43 @@ In Elpher Menu mode, the following commands are defined:
 \\[Buffer-menu-bury]    Bury the buffer listed on this line."
   (add-hook 'tabulated-list-revert-hook 'elpher-menu-refresh nil t))
 
+(defun elpher-menu-this-window ()
+  "Select this line’s buffer in this window.
+Switch to the buffer, if possible. If there is no buffer, chances
+are that we're looking at a history item. Let's visit the item
+instead of complaining that their buffers have been killed."
+  (interactive)
+  (elpher-menu-handle-buffer-or-data 'switch-to-buffer))
+
+(defun elpher-menu-other-window ()
+  "Select this line’s buffer in other window, leaving buffer menu visible."
+  (interactive)
+  (elpher-menu-handle-buffer-or-data 'switch-to-buffer-other-window))
+
+(defun elpher-menu-switch-other-window ()
+  "Make the other window select this line's buffer.
+The current window remains selected."
+  (interactive)
+  (elpher-menu-handle-buffer-or-data
+   (lambda (buf) (display-buffer buf t))))
+
+(defun elpher-menu-handle-buffer-or-data (buffer-func)
+  "Handle an item in `elpher-menu-mode'.
+Determine the entry ID of the Tabulated List entry at point. If
+ID is a buffer, invoke BUFFER-FUNC on it. Otherwise, ID is a
+list (BUFFER FUNC ARGS...). Switch to BUFFER using BUFFER-FUNC
+and apply FUNC to ARGS."
+  (let ((data (tabulated-list-get-id)))
+    (cond ((bufferp data)
+          (funcall buffer-func data))
+         ((and (listp data)
+               (buffer-live-p (nth 0 data))
+               (fboundp (nth 1 data)))
+          (funcall buffer-func (nth 0 data))
+          (apply (nth 1 data) (nthcdr 2 data)))
+         (t
+          (error "There's no entry on this line of the menu")))))
+
 (defvar elpher-title nil)
 
 (defun elpher-find-title ()
@@ -2142,45 +2183,126 @@ In Elpher Menu mode, the following commands are defined:
     (let ((start (text-property-any
                  (point-min) (point-max)
                  'face 'elpher-gemini-heading1)))
-      (if start
-         (save-excursion
-           (goto-char start)
-           (setq-local elpher-title
-                       (buffer-substring-no-properties
-                        start (line-end-position))))
-       "none"))))
-
-(defun elpher-menu-refresh ()
-  "Refresh the list of buffers."
-    ;; Set up `tabulated-list-format'.
+      (when start
+       (save-excursion
+         (goto-char start)
+         (setq-local elpher-title
+                     (buffer-substring-no-properties
+                      start (line-end-position))))))))
+
+(defun elpher-menu-refresh (&optional arg)
+  "Refresh the list of buffers.
+With an optional argument, add all the history items, too. Note
+that there are no buffers for history items so many of the buffer
+menu commands won't work on them."
     (setq tabulated-list-format
          (vector '("T" 1 t)
-                 '("URL" 35 t)
-                 '("Name" 35 t))
-         tabulated-list-sort-key '("Name"))
+                 '("Name" 30 t)
+                 '("URL" 40 t))
+         tabulated-list-sort-key nil)
     ;; Collect info for each buffer we're interested in.
     (let (entries)
       (dolist (buf (buffer-list))
        (with-current-buffer buf
-         (when (memq major-mode '(elpher-mode eww-mode))
-           (push (list buf
-                       (vector
-                        (cond ((eq major-mode 'elpher-mode) "E")
-                              ((eq major-mode 'eww-mode) "W"))
-                        (cond ((eq major-mode 'elpher-mode)
-                               (or (elpher-address-to-url
-                                    (elpher-page-address elpher-current-page))
-                                   "none"))
-                              ((eq major-mode 'eww-mode)
-                               (eww-current-url)))
-                        (cond ((eq major-mode 'elpher-mode)
-                               (elpher-find-title))
-                              ((eq major-mode 'eww-mode)
-                               (plist-get eww-data :title)))))
-                 entries))))
+         (when (memq major-mode '(elpher-mode eww-mode gemini-mode))
+           (if arg
+               (setq entries (nconc (elpher-menu-refresh-history) entries))
+             (push (elpher-menu-refresh-current) entries)))))
       (setq tabulated-list-entries (nreverse entries)))
     (tabulated-list-init-header))
 
+(defun elpher-menu-refresh-current ()
+  "Returns an item for `elpher-menu-refresh'
+based on the current buffer.
+
+An item is a list (BUFFER VECTOR) where BUFFER is the buffer this
+item refers to and VECTOR is what to display in the tabulated
+list established by `elpher-menu-refresh'. See
+`tabulated-list-format'."
+  (list (current-buffer)
+       (cond ((eq major-mode 'elpher-mode)
+              (vector "G"
+                      (or (elpher-find-title)
+                          (elpher-page-display-string elpher-current-page)
+                          (buffer-name))
+                      (or (elpher-address-to-url
+                           (elpher-page-address elpher-current-page))
+                          "none")))
+             ((eq major-mode 'gemini-mode)
+              (vector "E"
+                      (or (elpher-page-display-string elpher-current-page)
+                          (buffer-name))
+                      (or (elpher-address-to-url
+                           (elpher-page-address elpher-current-page))
+                          "none")))
+             ((eq major-mode 'eww-mode)
+              (vector "W"
+                      (or (plist-get eww-data :title)
+                          (buffer-name))
+                      (or (eww-current-url)
+                          "none"))))))
+
+(defun elpher-menu-refresh-history ()
+  "Return current entries for `elpher-menu-refresh'.
+This returns a list of items for the current buffer, based on the
+buffer's history.
+
+An item is a list (BUFFER VECTOR) where BUFFER is the buffer this
+item refers to and VECTOR is what to display in the tabulated
+list established by `elpher-menu-refresh'. See
+`tabulated-list-format'."
+  ;; Every section starts with the current page, followed by some
+  ;; history items, and ends with the separator.
+  (let ((separator (list nil
+                        (vector "-"
+                                (make-string 25 ?-)
+                                (make-string 25 ?-)))))
+    (cond ((eq major-mode 'elpher-mode)
+          ;; A pair is (BUFFER-OR-DATA . PAGE) where BUFFER-OR-DTA is
+          ;; the current buffer, if possible, or list (BUFFER FUNC
+          ;; &rest ARGS) telling us which BUFFER to switch to, and
+          ;; what function to call. The last item of elpher-history
+          ;; has a nil page, so when that shows up, use the separator
+          (mapcar (lambda (pair)
+                    (if (cdr pair)
+                        (list (car pair)
+                              (vector "G"
+                                      (or (elpher-page-display-string (cdr pair)) "?")
+                                      (or (elpher-address-to-url
+                                           (elpher-page-address (cdr pair))) "none")))
+                      separator))
+                  (cons (cons (current-buffer) elpher-current-page)
+                        (mapcar (lambda (page)
+                                  (cons (list (current-buffer) 'elpher-visit-page page)
+                                        page))
+                                elpher-history))))
+         ((eq major-mode 'gemini-mode)
+          ;; No history means a list of one item. Add a separator.
+          (list (list (current-buffer)
+                      (vector "E"
+                              (or (elpher-page-display-string elpher-current-page)
+                                  (buffer-name))
+                              (or (elpher-address-to-url
+                                   (elpher-page-address elpher-current-page)))))
+                separator))
+          ((eq major-mode 'eww-mode)
+          ;; A pair is (BUFFER-OR-DATA . PAGE) where BUFFER-OR-DTA is
+          ;; the current buffer, if possible, or list (BUFFER FUNC
+          ;; &rest ARGS) telling us which BUFFER to switch to, and
+          ;; what function to call. Add the separator at the end.
+           (nconc (cons (list (current-buffer)
+                              (vector "W"
+                                      (or (plist-get eww-data :title) "none")
+                                      (or (plist-get eww-data :url) "none")))
+                        (mapcar (lambda (data)
+                                  (list
+                                   (list (current-buffer) 'eww-restore-history data)
+                                   (vector "W"
+                                           (or (plist-get data :title) "none")
+                                           (or (plist-get data :url) "none"))))
+                                eww-history))
+                  (list separator))))))
+
 ;;; Main start procedure
 ;;