branch: externals/bufferlo commit cb215f5e98fbce4000bb2657ef021a06bcdc8848 Author: shipmints <shipmi...@gmail.com> Commit: Florian Rommel <m...@florommel.de>
Add bufferlo-bookmark-tab-failed-buffer-policy * README.org: Document the new feature. * bufferlo.el (bufferlo-bookmark-tab-failed-buffer-policy): New defcustom. (bufferlo--bookmark-tab-handler): Handle the new option. --- README.org | 9 ++++ bufferlo.el | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 128 insertions(+), 19 deletions(-) diff --git a/README.org b/README.org index 8ef97d287e..a0138748c3 100644 --- a/README.org +++ b/README.org @@ -583,6 +583,14 @@ Note: 'raise is considered to act as 'clear by bookmark set loading. (setq bufferlo-bookmark-tab-in-bookmarked-frame-policy 'clear) ; silently clear the loaded tab bookmark (setq bufferlo-bookmark-tab-in-bookmarked-frame-policy 'clear-warn) ; clear the loaded tab bookmark with a message #+end_src +#+begin_src emacs-lisp + ;; handle buffer bookmarks that could not be restored + (setq bufferlo-bookmark-tab-failed-buffer-policy 'placeholder) ; placeholder buffer and buffer name; the default + (setq bufferlo-bookmark-tab-failed-buffer-policy 'placeholder-orig) ; placeholder buffer with original buffer name + (setq bufferlo-bookmark-tab-failed-buffer-policy "*scratch*") ; default to a specific buffer + (setq bufferlo-bookmark-tab-failed-buffer-policy #'my/failed-bookmark-handler) ; function to call that returns a buffer + (setq bufferlo-bookmark-tab-failed-buffer-policy nil) ; ignore +#+end_src *** Bookmark set options @@ -1091,6 +1099,7 @@ remain in force until they are saved if this policy is set to t. (setq bufferlo-bookmark-tab-replace-policy 'new) (setq bufferlo-bookmark-tab-duplicate-policy 'prompt) (setq bufferlo-bookmark-tab-in-bookmarked-frame-policy 'prompt) + (setq bufferlo-bookmark-tab-failed-buffer-policy 'placeholder) (setq bufferlo-bookmarks-save-duplicates-policy 'prompt) (setq bufferlo-bookmarks-save-frame-policy 'all) (setq bufferlo-bookmarks-load-tabs-make-frame t) diff --git a/bufferlo.el b/bufferlo.el index 7c0efef919..3d72f6b041 100644 --- a/bufferlo.el +++ b/bufferlo.el @@ -357,6 +357,41 @@ bookmark if the frame bookmark is saved." (const :tag "Clear (silently)" clear) (const :tag "Clear (with message)" clear-warn))) +(defcustom bufferlo-bookmark-tab-failed-buffer-policy nil + "Control failed buffer bookmark restore handling. + +This controls the handling of buffers in the bookmark's local buffer list +whose individual bookmark could not be restored (e.g., because the file +does not exist anymore) and for buffers that were not bookmarkable at all. + +\\='placeholder creates a unique placeholder buffer in place of the +buffer that could not be restored. By default, the placeholder buffer has +a special name. This buffer will not have a file associated with it. +Each bookmark gets its own unique buffer name. + +\\='placeholder-orig creates a placeholder buffer with the original +buffer name. This buffer will not have a file associated with it. +If a buffer with the same name already exists, bufferlo does not create a +placeholder buffer but uses this buffer instead. + +Use a string to select or create the buffer named by the string; e.g., +\"*scratch*\". + +Use a function that returns a buffer. The function is passed the +original buffer name that failed to load. + +nil does not create placeholder buffers for failed bookmarks. However, +similar to the \\='placeholder-orig policy, if a buffer with the same name +exists in the Emacs session, bufferlo will use this buffer. +If all buffers fail to restore and no matching existing buffers are found, +the default buffer shown will be chosen by Emacs." + :package-version '(bufferlo . "1.1") + :type '(radio (const :tag "Placeholder" placeholder) + (const :tag "Placeholder w/failed buffer name" placeholder-orig) + (string :tag "Buffer to select") + (function :tag "Function to call") + (const :tag "Ignore" nil))) + (defcustom bufferlo-bookmarks-save-duplicates-policy 'prompt "Control duplicates when saving all bookmarks. @@ -520,7 +555,9 @@ frame bookmark is a collection of tab bookmarks." Each function takes the following arguments: bookmark-name: source bookmark name effective-bookmark-name: nil, if tab bookmark cleared - tab: the handled tab" + tab: the handled tab + succ-buffer-names: list of buffer names successfully restored + fail-buffer-names: list of buffer names that could not be restored" :type 'hook) (defcustom bufferlo-bookmark-frame-handler-functions nil @@ -2325,6 +2362,40 @@ invoking action. This functions throws :abort when the user quits." ("clear" 'clear) (_ (throw :abort t))))) +(defun bufferlo--bookmark-insert-placeholer (orig-name) + (let ((buffer-existed (get-buffer orig-name)) + (fail-buffer + (cond + ((eq 'placeholder + bufferlo-bookmark-tab-failed-buffer-policy) + ;; Do not insert a placeholder for a placeholder + (if (string-match-p "\\`\\*bufferlo failed buffer .*\\*\\'" orig-name) + (generate-new-buffer orig-name) + (generate-new-buffer (format-message "*bufferlo failed buffer `%s'*" + orig-name)))) + ((eq 'placeholder-orig + bufferlo-bookmark-tab-failed-buffer-policy) + (get-buffer-create orig-name)) + ((stringp + bufferlo-bookmark-tab-failed-buffer-policy) + (get-buffer-create + bufferlo-bookmark-tab-failed-buffer-policy)) + ((functionp + bufferlo-bookmark-tab-failed-buffer-policy) + (funcall + bufferlo-bookmark-tab-failed-buffer-policy + orig-name)) + (t nil)))) + (when fail-buffer + (switch-to-buffer fail-buffer nil 'force-same-window) + (when (and (not buffer-existed) + (memq bufferlo-bookmark-tab-failed-buffer-policy + '(placeholder placeholder-orig))) + (insert + (format-message + "Bufferlo could not restore buffer `%s'" orig-name)) + (set-buffer-modified-p nil))))) + (defvar bufferlo--bookmark-handler-no-message nil) (defun bufferlo--bookmark-tab-handler (bookmark &optional no-message embedded-tab) @@ -2340,6 +2411,8 @@ this bookmark is embedded in a frame bookmark." nil)) (abm (assoc bookmark-name (bufferlo--active-bookmarks))) (disconnect-tbm-p) + (succ-buffer-names) + (fail-buffer-names) (msg) (msg-append (lambda (s) (setq msg (concat msg "; " s))))) @@ -2393,40 +2466,62 @@ this bookmark is embedded in a frame bookmark." ;; Do the real work: restore the tab ;; NOTE: No :abort throws after this point (let* ((ws (copy-tree (alist-get 'window bookmark))) - (dummy (generate-new-buffer " *bufferlo dummy buffer*")); + (dummy (generate-new-buffer " *bufferlo dummy buffer*")) (restore (lambda (bm) (let ((orig-name (car bm)) - (record (cadr bm))) + (record (cadr bm)) + (restore-failed)) + (set-buffer dummy) - (condition-case err - (progn (funcall (or (bookmark-get-handler record) - 'bookmark-default-handler) - record) - (run-hooks 'bookmark-after-jump-hook)) - (error - (message "Bufferlo bookmark: Could not restore %s (error %s)" - orig-name err))) + ;; Test if bookmark-handler did not complain... + (setq restore-failed + (condition-case err + (progn + (funcall (or (bookmark-get-handler record) + 'bookmark-default-handler) + record) + (run-hooks 'bookmark-after-jump-hook) + nil) + (error + (message "Bufferlo bookmark: Could not restore %s (error %s)" + orig-name err) + t))) + ;; ...then test that the buffer actually changed. + (setq restore-failed (or restore-failed + (eq (current-buffer) dummy))) + + (if restore-failed + (progn + (bufferlo--bookmark-insert-placeholer orig-name) + (push orig-name fail-buffer-names)) + (push orig-name succ-buffer-names)) + (unless (eq (current-buffer) dummy) ;; Return a list of (cons <string> <buffer>). ;; The buffer may be renamed later (by uniquify). ;; Using the buffer name directly would not ;; account for this! (cons orig-name (current-buffer)))))) + (renamed (mapcar restore (alist-get 'buffer-bookmarks bookmark))) (replace-renamed (lambda (b) (if-let* ((replace (assoc b renamed))) (cdr replace) b))) - (bl (mapcar replace-renamed (alist-get 'buffer-list bookmark))) - ;; Some of the bl items may already be buffers after renaming. + (bm-buffer-list (mapcar replace-renamed + (alist-get 'buffer-list bookmark))) + ;; Some of the items may already be buffers after renaming. ;; Others are still buffer names (strings). These items had no ;; bookmark associated with them. - (bl (seq-filter #'get-buffer bl)) - (bl (mapcar #'get-buffer bl))) + (bm-buffer-list (seq-filter #'get-buffer bm-buffer-list)) + (bm-buffer-list (mapcar #'get-buffer bm-buffer-list))) + (kill-buffer dummy) + ;; Note that we replace buffer names with buffers in ws. ;; `window-state-put' accepts this. (bufferlo--ws-replace-buffer-names ws renamed) + ;; We do the following to work around two problems with ;; bookmark--jump-via. In older versions, when called ;; interactively and not through bufferlo commands, it calls a @@ -2449,7 +2544,7 @@ this bookmark is embedded in a frame bookmark." (let ((tab-bar-tab-post-select-functions)) (tab-bar-select-tab tab-number) ; defensive (window-state-put ws (frame-root-window) 'safe) - (set-frame-parameter nil 'buffer-list bl) + (set-frame-parameter nil 'buffer-list bm-buffer-list) (set-frame-parameter nil 'buried-buffer-list nil) (setf (alist-get 'bufferlo-bookmark-tab-name (cdr (bufferlo--current-tab))) @@ -2459,14 +2554,19 @@ this bookmark is embedded in a frame bookmark." 'bufferlo-bookmark-tab-handler-functions bookmark-name (unless disconnect-tbm-p bookmark-name) - (bufferlo--current-tab)))))) + (bufferlo--current-tab) + succ-buffer-names + fail-buffer-names) + buffer)))) (add-hook 'bookmark-after-jump-hook bm-after-jump-hook-sym -99))) ;; Log message (unless (or no-message bufferlo--bookmark-handler-no-message) (message "Restored bufferlo tab bookmark%s%s" - (if bookmark-name (format ": %s" bookmark-name) "") - (or msg "")))))) + (if orig-bookmark-name (format ": %s" orig-bookmark-name) "") + (or msg ""))) + ; explicitly return success; abort returns non-nil + nil))) ;; We use a short name here as bookmark-bmenu-list hard codes width of 8 chars (put #'bufferlo--bookmark-tab-handler 'bookmark-handler-type "B-Tab")