[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 43c8384 11/11: Add 'packages/wconf/' from commit 'b8ea22f8
From: |
Ingo Lohmar |
Subject: |
[elpa] master 43c8384 11/11: Add 'packages/wconf/' from commit 'b8ea22f80bff19222136d9495f685888dc682b9d' |
Date: |
Sun, 02 Aug 2015 14:43:06 +0000 |
branch: master
commit 43c83842f7f4e1fa4c7991f80ef34bf569effc79
Merge: 6340c15 b8ea22f
Author: Ingo Lohmar <address@hidden>
Commit: Ingo Lohmar <address@hidden>
Add 'packages/wconf/' from commit 'b8ea22f80bff19222136d9495f685888dc682b9d'
git-subtree-dir: packages/wconf
git-subtree-mainline: 6340c15fb07a979b6c7bd2e8913e499091c257ff
git-subtree-split: b8ea22f80bff19222136d9495f685888dc682b9d
---
packages/wconf/.gitignore | 1 +
packages/wconf/README.org | 108 ++++++++++++++
packages/wconf/wconf.el | 343 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 452 insertions(+), 0 deletions(-)
diff --git a/packages/wconf/.gitignore b/packages/wconf/.gitignore
new file mode 100644
index 0000000..c531d98
--- /dev/null
+++ b/packages/wconf/.gitignore
@@ -0,0 +1 @@
+*.elc
diff --git a/packages/wconf/README.org b/packages/wconf/README.org
new file mode 100644
index 0000000..a13d508
--- /dev/null
+++ b/packages/wconf/README.org
@@ -0,0 +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 --git a/packages/wconf/wconf.el b/packages/wconf/wconf.el
new file mode 100644
index 0000000..57add63
--- /dev/null
+++ b/packages/wconf/wconf.el
@@ -0,0 +1,343 @@
+;;; wconf.el --- Minimal window layout manager -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+;; Author: Ingo Lohmar <address@hidden>
+;; 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
- [elpa] master 5f9ee38 04/11: Check file can be read, (continued)
- [elpa] master 5f9ee38 04/11: Check file can be read, Ingo Lohmar, 2015/08/02
- [elpa] master dcce243 03/11: Update README and docstring, Ingo Lohmar, 2015/08/02
- [elpa] master 3bc4e13 02/11: Add a README, Ingo Lohmar, 2015/08/02
- [elpa] master facc191 01/11: Initial commit, Ingo Lohmar, 2015/08/02
- [elpa] master 8f3160b 07/11: Fix missing dependency on cl-lib, Ingo Lohmar, 2015/08/02
- [elpa] master 2cb1de6 06/11: Fix/enhance interactive declarations, this fixes #1, Ingo Lohmar, 2015/08/02
- [elpa] master e5f23d1 05/11: Only deal with window configurations now, update doc, Ingo Lohmar, 2015/08/02
- [elpa] master b8ea22f 10/11: Bump version and copyright, Ingo Lohmar, 2015/08/02
- [elpa] master ae106c9 08/11: Make interactive functions more robust/friendly, Ingo Lohmar, 2015/08/02
- [elpa] master f67e959 09/11: Use "wconf" as package and filename prefix, Ingo Lohmar, 2015/08/02
- [elpa] master 43c8384 11/11: Add 'packages/wconf/' from commit 'b8ea22f80bff19222136d9495f685888dc682b9d',
Ingo Lohmar <=