Compare commits
3 commits
2b6e9edc6d
...
ae4c4426cf
Author | SHA1 | Date | |
---|---|---|---|
KemoNine | ae4c4426cf | ||
KemoNine | c6acdcb3a2 | ||
KemoNine | c05bd7c9ce |
|
@ -1,105 +0,0 @@
|
|||
;;; burly-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(add-to-list 'load-path (directory-file-name
|
||||
(or (file-name-directory #$) (car load-path))))
|
||||
|
||||
|
||||
;;;### (autoloads nil "burly" "burly.el" (0 0 0 0))
|
||||
;;; Generated autoloads from burly.el
|
||||
|
||||
(autoload 'burly-open-last-bookmark "burly" "\
|
||||
Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on
|
||||
a project." t nil)
|
||||
|
||||
(autoload 'burly-kill-buffer-url "burly" "\
|
||||
Copy BUFFER's URL to the kill ring.
|
||||
|
||||
\(fn BUFFER)" t nil)
|
||||
|
||||
(autoload 'burly-kill-frames-url "burly" "\
|
||||
Copy current frameset's URL to the kill ring." t nil)
|
||||
|
||||
(autoload 'burly-kill-windows-url "burly" "\
|
||||
Copy current frame's window configuration URL to the kill ring." t nil)
|
||||
|
||||
(autoload 'burly-open-url "burly" "\
|
||||
Open Burly URL.
|
||||
|
||||
\(fn URL)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-frames "burly" "\
|
||||
Bookmark the current frames as NAME.
|
||||
|
||||
\(fn NAME)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-windows "burly" "\
|
||||
Bookmark the current frame's window configuration as NAME.
|
||||
|
||||
\(fn NAME)" t nil)
|
||||
|
||||
(autoload 'burly-open-bookmark "burly" "\
|
||||
Restore a window configuration to the current frame from a Burly BOOKMARK.
|
||||
|
||||
\(fn BOOKMARK)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-handler "burly" "\
|
||||
Handler function for Burly BOOKMARK.
|
||||
|
||||
\(fn BOOKMARK)" nil nil)
|
||||
|
||||
(register-definition-prefixes "burly" '("burly-"))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "burly-tabs" "burly-tabs.el" (0 0 0 0))
|
||||
;;; Generated autoloads from burly-tabs.el
|
||||
|
||||
(defvar burly-tabs-mode nil "\
|
||||
Non-nil if Burly-Tabs mode is enabled.
|
||||
See the `burly-tabs-mode' command
|
||||
for a description of this minor mode.
|
||||
Setting this variable directly does not take effect;
|
||||
either customize it (see the info node `Easy Customization')
|
||||
or call the function `burly-tabs-mode'.")
|
||||
|
||||
(custom-autoload 'burly-tabs-mode "burly-tabs" nil)
|
||||
|
||||
(autoload 'burly-tabs-mode "burly-tabs" "\
|
||||
Integrate Burly with `tab-bar-mode'.
|
||||
When active, Burly bookmarks are opened in new tabs and named
|
||||
accordingly.
|
||||
|
||||
This is a minor mode. If called interactively, toggle the
|
||||
`Burly-Tabs mode' mode. If the prefix argument is positive,
|
||||
enable the mode, and if it is zero or negative, disable the mode.
|
||||
|
||||
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
|
||||
the mode if ARG is nil, omitted, or is a positive number.
|
||||
Disable the mode if ARG is a negative number.
|
||||
|
||||
To check whether the minor mode is enabled in the current buffer,
|
||||
evaluate `(default-value \\='burly-tabs-mode)'.
|
||||
|
||||
The mode's hook is called both when the mode is enabled and when
|
||||
it is disabled.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(register-definition-prefixes "burly-tabs" '("burly-"))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil nil ("burly-pkg.el") (0 0 0 0))
|
||||
|
||||
;;;***
|
||||
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; burly-autoloads.el ends here
|
|
@ -1,13 +0,0 @@
|
|||
(define-package "burly" "20221024.2019" "Save and restore frame/window configurations with buffers"
|
||||
'((emacs "27.1")
|
||||
(map "2.1"))
|
||||
:commit "f570fa87ee72a451f535cfb038d81798a01a7e20" :authors
|
||||
'(("Adam Porter" . "adam@alphapapa.net"))
|
||||
:maintainer
|
||||
'("Adam Porter" . "adam@alphapapa.net")
|
||||
:keywords
|
||||
'("convenience")
|
||||
:url "https://github.com/alphapapa/burly.el")
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
|
@ -1,90 +0,0 @@
|
|||
;;; burly-tabs.el --- Burly's support for tab-bar -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2022 Adam Porter
|
||||
|
||||
;; Author: Adam Porter <adam@alphapapa.net>
|
||||
;; Keywords: tab-bar, tabs, frames
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This library provides support for `tab-bar-mode'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;; TODO: In Emacs 28, add an entry to the tab context menu. See
|
||||
;; <https://lists.gnu.org/archive/html/emacs-devel/2021-09/msg00590.html>.
|
||||
|
||||
;;;; Requirements
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'map)
|
||||
(require 'tab-bar)
|
||||
|
||||
(require 'burly)
|
||||
|
||||
;;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode burly-tabs-mode
|
||||
"Integrate Burly with `tab-bar-mode'.
|
||||
When active, Burly bookmarks are opened in new tabs and named
|
||||
accordingly."
|
||||
:global t
|
||||
:group 'burly
|
||||
(unless (version<= "28.1" emacs-version)
|
||||
(user-error "`burly-tabs-mode' requires Emacs 28.1 or later"))
|
||||
(if burly-tabs-mode
|
||||
(progn
|
||||
(advice-add #'burly--windows-set :before #'burly-tabs--windows-set-before-advice)
|
||||
(advice-add #'burly--windows-set :after #'burly-tabs--windows-set-after-advice))
|
||||
;; Disable mode.
|
||||
(advice-remove #'burly--windows-set #'burly-tabs--windows-set-before-advice)
|
||||
(advice-remove #'burly--windows-set #'burly-tabs--windows-set-after-advice)))
|
||||
|
||||
(cl-defun burly-tabs-reset-tab (&optional (tab (tab-bar--current-tab-find)))
|
||||
"Reset TAB to its saved configuration.
|
||||
Resets TAB to the Burly bookmark that it was created from."
|
||||
(interactive)
|
||||
(pcase-let* ((`(,_ . ,(map burly-bookmark-name)) tab))
|
||||
(unless burly-bookmark-name
|
||||
(user-error "Tab has no associated Burly bookmark (`burly-tabs-mode' must be enabled when opening a bookmark)"))
|
||||
(burly-open-bookmark burly-bookmark-name)))
|
||||
|
||||
(defalias 'burly-reset-tab #'burly-tabs-reset-tab)
|
||||
|
||||
;;;; Functions
|
||||
|
||||
(defun burly-tabs--windows-set-before-advice (&rest _ignore)
|
||||
"Cause bookmark to be opened in a tab by that name.
|
||||
If a tab already exists named the value of
|
||||
`burly-opened-bookmark-name', select it; otherwise call
|
||||
`tab-bar-new-tab'. To be used as advice to
|
||||
`burly-open-bookmark'."
|
||||
(if (tab-bar--tab-index-by-name burly-opened-bookmark-name)
|
||||
(tab-bar-select-tab-by-name burly-opened-bookmark-name)
|
||||
(tab-bar-new-tab)))
|
||||
|
||||
(defun burly-tabs--windows-set-after-advice (&rest _ignore)
|
||||
"Set current tab's `burly-bookmark-name' to BOOKMARK-NAME.
|
||||
To be used as advice to `burly--windows-set'."
|
||||
(tab-rename burly-opened-bookmark-name)
|
||||
(let ((current-tab (tab-bar--current-tab-find)))
|
||||
(setf (alist-get 'burly-bookmark-name (cdr current-tab))
|
||||
burly-opened-bookmark-name)))
|
||||
|
||||
(provide 'burly-tabs)
|
||||
|
||||
;;; burly-tabs.el ends here
|
|
@ -1,590 +0,0 @@
|
|||
;;; burly.el --- Save and restore frame/window configurations with buffers -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Adam Porter
|
||||
|
||||
;; Author: Adam Porter <adam@alphapapa.net>
|
||||
;; URL: https://github.com/alphapapa/burly.el
|
||||
;; Version: 0.3-pre
|
||||
;; Package-Requires: ((emacs "27.1") (map "2.1"))
|
||||
;; Keywords: convenience
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package provides tools to save and restore frame and window
|
||||
;; configurations in Emacs, including buffers that may not be live
|
||||
;; anymore. In this way, it's like a lightweight "workspace" manager,
|
||||
;; allowing you to easily restore one or more frames, including their
|
||||
;; windows, the windows' layout, and their buffers.
|
||||
|
||||
;; Internally it uses Emacs's bookmarks system to restore buffers to
|
||||
;; their previous contents and location. This provides power and
|
||||
;; extensibility, since many major modes already integrate with
|
||||
;; Emacs's bookmarks system. However, in case a mode's bookmarking
|
||||
;; function isn't satisfactory, Burly allows the user to customize
|
||||
;; buffer-restoring functions for specific modes.
|
||||
|
||||
;; For Org mode, Burly provides such custom functions so that narrowed
|
||||
;; and indirect Org buffers are properly restored, and headings are
|
||||
;; located by outline path in case they've moved since a bookmark was
|
||||
;; made (the org-bookmark-heading package also provides this through
|
||||
;; the Emacs bookmark system, but users may not have it installed, and
|
||||
;; the functionality is too useful to not include here by default).
|
||||
|
||||
;; Internally, buffers and window configurations are also encoded as
|
||||
;; URLs, and users may also save and open those URLs instead of using
|
||||
;; Emacs bookmarks. (The name "Burly" comes from "buffer URL.")
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;;; Requirements
|
||||
|
||||
(require 'bookmark)
|
||||
(require 'cl-lib)
|
||||
(require 'map)
|
||||
(require 'subr-x)
|
||||
(require 'thingatpt)
|
||||
(require 'url-parse)
|
||||
(require 'url-util)
|
||||
|
||||
;;;; Variables
|
||||
|
||||
(defvar burly--window-state nil
|
||||
"Used to work around `bookmark--jump-via' affecting window configuration.")
|
||||
|
||||
(defvar burly-opened-bookmark-name nil
|
||||
"The name of the last bookmark opened by Burly.")
|
||||
|
||||
;;;; Customization
|
||||
|
||||
(defgroup burly nil
|
||||
"Save and restore window configurations and their buffers."
|
||||
:group 'convenience
|
||||
:link '(url-link "https://github.com/alphapapa/burly.el")
|
||||
:link '(custom-manual "(Burly)Usage"))
|
||||
|
||||
(defcustom burly-bookmark-prefix "Burly: "
|
||||
"Prefix string for the name of new Burly bookmarks."
|
||||
:type 'string)
|
||||
|
||||
(defcustom burly-major-mode-alist
|
||||
(list (cons 'org-mode
|
||||
(list (cons 'make-url-fn #'burly--org-mode-buffer-url)
|
||||
(cons 'follow-url-fn #'burly-follow-url-org-mode))))
|
||||
"Alist mapping major modes to the appropriate Burly functions."
|
||||
:type '(alist :key-type symbol
|
||||
:value-type (set (cons (const make-url-fn) (function :tag "Make-URL function"))
|
||||
(cons (const follow-url-fn) (function :tag "Follow-URL function")))))
|
||||
|
||||
(defcustom burly-frameset-filter-alist '((name . nil))
|
||||
"Alist of frame parameters and filtering functions.
|
||||
See variable `frameset-filter-alist'."
|
||||
:type '(alist :key-type (symbol :tag "Frame parameter")
|
||||
:value-type (choice (const :tag "Always copied" nil)
|
||||
(const :tag "Never copied" :never)
|
||||
(function :tag "Filter function"))))
|
||||
|
||||
(defcustom burly-window-persistent-parameters
|
||||
(list (cons 'burly-url 'writable)
|
||||
(cons 'header-line-format 'writable)
|
||||
(cons 'mode-line-format 'writable)
|
||||
(cons 'tab-line-format 'writable)
|
||||
(cons 'no-other-window 'writable)
|
||||
(cons 'no-delete-other-windows 'writable)
|
||||
(cons 'window-preserved-size 'writable)
|
||||
(cons 'window-side 'writable)
|
||||
(cons 'window-slot 'writable))
|
||||
"Additional window parameters to persist.
|
||||
See Info node `(elisp)Window Parameters'. See also option
|
||||
`burly-set-window-persistent-parameters'."
|
||||
:type '(alist :key-type (symbol :tag "Window parameter")
|
||||
:value-type (choice (const :tag "Not saved" nil)
|
||||
(const :tag "Saved" writable))))
|
||||
|
||||
(defcustom burly-set-window-persistent-parameters t
|
||||
"Sync `window-persistent-parameters' with `burly' option.
|
||||
When this option is non-nil, `window-persistent-parameters' is
|
||||
set to the value of `burly-window-persistent-parameters' when
|
||||
Burly restores a window configuration.
|
||||
|
||||
By default, `window-persistent-parameters' does not save many of
|
||||
the parameters that are in the default value of
|
||||
`burly-window-persistent-parameters', which causes, e.g. a
|
||||
built-in command like `window-toggle-side-windows' to not persist
|
||||
such parameters when side windows are toggled (which could,
|
||||
e.g. cause a window's `mode-line-format' to not persist). So
|
||||
enabling this option solves that.
|
||||
|
||||
Note: When this option is non-nil,
|
||||
`burly-window-persistent-parameters' should be set heeding the
|
||||
warning in the manual about not using the `writable' value for
|
||||
parameters whose values do not have a read syntax."
|
||||
:type 'boolean)
|
||||
|
||||
;;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-last-bookmark ()
|
||||
"Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on
|
||||
a project."
|
||||
(interactive)
|
||||
(unless burly-opened-bookmark-name
|
||||
(user-error "Use command `burly-open-bookmark' first"))
|
||||
(burly-open-bookmark burly-opened-bookmark-name))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-buffer-url (buffer)
|
||||
"Copy BUFFER's URL to the kill ring."
|
||||
(interactive "bBuffer: ")
|
||||
(let ((url (burly-buffer-url (get-buffer buffer))))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-frames-url ()
|
||||
"Copy current frameset's URL to the kill ring."
|
||||
(interactive)
|
||||
(let ((url (burly-frames-url)))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-windows-url ()
|
||||
"Copy current frame's window configuration URL to the kill ring."
|
||||
(interactive)
|
||||
(let ((url (burly-windows-url)))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-url (url)
|
||||
"Open Burly URL."
|
||||
;; FIXME: If point is on an "emacs+burly..." URL, but it's after the "emacs+burly"
|
||||
;; part, `thing-at-point-url-at-point' doesn't pick up the whole URL.
|
||||
(interactive (list (or (thing-at-point-url-at-point t)
|
||||
(read-string "URL: "))))
|
||||
(cl-assert (string-prefix-p "emacs+burly+" url) t "burly-open-url: URL not an emacs+burly one:")
|
||||
(pcase-let* ((urlobj (url-generic-parse-url url))
|
||||
((cl-struct url type) urlobj)
|
||||
(subtype (car (last (split-string type "+" 'omit-nulls)))))
|
||||
(pcase-exhaustive subtype
|
||||
((or "bookmark" "file" "name") (pop-to-buffer (burly-url-buffer url)))
|
||||
("frames" (burly--frameset-restore urlobj))
|
||||
("windows" (burly--windows-set urlobj)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-frames (name)
|
||||
"Bookmark the current frames as NAME."
|
||||
(interactive
|
||||
(list (completing-read "Save Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(let ((record (list (cons 'url (burly-frames-url))
|
||||
(cons 'handler #'burly-bookmark-handler))))
|
||||
(bookmark-store name record nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-windows (name)
|
||||
"Bookmark the current frame's window configuration as NAME."
|
||||
(interactive
|
||||
(list (completing-read "Save Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(let ((record (list (cons 'url (burly-windows-url))
|
||||
(cons 'handler #'burly-bookmark-handler))))
|
||||
(bookmark-store name record nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-bookmark (bookmark)
|
||||
"Restore a window configuration to the current frame from a Burly BOOKMARK."
|
||||
(interactive
|
||||
(list (completing-read "Open Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(cl-assert (and bookmark (not (string-empty-p bookmark))) nil
|
||||
"(burly-open-bookmark): Invalid Burly bookmark: '%s'" bookmark)
|
||||
(bookmark-jump bookmark))
|
||||
|
||||
;;;; Functions
|
||||
|
||||
;;;;; Buffers
|
||||
|
||||
(defun burly-url-buffer (url)
|
||||
"Return buffer for URL."
|
||||
(cl-assert (string-prefix-p "emacs+burly+" url) t "burly-url-buffer: URL not an emacs+burly one: %s" url)
|
||||
(pcase-let* ((urlobj (url-generic-parse-url url))
|
||||
((cl-struct url type) urlobj)
|
||||
(subtype (car (last (split-string type "+" 'omit-nulls)))))
|
||||
(pcase-exhaustive subtype
|
||||
("bookmark" (burly--bookmark-url-buffer urlobj))
|
||||
("file" (burly--file-url-buffer urlobj))
|
||||
("name" (let ((buffer-name (decode-coding-string (cdr (url-path-and-query urlobj))
|
||||
'utf-8-unix)))
|
||||
(or (get-buffer buffer-name)
|
||||
(with-current-buffer (get-buffer-create (concat "*Burly (error): " buffer-name "*"))
|
||||
(insert "Burly was unable to get a buffer named: " buffer-name "\n"
|
||||
"URL: " url "\n"
|
||||
"Please report this error to the developer\n\n")
|
||||
(current-buffer))))))))
|
||||
|
||||
(defun burly-buffer-url (buffer)
|
||||
"Return URL for BUFFER."
|
||||
(let* ((major-mode (buffer-local-value 'major-mode buffer))
|
||||
(make-url-fn (map-nested-elt burly-major-mode-alist (list major-mode 'make-url-fn))))
|
||||
(cond (make-url-fn (funcall make-url-fn buffer))
|
||||
(t (or (with-current-buffer buffer
|
||||
(when-let* ((record (ignore-errors
|
||||
(bookmark-make-record))))
|
||||
(cl-labels ((encode (element)
|
||||
(cl-typecase element
|
||||
(string (encode-coding-string element 'utf-8-unix))
|
||||
(proper-list (mapcar #'encode element))
|
||||
(cons (cons (encode (car element))
|
||||
(encode (cdr element))))
|
||||
(t element))))
|
||||
;; Encode all strings in record with UTF-8.
|
||||
;; NOTE: If we stop using URLs in the future, maybe this won't be needed.
|
||||
(setf record (encode record)))
|
||||
(burly--bookmark-record-url record)))
|
||||
;; Buffer can't seem to be bookmarked, so record it as
|
||||
;; a name-only buffer. For some reason, it works
|
||||
;; better to use the buffer name in the query string
|
||||
;; rather than the filename/path part.
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+name" nil nil nil nil
|
||||
(concat "?" (encode-coding-string (buffer-name buffer)
|
||||
'utf-8-unix))
|
||||
nil nil 'fullness)))))))
|
||||
|
||||
;;;;; Files
|
||||
|
||||
(defun burly--file-url-buffer (urlobj)
|
||||
"Return buffer for \"emacs+burly+file:\" URLOBJ."
|
||||
(pcase-let* ((`(,path . ,query-string) (url-path-and-query urlobj))
|
||||
(query (url-parse-query-string query-string))
|
||||
(buffer (find-file-noselect path))
|
||||
(major-mode (buffer-local-value 'major-mode buffer))
|
||||
(follow-fn (map-nested-elt burly-major-mode-alist (list major-mode 'follow-url-fn))))
|
||||
(cl-assert follow-fn nil "Major mode not in `burly-major-mode-alist': %s" major-mode)
|
||||
(funcall follow-fn :buffer buffer :query query)))
|
||||
|
||||
;;;;; Frames
|
||||
|
||||
;; Looks like frameset.el should make this pretty easy.
|
||||
|
||||
(require 'frameset)
|
||||
|
||||
(cl-defun burly-frames-url (&optional (frames (frame-list)))
|
||||
"Return URL for frameset of FRAMES.
|
||||
FRAMES defaults to all live frames."
|
||||
(dolist (frame frames)
|
||||
;; Set URL window parameter for each window before saving state.
|
||||
(burly--windows-set-url (window-list frame 'never)))
|
||||
(let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(frameset-filter-alist (append burly-frameset-filter-alist frameset-filter-alist))
|
||||
(query (frameset-save frames))
|
||||
(print-length nil) ; Important!
|
||||
(filename (concat "?" (url-hexify-string (prin1-to-string query))))
|
||||
(url (url-recreate-url (url-parse-make-urlobj "emacs+burly+frames" nil nil nil nil
|
||||
filename))))
|
||||
(dolist (frame frames)
|
||||
;; Clear window parameters.
|
||||
(burly--windows-set-url (window-list frame 'never) 'nullify))
|
||||
url))
|
||||
|
||||
(defun burly--frameset-restore (urlobj)
|
||||
"Restore FRAMESET according to URLOBJ."
|
||||
(setf window-persistent-parameters (copy-sequence burly-window-persistent-parameters))
|
||||
(pcase-let* ((`(,_ . ,query-string) (url-path-and-query urlobj))
|
||||
(frameset (read (url-unhex-string query-string)))
|
||||
(frameset-filter-alist (append burly-frameset-filter-alist frameset-filter-alist)))
|
||||
;; Restore buffers. (Apparently `cl-loop''s in-ref doesn't work with
|
||||
;; its destructuring, so we can't just `setf' on `window-state'.)
|
||||
(setf (frameset-states frameset)
|
||||
(cl-loop for (frame-parameters . window-state) in (frameset-states frameset)
|
||||
collect (cons frame-parameters (burly--bufferize-window-state window-state))))
|
||||
(condition-case err
|
||||
(frameset-restore frameset)
|
||||
(error (delay-warning 'burly (format "Error while restoring frameset: ERROR:%S FRAMESET:%S" err frameset))))))
|
||||
|
||||
;;;;; Windows
|
||||
|
||||
(cl-defun burly-windows-url (&optional (frame (selected-frame)))
|
||||
"Return URL for window configuration on FRAME."
|
||||
(with-selected-frame frame
|
||||
(let* ((query (burly--window-state frame))
|
||||
(print-length nil) ; Important!
|
||||
(filename (concat "?" (url-hexify-string (prin1-to-string query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+windows" nil nil nil nil
|
||||
filename)))))
|
||||
|
||||
(cl-defun burly--window-state (&optional (frame (selected-frame)))
|
||||
"Return window state for FRAME.
|
||||
Sets `burly-url' window parameter in each window before
|
||||
serializing."
|
||||
(with-selected-frame frame
|
||||
;; Set URL window parameter for each window before saving state.
|
||||
(burly--windows-set-url (window-list nil 'never))
|
||||
(let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(window-state (window-state-get nil 'writable)))
|
||||
;; Clear window parameters we set (because they aren't kept
|
||||
;; current, so leaving them could be confusing).
|
||||
(burly--windows-set-url (window-list nil 'never) 'nullify)
|
||||
window-state)))
|
||||
|
||||
(defun burly--windows-set-url (windows &optional nullify)
|
||||
"Set `burly-url' window parameter in WINDOWS.
|
||||
If NULLIFY, set the parameter to nil."
|
||||
(dolist (window windows)
|
||||
(let ((value (if nullify nil (burly-buffer-url (window-buffer window)))))
|
||||
(set-window-parameter window 'burly-url value))))
|
||||
|
||||
(defun burly--windows-set (urlobj)
|
||||
"Set window configuration according to URLOBJ."
|
||||
(setf window-persistent-parameters (copy-sequence burly-window-persistent-parameters))
|
||||
(pcase-let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(`(,_ . ,query-string) (url-path-and-query urlobj))
|
||||
;; FIXME: Remove this condition-case eventually, after giving users time to update their bookmarks.
|
||||
(state (condition-case nil
|
||||
(read (url-unhex-string query-string))
|
||||
(invalid-read-syntax (display-warning 'burly "Please recreate that Burly bookmark (storage format changed)")
|
||||
(read query-string))))
|
||||
(state (burly--bufferize-window-state state)))
|
||||
(window-state-put state (frame-root-window))
|
||||
;; HACK: Since `bookmark--jump-via' insists on calling a
|
||||
;; buffer-display function after handling the bookmark, we add a
|
||||
;; function to `bookmark-after-jump-hook' to restore the window
|
||||
;; configuration that we just set.
|
||||
(setf burly--window-state (window-state-get (frame-root-window) 'writable))
|
||||
(push #'burly--bookmark-window-state-hack bookmark-after-jump-hook)))
|
||||
|
||||
(defun burly--bufferize-window-state (state)
|
||||
"Return window state STATE with its buffers reincarnated."
|
||||
(cl-labels ((bufferize-state
|
||||
;; Set windows' buffers in STATE.
|
||||
(state) (pcase state
|
||||
(`(leaf . ,_attrs) (bufferize-leaf state))
|
||||
((pred atom) state)
|
||||
(`(,_key . ,(pred atom)) state)
|
||||
((pred list) (mapcar #'bufferize-state state))))
|
||||
(bufferize-leaf
|
||||
(leaf) (pcase-let* ((`(leaf . ,attrs) leaf)
|
||||
((map parameters buffer) attrs)
|
||||
((map burly-url) parameters)
|
||||
(`(,_buffer-name . ,buffer-attrs) buffer)
|
||||
(new-buffer (burly-url-buffer burly-url)))
|
||||
(setf (map-elt attrs 'buffer) (cons new-buffer buffer-attrs))
|
||||
(cons 'leaf attrs))))
|
||||
(if-let ((leaf-pos (cl-position 'leaf state)))
|
||||
;; A one-window frame: the elements following `leaf' are that window's params.
|
||||
(append (cl-subseq state 0 leaf-pos)
|
||||
(bufferize-leaf (cl-subseq state leaf-pos)))
|
||||
;; Multi-window frame.
|
||||
(bufferize-state state))))
|
||||
|
||||
;;;;; Bookmarks
|
||||
|
||||
(defun burly--bookmark-window-state-hack (&optional _)
|
||||
"Put window state from `burly--window-state'.
|
||||
This function is to be called in `bookmark-after-jump-hook' to
|
||||
work around `bookmark--jump-via's calling a buffer-display
|
||||
function which changes the window configuration after
|
||||
`burly--windows-set' has set it. This function removes itself
|
||||
from the hook."
|
||||
(unwind-protect
|
||||
(progn
|
||||
(cl-assert burly--window-state)
|
||||
(window-state-put burly--window-state (frame-root-window)))
|
||||
(setf bookmark-after-jump-hook (delete #'burly--bookmark-window-state-hack bookmark-after-jump-hook)
|
||||
burly--window-state nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-handler (bookmark)
|
||||
"Handler function for Burly BOOKMARK."
|
||||
(let ((previous-name burly-opened-bookmark-name))
|
||||
;; Set opened bookmark name before actually opening it so that the
|
||||
;; tabs-mode advice functions can use it beforehand.
|
||||
(setf burly-opened-bookmark-name (car bookmark))
|
||||
(condition-case err
|
||||
(burly-open-url (alist-get 'url (bookmark-get-bookmark-record bookmark)))
|
||||
(error (setf burly-opened-bookmark-name previous-name)
|
||||
(signal (car err) (cdr err))))))
|
||||
|
||||
(defun burly--bookmark-record-url (record)
|
||||
"Return a URL for bookmark RECORD."
|
||||
(cl-assert record)
|
||||
(pcase-let* ((`(,name . ,props) record)
|
||||
(print-length nil) ; Important!
|
||||
(query (cl-loop for prop in props
|
||||
;; HACK: Remove unreadable values from props.
|
||||
do (cl-loop for value in-ref (cdr prop)
|
||||
when (or (bufferp value))
|
||||
do (setf value nil))
|
||||
collect (list (car prop) (prin1-to-string (cdr prop)))))
|
||||
(filename (concat (url-hexify-string name) "?" (url-build-query-string (remove nil query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+bookmark" nil nil nil nil
|
||||
filename nil nil 'fullness))))
|
||||
|
||||
(defun burly--bookmark-url-buffer (urlobj)
|
||||
"Return buffer for bookmark specified by URLOBJ.
|
||||
URLOBJ should be a URL object as returned by
|
||||
`url-generic-parse-url'."
|
||||
(pcase-let* ((`(,path . ,query-string) (url-path-and-query urlobj))
|
||||
(query (url-parse-query-string query-string))
|
||||
;; Convert back to alist.
|
||||
(props (cl-loop for prop in query
|
||||
for key = (intern (car prop))
|
||||
for value = (pcase key
|
||||
('handler (intern (cadr prop)))
|
||||
('help-args (read (cadr prop)))
|
||||
('help-fn (ignore-errors
|
||||
;; NOTE: Due to changes in help-mode.el which serialize natively
|
||||
;; compiled subrs in the bookmark record, which cannot be read
|
||||
;; back (which actually break the entire bookmark system when
|
||||
;; such a record is saved in the bookmarks file), we have to
|
||||
;; workaround a failure to read here. See bug#56643.
|
||||
(read (cadr prop))))
|
||||
('position (cl-parse-integer (cadr prop)))
|
||||
(_ (read (cadr prop))))
|
||||
collect (cons key value)))
|
||||
(record (cons path props)))
|
||||
(cl-labels ((decode (element)
|
||||
(cl-typecase element
|
||||
(string (decode-coding-string element 'utf-8-unix))
|
||||
(proper-list (mapcar #'decode element))
|
||||
(cons (cons
|
||||
(decode (car element))
|
||||
(decode (cdr element))))
|
||||
(t element))))
|
||||
;; Decode all strings in record with UTF-8.
|
||||
;; NOTE: If we stop using URLs in the future, maybe this won't be needed.
|
||||
(setf record (decode record)))
|
||||
(save-window-excursion
|
||||
(condition-case err
|
||||
(bookmark-jump record)
|
||||
(error (delay-warning 'burly (format "Error while opening bookmark: ERROR:%S RECORD:%S" err record))))
|
||||
(current-buffer))))
|
||||
|
||||
(defun burly-bookmark-names ()
|
||||
"Return list of all Burly bookmark names."
|
||||
(cl-loop for bookmark in bookmark-alist
|
||||
for (_name . params) = bookmark
|
||||
when (equal #'burly-bookmark-handler (alist-get 'handler params))
|
||||
collect (car bookmark)))
|
||||
|
||||
;;;;; Org buffers
|
||||
|
||||
;; We only require Org when compiling the file. At runtime, Org will
|
||||
;; be loaded before we call any of its functions, because we load the
|
||||
;; Org file into a buffer first, which activates `org-mode'.
|
||||
|
||||
(eval-when-compile
|
||||
(require 'org))
|
||||
|
||||
(declare-function org-before-first-heading-p "org")
|
||||
(declare-function org-back-to-heading "org")
|
||||
(declare-function org-find-olp "org")
|
||||
(declare-function org-tree-to-indirect-buffer "org")
|
||||
(declare-function org-narrow-to-subtree "org")
|
||||
(declare-function org-heading-components "org")
|
||||
(declare-function org-up-heading-safe "org")
|
||||
|
||||
(defun burly--org-mode-buffer-url (buffer)
|
||||
"Return URL for Org BUFFER."
|
||||
(with-current-buffer buffer
|
||||
(cl-assert (or (buffer-file-name buffer)
|
||||
(buffer-file-name (buffer-base-buffer buffer)))
|
||||
nil "Buffer has no file name: %s" buffer)
|
||||
(let* ((narrowed (buffer-narrowed-p))
|
||||
(indirect (buffer-base-buffer buffer))
|
||||
(top-olp
|
||||
;; For narrowing purposes, start with the heading at the top of the buffer.
|
||||
(when (buffer-narrowed-p)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
;; `org-get-outline-path' replaces links in headings with their
|
||||
;; descriptions, which prevents using them in regexp searches.
|
||||
(when (org-heading-components)
|
||||
(org-with-wide-buffer
|
||||
(nreverse (cl-loop collect (substring-no-properties (nth 4 (org-heading-components)))
|
||||
while (org-up-heading-safe))))))))
|
||||
(point-olp
|
||||
(when (ignore-errors (org-heading-components))
|
||||
(org-with-wide-buffer
|
||||
(nreverse (cl-loop collect (substring-no-properties (nth 4 (org-heading-components)))
|
||||
while (org-up-heading-safe))))))
|
||||
(pos (point))
|
||||
(relative-pos (when top-olp
|
||||
(- (point) (save-excursion
|
||||
(org-back-to-heading)
|
||||
(point)))))
|
||||
(print-length nil) ; Important!
|
||||
(query (list (list "pos" pos)
|
||||
(when top-olp
|
||||
(list "top-olp" (prin1-to-string top-olp)))
|
||||
(when point-olp
|
||||
(list "point-olp" (prin1-to-string point-olp)))
|
||||
(when relative-pos
|
||||
(list "relative-pos" relative-pos))
|
||||
(when indirect
|
||||
(list "indirect" "t"))
|
||||
(when narrowed
|
||||
(list "narrowed" "t"))))
|
||||
(buffer-file (or (buffer-file-name buffer)
|
||||
(buffer-file-name (buffer-base-buffer buffer))))
|
||||
(filename (concat buffer-file "?" (url-build-query-string (remove nil query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+file" nil nil nil nil
|
||||
filename nil nil 'fullness)))))
|
||||
|
||||
(cl-defun burly-follow-url-org-mode (&key buffer query)
|
||||
"In BUFFER, jump to heading and position from QUERY, and return a buffer.
|
||||
If QUERY specifies that the buffer should be indirect, a new,
|
||||
indirect buffer is returned. Otherwise BUFFER is returned."
|
||||
;; `pcase's map support uses `alist-get', which does not work with string keys
|
||||
;; unless its TESTFN arg is bound to, e.g. `equal', but `map-elt' has deprecated
|
||||
;; its TESTFN arg, and there's no way to pass it or bind it when using `pcase'
|
||||
;; anyway. So we rebind `alist-get' to a function that uses `assoc-string'.
|
||||
(with-current-buffer buffer
|
||||
(cl-letf (((symbol-function 'alist-get)
|
||||
(lambda (key alist &optional _default _remove _testfn)
|
||||
;; Only the first value in the list of values is returned, so multiple
|
||||
;; values are not supported. I don't expect this to be a problem...
|
||||
(cadr (assoc-string key alist)))))
|
||||
(pcase-let* (((map ("pos" pos)
|
||||
("indirect" indirect)
|
||||
("narrowed" narrowed)
|
||||
("top-olp" top-olp)
|
||||
("point-olp" point-olp)
|
||||
("relative-pos" relative-pos))
|
||||
query)
|
||||
(heading-pos (when top-olp
|
||||
(org-find-olp (read top-olp) 'this-buffer))))
|
||||
(widen)
|
||||
(if heading-pos
|
||||
(goto-char heading-pos)
|
||||
(goto-char (string-to-number pos)))
|
||||
(cond (indirect (org-tree-to-indirect-buffer))
|
||||
(narrowed (progn
|
||||
(org-narrow-to-subtree)
|
||||
(goto-char (org-find-olp (read point-olp) 'this-buffer)))))
|
||||
(when (and heading-pos relative-pos)
|
||||
(forward-char (string-to-number relative-pos)))
|
||||
(current-buffer)))))
|
||||
|
||||
;;;; Footer
|
||||
|
||||
(provide 'burly)
|
||||
|
||||
;;; burly.el ends here
|
|
@ -1,325 +0,0 @@
|
|||
This is README.info, produced by makeinfo version 5.2 from README.texi.
|
||||
|
||||
INFO-DIR-SECTION Emacs
|
||||
START-INFO-DIR-ENTRY
|
||||
* Burly: (burly). Save and restore window configurations and their buffers.
|
||||
END-INFO-DIR-ENTRY
|
||||
|
||||
|
||||
File: README.info, Node: Top, Next: Installation, Up: (dir)
|
||||
|
||||
Burly.el
|
||||
********
|
||||
|
||||
This package provides tools to save and restore frame and window
|
||||
configurations in Emacs, including buffers that may not be live anymore.
|
||||
In this way, it’s like a lightweight "workspace" manager, allowing you
|
||||
to easily restore one or more frames, including their windows, the
|
||||
windows’ layout, and their buffers.
|
||||
|
||||
Internally it uses Emacs’s bookmarks system to restore buffers to
|
||||
their previous contents and location. This provides power and
|
||||
extensibility, since many major modes already integrate with Emacs’s
|
||||
bookmarks system. However, in case a mode’s bookmarking function isn’t
|
||||
satisfactory, Burly allows the user to customize buffer-restoring
|
||||
functions for specific modes.
|
||||
|
||||
For Org mode, Burly provides such custom functions so that narrowed
|
||||
and indirect Org buffers are properly restored, and headings are located
|
||||
by outline path in case they’ve moved since a bookmark was made (the
|
||||
org-bookmark-heading (https://github.com/alphapapa/org-bookmark-heading)
|
||||
package also provides this through the Emacs bookmark system, but users
|
||||
may not have it installed, and the functionality is too useful to not
|
||||
include here by default).
|
||||
|
||||
Internally, buffers and frame/window configurations are also encoded
|
||||
as URLs, and users may also save and open those URLs instead of using
|
||||
Emacs bookmarks. (The name "Burly" comes from "buffer URL.") For
|
||||
example, a URL to the ‘Installation/Quelpa’ heading in this file, as I’m
|
||||
writing it, looks like this:
|
||||
|
||||
emacs+burly+file:///home/me/src/emacs/burly.el/README.org?pos=2651&outline-path=%28%22Installation%22%20%22Quelpa%22%29&relative-pos=308
|
||||
|
||||
In terms of built-in features, Burly may be seen as integrating or
|
||||
leveraging the built-in libraries ‘bookmark.el’, ‘window.el’, and
|
||||
‘frameset.el’.
|
||||
|
||||
* Menu:
|
||||
|
||||
* Installation::
|
||||
* Usage::
|
||||
* Changelog::
|
||||
* Development::
|
||||
* Credits::
|
||||
* License::
|
||||
|
||||
— The Detailed Node Listing —
|
||||
|
||||
Installation
|
||||
|
||||
* MELPA::
|
||||
* Quelpa::
|
||||
* Manual::
|
||||
|
||||
Usage
|
||||
|
||||
* Bookmark commands::
|
||||
* URL commands::
|
||||
* Tab bar::
|
||||
* Tips::
|
||||
|
||||
Changelog
|
||||
|
||||
* 0.3-pre: 03-pre.
|
||||
* 0.2: 02.
|
||||
* 0.1: 01.
|
||||
|
||||
|
||||
|
||||
File: README.info, Node: Installation, Next: Usage, Prev: Top, Up: Top
|
||||
|
||||
1 Installation
|
||||
**************
|
||||
|
||||
* Menu:
|
||||
|
||||
* MELPA::
|
||||
* Quelpa::
|
||||
* Manual::
|
||||
|
||||
|
||||
File: README.info, Node: MELPA, Next: Quelpa, Up: Installation
|
||||
|
||||
1.1 MELPA
|
||||
=========
|
||||
|
||||
If you installed from MELPA, you’re done. Just run one of the commands
|
||||
below.
|
||||
|
||||
|
||||
File: README.info, Node: Quelpa, Next: Manual, Prev: MELPA, Up: Installation
|
||||
|
||||
1.2 Quelpa
|
||||
==========
|
||||
|
||||
The easiest way is to install with quelpa-use-package
|
||||
(https://github.com/quelpa/quelpa-use-package), like this:
|
||||
|
||||
(use-package burly
|
||||
:quelpa (burly :fetcher github :repo "alphapapa/burly.el"))
|
||||
|
||||
|
||||
File: README.info, Node: Manual, Prev: Quelpa, Up: Installation
|
||||
|
||||
1.3 Manual
|
||||
==========
|
||||
|
||||
1. Install version 2.1 or later of the ‘map’ library from GNU ELPA.
|
||||
2. Copy ‘burly.el’ into a directory in your ‘load-path’, then
|
||||
‘(require 'burly)’.
|
||||
|
||||
|
||||
File: README.info, Node: Usage, Next: Changelog, Prev: Installation, Up: Top
|
||||
|
||||
2 Usage
|
||||
*******
|
||||
|
||||
* Menu:
|
||||
|
||||
* Bookmark commands::
|
||||
* URL commands::
|
||||
* Tab bar::
|
||||
* Tips::
|
||||
|
||||
|
||||
File: README.info, Node: Bookmark commands, Next: URL commands, Up: Usage
|
||||
|
||||
2.1 Bookmark commands
|
||||
=====================
|
||||
|
||||
Most users will probably use Burly by bookmarking frame and window
|
||||
configurations and accessing them with these commands:
|
||||
|
||||
• ‘burly-bookmark-frames’: Bookmark the current frames and their
|
||||
window configurations.
|
||||
• ‘burly-bookmark-windows’: Bookmark the current frame’s window
|
||||
configuration.
|
||||
• ‘burly-open-bookmark’: Select and open a Burly bookmark.
|
||||
• ‘burly-open-last-bookmark’: Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on a
|
||||
project.
|
||||
|
||||
Note that bookmarks created by Burly are regular Emacs bookmarks, so
|
||||
they can be managed by Emacs’s built-in bookmark commands, e.g.
|
||||
‘list-bookmarks’, ‘bookmark-delete’, etc.
|
||||
|
||||
|
||||
File: README.info, Node: URL commands, Next: Tab bar, Prev: Bookmark commands, Up: Usage
|
||||
|
||||
2.2 URL commands
|
||||
================
|
||||
|
||||
These commands work on URL strings. While most users probably won’t use
|
||||
these, they may be useful for building custom tooling.
|
||||
|
||||
• ‘burly-open-url’: Open a Burly URL (at point, or prompt for one),
|
||||
displaying the buffer(s) in the current window or frame.
|
||||
• ‘burly-kill-buffer-url’: Copy BUFFER’s URL to the kill ring.
|
||||
• ‘burly-kill-frames-url’: Copy the current frameset’s URL to the
|
||||
kill ring.
|
||||
• ‘burly-kill-windows-url’: Copy the current frame’s window
|
||||
configuration URL to the kill ring.
|
||||
|
||||
|
||||
File: README.info, Node: Tab bar, Next: Tips, Prev: URL commands, Up: Usage
|
||||
|
||||
2.3 Tab bar
|
||||
===========
|
||||
|
||||
Burly supports Emacs’s ‘tab-bar-mode’ with ‘burly-tabs-mode’. When
|
||||
active, Burly bookmarks are opened in new tabs, and the tabs are named
|
||||
according to the bookmarks. Reopening a Burly bookmark uses the
|
||||
designated tab, if it already exists, and tabs may be reset to their
|
||||
bookmarked state with the command ‘burly-reset-tab’ (which you might
|
||||
bind to ‘C-x t R’).
|
||||
|
||||
|
||||
File: README.info, Node: Tips, Prev: Tab bar, Up: Usage
|
||||
|
||||
2.4 Tips
|
||||
========
|
||||
|
||||
• You can customize settings in the ‘burly’ group.
|
||||
• An Info manual is included with this package.
|
||||
|
||||
|
||||
File: README.info, Node: Changelog, Next: Development, Prev: Usage, Up: Top
|
||||
|
||||
3 Changelog
|
||||
***********
|
||||
|
||||
* Menu:
|
||||
|
||||
* 0.3-pre: 03-pre.
|
||||
* 0.2: 02.
|
||||
* 0.1: 01.
|
||||
|
||||
|
||||
File: README.info, Node: 03-pre, Next: 02, Up: Changelog
|
||||
|
||||
3.1 0.3-pre
|
||||
===========
|
||||
|
||||
*Added*
|
||||
• Command ‘burly-tabs-mode’, which integrates Burly with
|
||||
‘tab-bar-mode’. When active, Burly bookmarks are opened in new
|
||||
tabs, and the tabs are named according to the bookmark.
|
||||
• Command ‘burly-reset-tab’, which resets a tab to the state of the
|
||||
bookmark which opened it.
|
||||
|
||||
*Changed*
|
||||
• Emacs version 28.1 or later is required.
|
||||
|
||||
*Fixed*
|
||||
• Buffers that can’t be restored by name no longer cause an error
|
||||
which aborts restoration of the rest of the bookmark’s buffers.
|
||||
• Added workaround for regression in Emacs 28 regarding bookmarks for
|
||||
‘help-mode’ buffers. (See Emacs bug 56643
|
||||
(https://debbugs.gnu.org/cgi/bugreport.cgi?bug=56643).)
|
||||
|
||||
|
||||
File: README.info, Node: 02, Next: 01, Prev: 03-pre, Up: Changelog
|
||||
|
||||
3.2 0.2
|
||||
=======
|
||||
|
||||
*Added*
|
||||
• Bookmark commands use ‘completing-read’ and offer existing Burly
|
||||
bookmark names, making it easier to update bookmarks. (Thanks to
|
||||
Erik Sjöstrand (https://github.com/Kungsgeten).)
|
||||
• Command ‘burly-open-last-bookmark’.
|
||||
• Option ‘burly-set-window-persistent-parameters’, which synchronizes
|
||||
‘window-persistent-parameters’ with
|
||||
‘burly-window-persistent-parameters’, ensuring that built-in Emacs
|
||||
commands like ‘window-toggle-side-windows’ persist parameters that
|
||||
are persisted with Burly.
|
||||
|
||||
*Changed*
|
||||
• Option ‘burly-window-persistent-parameters’’s default value
|
||||
includes more window parameters, like header/mode line, side, slot,
|
||||
etc, making it easier to restore an overview of a project or
|
||||
"workspace."
|
||||
• Emacs version 27.1 or later is required.
|
||||
|
||||
*Fixed*
|
||||
• Narrow Org buffers to correct heading (at the top of the buffer
|
||||
rather than at point).
|
||||
• Buffers whose names have multibyte characters. (Fixes #43
|
||||
(https://github.com/alphapapa/burly.el/issues/43). Thanks to Liu
|
||||
Hui (https://github.com/ilupin) for reporting.)
|
||||
• Bind ‘print-level’ to nil where ‘prin1-to-string’ is used (in case
|
||||
the value is non-nil in a user’s config, which would cause
|
||||
truncated values).
|
||||
|
||||
|
||||
File: README.info, Node: 01, Prev: 02, Up: Changelog
|
||||
|
||||
3.3 0.1
|
||||
=======
|
||||
|
||||
Initial release.
|
||||
|
||||
|
||||
File: README.info, Node: Development, Next: Credits, Prev: Changelog, Up: Top
|
||||
|
||||
4 Development
|
||||
*************
|
||||
|
||||
Bug reports, feature requests, suggestions — _oh my_!
|
||||
|
||||
|
||||
File: README.info, Node: Credits, Next: License, Prev: Development, Up: Top
|
||||
|
||||
5 Credits
|
||||
*********
|
||||
|
||||
• Thanks to Clemens Radermacher (https://github.com/clemera) and
|
||||
Robert Weiner (https://github.com/rswgnu) for their suggestions.
|
||||
• Thanks to Trey Peacock (https://github.com/tpeacock19) for
|
||||
extensive feedback on pre-release versions.
|
||||
|
||||
|
||||
File: README.info, Node: License, Prev: Credits, Up: Top
|
||||
|
||||
6 License
|
||||
*********
|
||||
|
||||
GPLv3
|
||||
|
||||
|
||||
|
||||
Tag Table:
|
||||
Node: Top219
|
||||
Node: Installation2378
|
||||
Node: MELPA2529
|
||||
Node: Quelpa2701
|
||||
Node: Manual3014
|
||||
Node: Usage3283
|
||||
Node: Bookmark commands3454
|
||||
Node: URL commands4320
|
||||
Node: Tab bar5001
|
||||
Node: Tips5490
|
||||
Node: Changelog5685
|
||||
Node: 03-pre5847
|
||||
Node: 026658
|
||||
Node: 018079
|
||||
Node: Development8173
|
||||
Node: Credits8344
|
||||
Node: License8704
|
||||
|
||||
End Tag Table
|
||||
|
||||
|
||||
Local Variables:
|
||||
coding: utf-8
|
||||
End:
|
|
@ -1,19 +0,0 @@
|
|||
This is the file .../info/dir, which contains the
|
||||
topmost node of the Info hierarchy, called (dir)Top.
|
||||
The first time you invoke Info you start off looking at this node.
|
||||
|
||||
File: dir, Node: Top This is the top of the INFO tree
|
||||
|
||||
This (the Directory node) gives a menu of major topics.
|
||||
Typing "q" exits, "H" lists all Info commands, "d" returns here,
|
||||
"h" gives a primer for first-timers,
|
||||
"mEmacs<Return>" visits the Emacs manual, etc.
|
||||
|
||||
In Emacs, you can click mouse button 2 on a menu item or cross reference
|
||||
to select it.
|
||||
|
||||
* Menu:
|
||||
|
||||
Emacs
|
||||
* Burly: (burly). Save and restore window configurations and
|
||||
their buffers.
|
|
@ -173,3 +173,9 @@
|
|||
'(default ((t (:family "MonoLisa Variable" :height 100))))
|
||||
'(fixed-pitch ((t (:family "MonoLisa Variable" :height 120))))
|
||||
'(variable-pitch ((t (:family "Atkinson Hyperlegible" :height 120)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; set ui to standard org-workspace at launch
|
||||
(add-hook 'after-init-hook (lambda ()
|
||||
(kmn/workspace-code-empty)
|
||||
))
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
|
||||
(package-initialize) ; this goes in chemacs2 init -- DO NOT UNCOMMENT
|
||||
(add-to-list 'package-selected-packages
|
||||
'(persp-mode burly rainbow-mode rainbow-delimiters markdown-mode devdocs devdocs-browser focus zoom popwin dired-single diredfl xclip doominhibitinhibit-modeline magit helpful helm helm-org helm-ls-git dired-rainbow dired-rainbow-listing dired-single dash s origami modus-themes use-package)
|
||||
'(persp-mode rainbow-mode rainbow-delimiters markdown-mode devdocs devdocs-browser focus zoom popwin dired-single diredfl xclip doominhibitinhibit-modeline magit helpful helm helm-org helm-ls-git dired-rainbow dired-rainbow-listing dired-single dash s origami modus-themes use-package)
|
||||
)
|
||||
(require 'use-package)
|
||||
|
||||
|
@ -479,19 +479,6 @@
|
|||
;(tab-bar-mode t)
|
||||
;(burly-tabs-mode 1)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; burly
|
||||
(require 'burly)
|
||||
|
||||
; C-x r b 'helm-filtered-bookmarks (list-bookmarks) defined above in helm section
|
||||
(global-set-key (kbd "C-x r l") 'helm-filtered-bookmarks) ; list-bookmarks
|
||||
(global-set-key (kbd "C-x r o") 'burly-open-bookmark)
|
||||
(global-set-key (kbd "C-x r O") 'burly-reset-tab)
|
||||
(global-set-key (kbd "C-x r k") 'helm-bookmark-rename)
|
||||
(global-set-key (kbd "C-x r k") 'bookmark-delete)
|
||||
(global-set-key (kbd "C-x r f") 'burly-bookmark-frames)
|
||||
(global-set-key (kbd "C-x r w") 'burly-bookmark-windows)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; persp-mode
|
||||
(setq persp-auto-resume-time -1.0)
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; various workspaces used day to day
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; load most recent auto save
|
||||
(defun kmn/workspace-load-auto ()
|
||||
(interactive)
|
||||
(persp-load-state-from-file "persp-auto-save")
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; save the current workspace to `latest` filename
|
||||
(defun kmn/workspace-save-latest ()
|
||||
(interactive)
|
||||
(persp-save-state-to-file "latest")
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; load `latest` file name to current workspace
|
||||
(defun kmn/workspace-load-latest ()
|
||||
(interactive)
|
||||
(persp-load-state-from-file "latest")
|
||||
)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; org-mode workspace
|
||||
(defun kmn/workspace-org ()
|
||||
|
@ -18,4 +38,19 @@
|
|||
(find-file "~/org/health/health_mike.org")
|
||||
; put agenda in top side window (works around org-mode limitations
|
||||
(kmn/org-agenda-on-top)
|
||||
(next-multiframe-window)
|
||||
(goto-char 0)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; empty code workspace
|
||||
(defun kmn/workspace-code-empty ()
|
||||
(interactive)
|
||||
(delete-other-windows)
|
||||
; start with scratch buffer active
|
||||
(switch-to-buffer (get-buffer "*scratch*"))
|
||||
; default scratch buffer has 2 comments @ top of file, jump past them
|
||||
(with-no-warnings (goto-line 3))
|
||||
; show dired at `~` in left side window
|
||||
(kmn/dired-default-directory-on-left)
|
||||
)
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
;;; burly-autoloads.el --- automatically extracted autoloads -*- lexical-binding: t -*-
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(add-to-list 'load-path (directory-file-name
|
||||
(or (file-name-directory #$) (car load-path))))
|
||||
|
||||
|
||||
;;;### (autoloads nil "burly" "burly.el" (0 0 0 0))
|
||||
;;; Generated autoloads from burly.el
|
||||
|
||||
(autoload 'burly-open-last-bookmark "burly" "\
|
||||
Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on
|
||||
a project." t nil)
|
||||
|
||||
(autoload 'burly-kill-buffer-url "burly" "\
|
||||
Copy BUFFER's URL to the kill ring.
|
||||
|
||||
\(fn BUFFER)" t nil)
|
||||
|
||||
(autoload 'burly-kill-frames-url "burly" "\
|
||||
Copy current frameset's URL to the kill ring." t nil)
|
||||
|
||||
(autoload 'burly-kill-windows-url "burly" "\
|
||||
Copy current frame's window configuration URL to the kill ring." t nil)
|
||||
|
||||
(autoload 'burly-open-url "burly" "\
|
||||
Open Burly URL.
|
||||
|
||||
\(fn URL)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-frames "burly" "\
|
||||
Bookmark the current frames as NAME.
|
||||
|
||||
\(fn NAME)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-windows "burly" "\
|
||||
Bookmark the current frame's window configuration as NAME.
|
||||
|
||||
\(fn NAME)" t nil)
|
||||
|
||||
(autoload 'burly-open-bookmark "burly" "\
|
||||
Restore a window configuration to the current frame from a Burly BOOKMARK.
|
||||
|
||||
\(fn BOOKMARK)" t nil)
|
||||
|
||||
(autoload 'burly-bookmark-handler "burly" "\
|
||||
Handler function for Burly BOOKMARK.
|
||||
|
||||
\(fn BOOKMARK)" nil nil)
|
||||
|
||||
(register-definition-prefixes "burly" '("burly-"))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil "burly-tabs" "burly-tabs.el" (0 0 0 0))
|
||||
;;; Generated autoloads from burly-tabs.el
|
||||
|
||||
(defvar burly-tabs-mode nil "\
|
||||
Non-nil if Burly-Tabs mode is enabled.
|
||||
See the `burly-tabs-mode' command
|
||||
for a description of this minor mode.
|
||||
Setting this variable directly does not take effect;
|
||||
either customize it (see the info node `Easy Customization')
|
||||
or call the function `burly-tabs-mode'.")
|
||||
|
||||
(custom-autoload 'burly-tabs-mode "burly-tabs" nil)
|
||||
|
||||
(autoload 'burly-tabs-mode "burly-tabs" "\
|
||||
Integrate Burly with `tab-bar-mode'.
|
||||
When active, Burly bookmarks are opened in new tabs and named
|
||||
accordingly.
|
||||
|
||||
This is a minor mode. If called interactively, toggle the
|
||||
`Burly-Tabs mode' mode. If the prefix argument is positive,
|
||||
enable the mode, and if it is zero or negative, disable the mode.
|
||||
|
||||
If called from Lisp, toggle the mode if ARG is `toggle'. Enable
|
||||
the mode if ARG is nil, omitted, or is a positive number.
|
||||
Disable the mode if ARG is a negative number.
|
||||
|
||||
To check whether the minor mode is enabled in the current buffer,
|
||||
evaluate `(default-value \\='burly-tabs-mode)'.
|
||||
|
||||
The mode's hook is called both when the mode is enabled and when
|
||||
it is disabled.
|
||||
|
||||
\(fn &optional ARG)" t nil)
|
||||
|
||||
(register-definition-prefixes "burly-tabs" '("burly-"))
|
||||
|
||||
;;;***
|
||||
|
||||
;;;### (autoloads nil nil ("burly-pkg.el") (0 0 0 0))
|
||||
|
||||
;;;***
|
||||
|
||||
;; Local Variables:
|
||||
;; version-control: never
|
||||
;; no-byte-compile: t
|
||||
;; no-update-autoloads: t
|
||||
;; coding: utf-8
|
||||
;; End:
|
||||
;;; burly-autoloads.el ends here
|
|
@ -1,13 +0,0 @@
|
|||
(define-package "burly" "20221024.2019" "Save and restore frame/window configurations with buffers"
|
||||
'((emacs "27.1")
|
||||
(map "2.1"))
|
||||
:commit "f570fa87ee72a451f535cfb038d81798a01a7e20" :authors
|
||||
'(("Adam Porter" . "adam@alphapapa.net"))
|
||||
:maintainer
|
||||
'("Adam Porter" . "adam@alphapapa.net")
|
||||
:keywords
|
||||
'("convenience")
|
||||
:url "https://github.com/alphapapa/burly.el")
|
||||
;; Local Variables:
|
||||
;; no-byte-compile: t
|
||||
;; End:
|
|
@ -1,90 +0,0 @@
|
|||
;;; burly-tabs.el --- Burly's support for tab-bar -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2022 Adam Porter
|
||||
|
||||
;; Author: Adam Porter <adam@alphapapa.net>
|
||||
;; Keywords: tab-bar, tabs, frames
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This library provides support for `tab-bar-mode'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;; TODO: In Emacs 28, add an entry to the tab context menu. See
|
||||
;; <https://lists.gnu.org/archive/html/emacs-devel/2021-09/msg00590.html>.
|
||||
|
||||
;;;; Requirements
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'map)
|
||||
(require 'tab-bar)
|
||||
|
||||
(require 'burly)
|
||||
|
||||
;;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode burly-tabs-mode
|
||||
"Integrate Burly with `tab-bar-mode'.
|
||||
When active, Burly bookmarks are opened in new tabs and named
|
||||
accordingly."
|
||||
:global t
|
||||
:group 'burly
|
||||
(unless (version<= "28.1" emacs-version)
|
||||
(user-error "`burly-tabs-mode' requires Emacs 28.1 or later"))
|
||||
(if burly-tabs-mode
|
||||
(progn
|
||||
(advice-add #'burly--windows-set :before #'burly-tabs--windows-set-before-advice)
|
||||
(advice-add #'burly--windows-set :after #'burly-tabs--windows-set-after-advice))
|
||||
;; Disable mode.
|
||||
(advice-remove #'burly--windows-set #'burly-tabs--windows-set-before-advice)
|
||||
(advice-remove #'burly--windows-set #'burly-tabs--windows-set-after-advice)))
|
||||
|
||||
(cl-defun burly-tabs-reset-tab (&optional (tab (tab-bar--current-tab-find)))
|
||||
"Reset TAB to its saved configuration.
|
||||
Resets TAB to the Burly bookmark that it was created from."
|
||||
(interactive)
|
||||
(pcase-let* ((`(,_ . ,(map burly-bookmark-name)) tab))
|
||||
(unless burly-bookmark-name
|
||||
(user-error "Tab has no associated Burly bookmark (`burly-tabs-mode' must be enabled when opening a bookmark)"))
|
||||
(burly-open-bookmark burly-bookmark-name)))
|
||||
|
||||
(defalias 'burly-reset-tab #'burly-tabs-reset-tab)
|
||||
|
||||
;;;; Functions
|
||||
|
||||
(defun burly-tabs--windows-set-before-advice (&rest _ignore)
|
||||
"Cause bookmark to be opened in a tab by that name.
|
||||
If a tab already exists named the value of
|
||||
`burly-opened-bookmark-name', select it; otherwise call
|
||||
`tab-bar-new-tab'. To be used as advice to
|
||||
`burly-open-bookmark'."
|
||||
(if (tab-bar--tab-index-by-name burly-opened-bookmark-name)
|
||||
(tab-bar-select-tab-by-name burly-opened-bookmark-name)
|
||||
(tab-bar-new-tab)))
|
||||
|
||||
(defun burly-tabs--windows-set-after-advice (&rest _ignore)
|
||||
"Set current tab's `burly-bookmark-name' to BOOKMARK-NAME.
|
||||
To be used as advice to `burly--windows-set'."
|
||||
(tab-rename burly-opened-bookmark-name)
|
||||
(let ((current-tab (tab-bar--current-tab-find)))
|
||||
(setf (alist-get 'burly-bookmark-name (cdr current-tab))
|
||||
burly-opened-bookmark-name)))
|
||||
|
||||
(provide 'burly-tabs)
|
||||
|
||||
;;; burly-tabs.el ends here
|
|
@ -1,590 +0,0 @@
|
|||
;;; burly.el --- Save and restore frame/window configurations with buffers -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2020 Adam Porter
|
||||
|
||||
;; Author: Adam Porter <adam@alphapapa.net>
|
||||
;; URL: https://github.com/alphapapa/burly.el
|
||||
;; Version: 0.3-pre
|
||||
;; Package-Requires: ((emacs "27.1") (map "2.1"))
|
||||
;; Keywords: convenience
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package provides tools to save and restore frame and window
|
||||
;; configurations in Emacs, including buffers that may not be live
|
||||
;; anymore. In this way, it's like a lightweight "workspace" manager,
|
||||
;; allowing you to easily restore one or more frames, including their
|
||||
;; windows, the windows' layout, and their buffers.
|
||||
|
||||
;; Internally it uses Emacs's bookmarks system to restore buffers to
|
||||
;; their previous contents and location. This provides power and
|
||||
;; extensibility, since many major modes already integrate with
|
||||
;; Emacs's bookmarks system. However, in case a mode's bookmarking
|
||||
;; function isn't satisfactory, Burly allows the user to customize
|
||||
;; buffer-restoring functions for specific modes.
|
||||
|
||||
;; For Org mode, Burly provides such custom functions so that narrowed
|
||||
;; and indirect Org buffers are properly restored, and headings are
|
||||
;; located by outline path in case they've moved since a bookmark was
|
||||
;; made (the org-bookmark-heading package also provides this through
|
||||
;; the Emacs bookmark system, but users may not have it installed, and
|
||||
;; the functionality is too useful to not include here by default).
|
||||
|
||||
;; Internally, buffers and window configurations are also encoded as
|
||||
;; URLs, and users may also save and open those URLs instead of using
|
||||
;; Emacs bookmarks. (The name "Burly" comes from "buffer URL.")
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;;; Requirements
|
||||
|
||||
(require 'bookmark)
|
||||
(require 'cl-lib)
|
||||
(require 'map)
|
||||
(require 'subr-x)
|
||||
(require 'thingatpt)
|
||||
(require 'url-parse)
|
||||
(require 'url-util)
|
||||
|
||||
;;;; Variables
|
||||
|
||||
(defvar burly--window-state nil
|
||||
"Used to work around `bookmark--jump-via' affecting window configuration.")
|
||||
|
||||
(defvar burly-opened-bookmark-name nil
|
||||
"The name of the last bookmark opened by Burly.")
|
||||
|
||||
;;;; Customization
|
||||
|
||||
(defgroup burly nil
|
||||
"Save and restore window configurations and their buffers."
|
||||
:group 'convenience
|
||||
:link '(url-link "https://github.com/alphapapa/burly.el")
|
||||
:link '(custom-manual "(Burly)Usage"))
|
||||
|
||||
(defcustom burly-bookmark-prefix "Burly: "
|
||||
"Prefix string for the name of new Burly bookmarks."
|
||||
:type 'string)
|
||||
|
||||
(defcustom burly-major-mode-alist
|
||||
(list (cons 'org-mode
|
||||
(list (cons 'make-url-fn #'burly--org-mode-buffer-url)
|
||||
(cons 'follow-url-fn #'burly-follow-url-org-mode))))
|
||||
"Alist mapping major modes to the appropriate Burly functions."
|
||||
:type '(alist :key-type symbol
|
||||
:value-type (set (cons (const make-url-fn) (function :tag "Make-URL function"))
|
||||
(cons (const follow-url-fn) (function :tag "Follow-URL function")))))
|
||||
|
||||
(defcustom burly-frameset-filter-alist '((name . nil))
|
||||
"Alist of frame parameters and filtering functions.
|
||||
See variable `frameset-filter-alist'."
|
||||
:type '(alist :key-type (symbol :tag "Frame parameter")
|
||||
:value-type (choice (const :tag "Always copied" nil)
|
||||
(const :tag "Never copied" :never)
|
||||
(function :tag "Filter function"))))
|
||||
|
||||
(defcustom burly-window-persistent-parameters
|
||||
(list (cons 'burly-url 'writable)
|
||||
(cons 'header-line-format 'writable)
|
||||
(cons 'mode-line-format 'writable)
|
||||
(cons 'tab-line-format 'writable)
|
||||
(cons 'no-other-window 'writable)
|
||||
(cons 'no-delete-other-windows 'writable)
|
||||
(cons 'window-preserved-size 'writable)
|
||||
(cons 'window-side 'writable)
|
||||
(cons 'window-slot 'writable))
|
||||
"Additional window parameters to persist.
|
||||
See Info node `(elisp)Window Parameters'. See also option
|
||||
`burly-set-window-persistent-parameters'."
|
||||
:type '(alist :key-type (symbol :tag "Window parameter")
|
||||
:value-type (choice (const :tag "Not saved" nil)
|
||||
(const :tag "Saved" writable))))
|
||||
|
||||
(defcustom burly-set-window-persistent-parameters t
|
||||
"Sync `window-persistent-parameters' with `burly' option.
|
||||
When this option is non-nil, `window-persistent-parameters' is
|
||||
set to the value of `burly-window-persistent-parameters' when
|
||||
Burly restores a window configuration.
|
||||
|
||||
By default, `window-persistent-parameters' does not save many of
|
||||
the parameters that are in the default value of
|
||||
`burly-window-persistent-parameters', which causes, e.g. a
|
||||
built-in command like `window-toggle-side-windows' to not persist
|
||||
such parameters when side windows are toggled (which could,
|
||||
e.g. cause a window's `mode-line-format' to not persist). So
|
||||
enabling this option solves that.
|
||||
|
||||
Note: When this option is non-nil,
|
||||
`burly-window-persistent-parameters' should be set heeding the
|
||||
warning in the manual about not using the `writable' value for
|
||||
parameters whose values do not have a read syntax."
|
||||
:type 'boolean)
|
||||
|
||||
;;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-last-bookmark ()
|
||||
"Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on
|
||||
a project."
|
||||
(interactive)
|
||||
(unless burly-opened-bookmark-name
|
||||
(user-error "Use command `burly-open-bookmark' first"))
|
||||
(burly-open-bookmark burly-opened-bookmark-name))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-buffer-url (buffer)
|
||||
"Copy BUFFER's URL to the kill ring."
|
||||
(interactive "bBuffer: ")
|
||||
(let ((url (burly-buffer-url (get-buffer buffer))))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-frames-url ()
|
||||
"Copy current frameset's URL to the kill ring."
|
||||
(interactive)
|
||||
(let ((url (burly-frames-url)))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-kill-windows-url ()
|
||||
"Copy current frame's window configuration URL to the kill ring."
|
||||
(interactive)
|
||||
(let ((url (burly-windows-url)))
|
||||
(kill-new url)
|
||||
(message "%s" url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-url (url)
|
||||
"Open Burly URL."
|
||||
;; FIXME: If point is on an "emacs+burly..." URL, but it's after the "emacs+burly"
|
||||
;; part, `thing-at-point-url-at-point' doesn't pick up the whole URL.
|
||||
(interactive (list (or (thing-at-point-url-at-point t)
|
||||
(read-string "URL: "))))
|
||||
(cl-assert (string-prefix-p "emacs+burly+" url) t "burly-open-url: URL not an emacs+burly one:")
|
||||
(pcase-let* ((urlobj (url-generic-parse-url url))
|
||||
((cl-struct url type) urlobj)
|
||||
(subtype (car (last (split-string type "+" 'omit-nulls)))))
|
||||
(pcase-exhaustive subtype
|
||||
((or "bookmark" "file" "name") (pop-to-buffer (burly-url-buffer url)))
|
||||
("frames" (burly--frameset-restore urlobj))
|
||||
("windows" (burly--windows-set urlobj)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-frames (name)
|
||||
"Bookmark the current frames as NAME."
|
||||
(interactive
|
||||
(list (completing-read "Save Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(let ((record (list (cons 'url (burly-frames-url))
|
||||
(cons 'handler #'burly-bookmark-handler))))
|
||||
(bookmark-store name record nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-windows (name)
|
||||
"Bookmark the current frame's window configuration as NAME."
|
||||
(interactive
|
||||
(list (completing-read "Save Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(let ((record (list (cons 'url (burly-windows-url))
|
||||
(cons 'handler #'burly-bookmark-handler))))
|
||||
(bookmark-store name record nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-open-bookmark (bookmark)
|
||||
"Restore a window configuration to the current frame from a Burly BOOKMARK."
|
||||
(interactive
|
||||
(list (completing-read "Open Burly bookmark: " (burly-bookmark-names)
|
||||
nil nil burly-bookmark-prefix)))
|
||||
(cl-assert (and bookmark (not (string-empty-p bookmark))) nil
|
||||
"(burly-open-bookmark): Invalid Burly bookmark: '%s'" bookmark)
|
||||
(bookmark-jump bookmark))
|
||||
|
||||
;;;; Functions
|
||||
|
||||
;;;;; Buffers
|
||||
|
||||
(defun burly-url-buffer (url)
|
||||
"Return buffer for URL."
|
||||
(cl-assert (string-prefix-p "emacs+burly+" url) t "burly-url-buffer: URL not an emacs+burly one: %s" url)
|
||||
(pcase-let* ((urlobj (url-generic-parse-url url))
|
||||
((cl-struct url type) urlobj)
|
||||
(subtype (car (last (split-string type "+" 'omit-nulls)))))
|
||||
(pcase-exhaustive subtype
|
||||
("bookmark" (burly--bookmark-url-buffer urlobj))
|
||||
("file" (burly--file-url-buffer urlobj))
|
||||
("name" (let ((buffer-name (decode-coding-string (cdr (url-path-and-query urlobj))
|
||||
'utf-8-unix)))
|
||||
(or (get-buffer buffer-name)
|
||||
(with-current-buffer (get-buffer-create (concat "*Burly (error): " buffer-name "*"))
|
||||
(insert "Burly was unable to get a buffer named: " buffer-name "\n"
|
||||
"URL: " url "\n"
|
||||
"Please report this error to the developer\n\n")
|
||||
(current-buffer))))))))
|
||||
|
||||
(defun burly-buffer-url (buffer)
|
||||
"Return URL for BUFFER."
|
||||
(let* ((major-mode (buffer-local-value 'major-mode buffer))
|
||||
(make-url-fn (map-nested-elt burly-major-mode-alist (list major-mode 'make-url-fn))))
|
||||
(cond (make-url-fn (funcall make-url-fn buffer))
|
||||
(t (or (with-current-buffer buffer
|
||||
(when-let* ((record (ignore-errors
|
||||
(bookmark-make-record))))
|
||||
(cl-labels ((encode (element)
|
||||
(cl-typecase element
|
||||
(string (encode-coding-string element 'utf-8-unix))
|
||||
(proper-list (mapcar #'encode element))
|
||||
(cons (cons (encode (car element))
|
||||
(encode (cdr element))))
|
||||
(t element))))
|
||||
;; Encode all strings in record with UTF-8.
|
||||
;; NOTE: If we stop using URLs in the future, maybe this won't be needed.
|
||||
(setf record (encode record)))
|
||||
(burly--bookmark-record-url record)))
|
||||
;; Buffer can't seem to be bookmarked, so record it as
|
||||
;; a name-only buffer. For some reason, it works
|
||||
;; better to use the buffer name in the query string
|
||||
;; rather than the filename/path part.
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+name" nil nil nil nil
|
||||
(concat "?" (encode-coding-string (buffer-name buffer)
|
||||
'utf-8-unix))
|
||||
nil nil 'fullness)))))))
|
||||
|
||||
;;;;; Files
|
||||
|
||||
(defun burly--file-url-buffer (urlobj)
|
||||
"Return buffer for \"emacs+burly+file:\" URLOBJ."
|
||||
(pcase-let* ((`(,path . ,query-string) (url-path-and-query urlobj))
|
||||
(query (url-parse-query-string query-string))
|
||||
(buffer (find-file-noselect path))
|
||||
(major-mode (buffer-local-value 'major-mode buffer))
|
||||
(follow-fn (map-nested-elt burly-major-mode-alist (list major-mode 'follow-url-fn))))
|
||||
(cl-assert follow-fn nil "Major mode not in `burly-major-mode-alist': %s" major-mode)
|
||||
(funcall follow-fn :buffer buffer :query query)))
|
||||
|
||||
;;;;; Frames
|
||||
|
||||
;; Looks like frameset.el should make this pretty easy.
|
||||
|
||||
(require 'frameset)
|
||||
|
||||
(cl-defun burly-frames-url (&optional (frames (frame-list)))
|
||||
"Return URL for frameset of FRAMES.
|
||||
FRAMES defaults to all live frames."
|
||||
(dolist (frame frames)
|
||||
;; Set URL window parameter for each window before saving state.
|
||||
(burly--windows-set-url (window-list frame 'never)))
|
||||
(let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(frameset-filter-alist (append burly-frameset-filter-alist frameset-filter-alist))
|
||||
(query (frameset-save frames))
|
||||
(print-length nil) ; Important!
|
||||
(filename (concat "?" (url-hexify-string (prin1-to-string query))))
|
||||
(url (url-recreate-url (url-parse-make-urlobj "emacs+burly+frames" nil nil nil nil
|
||||
filename))))
|
||||
(dolist (frame frames)
|
||||
;; Clear window parameters.
|
||||
(burly--windows-set-url (window-list frame 'never) 'nullify))
|
||||
url))
|
||||
|
||||
(defun burly--frameset-restore (urlobj)
|
||||
"Restore FRAMESET according to URLOBJ."
|
||||
(setf window-persistent-parameters (copy-sequence burly-window-persistent-parameters))
|
||||
(pcase-let* ((`(,_ . ,query-string) (url-path-and-query urlobj))
|
||||
(frameset (read (url-unhex-string query-string)))
|
||||
(frameset-filter-alist (append burly-frameset-filter-alist frameset-filter-alist)))
|
||||
;; Restore buffers. (Apparently `cl-loop''s in-ref doesn't work with
|
||||
;; its destructuring, so we can't just `setf' on `window-state'.)
|
||||
(setf (frameset-states frameset)
|
||||
(cl-loop for (frame-parameters . window-state) in (frameset-states frameset)
|
||||
collect (cons frame-parameters (burly--bufferize-window-state window-state))))
|
||||
(condition-case err
|
||||
(frameset-restore frameset)
|
||||
(error (delay-warning 'burly (format "Error while restoring frameset: ERROR:%S FRAMESET:%S" err frameset))))))
|
||||
|
||||
;;;;; Windows
|
||||
|
||||
(cl-defun burly-windows-url (&optional (frame (selected-frame)))
|
||||
"Return URL for window configuration on FRAME."
|
||||
(with-selected-frame frame
|
||||
(let* ((query (burly--window-state frame))
|
||||
(print-length nil) ; Important!
|
||||
(filename (concat "?" (url-hexify-string (prin1-to-string query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+windows" nil nil nil nil
|
||||
filename)))))
|
||||
|
||||
(cl-defun burly--window-state (&optional (frame (selected-frame)))
|
||||
"Return window state for FRAME.
|
||||
Sets `burly-url' window parameter in each window before
|
||||
serializing."
|
||||
(with-selected-frame frame
|
||||
;; Set URL window parameter for each window before saving state.
|
||||
(burly--windows-set-url (window-list nil 'never))
|
||||
(let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(window-state (window-state-get nil 'writable)))
|
||||
;; Clear window parameters we set (because they aren't kept
|
||||
;; current, so leaving them could be confusing).
|
||||
(burly--windows-set-url (window-list nil 'never) 'nullify)
|
||||
window-state)))
|
||||
|
||||
(defun burly--windows-set-url (windows &optional nullify)
|
||||
"Set `burly-url' window parameter in WINDOWS.
|
||||
If NULLIFY, set the parameter to nil."
|
||||
(dolist (window windows)
|
||||
(let ((value (if nullify nil (burly-buffer-url (window-buffer window)))))
|
||||
(set-window-parameter window 'burly-url value))))
|
||||
|
||||
(defun burly--windows-set (urlobj)
|
||||
"Set window configuration according to URLOBJ."
|
||||
(setf window-persistent-parameters (copy-sequence burly-window-persistent-parameters))
|
||||
(pcase-let* ((window-persistent-parameters (append burly-window-persistent-parameters
|
||||
window-persistent-parameters))
|
||||
(`(,_ . ,query-string) (url-path-and-query urlobj))
|
||||
;; FIXME: Remove this condition-case eventually, after giving users time to update their bookmarks.
|
||||
(state (condition-case nil
|
||||
(read (url-unhex-string query-string))
|
||||
(invalid-read-syntax (display-warning 'burly "Please recreate that Burly bookmark (storage format changed)")
|
||||
(read query-string))))
|
||||
(state (burly--bufferize-window-state state)))
|
||||
(window-state-put state (frame-root-window))
|
||||
;; HACK: Since `bookmark--jump-via' insists on calling a
|
||||
;; buffer-display function after handling the bookmark, we add a
|
||||
;; function to `bookmark-after-jump-hook' to restore the window
|
||||
;; configuration that we just set.
|
||||
(setf burly--window-state (window-state-get (frame-root-window) 'writable))
|
||||
(push #'burly--bookmark-window-state-hack bookmark-after-jump-hook)))
|
||||
|
||||
(defun burly--bufferize-window-state (state)
|
||||
"Return window state STATE with its buffers reincarnated."
|
||||
(cl-labels ((bufferize-state
|
||||
;; Set windows' buffers in STATE.
|
||||
(state) (pcase state
|
||||
(`(leaf . ,_attrs) (bufferize-leaf state))
|
||||
((pred atom) state)
|
||||
(`(,_key . ,(pred atom)) state)
|
||||
((pred list) (mapcar #'bufferize-state state))))
|
||||
(bufferize-leaf
|
||||
(leaf) (pcase-let* ((`(leaf . ,attrs) leaf)
|
||||
((map parameters buffer) attrs)
|
||||
((map burly-url) parameters)
|
||||
(`(,_buffer-name . ,buffer-attrs) buffer)
|
||||
(new-buffer (burly-url-buffer burly-url)))
|
||||
(setf (map-elt attrs 'buffer) (cons new-buffer buffer-attrs))
|
||||
(cons 'leaf attrs))))
|
||||
(if-let ((leaf-pos (cl-position 'leaf state)))
|
||||
;; A one-window frame: the elements following `leaf' are that window's params.
|
||||
(append (cl-subseq state 0 leaf-pos)
|
||||
(bufferize-leaf (cl-subseq state leaf-pos)))
|
||||
;; Multi-window frame.
|
||||
(bufferize-state state))))
|
||||
|
||||
;;;;; Bookmarks
|
||||
|
||||
(defun burly--bookmark-window-state-hack (&optional _)
|
||||
"Put window state from `burly--window-state'.
|
||||
This function is to be called in `bookmark-after-jump-hook' to
|
||||
work around `bookmark--jump-via's calling a buffer-display
|
||||
function which changes the window configuration after
|
||||
`burly--windows-set' has set it. This function removes itself
|
||||
from the hook."
|
||||
(unwind-protect
|
||||
(progn
|
||||
(cl-assert burly--window-state)
|
||||
(window-state-put burly--window-state (frame-root-window)))
|
||||
(setf bookmark-after-jump-hook (delete #'burly--bookmark-window-state-hack bookmark-after-jump-hook)
|
||||
burly--window-state nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun burly-bookmark-handler (bookmark)
|
||||
"Handler function for Burly BOOKMARK."
|
||||
(let ((previous-name burly-opened-bookmark-name))
|
||||
;; Set opened bookmark name before actually opening it so that the
|
||||
;; tabs-mode advice functions can use it beforehand.
|
||||
(setf burly-opened-bookmark-name (car bookmark))
|
||||
(condition-case err
|
||||
(burly-open-url (alist-get 'url (bookmark-get-bookmark-record bookmark)))
|
||||
(error (setf burly-opened-bookmark-name previous-name)
|
||||
(signal (car err) (cdr err))))))
|
||||
|
||||
(defun burly--bookmark-record-url (record)
|
||||
"Return a URL for bookmark RECORD."
|
||||
(cl-assert record)
|
||||
(pcase-let* ((`(,name . ,props) record)
|
||||
(print-length nil) ; Important!
|
||||
(query (cl-loop for prop in props
|
||||
;; HACK: Remove unreadable values from props.
|
||||
do (cl-loop for value in-ref (cdr prop)
|
||||
when (or (bufferp value))
|
||||
do (setf value nil))
|
||||
collect (list (car prop) (prin1-to-string (cdr prop)))))
|
||||
(filename (concat (url-hexify-string name) "?" (url-build-query-string (remove nil query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+bookmark" nil nil nil nil
|
||||
filename nil nil 'fullness))))
|
||||
|
||||
(defun burly--bookmark-url-buffer (urlobj)
|
||||
"Return buffer for bookmark specified by URLOBJ.
|
||||
URLOBJ should be a URL object as returned by
|
||||
`url-generic-parse-url'."
|
||||
(pcase-let* ((`(,path . ,query-string) (url-path-and-query urlobj))
|
||||
(query (url-parse-query-string query-string))
|
||||
;; Convert back to alist.
|
||||
(props (cl-loop for prop in query
|
||||
for key = (intern (car prop))
|
||||
for value = (pcase key
|
||||
('handler (intern (cadr prop)))
|
||||
('help-args (read (cadr prop)))
|
||||
('help-fn (ignore-errors
|
||||
;; NOTE: Due to changes in help-mode.el which serialize natively
|
||||
;; compiled subrs in the bookmark record, which cannot be read
|
||||
;; back (which actually break the entire bookmark system when
|
||||
;; such a record is saved in the bookmarks file), we have to
|
||||
;; workaround a failure to read here. See bug#56643.
|
||||
(read (cadr prop))))
|
||||
('position (cl-parse-integer (cadr prop)))
|
||||
(_ (read (cadr prop))))
|
||||
collect (cons key value)))
|
||||
(record (cons path props)))
|
||||
(cl-labels ((decode (element)
|
||||
(cl-typecase element
|
||||
(string (decode-coding-string element 'utf-8-unix))
|
||||
(proper-list (mapcar #'decode element))
|
||||
(cons (cons
|
||||
(decode (car element))
|
||||
(decode (cdr element))))
|
||||
(t element))))
|
||||
;; Decode all strings in record with UTF-8.
|
||||
;; NOTE: If we stop using URLs in the future, maybe this won't be needed.
|
||||
(setf record (decode record)))
|
||||
(save-window-excursion
|
||||
(condition-case err
|
||||
(bookmark-jump record)
|
||||
(error (delay-warning 'burly (format "Error while opening bookmark: ERROR:%S RECORD:%S" err record))))
|
||||
(current-buffer))))
|
||||
|
||||
(defun burly-bookmark-names ()
|
||||
"Return list of all Burly bookmark names."
|
||||
(cl-loop for bookmark in bookmark-alist
|
||||
for (_name . params) = bookmark
|
||||
when (equal #'burly-bookmark-handler (alist-get 'handler params))
|
||||
collect (car bookmark)))
|
||||
|
||||
;;;;; Org buffers
|
||||
|
||||
;; We only require Org when compiling the file. At runtime, Org will
|
||||
;; be loaded before we call any of its functions, because we load the
|
||||
;; Org file into a buffer first, which activates `org-mode'.
|
||||
|
||||
(eval-when-compile
|
||||
(require 'org))
|
||||
|
||||
(declare-function org-before-first-heading-p "org")
|
||||
(declare-function org-back-to-heading "org")
|
||||
(declare-function org-find-olp "org")
|
||||
(declare-function org-tree-to-indirect-buffer "org")
|
||||
(declare-function org-narrow-to-subtree "org")
|
||||
(declare-function org-heading-components "org")
|
||||
(declare-function org-up-heading-safe "org")
|
||||
|
||||
(defun burly--org-mode-buffer-url (buffer)
|
||||
"Return URL for Org BUFFER."
|
||||
(with-current-buffer buffer
|
||||
(cl-assert (or (buffer-file-name buffer)
|
||||
(buffer-file-name (buffer-base-buffer buffer)))
|
||||
nil "Buffer has no file name: %s" buffer)
|
||||
(let* ((narrowed (buffer-narrowed-p))
|
||||
(indirect (buffer-base-buffer buffer))
|
||||
(top-olp
|
||||
;; For narrowing purposes, start with the heading at the top of the buffer.
|
||||
(when (buffer-narrowed-p)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
;; `org-get-outline-path' replaces links in headings with their
|
||||
;; descriptions, which prevents using them in regexp searches.
|
||||
(when (org-heading-components)
|
||||
(org-with-wide-buffer
|
||||
(nreverse (cl-loop collect (substring-no-properties (nth 4 (org-heading-components)))
|
||||
while (org-up-heading-safe))))))))
|
||||
(point-olp
|
||||
(when (ignore-errors (org-heading-components))
|
||||
(org-with-wide-buffer
|
||||
(nreverse (cl-loop collect (substring-no-properties (nth 4 (org-heading-components)))
|
||||
while (org-up-heading-safe))))))
|
||||
(pos (point))
|
||||
(relative-pos (when top-olp
|
||||
(- (point) (save-excursion
|
||||
(org-back-to-heading)
|
||||
(point)))))
|
||||
(print-length nil) ; Important!
|
||||
(query (list (list "pos" pos)
|
||||
(when top-olp
|
||||
(list "top-olp" (prin1-to-string top-olp)))
|
||||
(when point-olp
|
||||
(list "point-olp" (prin1-to-string point-olp)))
|
||||
(when relative-pos
|
||||
(list "relative-pos" relative-pos))
|
||||
(when indirect
|
||||
(list "indirect" "t"))
|
||||
(when narrowed
|
||||
(list "narrowed" "t"))))
|
||||
(buffer-file (or (buffer-file-name buffer)
|
||||
(buffer-file-name (buffer-base-buffer buffer))))
|
||||
(filename (concat buffer-file "?" (url-build-query-string (remove nil query)))))
|
||||
(url-recreate-url (url-parse-make-urlobj "emacs+burly+file" nil nil nil nil
|
||||
filename nil nil 'fullness)))))
|
||||
|
||||
(cl-defun burly-follow-url-org-mode (&key buffer query)
|
||||
"In BUFFER, jump to heading and position from QUERY, and return a buffer.
|
||||
If QUERY specifies that the buffer should be indirect, a new,
|
||||
indirect buffer is returned. Otherwise BUFFER is returned."
|
||||
;; `pcase's map support uses `alist-get', which does not work with string keys
|
||||
;; unless its TESTFN arg is bound to, e.g. `equal', but `map-elt' has deprecated
|
||||
;; its TESTFN arg, and there's no way to pass it or bind it when using `pcase'
|
||||
;; anyway. So we rebind `alist-get' to a function that uses `assoc-string'.
|
||||
(with-current-buffer buffer
|
||||
(cl-letf (((symbol-function 'alist-get)
|
||||
(lambda (key alist &optional _default _remove _testfn)
|
||||
;; Only the first value in the list of values is returned, so multiple
|
||||
;; values are not supported. I don't expect this to be a problem...
|
||||
(cadr (assoc-string key alist)))))
|
||||
(pcase-let* (((map ("pos" pos)
|
||||
("indirect" indirect)
|
||||
("narrowed" narrowed)
|
||||
("top-olp" top-olp)
|
||||
("point-olp" point-olp)
|
||||
("relative-pos" relative-pos))
|
||||
query)
|
||||
(heading-pos (when top-olp
|
||||
(org-find-olp (read top-olp) 'this-buffer))))
|
||||
(widen)
|
||||
(if heading-pos
|
||||
(goto-char heading-pos)
|
||||
(goto-char (string-to-number pos)))
|
||||
(cond (indirect (org-tree-to-indirect-buffer))
|
||||
(narrowed (progn
|
||||
(org-narrow-to-subtree)
|
||||
(goto-char (org-find-olp (read point-olp) 'this-buffer)))))
|
||||
(when (and heading-pos relative-pos)
|
||||
(forward-char (string-to-number relative-pos)))
|
||||
(current-buffer)))))
|
||||
|
||||
;;;; Footer
|
||||
|
||||
(provide 'burly)
|
||||
|
||||
;;; burly.el ends here
|
|
@ -1,325 +0,0 @@
|
|||
This is README.info, produced by makeinfo version 5.2 from README.texi.
|
||||
|
||||
INFO-DIR-SECTION Emacs
|
||||
START-INFO-DIR-ENTRY
|
||||
* Burly: (burly). Save and restore window configurations and their buffers.
|
||||
END-INFO-DIR-ENTRY
|
||||
|
||||
|
||||
File: README.info, Node: Top, Next: Installation, Up: (dir)
|
||||
|
||||
Burly.el
|
||||
********
|
||||
|
||||
This package provides tools to save and restore frame and window
|
||||
configurations in Emacs, including buffers that may not be live anymore.
|
||||
In this way, it’s like a lightweight "workspace" manager, allowing you
|
||||
to easily restore one or more frames, including their windows, the
|
||||
windows’ layout, and their buffers.
|
||||
|
||||
Internally it uses Emacs’s bookmarks system to restore buffers to
|
||||
their previous contents and location. This provides power and
|
||||
extensibility, since many major modes already integrate with Emacs’s
|
||||
bookmarks system. However, in case a mode’s bookmarking function isn’t
|
||||
satisfactory, Burly allows the user to customize buffer-restoring
|
||||
functions for specific modes.
|
||||
|
||||
For Org mode, Burly provides such custom functions so that narrowed
|
||||
and indirect Org buffers are properly restored, and headings are located
|
||||
by outline path in case they’ve moved since a bookmark was made (the
|
||||
org-bookmark-heading (https://github.com/alphapapa/org-bookmark-heading)
|
||||
package also provides this through the Emacs bookmark system, but users
|
||||
may not have it installed, and the functionality is too useful to not
|
||||
include here by default).
|
||||
|
||||
Internally, buffers and frame/window configurations are also encoded
|
||||
as URLs, and users may also save and open those URLs instead of using
|
||||
Emacs bookmarks. (The name "Burly" comes from "buffer URL.") For
|
||||
example, a URL to the ‘Installation/Quelpa’ heading in this file, as I’m
|
||||
writing it, looks like this:
|
||||
|
||||
emacs+burly+file:///home/me/src/emacs/burly.el/README.org?pos=2651&outline-path=%28%22Installation%22%20%22Quelpa%22%29&relative-pos=308
|
||||
|
||||
In terms of built-in features, Burly may be seen as integrating or
|
||||
leveraging the built-in libraries ‘bookmark.el’, ‘window.el’, and
|
||||
‘frameset.el’.
|
||||
|
||||
* Menu:
|
||||
|
||||
* Installation::
|
||||
* Usage::
|
||||
* Changelog::
|
||||
* Development::
|
||||
* Credits::
|
||||
* License::
|
||||
|
||||
— The Detailed Node Listing —
|
||||
|
||||
Installation
|
||||
|
||||
* MELPA::
|
||||
* Quelpa::
|
||||
* Manual::
|
||||
|
||||
Usage
|
||||
|
||||
* Bookmark commands::
|
||||
* URL commands::
|
||||
* Tab bar::
|
||||
* Tips::
|
||||
|
||||
Changelog
|
||||
|
||||
* 0.3-pre: 03-pre.
|
||||
* 0.2: 02.
|
||||
* 0.1: 01.
|
||||
|
||||
|
||||
|
||||
File: README.info, Node: Installation, Next: Usage, Prev: Top, Up: Top
|
||||
|
||||
1 Installation
|
||||
**************
|
||||
|
||||
* Menu:
|
||||
|
||||
* MELPA::
|
||||
* Quelpa::
|
||||
* Manual::
|
||||
|
||||
|
||||
File: README.info, Node: MELPA, Next: Quelpa, Up: Installation
|
||||
|
||||
1.1 MELPA
|
||||
=========
|
||||
|
||||
If you installed from MELPA, you’re done. Just run one of the commands
|
||||
below.
|
||||
|
||||
|
||||
File: README.info, Node: Quelpa, Next: Manual, Prev: MELPA, Up: Installation
|
||||
|
||||
1.2 Quelpa
|
||||
==========
|
||||
|
||||
The easiest way is to install with quelpa-use-package
|
||||
(https://github.com/quelpa/quelpa-use-package), like this:
|
||||
|
||||
(use-package burly
|
||||
:quelpa (burly :fetcher github :repo "alphapapa/burly.el"))
|
||||
|
||||
|
||||
File: README.info, Node: Manual, Prev: Quelpa, Up: Installation
|
||||
|
||||
1.3 Manual
|
||||
==========
|
||||
|
||||
1. Install version 2.1 or later of the ‘map’ library from GNU ELPA.
|
||||
2. Copy ‘burly.el’ into a directory in your ‘load-path’, then
|
||||
‘(require 'burly)’.
|
||||
|
||||
|
||||
File: README.info, Node: Usage, Next: Changelog, Prev: Installation, Up: Top
|
||||
|
||||
2 Usage
|
||||
*******
|
||||
|
||||
* Menu:
|
||||
|
||||
* Bookmark commands::
|
||||
* URL commands::
|
||||
* Tab bar::
|
||||
* Tips::
|
||||
|
||||
|
||||
File: README.info, Node: Bookmark commands, Next: URL commands, Up: Usage
|
||||
|
||||
2.1 Bookmark commands
|
||||
=====================
|
||||
|
||||
Most users will probably use Burly by bookmarking frame and window
|
||||
configurations and accessing them with these commands:
|
||||
|
||||
• ‘burly-bookmark-frames’: Bookmark the current frames and their
|
||||
window configurations.
|
||||
• ‘burly-bookmark-windows’: Bookmark the current frame’s window
|
||||
configuration.
|
||||
• ‘burly-open-bookmark’: Select and open a Burly bookmark.
|
||||
• ‘burly-open-last-bookmark’: Open the last-opened Burly bookmark.
|
||||
Helpful for, e.g. quickly restoring an overview while working on a
|
||||
project.
|
||||
|
||||
Note that bookmarks created by Burly are regular Emacs bookmarks, so
|
||||
they can be managed by Emacs’s built-in bookmark commands, e.g.
|
||||
‘list-bookmarks’, ‘bookmark-delete’, etc.
|
||||
|
||||
|
||||
File: README.info, Node: URL commands, Next: Tab bar, Prev: Bookmark commands, Up: Usage
|
||||
|
||||
2.2 URL commands
|
||||
================
|
||||
|
||||
These commands work on URL strings. While most users probably won’t use
|
||||
these, they may be useful for building custom tooling.
|
||||
|
||||
• ‘burly-open-url’: Open a Burly URL (at point, or prompt for one),
|
||||
displaying the buffer(s) in the current window or frame.
|
||||
• ‘burly-kill-buffer-url’: Copy BUFFER’s URL to the kill ring.
|
||||
• ‘burly-kill-frames-url’: Copy the current frameset’s URL to the
|
||||
kill ring.
|
||||
• ‘burly-kill-windows-url’: Copy the current frame’s window
|
||||
configuration URL to the kill ring.
|
||||
|
||||
|
||||
File: README.info, Node: Tab bar, Next: Tips, Prev: URL commands, Up: Usage
|
||||
|
||||
2.3 Tab bar
|
||||
===========
|
||||
|
||||
Burly supports Emacs’s ‘tab-bar-mode’ with ‘burly-tabs-mode’. When
|
||||
active, Burly bookmarks are opened in new tabs, and the tabs are named
|
||||
according to the bookmarks. Reopening a Burly bookmark uses the
|
||||
designated tab, if it already exists, and tabs may be reset to their
|
||||
bookmarked state with the command ‘burly-reset-tab’ (which you might
|
||||
bind to ‘C-x t R’).
|
||||
|
||||
|
||||
File: README.info, Node: Tips, Prev: Tab bar, Up: Usage
|
||||
|
||||
2.4 Tips
|
||||
========
|
||||
|
||||
• You can customize settings in the ‘burly’ group.
|
||||
• An Info manual is included with this package.
|
||||
|
||||
|
||||
File: README.info, Node: Changelog, Next: Development, Prev: Usage, Up: Top
|
||||
|
||||
3 Changelog
|
||||
***********
|
||||
|
||||
* Menu:
|
||||
|
||||
* 0.3-pre: 03-pre.
|
||||
* 0.2: 02.
|
||||
* 0.1: 01.
|
||||
|
||||
|
||||
File: README.info, Node: 03-pre, Next: 02, Up: Changelog
|
||||
|
||||
3.1 0.3-pre
|
||||
===========
|
||||
|
||||
*Added*
|
||||
• Command ‘burly-tabs-mode’, which integrates Burly with
|
||||
‘tab-bar-mode’. When active, Burly bookmarks are opened in new
|
||||
tabs, and the tabs are named according to the bookmark.
|
||||
• Command ‘burly-reset-tab’, which resets a tab to the state of the
|
||||
bookmark which opened it.
|
||||
|
||||
*Changed*
|
||||
• Emacs version 28.1 or later is required.
|
||||
|
||||
*Fixed*
|
||||
• Buffers that can’t be restored by name no longer cause an error
|
||||
which aborts restoration of the rest of the bookmark’s buffers.
|
||||
• Added workaround for regression in Emacs 28 regarding bookmarks for
|
||||
‘help-mode’ buffers. (See Emacs bug 56643
|
||||
(https://debbugs.gnu.org/cgi/bugreport.cgi?bug=56643).)
|
||||
|
||||
|
||||
File: README.info, Node: 02, Next: 01, Prev: 03-pre, Up: Changelog
|
||||
|
||||
3.2 0.2
|
||||
=======
|
||||
|
||||
*Added*
|
||||
• Bookmark commands use ‘completing-read’ and offer existing Burly
|
||||
bookmark names, making it easier to update bookmarks. (Thanks to
|
||||
Erik Sjöstrand (https://github.com/Kungsgeten).)
|
||||
• Command ‘burly-open-last-bookmark’.
|
||||
• Option ‘burly-set-window-persistent-parameters’, which synchronizes
|
||||
‘window-persistent-parameters’ with
|
||||
‘burly-window-persistent-parameters’, ensuring that built-in Emacs
|
||||
commands like ‘window-toggle-side-windows’ persist parameters that
|
||||
are persisted with Burly.
|
||||
|
||||
*Changed*
|
||||
• Option ‘burly-window-persistent-parameters’’s default value
|
||||
includes more window parameters, like header/mode line, side, slot,
|
||||
etc, making it easier to restore an overview of a project or
|
||||
"workspace."
|
||||
• Emacs version 27.1 or later is required.
|
||||
|
||||
*Fixed*
|
||||
• Narrow Org buffers to correct heading (at the top of the buffer
|
||||
rather than at point).
|
||||
• Buffers whose names have multibyte characters. (Fixes #43
|
||||
(https://github.com/alphapapa/burly.el/issues/43). Thanks to Liu
|
||||
Hui (https://github.com/ilupin) for reporting.)
|
||||
• Bind ‘print-level’ to nil where ‘prin1-to-string’ is used (in case
|
||||
the value is non-nil in a user’s config, which would cause
|
||||
truncated values).
|
||||
|
||||
|
||||
File: README.info, Node: 01, Prev: 02, Up: Changelog
|
||||
|
||||
3.3 0.1
|
||||
=======
|
||||
|
||||
Initial release.
|
||||
|
||||
|
||||
File: README.info, Node: Development, Next: Credits, Prev: Changelog, Up: Top
|
||||
|
||||
4 Development
|
||||
*************
|
||||
|
||||
Bug reports, feature requests, suggestions — _oh my_!
|
||||
|
||||
|
||||
File: README.info, Node: Credits, Next: License, Prev: Development, Up: Top
|
||||
|
||||
5 Credits
|
||||
*********
|
||||
|
||||
• Thanks to Clemens Radermacher (https://github.com/clemera) and
|
||||
Robert Weiner (https://github.com/rswgnu) for their suggestions.
|
||||
• Thanks to Trey Peacock (https://github.com/tpeacock19) for
|
||||
extensive feedback on pre-release versions.
|
||||
|
||||
|
||||
File: README.info, Node: License, Prev: Credits, Up: Top
|
||||
|
||||
6 License
|
||||
*********
|
||||
|
||||
GPLv3
|
||||
|
||||
|
||||
|
||||
Tag Table:
|
||||
Node: Top219
|
||||
Node: Installation2378
|
||||
Node: MELPA2529
|
||||
Node: Quelpa2701
|
||||
Node: Manual3014
|
||||
Node: Usage3283
|
||||
Node: Bookmark commands3454
|
||||
Node: URL commands4320
|
||||
Node: Tab bar5001
|
||||
Node: Tips5490
|
||||
Node: Changelog5685
|
||||
Node: 03-pre5847
|
||||
Node: 026658
|
||||
Node: 018079
|
||||
Node: Development8173
|
||||
Node: Credits8344
|
||||
Node: License8704
|
||||
|
||||
End Tag Table
|
||||
|
||||
|
||||
Local Variables:
|
||||
coding: utf-8
|
||||
End:
|
|
@ -1,19 +0,0 @@
|
|||
This is the file .../info/dir, which contains the
|
||||
topmost node of the Info hierarchy, called (dir)Top.
|
||||
The first time you invoke Info you start off looking at this node.
|
||||
|
||||
File: dir, Node: Top This is the top of the INFO tree
|
||||
|
||||
This (the Directory node) gives a menu of major topics.
|
||||
Typing "q" exits, "H" lists all Info commands, "d" returns here,
|
||||
"h" gives a primer for first-timers,
|
||||
"mEmacs<Return>" visits the Emacs manual, etc.
|
||||
|
||||
In Emacs, you can click mouse button 2 on a menu item or cross reference
|
||||
to select it.
|
||||
|
||||
* Menu:
|
||||
|
||||
Emacs
|
||||
* Burly: (burly). Save and restore window configurations and
|
||||
their buffers.
|
Reference in a new issue