]> code.delx.au - gnu-emacs-elpa/commitdiff
Add 'packages/wconf/' from commit 'b8ea22f80bff19222136d9495f685888dc682b9d'
authorIngo Lohmar <i.lohmar@gmail.com>
Sun, 2 Aug 2015 14:39:46 +0000 (16:39 +0200)
committerIngo Lohmar <i.lohmar@gmail.com>
Sun, 2 Aug 2015 14:39:46 +0000 (16:39 +0200)
git-subtree-dir: packages/wconf
git-subtree-mainline: 6340c15fb07a979b6c7bd2e8913e499091c257ff
git-subtree-split: b8ea22f80bff19222136d9495f685888dc682b9d

1  2 
packages/wconf/.gitignore
packages/wconf/README.org
packages/wconf/wconf.el

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c531d9867f6c223be1daf0f6da7538feb11966d8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++*.elc
index 0000000000000000000000000000000000000000,a13d508d996291f10c95a9a42d80f0fdaa8b0aed..a13d508d996291f10c95a9a42d80f0fdaa8b0aed
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,108 +1,108 @@@
+ * wconf
+ ** About
+ =wconf= is a minimal window configuration manager for [[http://www.gnu.org/software/emacs/][GNU Emacs]].  Its
+ goal is to have several window configurations easily available, to
+ switch between them, and to save them to disk and later restore them.
+ For example, I might have a default "workspace" for miscellaneous stuff,
+ and then I might have workspaces "UI", "MT", "DB" for a classic 3-tier
+ application.
+ Can double as a "boss" key; not that you would ever use something like
+ that yourself.
+ ** Using the Package
+ Make sure the files are in your =load-path=, and either =(require
+ 'wconf)= or make sure its autoloads are known to Emacs --- if you have
+ installed from a package archive, that should take care of both.
+ Here is an example from my configuration to show you the intended use of
+ =wconf=.
+ #+begin_src emacs-lisp
+ (add-hook 'desktop-after-read-hook      ;so we have all buffers again
+           (lambda ()
+             (wconf-load)
+             (wconf-switch-to-config 0)
+             (add-hook 'kill-emacs-hook
+                       (lambda ()
+                         (wconf-store-all)
+                         (wconf-save))))
+           'append)
+ (global-set-key (kbd "C-c w s") #'wconf-store)
+ (global-set-key (kbd "C-c w S") #'wconf-store-all)
+ (global-set-key (kbd "C-c w r") #'wconf-restore)
+ (global-set-key (kbd "C-c w R") #'wconf-restore-all)
+ (global-set-key (kbd "C-c w w") #'wconf-switch-to-config)
+ (global-set-key (kbd "C-<prior>") #'wconf-use-previous)
+ (global-set-key (kbd "C-<next>") #'wconf-use-next)
+ #+end_src
+ After =desktop= has restored all file buffers from my last session,
+ =wconf-load= will get its stored configs from =wconf-file= (defaults to
+ =<YOUREMACSDIR>/wconf-window-configs.el=), and I want it to immediately
+ switch to the first configuration.  Then we hook into killing emacs to
+ store and save all our configurations in that same file.  The global key
+ bindings expose those commands that I consider useful in day-to-day
+ work.
+ ** Concepts
+ The main idea is +stolen from+ inspired by =workgroups=.  We keep a list
+ of configuration pairs.  Each such pair consists of an /active/
+ configuration (what you see when you switch to this slot of the list),
+ and a /stored/ one (what you have in the back, and maybe save to disk at
+ some point).  In =workgroups= parlance, these are the working and base
+ configs.
+ At each point in time there is (at most) one configuration current.  You
+ can explictly store and restore the current active configuration to/from
+ the stored one, or do likewise for all configurations.  For example, you
+ might decide that you have a carefully hand-crafted set of
+ configurations that you always want to start from, but that you do not
+ wish to change this setup, except when doing so explicitly.  That's
+ easy: just remove the =(wconf-store-all)= call from the above hook
+ function.
+ A nice feature of =wconf= is that it does not alter any hooks or
+ settings outside its own small world, and I intend to keep it that way.
+ This implies that the currently active configuration is only updated
+ explicitly, via one the functions/commands in the package.
+ ** Rationale, and Other Packages
+ I used https://github.com/tlh/workgroups.el for several years.  It is a
+ great package, which offers a lot of additional features besides the
+ core business of managing window configs.  It also has some
+ shortcomings, is somewhat complex (at 79k), and I occasionally
+ experienced minor glitches.  Most importantly, it has been unmaintained
+ for roughly 4 years now.
+ https://github.com/pashinin/workgroups2 promises to pick up where
+ workgroups left, and is actively maintained.  The main difference, as I
+ understand it, is the desire to restore "special" buffers as well (help,
+ info, org-mode agendas, notmuch mail, you name it).  Finally trying it,
+ it did not provide a lot of benefit for my personal needs, but added
+ still more complexity.  The functionality that I want should not require
+ 179k of elisp.
+ Nowadays (at least since the GNU Emacs 24.4 release), there are proper
+ lisp-reader (de)serializations for both frame and window configurations,
+ and =window.el= and =frameset.el= provide functions to deal with them
+ (relatively) comfortably.  Desktop already (re)stores a single
+ configuration.  That's when I decided that it's time to roll my own:
+ build something light on top of what's already there, in order to
+ provide persistent switchable configurations.
+ ** Notes, TODO
+ I only use a single fullscreen frame all the time.  An earlier version
+ of this package stored configurations as whole framesets, without any
+ effort to deal with the multiple-frames case.  It has now changed to
+ deal with window configurations in a single frame only.  This is much
+ better defined, simpler, and no longer suffers from annoying flickering
+ effects.
+ Calling the package commands from different frames inside a single
+ session may or may not result in the behavior you want.  In the latter
+ case, feel free to open an issue and describe what happens and what you
+ expected/wanted to happen.  I explicitly do /not/ guarantee that the
+ code will change to suit your wishes --- but if minor changes could
+ render it more generally useful, I am all ears.
+ I am thinking about some rescaling options to deal with restoring on
+ frames of different sizes (possible due to a different screen size).
+ Filtering options for what is generally (re)stored (and how) might be a
+ pleasant side effect.  Don't hold your breath.
diff --combined packages/wconf/wconf.el
index 0000000000000000000000000000000000000000,57add63d804131a56fcf7db1787610a1b82914f3..57add63d804131a56fcf7db1787610a1b82914f3
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,343 +1,343 @@@
+ ;;; wconf.el --- Minimal window layout manager   -*- lexical-binding: t; -*-
+ ;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+ ;; Author: Ingo Lohmar <i.lohmar@gmail.com>
+ ;; URL: https://github.com/ilohmar/wconf
+ ;; Version: 0.2.0
+ ;; Keywords: windows, frames, layout
+ ;; Package-Requires: ((emacs "24.4"))
+ ;; This file is part of GNU Emacs.
+ ;; 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+ ;;; Commentary:
+ ;; See the file README.org
+ ;;; Code:
+ (defgroup wconf nil
+   "Easily use several window configurations."
+   :group 'convenience)
+ (defcustom wconf-change-config-function #'wconf-change-config-default
+   "Function called with current config whenever it is set."
+   :group 'wconf)
+ (defcustom wconf-file (expand-file-name "wconf-window-configs.el"
+                                         user-emacs-directory)
+   "File used to save and load window configurations."
+   :group 'wconf)
+ (defcustom wconf-fallback-buffer-name "*scratch*"
+   "Name of the buffer to substitute for buffers which are not available."
+   :group 'wconf)
+ (defcustom wconf-no-configs-string "-----"
+   "String to use if there are no configurations at all."
+   :group 'wconf)
+ (defcustom wconf-no-config-name "---"
+   "String to use for the empty window configuration."
+   :group 'wconf)
+ ;; internal variables and helper functions
+ (defvar wconf--configs nil
+   "List of configurations; each item a list (active stored name).")
+ (defvar wconf--index nil
+   "Index of currently shown configuration.  After clean and load
+ this can be nil although wconf--configs is not empty.")
+ (defvar wconf-string nil
+   "String representing information on the current configuration.")
+ (require 'cl-lib)
+ (defsubst wconf--ensure-configs (&optional current)
+   (unless wconf--configs
+     (error "wconf: No window configurations"))
+   (when (and current (not wconf--index))
+     (error "wconf: No window configuration is currently used")))
+ (defsubst wconf--ensure-index (&optional index)
+   (unless (<= 0 index (1- (length wconf--configs)))
+     (error "wconf: No window configuration index %s" index)))
+ (defun wconf--current-config ()
+   (window-state-get (frame-root-window (selected-frame))
+                     'writable))
+ (defun wconf- (index)
+   (nth index wconf--configs))
+ (defun wconf--to-string (index)
+   (if index
+       (format "%s:%s"
+               (number-to-string index)
+               (cl-caddr (wconf- index)))
+     (concat "-:" wconf-no-config-name)))
+ (defun wconf--update-info ()
+   (when (functionp wconf-change-config-function)
+     (funcall wconf-change-config-function
+              ;; both will be nil if no list
+              wconf--index
+              (and wconf--index
+                   (car (wconf- wconf--index))))))
+ (defun wconf--update-active-config ()
+   (when wconf--index
+     (setf (car (wconf- wconf--index)) (wconf--current-config))))
+ (defun wconf--use-config (index)
+   (setq wconf--index index)
+   (window-state-put (car (wconf- wconf--index))
+                     (frame-root-window (selected-frame))
+                     'safe)
+   (wconf--update-info))
+ (defun wconf--reset ()
+   "Remove all configurations."
+   (setq wconf--configs nil)
+   (setq wconf--index nil)
+   (wconf--update-info))
+ (defun wconf--copy (wc)
+   "Return a deep copy of WC, using `copy-tree'."
+   (copy-tree wc t))
+ ;; global stuff
+ (defun wconf-change-config-default (index config)
+   "Update `wconf-string' to represent configuration CONFIG at
+ position INDEX."
+   (setq wconf-string (if wconf--configs
+                          (wconf--to-string index)
+                        wconf-no-configs-string))
+   (force-mode-line-update))
+ (defun wconf-save (&optional filename)
+   "Save stored configurations in FILENAME, defaults to
+ `wconf-file'."
+   (interactive "F")
+   (let ((filename (or filename wconf-file)))
+     (with-temp-file filename
+       (prin1 (mapcar #'cdr wconf--configs) ;-> (wc name)
+              (current-buffer)))
+     (message "wconf: Save stored configurations in %s" filename)))
+ (defun wconf--sanitize-buffer (b)
+   (unless (get-buffer (cadr b))
+     (setf (cadr b) wconf-fallback-buffer-name
+           (cdr (assoc 'start b)) 1
+           (cdr (assoc 'point b)) 1
+           (cdr (assoc 'dedicated b)) nil)))
+ (defun wconf--sanitize-window-tree (node)
+   (let ((buf (assoc 'buffer node)))
+     (if buf                             ;in a leaf already
+         (wconf--sanitize-buffer buf)
+       (mapc (lambda (x)
+               (when (and (consp x)
+                          (memq (car x) '(leaf vc hc)))
+                 (wconf--sanitize-window-tree (cdr x))))
+             node))))
+ ;;;###autoload
+ (defun wconf-load (&optional filename)
+   "Load stored configurations from FILENAME, defaults to
+ `wconf-file'."
+   (interactive "f")
+   (let ((filename (or filename wconf-file)))
+     (unless (file-readable-p filename)
+       (error "wconf: Cannot read file %s" filename))
+     (wconf--reset)
+     (with-temp-buffer
+       (insert-file-contents filename)
+       (goto-char (point-min))
+       (setq wconf--configs
+             (mapcar
+              (lambda (f)                ;(wc name)
+                (wconf--sanitize-window-tree (car f))
+                (cons (wconf--copy (car f)) f))
+              (read (current-buffer)))))
+     (message "wconf: Load stored configurations from %s" filename))
+   (wconf--update-info))
+ ;; these functions affect the whole list of configs
+ ;;;###autoload
+ (defun wconf-create (&optional new)
+   "Clone the current configuration or create a new \"empty\" one.
+ The new configuration is appended to the list and becomes active.
+ With optional prefix argument NEW, or if there are no
+ configurations yet, create a new configuration from the current
+ window config."
+   (interactive "P")
+   (wconf--update-active-config)
+   (setq wconf--configs
+         (append wconf--configs
+                 (list
+                  (if (or new (not wconf--configs))
+                      (progn
+                        (message "wconf: Created new configuration %s"
+                                 (length wconf--configs))
+                        (list (wconf--current-config)
+                              (wconf--current-config)
+                              "new"))
+                    (wconf--ensure-configs 'current)
+                    (let ((wc (wconf- wconf--index)))
+                      (message "wconf: Cloned configuration %s"
+                               (wconf--to-string wconf--index))
+                      (list (wconf--copy (car wc))
+                            (wconf--copy (cadr wc))
+                            (cl-caddr wc)))))))
+   (wconf--use-config (1- (length wconf--configs))))
+ (defun wconf-kill ()
+   "Kill current configuration."
+   (interactive)
+   (wconf--ensure-configs 'current)
+   (let ((old-string (wconf--to-string wconf--index)))
+     (setq wconf--configs
+           (append (butlast wconf--configs
+                            (- (length wconf--configs) wconf--index))
+                   (last wconf--configs
+                         (- (length wconf--configs) wconf--index 1))))
+     (if wconf--configs
+         (wconf--use-config (if (< (1- (length wconf--configs)) wconf--index)
+                                (1- wconf--index)
+                              wconf--index))
+       (wconf--reset)
+       (wconf--update-info))
+     (message "wconf: Killed configuration %s" old-string)))
+ (defun wconf-swap (i j)
+   "Swap configurations at positions I and J."
+   (interactive
+    (progn
+      (wconf--ensure-configs 'current)   ;interactive?  then want current config
+      (list
+       wconf--index
+       (read-number "Swap current config with index: "))))
+   (wconf--ensure-configs)
+   (wconf--ensure-index i)
+   (wconf--ensure-index j)
+   (wconf--update-active-config)
+   (let ((wc (wconf- i)))
+     (setf (nth i wconf--configs) (wconf- j))
+     (setf (nth j wconf--configs) wc))
+   (when (memq wconf--index (list i j))
+     (wconf--use-config wconf--index))
+   (message "wconf: Swapped configurations %s and %s"
+            (number-to-string i) (number-to-string j)))
+ ;; manipulate single config
+ (defun wconf-rename (name)
+   "Rename current configuration to NAME."
+   (interactive
+    (progn
+      (wconf--ensure-configs 'current)
+      (list
+       (read-string "New window configuration name: "
+                    (cl-caddr (wconf- wconf--index))))))
+   (wconf--ensure-configs 'current)
+   (setf (cl-caddr (wconf- wconf--index)) name)
+   (message "wconf: Renamed configuration to \"%s\"" name)
+   (wconf--update-info))
+ ;; interaction b/w stored and active configs
+ ;; these commands only make sense when there are wconf--configs, and
+ ;; after wconf--index has become non-nil
+ (defsubst wconf--store (wc)
+   (setf (cadr wc) (wconf--copy (car wc))))
+ (defsubst wconf--restore (wc)
+   (setf (car wc) (wconf--copy (cadr wc))))
+ (defun wconf-store ()
+   "Store currently active configuration."
+   (interactive)
+   (wconf--ensure-configs 'current)
+   (wconf--update-active-config)
+   (wconf--store (wconf- wconf--index))
+   (message "wconf: Stored configuration %s" (wconf--to-string wconf--index)))
+ (defun wconf-store-all ()
+   "Store all active configurations."
+   (interactive)
+   (wconf--ensure-configs 'current)
+   (wconf--update-active-config)
+   (mapc #'wconf--store wconf--configs)
+   (message "wconf: Stored all configurations"))
+ (defun wconf-restore ()
+   "Restore stored configuration."
+   (interactive)
+   (wconf--ensure-configs 'current)
+   (wconf--restore (wconf- wconf--index))
+   (wconf--use-config wconf--index)
+   (message "wconf: Restored configuration %s" (wconf--to-string wconf--index)))
+ (defun wconf-restore-all ()
+   "Restore all stored configurations."
+   (interactive)
+   (wconf--ensure-configs 'current)
+   (mapc #'wconf--restore wconf--configs)
+   (wconf--use-config wconf--index)
+   (message "wconf: Restored all configurations"))
+ ;; change config
+ (defun wconf-switch-to-config (index &optional force)
+   "Change to current config INDEX."
+   (interactive "P")
+   (wconf--ensure-configs)
+   (let ((index (or index
+                    (read-number "Switch to config number: "))))
+     (wconf--ensure-index index)
+     ;; remember active config (w/o name etc)
+     (wconf--update-active-config)
+     ;; maybe use new configuration
+     (if (and (eq wconf--index index)
+              (not force))
+         (message "wconf: Nothing to do")
+       (wconf--use-config index)
+       (message "wconf: Switched to configuration %s"
+                (wconf--to-string index)))))
+ (defun wconf-use-previous ()
+   "Switch to previous window configuration."
+   (interactive)
+   (wconf--ensure-configs)
+   (wconf-switch-to-config (mod (1- (or wconf--index 1))
+                                (length wconf--configs))))
+ (defun wconf-use-next ()
+   "Switch to next window configuration."
+   (interactive)
+   (wconf--ensure-configs)
+   (wconf-switch-to-config (mod (1+ (or wconf--index -1))
+                                (length wconf--configs))))
+ (provide 'wconf)
+ ;;; wconf.el ends here