I use many workgroups in my workflow (package workgroups or workgroups2). If I switch to some workgroup and try to winner-undo I get window configuration from previous workgroup.
Can I use separate winner-undo history for each workgroup?
I've just pushed a commit in workgroups2.
Now winner-undo, winner-redo commands are remapped to workgroups' commands which do the same thing as winner but for each workgroup.
Upd. When I tested workgroups2 undo functionality last time, I made a mistake and misunderstood the behaviour of wg-undo-wconfig-change (wg-redo-wconfig-change). So, workgroups2 just has the ability to save the history of each workgroup like winner-mode does it. This solution can be used for workgroups package only.
I wrote this:
(defvar wg-winner-vars nil)
(defvar wg-winner-hash nil)
(setq wg-winner-vars '(winner-ring-alist
winner-currents
winner-point-alist
winner-undone-data
winner-undo-counter
winner-pending-undo-ring))
(setq wg-winner-hash (make-hash-table :test 'equal))
(defun wg-winner-put (winner-name)
(let ((wg (ignore-errors (wg-name (wg-current-workgroup)))))
(if wg
(puthash (list wg winner-name) (eval winner-name) wg-winner-hash))))
(defun wg-winner-get (winner-name)
(let ((wg (ignore-errors (wg-name (wg-current-workgroup)))))
(if wg
(eval `(setq ,winner-name (gethash '(,wg ,winner-name) wg-winner-hash))))))
(defun wg-winner-save ()
(if winner-mode
(progn
(winner-mode -1)
(defun wg-winner-mode-restore ()
(winner-mode 1)))
(defun wg-winner-mode-restore ()))
(mapcar 'wg-winner-put wg-winner-vars))
(defun wg-winner-load ()
(mapcar 'wg-winner-get wg-winner-vars)
(wg-winner-mode-restore))
(defadvice wg-switch-to-workgroup (before wg-winner-before activate)
(wg-winner-save))
(defadvice wg-switch-to-workgroup (after wg-winner-after activate)
(wg-winner-load))
If you're happy to write the elisp:
winner-ring-alist is an alist of window configuration rings keyed by frame (i.e. a ring of window configs per frame).
I would suggest you define a new alist keyed by frame and by the workgroup identifier (whatever that is), in which you can store the workgroup-specific winner config ring for each frame.
I imagine there's a "switch workgroup" hook you can then use to write the current frame's config ring to your alist (for the workgroup you're switching from), and then replace the ring for the current frame with your stored config for the workground you're switching to.
There's probably not much more to it. As long as winner sees the data it needs, it'll probably "just work".
If it works nicely, consider contributing the code back to the workgroups project.
Related
Is there a test to determine what type of hook is running -- e.g., window-configuration-change-hook?
For example, I have a function that contains conditions used determine whether it should be run -- it is used in conjunction with post-command-hook. I would like to use the same function for the window-configuration-change-hook, without triggering the first set of conditions:
(when
(or
(and
(not window-configuration-change-hook) ;; illustrative example only
(memq this-command this-last-command-inclusions)
(not multiple-cursors-mode))
window-configuration-change-hook) ;; illustrative example only
. . .
AFAIK there is no standardized method to determine which hook is running.
If you want to use advice or something like that to store this information you have to be aware that hooks can run within hooks. See the following example.
That means you need a stack-like structure to store that information.
EDIT: The example includes now a hook-stack storing the currently running hooks.
Note, I do not recommend to use this method since it is quite critical and does not work in all cases. Better, advice the mode/function or whatever what you want to identify.
(defvar hook-stack nil)
(defadvice run-hooks (around register-hooks activate)
"Store current hook into `hook-stack'."
(let ((hooks (ad-get-args 0)))
(loop for h in hooks do
(unwind-protect
(progn
(push h hook-stack)
(ad-set-args 0 (list h))
ad-do-it))
(pop hook-stack)
)))
(ad-remove-advice 'run-hooks 'around 'register-hooks)
(setq hooks1 nil)
(setq hooks2 nil)
(add-hook 'hooks1 (lambda () (message "Running hooks1, hook-stack: %S" hook-stack)))
(add-hook 'hooks2 (lambda () (message "Running hooks2") (run-hooks 'hooks1)
(message "Finishing hooks2, hook-stack: %S" hook-stack)))
(run-hooks 'hooks2)
Note: This does not work if run-hooks is called from C instaead of lisp. Furthermore, there are other functions like run-hook-with-args-until-success.
Once in a while I manually set the font-family and size different from the default, and I use buffer-face-mode to do it. (To be exact I use the mouse & pick one from the dialog box.) Once I set it, I'd like it to stay set for that buffer, even if I change modes, so I tried a customization. The idea was to add a change-major-mode-hook (which runs just before buffer-locals get killed) that would save the buffer face, if it is set, in a function to be called later- that much seems to work. But then that function seems to be called too soon, and when the mode change is over, buffer-face-mode is not active.
Here's the customization I cam up with so far
(defun my-preserve-bufface-cmmh ()
"Keep the state of buffer-face-mode between major-mode changes"
(if (and (local-variable-p 'buffer-face-mode) buffer-face-mode)
(delay-mode-hooks
(message "face is %s" buffer-face-mode-face) ; Just to show me it has the right face
(let ((my-inner-face buffer-face-mode-face))
(run-mode-hooks
(message "inner %s" my-inner-face) ; it still has the right face here
(setq buffer-face-mode-face my-inner-face)
(buffer-face-mode))))))
(add-hook 'change-major-mode-hook
'my-preserve-bufface-cmmh)
The messages both run and show a custom face, as they should, when I'm changing major-mode in a buffer with the minor-mode buffer-face-mode set. I had thought the combination of delay-mode-hooks ... run-mode-hooks would make setq buffer-face-mode-face ... (buffer-face-mode) run after the new mode was set up, but apparently not.
Is this customization "close"/salvageable for my wants? Is there a cleaner way?
The first thing is that delayed-mode-hooks is itself a local variable.
That means, if you set it by (delay-mode-hooks (run-mode-hooks ...)) in change-major-mode-hook
this will have no effect since it is killed instantaneously.
The second thing is that the stuff within your run-mode-hooks is
evaluated within my-preserve-bufface-cmmh. It should be defined as a back-quoted hook function
`(lambda () ...) where you splice in the values you want to keep.
The alternative would be to use lexical binding (which google).
The 2nd thing is demonstrated in the following example (to be evaluated step-by-step):
(defun test (str)
(let ((mytest (concat "hello " str)))
(add-hook 'my-own-hook `(lambda () (message "mytest:%S" ,mytest)))))
(test "you")
(run-hooks 'my-own-hook)
(test "world")
(run-hooks 'my-own-hook)
(put :myface 'test)
If you want to keep the font buffer-local you have to use a local variable that survives kill-all-local-variables such as buffer-file-name. You can hook a property there:
Edit: Even if the symbol has a buffer local value its properties are not buffer local. Thus, the previous approach did not work. Better: Create your own permanent buffer-local variable:
(defvar-local my-preserve-bufface nil
"Keep the state of buffer-face-mode between major-mode changes")
(put 'my-preserve-bufface 'permanent-local t)
(defun my-preserve-bufface-put ()
"Keep the state of buffer-face-mode between major-mode changes"
(and (local-variable-p 'buffer-face-mode)
buffer-face-mode
(setq my-preserve-bufface buffer-face-mode-face)))
(defun my-preserve-bufface-get ()
"Keep the state of buffer-face-mode between major-mode changes"
(and my-preserve-bufface
(setq buffer-face-mode-face my-preserve-bufface)
(buffer-face-mode)))
(add-hook 'change-major-mode-hook 'my-preserve-bufface-put)
(add-hook 'after-change-major-mode-hook 'my-preserve-bufface-get)
Thanks for the educational comments and especially the answer/example from #user2708138, which I am going to accept because it does answer the question.
Yet I am also going to answer my own question, since I did come up with working code that is a more general solution. I went down this path after finding I also wanted my font-size changes maintained and that they were from text-scale-mode, one more minor-mode to keep. This code reads a list of minor-modes to preserve, without my having to figure out which variables they use. (It isn't too hard for a human to figure them out, but I wanted to try having emacs do it).
Alas there is no function I know of to retrieve the variables used by a minor-mode, but the modes I'm interested in use the convention minor-mode-var-name, so this code just filters buffer-local variables for that pattern.
; Save & restore minor modes I wish to be "permanent" if set
(setq my-preserve-minor-modes '(buffer-face-mode text-scale-mode))
(defun my-preserve-minor-modes-cmmh ()
"Keep the state of desired-permanent minor modes between major-mode changes. Assumes that associated buffer-local minor-mode variables to save begin with `minor-mode-'"
(setq my-restore-minor-modes-acmmh nil)
(dolist (mm my-preserve-minor-modes)
(when (and (local-variable-p mm) (symbol-value mm))
(push mm my-restore-minor-modes-acmmh)))
(when my-restore-minor-modes-acmmh
(add-hook 'after-change-major-mode-hook 'my-restore-minor-modes-acmmh)
; Predicate-list showing if symbol starts with a preserved mode
(let ((mm-p-l `(lambda (locvar-nm)
(or ,#(mapcar (lambda (mm)
`(and (< ,(length (symbol-name mm))
(length locvar-nm))
(string-prefix-p ,(symbol-name mm)
locvar-nm)))
my-restore-minor-modes-acmmh)))))
; For each found minor mode, create fn to restore its buf-local variables
(dolist (locvar (buffer-local-variables))
(if (and (listp locvar) (funcall mm-p-l (symbol-name (car locvar))))
(push `(lambda()(setq ,(car locvar) ',(cdr locvar)))
my-restore-minor-modes-acmmh))))))
(defun my-restore-minor-modes-acmmh ()
"After major-mode change, restore minor-mode state, and remove self from hook. It restores state by calling the function stored in the variable my-restore-minor-modes-acmmh."
(remove-hook 'after-change-major-mode-hook 'my-restore-minor-modes-acmmh)
(dolist (restore-f my-restore-minor-modes-acmmh) (funcall restore-f)))
(add-hook 'change-major-mode-hook 'my-preserve-minor-modes-cmmh)
I did know about the permanent-local property but I wasn't sure about any unwanted side-effects... probably unwarranted paranoia on my part!
My answer could be improved if there is ever a way to get a list of variables for a minor mode, or by having the user specify an alist of variables per minor mode- in either case we wouldn't have to loop over buffer-local-variables anymore, and maybe simply making those all permanent-local would be all we need, simplifying the code quite a bit. Anyway, figuring all this out (with your help & looking at the fine manual) was quite educational.
I'm starting to use quite heavily the commands C-x r w and C-x r j to store windows configuration to registers and recall them at a later point, but I find a bit annoying that the cursor positions are stored as per the time when the window configuration was saved.
Basically I would like that the cursor positions are not stored (or are updated automatically), so that whenever I "jump" to a stored window configuration I get the same view as when I last visited it, not as when I created it.
Any ideas?
Ángel
I also found this very annoying and just coded up a solution. Store the window config using the normal functionality (current-window-configuration or window-configuration-to-register). Then, before applying the saved window config (or register),
Store the points of all open buffers.
Restore the window config.
Apply the stored points to the current windows.
The code below does this. You'll have to hook up restore-window-configuration to the register code yourself though.
(defun buffer-point-map ()
(save-excursion
(mapcar (lambda (buffer) (cons (buffer-name buffer)
(progn (set-buffer buffer) (point))))
(buffer-list))))
(defun apply-buffer-points (buff-point-map)
(mapc (lambda (window) (let* ((buffer (window-buffer window))
(buffer-point (cdr (assoc (buffer-name buffer) buff-point-map))))
(when buffer-point (set-window-point window buffer-point))))
(window-list))
nil)
(defun restore-window-configuration (window-config)
(let ((points (buffer-point-map)))
(set-window-configuration window-config)
(apply-buffer-points points)))
If you take a look into a source code
(defun window-configuration-to-register (register &optional arg)
...
(set-register register (list (current-window-configuration) (point-marker))))
you'll see that it stores a point as the second argument.
Just re-define it like
(defun my-window-configuration-to-register (register &optional arg)
(interactive "cWindow configuration to register: \nP")
(set-register register (list (current-window-configuration) nil)))
and redefine a C-x r w shortcut as well to use my-window-configuration-to-register
(define-key (current-global-map) (kbd "C-x r w") 'my-window-configuration-to-register)
Or define an advice
(defadvice window-configuration-to-register (after window-configuration-to-register-no-point activate)
"Avoid storing current buffer's position in the register. We want to stay on the last used position, not to jump to the saved one"
(set-register register (list (current-window-configuration) nil)))
The only problem is that it brings up an error message when you jump to it. You may redefine jump-to-register to avoid it
I'll add another answer which uses different approach.
Before you jump to another register you may store the current window configuration. This way it will store your latest buffers position just before you jump.
This will not work in all cases, however. For example if you just switch to another buffer or create a buffer with M-x dired or something then it will not store the current window config.
(defvar current-window-conf-register nil)
(defadvice window-configuration-to-register (after window-configuration-to-register-current-reg activate)
(setq current-window-conf-register register))
(defadvice jump-to-register (before jump-to-register-store-window-conf activate)
(if current-window-conf-register (window-configuration-to-register current-window-conf-register))
(setq current-window-conf-register register))
As a indirect answer to your question, you might consider using revive.el instead, which supports saving and restoring window configurations across Emacs restart, but doesn't (I believe) store the point.
There are plenty of ways to fold code in Emacs and I've settled in on using the outline minor mode... it works great!
However, I really want my folding to be persisted when I close and re-open files. It is quite frustrating to have folding set up in a file the way I like it, only to have that lost when I restart Emacs.
Has anyone found a way to keep the folding state of a file persistent?
Edit: Now that I understand the question...
How about something like the following nippet of code. It seems to work for me, though I haven't figured out how to avoid being prompted for the file local variable every time.
(defvar omm-state nil
"file local variable storing outline overlays")
(defun omm-state-mode (&optional arg)
"poor man's minor mode to re-apply the outline overlays "
(interactive)
(omm-re-enable-outline-state)
(add-hook 'before-save-hook 'omm-state-save))
(defun omm-get-all-overlays ()
"return a list of outline information for all the current buffer"
(save-excursion
(let ((all-overlays (overlays-in (point-min) (point-max))))
(mapcar (lambda (o)
(list (overlay-start o) (overlay-end o) (overlay-get o 'invisible)))
(reverse all-overlays)))))
(defun omm-re-enable-outline-state (&optional arg)
"turn on outline-minor-mode and re-apply the outline information"
(outline-minor-mode 1)
(when (listp omm-state)
(mapcar (lambda (p)
(apply 'outline-flag-region p))
omm-state)))
(defun omm-state-save ()
"save the outline state in a file local variable
Note: this just replaces the existing value, you need to start
it off by adding something like this to your file:
# Local Variables:
# omm-state:()
# mode:omm-state
# End:
"
(ignore-errors
(save-excursion
(goto-char (point-max))
(when (search-backward "omm-state:" nil t)
(goto-char (match-end 0))
(kill-sexp)
(princ (omm-get-all-overlays) (current-buffer)))))
nil)
This solution requires you "seeding" your file with something like:
# Local Variables:
# omm-state:()
# mode:omm-state
# End:
I realize this is an old post but FWIW I created a minor mode that complements hs-minor-mode, outline-mode etc. I also "really want my folding to be persisted when I close and re-open files". :)
The package is in MELPA as of today and called persistent-overlays.
It is also available directly on github: https://github.com/mneilly/Emacs-Persistent-Overlays
EDIT: I understand there is keyboard-quit (which is normally bound to C-g); but I'm more interested to know about how one deals with editing functions that come with Emacs (like in this case). I run into this kind of situations from time to time when I want to change just a little bit of some build-in functions.
In emacs, when you hit M-ESC ESC (or ESC three times), you can get out of a lots of situations like transient-mark, etc. But I habitually hit the escape key (I actually remap this to a single hit of the escape key) more than I intended, and that ends up killing my windows configuration, which is quite annoying. The function keyboard-escape-quit is defined in simple.el:
(defun keyboard-escape-quit ()
"Exit the current \"mode\" (in a generalized sense of the word).
This command can exit an interactive command such as `query-replace',
can clear out a prefix argument or a region,
can get out of the minibuffer or other recursive edit,
cancel the use of the current buffer (for special-purpose buffers),
or go back to just one window (by deleting all but the selected window)."
(interactive)
(cond ((eq last-command 'mode-exited) nil)
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(current-prefix-arg
nil)
((and transient-mark-mode mark-active)
(deactivate-mark))
((> (recursion-depth) 0)
(exit-recursive-edit))
(buffer-quit-function
(funcall buffer-quit-function))
((not (one-window-p t))
(delete-other-windows))
((string-match "^ \\*" (buffer-name (current-buffer)))
(bury-buffer))))
And I can see that I don't want the lines:
((not (one-window-p t))
(delete-other-windows))
But what is the best way to modify this function? I can see only two ways: 1) modify simple.el 2) copy this function to my .emacs file and do the modifications there. Both ways are not really good; ideally I would like to see something on the line of defadvice, but I can't see how I can do it in this case.
You could use around advice and redefine the offending function to do what you want (i.e. one-window-p should always return t):
(defadvice keyboard-escape-quit (around my-keyboard-escape-quit activate)
(let (orig-one-window-p)
(fset 'orig-one-window-p (symbol-function 'one-window-p))
(fset 'one-window-p (lambda (&optional nomini all-frames) t))
(unwind-protect
ad-do-it
(fset 'one-window-p (symbol-function 'orig-one-window-p)))))
This kind of acts like a (let ...) but has to be more complicated because you need to override a function for a limited scope instead of a variable.
I usually find that 'keyboard-quit (C-g) works to get out of all of those situations.
However, if you really want to have a variant of this function, I think that copying to your .emacs file (and renaming, I usually usa a prefix of bp) and making the edits there is probably the best option.
EDIT, in response to edit: In general, whenever I want an edited version of an emacs function, I either write it myself, or copy it to my .emacs, rename it bp-whotever and then do appropriate edits.
The downside of this is that my .emacs is HUGE, and probably extra-crufty with ancient functions that are nolonger used... the upside is that whenever I need to write something new, I've got tons of sample code to look at...
Here's another, simpler piece of advice that takes advantage of the fact that keyboard-escape-quit calls buffer-quit-function before closing windows:
(defadvice keyboard-escape-quit
(around keyboard-escape-quit-dont-close-windows activate)
(let ((buffer-quit-function (lambda () ())))
ad-do-it))
Works with Emacs 25.1. (I originally used #scottfrazer's advice, but it's unhappy in 25.1. Haven't bothered debugging yet.)
A single press of the Escape key, by default, acts as a Meta prefix key; that is, a keybinding which involves the Meta key.
Triple-pressing the Escape key will run keyboard-escape-quit, which is like keyboard-quit but with more of a "do what I mean" behaviour.
This code may help with your use case. You can use this in your Emacs init file:
;;; esc always quits
(define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit)
(define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit)
(global-set-key [escape] 'keyboard-quit)
I'm more interested to know about how one deals with editing functions that come with Emacs (like in this case). I run into this kind of situations from time to time when I want to change just a little bit of some build-in functions.
This is exactly the purpose for which I created the library el-patch. You would put this in your init-file:
(el-patch-defun keyboard-escape-quit ()
"Exit the current \"mode\" (in a generalized sense of the word).
This command can exit an interactive command such as `query-replace',
can clear out a prefix argument or a region,
can get out of the minibuffer or other recursive edit,
cancel the use of the current buffer (for special-purpose buffers),
or go back to just one window (by deleting all but the selected window)."
(interactive)
(cond ((eq last-command 'mode-exited) nil)
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(current-prefix-arg
nil)
((and transient-mark-mode mark-active)
(deactivate-mark))
((> (recursion-depth) 0)
(exit-recursive-edit))
(buffer-quit-function
(funcall buffer-quit-function))
(el-patch-remove
((not (one-window-p t))
(delete-other-windows)))
((string-match "^ \\*" (buffer-name (current-buffer)))
(bury-buffer))))
Here's a new way using cl-lib instead of cl which is now deprecated:
;; Make it so keyboard-escape-quit doesn't delete-other-windows
(defadvice keyboard-escape-quit
(around keyboard-escape-quit-dont-delete-other-windows activate)
(cl-letf (((symbol-function 'delete-other-windows)
(lambda () nil)))
ad-do-it))
You'll need to make sure prior to it you have called:
(require 'cl-lib)