A smarter alternative to delete-window? - emacs

Sometimes I get multiple windows open for the same buffer (or a similar one) and I have to differentiate whether or not the buffer in the window is the same as another before deciding to either kill it or delete the window.
Is there a way in emacs to simply delete a window only if the buffer exists already in another? Ideally I would like the same function to also kill the buffer and the window if it is the only instance of the buffer in a window.

(defun delete-extra-windows ()
(interactive)
(let* ((selwin (selected-window))
(buf (window-buffer selwin)))
(walk-windows (lambda (ww)
(unless (eq ww selwin)
(when (eq (window-buffer ww) buf)
(delete-window ww))))
'NO-MINI 'THIS-FRAME)))

I added quit-window (normally bound to q in non-self-insert - AKA special - buffers) 15 years ago to solve a similar problem.
You can try it or its sibling quit-windows-on.

Your specification of what you wanted is not clear. You said "delete a window only if the buffer exists already in another". That means do not delete the window if the buffer does not exist in another window. Yet you also said "kill the buffer and the window if it is the only instance of the buffer in a window", which contradicts the first requirement.
I guess by "delete a window only if..." you really meant "delete only the window (not also the buffer) if...".
(defun delete-window-maybe-kill-buffer ()
"Delete selected window.
If no other window shows its buffer, kill the buffer too."
(interactive)
(let* ((selwin (selected-window))
(buf (window-buffer selwin)))
(delete-window selwin)
(unless (get-buffer-window buf 'visible) (kill-buffer buf))))

This is the behavior I was looking for. Thank you for helping with the basic function layour and logic. Elisp is still very confusing to work with and I appreciate help with getting through the rough spots.
See the code somment which explains the behavior. You should also be able to understand it directly from the source.
I've up-voted your previous answer which includes the bulk of the code I used.
;;; Delete the selected window without killing the buffer if the buffer is open
;;; in another; otherwise close the window and its buffer. If called on the
;;; last visible window then the buffer will simply be killed and replaced by
;;; the next available buffer.
(defun delete-window-maybe-kill-buffer ()
"Delete selected window.
If no other window shows its buffer, kill the buffer too."
(interactive)
(let* ((selwin (selected-window))
(buf (window-buffer selwin)))
(if (> (length (window-list)) 1)
(delete-window selwin)
(unless (get-buffer-window buf 'visible) (kill-buffer buf))
(kill-buffer buf))))

Related

Emacs/GDB: always display source in specific window with gdb-many-windows

I use GDB in Emacs 24 with gdb-many-windows set to t, usually in its own frame. I like to have a separate editing frame. It looks like this (apologies for my crude ASCII diagram):
+-------------+-------------+
| gdb | locals |
+-------------+-------------+
| source | I/O |
| | |
+-------------+-------------+
| stack | breakpoints |
+-------------+-------------+
This works pretty well except for one big problem. Whenever gdb needs to display a different source buffer, e.g., after up/down/step, it doesn't always show it in the "source" window. For example, if I have the same buffer open in a window in a different frame, it will raise that frame while keeping keyboard focus in the gdb frame. This is really annoying on a single-monitor setup when the frames cover each other.
I'd like gdb to always use the source window in the gdb-many-windows setup to display source, no matter if the same source buffer is displayed elsewhere. How can I do that?
EDIT: More detailed instructions to reproduce. I'm using Emacs 24.2.1 with GDB 7.5-ubuntu. I've seen this problem on Ubuntu 10.04 and Linux Mint Nadia with Cinnamon.
Evaluate this expression: (setq gdb-many-windows t)
Compile a C program with at least two files.
For example:
// foo.c
void bar(int);
void foo(int c) {
if (c > 0)
bar(c - 1);
}
int main(void) {
foo(100);
return 0;
}
// bar.c
void foo(int c);
void bar(int c) {
if (c > 0)
foo(c - 2);
}
// compile with gcc -g -O0 foo.c bar.c -o test
Let bar.c be displayed in the main frame. Open a new frame with M-x 5 2. In that frame, start gdb with M-x gdb. There should be six windows in that frame as shown above. Position the gdb frame on top of the source frame.
Set a breakpoint in main and step through calls to foo and bar. When bar is called, the main frame will be raised over the gdb frame since bar.c is already visible there, but keyboard focus will stay in the gdb frame.
I think the problem function is gdb-display-source-buffer in gud.el.gz. I'm planning to try overriding this with defadvice, but I'm not really familiar with advice. If I figure it out, I'll post an answer here.
The function causing this problem is actually gud-display-line in gud.el.gz. This function is responsible for positioning the overlay arrow in the source window on the current line and making sure it is visible. Here's the logic:
(let* ...
(window (and buffer
(or (get-buffer-window buffer)
(if (eq gud-minor-mode 'gdbmi)
(or (if (get-buffer-window buffer 'visible)
(display-buffer buffer nil 'visible))
(unless (gdb-display-source-buffer buffer)
(gdb-display-buffer buffer nil 'visible))))
(display-buffer buffer))))
I used defadvice to override the whole function; basically, I copied the source and changed the window selection logic.
(defadvice gud-display-line (around do-it-better activate)
(let* ...
(window (and buffer
(or (if (eq gud-minor-mode 'gdbmi)
(unless (gdb-display-source-buffer buffer)
(gdb-display-buffer buffer nil 'visible)))
(get-buffer-window buffer)
(display-buffer buffer))))
...)
Obviously not the most elegant solution. It also doesn't help when switching frames (with up/down/frame), so I'll edit this when I figure that out.
I have 24.3. And I cannot reproduce the problem with this version.
There gud-display-line looks as follows:
(defun gud-display-line (true-file line)
(let* ((last-nonmenu-event t) ; Prevent use of dialog box for questions.
(buffer
(with-current-buffer gud-comint-buffer
(gud-find-file true-file)))
(window (and buffer
(or (get-buffer-window buffer)
(display-buffer buffer))))
(pos))
(when buffer
(with-current-buffer buffer
(unless (or (verify-visited-file-modtime buffer) gud-keep-buffer)
(if (yes-or-no-p
(format "File %s changed on disk. Reread from disk? "
(buffer-name)))
(revert-buffer t t)
(setq gud-keep-buffer t)))
(save-restriction
(widen)
(goto-char (point-min))
(forward-line (1- line))
(setq pos (point))
(or gud-overlay-arrow-position
(setq gud-overlay-arrow-position (make-marker)))
(set-marker gud-overlay-arrow-position (point) (current-buffer))
;; If they turned on hl-line, move the hl-line highlight to
;; the arrow's line.
(when (featurep 'hl-line)
(cond
(global-hl-line-mode
(global-hl-line-highlight))
((and hl-line-mode hl-line-sticky-flag)
(hl-line-highlight)))))
(cond ((or (< pos (point-min)) (> pos (point-max)))
(widen)
(goto-char pos))))
(when window
(set-window-point window gud-overlay-arrow-position)
(if (eq gud-minor-mode 'gdbmi)
(setq gdb-source-window window))))))
The window setting is completely different from yours. Maybe, the above code is helpful or maybe you should upgrade to the new gud/gdb stuff.
I run Emacs 24.5 and for me this is still an issue. I manage my windows manually with dedicated windows now, mainly with the following function:
(defun gdb-restore-windows-gud-io-and-source ()
"Restore GUD buffer, IO buffer and source buffer next to each other."
(interactive)
;; Select dedicated GUD buffer.
(switch-to-buffer gud-comint-buffer)
(delete-other-windows)
(set-window-dedicated-p (get-buffer-window) t)
(when (or gud-last-last-frame gdb-show-main)
(let ((side-win (split-window nil nil t))
(bottom-win (split-window)))
;; Put source to the right.
(set-window-buffer
side-win
(if gud-last-last-frame
(gud-find-file (car gud-last-last-frame))
(gud-find-file gdb-main-file)))
(setq gdb-source-window side-win)
;; Show dedicated IO buffer at the bottom.
(set-window-buffer
bottom-win
(gdb-get-buffer-create 'gdb-inferior-io))
(set-window-dedicated-p bottom-win t))))
This shows the GUD window at the top left, the IO buffer at the bottom left and sets the source buffer to the right side. The GUD and the IO buffer are set to dedicated.

How can one quickly browse through lots of files in Emacs?

is there a way to quickly browse through lots of files in Emacs (24.3)? More specifically:
Let's assume an Emacs frame is split into two windows. Suppose focus is in the left window that has an open 'dired' buffer with lots of text files (or code). I would like to go up and down the list of files (e.g. with cursor keys), while at the same time the current file is shown in the right window. Even better the file is only viewed and closed once I move in the dired buffer to the next file. This would be very useful especially together with some 'omit' mode.
Can this be done in 'dired'? I also coudn't find this functionality in dired-x or in sunrise-commander. Is it possible?
The best candidates I tried already (and why they not solve the problem):
'v' which shows the current file, but also moves the attention
'C-o' which shows the current file, but after moving up or down, I have to press C-o again, also it generates lots of buffers
Thanks a lot for your help!
A simple and generic (while not optimum) solution could be via the C-x () mechanism.
First open the two panes in Emacs, with - say - top one being dired.
Press o to open the first file in the 2nd pane.
Then you can start the repetition mechanism:
do C-x ( to start recording a macro
do C-x k and return to close the buffer
do o again to go back to dired
do down key to go to next file
do o to open next file in bottom pane
do C-x ) to end the macro
From that point (being in bottom pane, dired in top pane), doing a mere
C-x e (and then only e if there is no other operation in between)
will automatically
close bottom pane file, go to top pane, down to next file, open it in bottom pane
There is maybe a more specific way to do that, but knowing the macro mechanism is anyway very helpful in Emacs.
Here's how I do this with view-mode:
(add-hook 'view-mode-hook
(lambda()
(define-key view-mode-map (kbd "n") 'dired-view-next)
(define-key view-mode-map (kbd "p") 'dired-view-prev)))
(defun dired-view-next ()
"Move to next dired line and view ."
(interactive)
(quit-window)
(dired-next-line 1)
(dired-view-file))
(defun dired-view-prev ()
"Move to next dired line and view ."
(interactive)
(quit-window)
(dired-next-line -1)
(dired-view-file))
UPD:
This one has two panes:
(defun dired-view-next-pane ()
(interactive)
(other-window 1)
(if view-mode
(kill-buffer))
(other-window -1)
(dired-next-line 1)
(view-file-other-window
(dired-get-file-for-visit))
(other-window -1))
Thanks a lot for all those answers. Summarizing I created the following solution (extending the answer of "abo-abo"):
;; little modification to dired-mode that let's you browse through lots of files
(add-hook 'dired-mode-hook
(lambda()
(define-key dired-mode-map (kbd "C-o") 'dired-view-current) ; was dired-display-file
(define-key dired-mode-map (kbd "n") 'dired-view-next) ; was dired-next-line
(define-key dired-mode-map (kbd "p") 'dired-view-previous))) ; was dired-previous-line
(defun dired-view-next ()
"Move down one line and view the current file in another window."
(interactive)
(dired-next-line)
(dired-view-current))
(defun dired-view-previous ()
"Move up one line and view the current file in another window."
(interactive)
(dired-previous-line)
(dired-view-current))
(defun dired-view-current ()
"View the current file in another window (possibly newly created)."
(interactive)
(if (not (window-parent))
(split-window)) ; create a new window if necessary
(let ((file (dired-get-file-for-visit))
(dbuffer (current-buffer)))
(other-window 1) ; switch to the other window
(unless (equal dbuffer (current-buffer)) ; don't kill the dired buffer
(if (or view-mode (equal major-mode 'dired-mode)) ; only if in view- or dired-mode
(kill-buffer))) ; ... kill it
(let ((filebuffer (get-file-buffer file)))
(if filebuffer ; does a buffer already look at the file
(switch-to-buffer filebuffer) ; simply switch
(view-file file)) ; ... view it
(other-window -1)))) ; give the attention back to the dired buffer
Three keys are changed:
C-o to view the current item in another window (possibly create one).
n to view the next item in another window.
p to view the previous item in another window.
This can be used in a dired buffer. Note that only dired-mode buffers and view-mode buffers get killed while moving up and down. If a file is shown that another buffer is already visiting (not in view-mode), that buffer is shown as well, but not killed when moving to the next. Another subtlety is the case when the passively shown buffer is the dired buffer used for going through the list (this can easily happen, when going inside a folder with RET). To handle this case, we first check whether we are trying to kill the initial dired buffer.
Load Icicles.
Define this command:
(defun my-find-file ()
"Like `icicle-find-file', but alt action views file temporarily.
Alternate action keys such as `C-S-down' visit the candidate file in
`view-mode' and kill the buffer of the last such viewed candidate."
(interactive)
(let ((icicle-candidate-alt-action-fn
(lambda (file)
(when (and my-last-viewed
(get-file-buffer my-last-viewed))
(kill-buffer (get-file-buffer my-last-viewed)))
(setq my-last-viewed (abbreviate-file-name file))
(view-file file)
(select-frame-set-input-focus
(window-frame (active-minibuffer-window))))))
(icicle-find-file-of-content)))
(defvar my-last-viewed nil
"Last file viewed by alternate action of `my-find-file'.")
Then you can:
Use M-x my-find-file (or bind it to a key - e.g., C-x C-f).
Optionally type part of a file name, to limit the matching names.
Optionally use down or up to cycle among file names.
Use C-S-down to visit the next file in order.
Repeat #4 to see other files in order.
Repeat #2 or #3 to see other sets of files.
End with RET to choose a file to visit or C-g to cancel.
Each file buffer you visited with C-S-down was killed when you
viewed the next one. You can also mix in C-down or C-RET to
also visit files whose buffers you do not want to kill
automatically. (Change view-file to find-file if you don't
want to visit in view-mode, which is read-only.)
[By default, the alternate action for icicle-find-file is
icicle-alt-act-fn-for-type, which prompts you for a file-
appropriate action to use on the particular candidate chosen for
the action. Command my-find-file just substitutes a different
alternate action function (for all candidates you choose).]
See also this thread from help-gnu-emacs#gnu.org. It is pretty much the same question as yours, I think. My replies there were pretty much the same as my reply here, but there are also replies from others that might help you as well.
Try
M-x speedbar
That might appeal to you
Another view-mode solution on top of ag-mode lists. I couldn't find a question for ag-mode, maybe this helps someone generalize a ffap-preview for any mode.
(defun directory-ag-results ()
(save-excursion
(goto-char (point-min))
(search-forward "\"")
(setq a (point))
(search-forward "\"")
(setq b (- (point) 1))
(buffer-substring-no-properties a b)))
(defun search-item-path ()
(let ((dir (directory-ag-results))
(file-parts (split-string (substring-no-properties (thing-at-point 'filename)) ":")))
(concat dir (nth 0 file-parts))))
(defun search-item-line ()
(let ((file-parts (split-string (substring-no-properties (thing-at-point 'filename)) ":")))
(- (string-to-number (nth 1 file-parts)) 1)))
(defun view-current ()
"Quickly view the current file in another window."
(if (not (window-parent))
(split-window)) ; create a new window if necessary
(let ((file (search-item-path))
(line (search-item-line))
(dbuffer (current-buffer)))
(other-window 1) ; switch to the other window
(unless (equal dbuffer (current-buffer)) ; don't kill the dired buffer
(if (or view-mode (equal major-mode 'dired-mode)) ; only if in view- or dired-mode
(kill-buffer))) ; ... kill it
(let ((filebuffer (get-file-buffer file)))
(if filebuffer ; does a buffer already look at the file
(switch-to-buffer filebuffer) ; simply switch
(progn
(view-file file) ; ... view it
(goto-char (point-min))
(next-line line)))
(other-window -1))))
(defun next-view-current ()
(interactive)
(next-line)
(view-current))
(defun previous-view-current ()
(interactive)
(previous-line)
(view-current))
(define-key ag-mode-map (kbd "M-p") 'previous-view-current)
(define-key ag-mode-map (kbd "M-n") 'next-view-current)
This is the one thing I think Sublime does better than Emacs. Blasphemy, I know! I like the "q to exit" feel of view-mode, rather than timer-based solutions, and like scrolling around a previewed file. This snippet navigates to the line number found in the search results, optimizing for browsing speed.
Note about the code: I tried polyfilling vc-root-dir from Emacs 25, but it doesn't really make sense for ag-mode since ag-mode's buffer is outside the repo you're searching in. I ended up pulling the root dir from the top of the "ag search" buffer.
Early stages. Improvements welcome.
Demo
Edit: It works for ag-mode, not dired. Demo gif.
Credits: abo-abo, user2979331
In the interest of keeping StackOverflow up to date, the package peep-dired does everything posted in the other answers, and I’m sure there are other packages as well. You don’t have to maintain copypasted or home-rolled lisp for this job.

Select the previously-selected window in emacs

I need an emacs built-in function or elisp function that can take me to the previously-selected window. I thought that (select-window (get-lru-window)) would do it, but if I run it several times, seems to just cycle between windows instead of swapping back and forth between them, which is what I expect.
Any other ideas?
There doesn't seem to be a way to get the most recently selected window in emacs (as opposed to the least recently used returned by get-lru-window). Internally emacs tracks use_time on windows, and get-lru-window uses that to find the "oldest" window. But unfortunately that is not exposed in elisp.
The window list is ordered in cyclic window ordering which doesn't help in your case.
The buffer-list is however ordered most-to-least recently used buffer (or not really strictly, there is a (bury-buffer) function to move a buffer last).
This means that, if you can transform your problem into something like "how can I switch to the buffer in a different window that was most recently the selected buffer", it should be possible.
One way would be to do something like this:
(defun switch-to-previous-buffer-in-a-different-window ()
(interactive)
(let* ((otherbuf (other-buffer (current-buffer) t))
(otherwin (get-buffer-window otherbuf)))
(if otherwin
(select-window otherwin)
(message "Last buffer (%s) is not currently visible" (buffer-name otherbuf)))))
Or the shorter, and more featureful:
(defun switch-to-previous-buffer-possibly-creating-new-window ()
(interactive)
(pop-to-buffer (other-buffer (current-buffer) t)))
Here other-buffer is used to get the most recently used buffer (except current-buffer). This should work fine as long as you don't switch buffers in the windows, because then other-buffer will no longer return the buffer in the other window, but the buffer you switched from in current window.
So instead of using other-buffer lets walk the buffer-list ourself to find the best candidate:
(defun switch-to-the-window-that-displays-the-most-recently-selected-buffer ()
(interactive)
(let* ((buflist (buffer-list (selected-frame))) ; get buffer list in this frames ordered
(buflist (delq (current-buffer) buflist)) ; if there are multiple windows showing same buffer.
(winlist (mapcar 'get-buffer-window buflist)) ; buf->win
(winlist (delq nil winlist)) ; remove non displayed windows
(winlist (delq (selected-window) winlist))) ; remove current-window
(if winlist
(select-window (car winlist))
(message "Couldn't find a suitable window to switch to"))))
Hope this helps.
If the last window switch was done programmatically, then it is possible to select the previously selected window.
(defun your-function ()
(interactive)
(let ((sw (selected-window)))
(do-something-useful-and-switch-window)
(select-window sw)))
If the last window switch was done manually, then it should be possible to overload the window switching command to update a global list of window selection order, which is then used to switch back.
(defun gs/pop-to-previous-window ()
(interactive)
(let ((win (get-mru-window t t t)))
(select-window win)))

Windows configuration to registers

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.

kill the associated buffer when close the emacs-client frame by pressing Alt+F4

I get used to emacsclient for the speedy response like vim, by putting emacs into sever mode with command "emacs --daemon". But I found it quite annoying that lots of buffers kept alive when I viewed some files and then closed them by pressing Alt+F4. I have to kill the buffer explicitly before closing the frame.
I want to know, if there is a way to make emacsclient behave more like a lightweight GUI editor(e.g. vim) in this point?
I think you're asking for trouble, but you you could try this:
(add-hook 'delete-frame-functions
(lambda (frame)
(let* ((window (frame-selected-window frame))
(buffer (and window (window-buffer window))))
(when (and buffer (buffer-file-name buffer))
(kill-buffer buffer)))))
I suggest that you use the command quit-window which does precisely what you want (with the prefix argument); it is already the binding for q in special-mode (i.e., not self-insert) buffers. You can bind it to, say, C-f4, and it will kill the buffer and the frame when you type C-u C-f4.
Do something like the following:
(defun my-kill-buffer-and-frame ()
"kill the current buffer and the current frame"
(interactive)
(when (y-or-n-p "Are you sure you wish to delete the current frame?")
(kill-buffer)
(delete-frame)))
If you're sure you always wnt to do it, you can get rid of the prompt:
(defun my-kill-buffer-and-frame ()
"kill the current buffer and the current frame"
(interactive)
(kill-buffer)
(delete-frame))
Then bind it to a key of your choice, like so:
(global-set-key [(f5)] 'my-kill-buffer-and-frame)
Enjoy!