Implement three commands for history items
authorAlex Schroeder <alex@gnu.org>
Mon, 20 Jul 2020 09:27:51 +0000 (11:27 +0200)
committerAlex Schroeder <alex@gnu.org>
Thu, 29 Oct 2020 10:51:30 +0000 (11:51 +0100)
In the Elpher menu, history are shown when invoking it using a prefix.
The history items aren't being shown in a buffer, so switching to
those buffers is not possible. Three buffer menu commands have been
implemented (RET, o, C-o) such that they work with history items.

elpher.el

index 6513ee7..87fe007 100644 (file)
--- a/elpher.el
+++ b/elpher.el
@@ -2028,9 +2028,9 @@ With an optional argument, add all the history items, too."
     (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)
@@ -2064,10 +2064,10 @@ With an optional argument, add all the history items, too."
       '(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]
@@ -2104,17 +2104,20 @@ With an optional argument, add all the history items, too."
 
 (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.
@@ -2134,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 ()
@@ -2152,8 +2192,9 @@ In Elpher Menu mode, the following commands are defined:
 
 (defun elpher-menu-refresh (&optional arg)
   "Refresh the list of buffers.
-With an optional argument, add all the history items, too."
-    ;; Set up `tabulated-list-format'.
+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" 40 t)
@@ -2210,38 +2251,56 @@ 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'."
-  (let ((separator (list (current-buffer)
-                        (vector
-                         "-"
-                         (make-string 25 ?-)
-                         (make-string 25 ?-)))))
+  ;; 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)
-          ;; every section starts with the current page and ends with
-          ;; the separator
-          (mapcar (lambda (page)
-                    (if page
-                        (list (current-buffer)
+          ;; 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-address-to-url
-                                           (elpher-page-address page)) "none")
-                                      (or (elpher-page-display-string page) "?")))
+                                           (elpher-page-address (cdr pair))) "none")
+                                      (or (elpher-page-display-string (cdr pair)) "?")))
                       separator))
-                  (cons elpher-current-page elpher-history)))
+                  (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
+          ;; No history means a list of one item. Add a separator.
           (list (list (current-buffer)
                       (vector "E"
                               (or (elpher-address-to-url
                                    (elpher-page-address elpher-current-page)))
                               (or (elpher-page-display-string elpher-current-page)
-                                  (buffer-name))))))
+                                  (buffer-name))))
+                separator))
           ((eq major-mode 'eww-mode)
-           (nconc (mapcar (lambda (data)
-                            (list (current-buffer)
-                                  (vector "W"
-                                          (or (plist-get data :url) "none")
-                                          (or (plist-get data :title) "none"))))
-                          (cons eww-data eww-history))
+          ;; 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 :url) "none")
+                                      (or (plist-get eww-data :title) "none")))
+                        (mapcar (lambda (data)
+                                  (list
+                                   (list (current-buffer) 'eww-restore-history data)
+                                   (vector "W"
+                                           (or (plist-get data :url) "none")
+                                           (or (plist-get data :title) "none"))))
+                                eww-history))
                   (list separator))))))
 
 ;;; Main start procedure