From b5600b7e1b01eea2d896c00e308bafcf1f8e37e9 Mon Sep 17 00:00:00 2001 From: Tim Vaughan Date: Mon, 29 Jun 2020 00:29:26 +0200 Subject: [PATCH] Testing collapsable bookmark groups. --- elpher.el | 157 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 133 insertions(+), 24 deletions(-) diff --git a/elpher.el b/elpher.el index 0fef0ac..4ae15d2 100644 --- a/elpher.el +++ b/elpher.el @@ -280,8 +280,8 @@ some servers which do not support IPv6 can take a long time to time-out." (setf (url-host url) (url-filename url)) (setf (url-filename url) "")) (when (or (equal (url-filename url) "") - (equal (url-filename url) "/")) - (setf (url-filename url) "/1"))) + (equal (url-filename url) "/1")) + (setf (url-filename url) "/"))) (when (equal "gemini" (url-type url)) ;; Gemini defaults (if (equal (url-filename url) "") @@ -510,7 +510,7 @@ unless NO-HISTORY is non-nil." '("gophers" "gemini"))) " [TLS encryption]" "")) - (header (concat display-string + (header (concat (replace-regexp-in-string "%" "%%" display-string) (propertize tls-string 'face 'bold)))) (setq header-line-format header)))) @@ -1556,20 +1556,98 @@ The result is rendered using RENDERER." ;; Bookmarks page page retrieval +(defvar elpher-bookmark-group-status (make-hash-table :test 'equal)) + +(defun elpher-make-bookmark-group-status (group-path) + (let ((existing (gethash group-path elpher-bookmark-group-status))) + (unless existing + (puthash group-path (list nil nil nil) elpher-bookmark-group-status)))) + +(defun elpher-bookmark-group-start (group-path) + (car (gethash group-path elpher-bookmark-group-status))) + +(defun elpher-bookmark-group-end (group-path) + (cadr (gethash group-path elpher-bookmark-group-status))) + +(defun elpher-bookmark-group-expanded-p (group-path) + (caddr (gethash group-path elpher-bookmark-group-status))) + +(defun elpher-bookmark-group-expanded-toggle (group-path) + (setcar (cddr (gethash group-path elpher-bookmark-group-status)) + (not (elpher-bookmark-group-expanded-p group-path)))) + +(defun elpher--bookmark-indent (group-path) + (insert (make-string (+ 1 (* 2 (length group-path))) ?\s))) + +(defun elpher--bookmark-group-starts-here (group-path) + (setcar (gethash group-path elpher-bookmark-group-status) (point))) + +(defun elpher--bookmark-group-ends-here (group-path) + (setcar (cdr (gethash group-path elpher-bookmark-group-status)) (point))) + +(defun elpher--update-bookmark-group-visibility (group-path) + (let ((start (elpher-bookmark-group-start group-path)) + (end (elpher-bookmark-group-end group-path)) + (inhibit-read-only t)) + (put-text-property start end 'invisible + (not (elpher-bookmark-group-expanded-p group-path))))) + +(defun elpher--expand-or-collapse-bookmark-group (button) + (let ((group-path (button-get button 'elpher-bookmark-group-path))) + (elpher-bookmark-group-expanded-toggle group-path) + (elpher--update-bookmark-group-visibility group-path))) + +(defun elpher--insert-bookmark (bookmark &optional group-path) + (let* ((display-string (elpher-bookmark-display-string bookmark)) + (address (elpher-address-from-url (elpher-bookmark-url bookmark))) + (type (if address (elpher-address-type address) nil)) + (type-map-entry (cdr (assoc type elpher-type-map)))) + (if type-map-entry + (let* ((face (elt type-map-entry 3)) + (filtered-display-string (ansi-color-filter-apply display-string)) + (page (elpher-make-page filtered-display-string address))) + (insert-text-button filtered-display-string + 'face face + 'elpher-page page + 'action #'elpher-click-link + 'follow-link t + 'help-echo #'elpher--page-button-help)) + (insert (propertize display-string 'face 'elpher-unknown))) + (insert "\n"))) + +(defun elpher--insert-bookmark-group (group-entries &optional group-path) + (if group-entries + (dolist (entry group-entries) + (elpher--bookmark-indent group-path) + (if (elpher-bookmark-p entry) + (elpher--insert-bookmark entry group-path) + (let* ((subgroup-name (elpher-bookmark-group-name entry)) + (subgroup-entries (elpher-bookmark-group-entries entry)) + (subgroup-path (cons subgroup-name group-path))) + (elpher-make-bookmark-group-status subgroup-path) + (insert-text-button subgroup-name + 'action #'elpher--expand-or-collapse-bookmark-group + 'elpher-bookmark-group-path subgroup-path + 'follow-link t + 'help-echo "Expand or collapse group.") + (insert "\n") + (elpher--bookmark-group-starts-here subgroup-path) + (elpher--insert-bookmark-group subgroup-entries subgroup-path) + (elpher--bookmark-group-ends-here subgroup-path) + (elpher--update-bookmark-group-visibility subgroup-path)))) + (elpher--bookmark-indent group-path) + (insert "No bookmarks found.\n"))) + (defun elpher-get-bookmarks-page (renderer) "Getter to load and display the current bookmark list (RENDERER must be nil)." (when renderer (elpher-visit-previous-page) (error "Command not supported for bookmarks page")) (elpher-with-clean-buffer + ;; (setq buffer-invisibility-spec '((expanded . t))) (insert "---- Bookmark list ----\n\n") (let ((bookmarks (elpher-load-bookmarks))) - (if bookmarks - (dolist (bookmark bookmarks) - (let ((display-string (elpher-bookmark-display-string bookmark)) - (address (elpher-address-from-url (elpher-bookmark-url bookmark)))) - (elpher-insert-index-record display-string address))) - (insert "No bookmarks found.\n"))) + (elpher--insert-bookmark-group bookmarks)) (insert "\n-----------------------\n" "\n" "- u: return to previous page\n" @@ -1590,8 +1668,8 @@ The result is rendered using RENDERER." (insert "\n--- Recently visited ---\n\n") (maphash (lambda (address _) (unless (elpher-address-special-p address) - (elpher-insert-index-record (elpher-address-to-url address) - address))) + (let ((url (elpher-address-to-url address))) + (elpher--insert-bookmark (elpher-make-bookmark url url))))) elpher-content-cache) (insert "\n------------------------\n") (elpher-restore-pos))) @@ -1605,7 +1683,7 @@ The result is rendered using RENDERER." DISPLAY-STRING determines how the bookmark will appear in the bookmark list, while URL is the url of the entry." (list display-string url)) - + (defun elpher-bookmark-display-string (bookmark) "Get the display string of BOOKMARK." (elt bookmark 0)) @@ -1618,6 +1696,28 @@ bookmark list, while URL is the url of the entry." "Get the address for BOOKMARK." (elt bookmark 1)) +(defun elpher-bookmark-p (entry) + "Determine if entry describes a bookmark. +Otherwise, it will be treated as a bookmark group." + (and (listp entry) + (= (length entry) 2) + (stringp (cadr entry)))) + +(defun elpher-make-bookmark-group (group-name &optional bookmarks) + "Make an elpher bookmark group. +GROUP-NAME is the name of the group. If non-nil, BOOKMARKS is a +list of one or more bookmarks or subgroups to appear within this +group." + (cons diplay-string bookmarks)) + +(defun elpher-bookmark-group-name (group) + "Returns the name of bookmark group GROUP." + (car group)) + +(defun elpher-bookmark-group-entries (group) + "Returns the entries held in bookmark group GROUP." + (cdr group)) + (defun elpher-save-bookmarks (bookmarks) "Record the bookmark list BOOKMARKS to the user's bookmark file. Beware that this completely replaces the existing contents of the file." @@ -1637,15 +1737,15 @@ Beware that this completely replaces the existing contents of the file." (insert-file-contents elpher-bookmarks-file) (goto-char (point-min)) (read (current-buffer)))))) - (if (and bookmarks (listp (cadar bookmarks))) - (progn - (message "Reading old bookmark file. (Will be updated on write.)") - (mapcar (lambda (old-bm) - (list (car old-bm) - (elpher-address-to-url (apply #'elpher-make-gopher-address - (cadr old-bm))))) - bookmarks)) - bookmarks))) + ;; (if (and bookmarks (listp (cadar bookmarks))) + ;; (progn + ;; (message "Reading old bookmark file. (Will be updated on write.)") + ;; (mapcar (lambda (old-bm) + ;; (list (car old-bm) + ;; (elpher-address-to-url (apply #'elpher-make-gopher-address + ;; (cadr old-bm))))) + ;; bookmarks)) + bookmarks)) (defun elpher-add-address-bookmark (address display-string) "Save a bookmark for ADDRESS with label DISPLAY-STRING.))) @@ -1672,12 +1772,18 @@ If ADDRESS is already bookmarked, update the label only." (defun elpher-next-link () "Move point to the next link on the current page." (interactive) - (forward-button 1)) + (while + (let ((next-button (forward-button 1))) + (or (not next-button) + (button-get next-button 'invisible))))) (defun elpher-prev-link () "Move point to the previous link on the current page." (interactive) - (backward-button 1)) + (while + (let ((prev-button (backward-button 1))) + (or (not prev-button) + (button-get prev-button 'invisible))))) (defun elpher-follow-current-link () "Open the link or url at point." @@ -1889,7 +1995,10 @@ When run interactively HOST-OR-URL is read from the minibuffer." (interactive) (let ((button (button-at (point)))) (if button - (elpher-info-page (button-get button 'elpher-page)) + (let ((page (button-get button 'elpher-page))) + (if page + (elpher-info-page (button-get button 'elpher-page)) + (error "Not an Elpher page"))) (error "No item selected")))) (defun elpher-info-current () -- 2.20.1