Using split-window-below in Emacs. How to get buffer of bottom window? - emacs

For instance, when editing an Emacs lisp file test.el, I would like to mark a text (for instance the name of a function) in the current buffer (I am using a single frame with a single window) then press a keyboard shortcut to split the current window into two parts, one above the other. The top window should display my orginal buffer as it was, the bottom window should display the buffer at the point where the function is defined (like (defun test-fun ...).
I have tried this code:
(defun test-split ()
(interactive)
(split-window-below 28)
(let (( w (next-window)))
(let ((buf (window-buffer w)))
(with-current-buffer buf
(beginning-of-buffer)
(re-search-forward "defun test-fun ")))))
but it does not work.. (It searches the top window, not the bottom window)

The two windows are displaying the same buffer, so with-current-buffer is valid for the original window, and hence there is no need for Emacs to switch to another window.
You could modify your code like so:
(defun test-split ()
(interactive)
(split-window-below 28)
(save-selected-window
(select-window (next-window))
(beginning-of-buffer)
(re-search-forward "defun test-fun ")))

Related

How to resize the undo-tree-visualizer window?

I have been trying to automatically resize the undo-tree-visualizer window whenever it opens up in emacs. To fix this I have made a hook, the hook does resize the window however once the hook is ran the cursor ends up on the *undo-tree* window, to solve this I have to use the (save-selected-window) command however it did not work. I then decided to try and force emacs to go back to the previous window by using the (evil-window-left) function (I am using evil mode) however it only results in giving me this error:
Error:
Debugger entered--Lisp error: (wrong-number-of-arguments (1 . 1) 0)
evil-window-left()
(if (not (get-buffer-window " *undo-tree*")) nil (select-window (get-buffer-window " *undo-tree*")) (evil-window-set-width 45) (evil-window-left))
undo-tree-hook()
run-hooks(change-major-mode-after-body-hook special-mode-hook undo-tree-visualizer-mode-hook)
apply(run-hooks (change-major-mode-after-body-hook special-mode-hook undo-tree-visualizer-mode-hook))
run-mode-hooks(undo-tree-visualizer-mode-hook)
undo-tree-visualizer-mode()
Code:
(defun undo-tree-hook ()
"Hook to make the undo-tree-visualizer window to automatically resize to become smaller"
(interactive)
(unless (not (get-buffer-window undo-tree-visualizer-buffer-name))
(select-window (get-buffer-window undo-tree-visualizer-buffer-name))
(evil-window-set-width 45)
(evil-window-left)))
(add-hook 'undo-tree-visualizer-mode-hook 'undo-tree-hook)
As Drew said, I needed to pass a parameter to the evil-window-left function and I also used save-excursion to save the point where I was on in the current buffer before I executed to the commands to switch to and resize the *undo-tree* buffer.
(defun undo-tree-hook ()
"Hook to make the undo-tree-visualizer window to automatically resize to become smaller"
(interactive)
(unless (not (get-buffer-window " *undo-tree*"))
(save-excursion
(select-window (get-buffer-window " *undo-tree*"))
(evil-window-set-width 45)
(evil-window-left 1))))
(add-hook 'undo-tree-visualizer-mode-hook 'undo-tree-hook)

how to show buffer content in real time in other window when focus is in buffer list

I have 2 windows, one is the buffer list, how do I show the buffer in the buffer list in the other windows when I use n and p to naviage in the buffer list?
Thanks a lot.
If I understand the question right, the answer is simply to use C-o in the buffer menu. that displays the buffer of the line you are on in another window, but it does not select that buffer. That is, it does not move the input focus to that buffer's window. The buffer list retains the input focus, so you can continue to use C-o on different lines, to display different buffers.
If you mean an Emacs window when you say "frame", then see above. The same is true even if the buffers are on different frames, with this caveat: Some window managers, including MS Windows, change the input focus to a new frame that is created. So if, for example, you have non-nil pop-up-frames (so that a separate frame is used to display a buffer), and if the buffer you choose to display (using C-o) is not already shown in some other frame, then displaying it not only creates a new frame for it but also shifts the focus to that new frame. If the buffer is already visible in another frame then C-o simply raises that frame.
There is currently no key bound in the buffer-list display, to both (a) move the cursor down or up to the next or previous buffer line and (b) invoke the C-o behavior of displaying the buffer named on the target buffer line. But you could easily define such a command and bind it to a key:
(defun show-next (arg)
"Show next line's buffer in another window."
(interactive "p")
(next-line arg)
(Buffer-menu-switch-other-window))
(defun show-previous (arg)
"Show previous line's buffer in another window."
(interactive "p")
(previous-line arg)
(Buffer-menu-switch-other-window))
(define-key Buffer-menu-mode-map "\M-n" 'show-next)
(define-key Buffer-menu-mode-map "\M-p" 'show-previous)
Here's my prototype for this feature in a dired buffer. It kills a buffer after it got visited.
This is a feature I like from the ranger file manager, it is handy when you explore a directory.
(setq show-next-current-buffer nil)
(defun show-next ()
(interactive)
(next-line 1)
(dired-find-file-other-window)
(if show-next-current-buffer (kill-buffer show-next-current-buffer))
(setq show-next-current-buffer (current-buffer))
(other-window 1)
)
(defun show-previous ()
(interactive)
(previous-line 1)
(dired-find-file-other-window)
(if show-next-current-buffer (kill-buffer show-next-current-buffer))
(setq show-next-current-buffer (current-buffer))
(other-window 1)
)
(define-key dired-mode-map "n" 'show-next)
(define-key dired-mode-map "p" 'show-previous)
edit: I've written a minor mode to enable/disable this feature easily. See https://gitlab.com/emacs-stuff/my-elisp/blob/master/dired-show.el and meld it to your needs.

How to customize Emacs split-window-X with the new window showing the next buffer?

In Emacs 21.x I don't know if via a specific customization of split-window or due to a different default behaviour by Emacs, invoking the split-window-below besides splitting the window, it switched the buffer in the non-focused window to the next buffer.
Currently (Emacs 24.x), the split-window and siblings split-window-below and split-window-right don't seem to allow such a customization. Is this true?
If so, how to tweak Emacs to have this behaviour? Redefining split-window or split-window-below and split-window-right to have an extra step of switching to the next on the non-focused window. This could be done with advices:
(defun split-window-and-next-buffer (new-window)
(let ((old-window (selected-window)))
(select-window new-window)
(next-buffer)
(select-window old-window)
new-window))
(defadvice split-window-right (after split-window-right-and-next-buffer
activate protect compile)
(split-window-and-next-buffer ad-return-value))
(defadvice split-window-below (after split-window-bellow-and-next-buffer
activate protect compile)
(split-window-and-next-buffer ad-return-value))
With the corrections indicated by lawlist which are already available above the advices already work and I get the intended behaviour, but it isn't customizable to have the old behaviour.
In response to the question, the original poster might want try changing the spelling of the word below within the code posted.
This function adds three lines of code (at the end) to the current version of Emacs Trunk split-window-below and renames the function to lawlist-split-window-below with a defalias. One closing parentheses was moved to the end of the function to permit using two of the let bindings defined farther up in the function. If the user wants focus in the new-window (after exiting the function) instead, then just remove the last line of code (select-window old-window).
(defun lawlist-split-window-below (&optional size)
"Split the selected window into two windows, one above the other.
The selected window is above. The newly split-off window is
below, and displays the 'next-buffer'. Return the new window.
If optional argument SIZE is omitted or nil, both windows get the
same height, or close to it. If SIZE is positive, the upper
\(selected) window gets SIZE lines. If SIZE is negative, the
lower (new) window gets -SIZE lines.
If the variable `split-window-keep-point' is non-nil, both
windows get the same value of point as the selected window.
Otherwise, the window starts are chosen so as to minimize the
amount of redisplay; this is convenient on slow terminals."
(interactive "P")
(let ((old-window (selected-window))
(old-point (window-point))
(size (and size (prefix-numeric-value size)))
moved-by-window-height moved new-window bottom)
(when (and size (< size 0) (< (- size) window-min-height))
;; `split-window' would not signal an error here.
(error "Size of new window too small"))
(setq new-window (split-window nil size))
(unless split-window-keep-point
(with-current-buffer (window-buffer)
;; Use `save-excursion' around vertical movements below
;; (Bug#10971). Note: When the selected window's buffer has a
;; header line, up to two lines of the buffer may not show up
;; in the resulting configuration.
(save-excursion
(goto-char (window-start))
(setq moved (vertical-motion (window-height)))
(set-window-start new-window (point))
(when (> (point) (window-point new-window))
(set-window-point new-window (point)))
(when (= moved (window-height))
(setq moved-by-window-height t)
(vertical-motion -1))
(setq bottom (point)))
(and moved-by-window-height
(<= bottom (point))
(set-window-point old-window (1- bottom)))
(and moved-by-window-height
(<= (window-start new-window) old-point)
(set-window-point new-window old-point)
(select-window new-window)))
;; Always copy quit-restore parameter in interactive use.
(let ((quit-restore (window-parameter old-window 'quit-restore)))
(when quit-restore
(set-window-parameter new-window 'quit-restore quit-restore)))
new-window)
(select-window new-window)
(next-buffer)
(select-window old-window)))
(defalias 'split-window-below 'lawlist-split-window-below)

Show all open buffers in Emacs

In emacs, is there an M-x command or key combo to render all current open buffers into different windows?
For clarity, let's suppose I have four open buffers, and I am only seeing one currently being displayed, and I would like in one step to show each buffer in one quadrant.
I recommend ibuffer. M-x ibuffer is probably what you want. Here's my ibuffer configuration:
;; *Messages* is so annoying. Also, I really like ibuffer
(require 'ibuf-ext)
(add-to-list 'ibuffer-never-show-predicates "^\\*Messages")
(add-to-list 'ibuffer-never-show-predicates "^\\*Completions")
(global-set-key (kbd "C-b") 'ibuffer)
(kill-buffer "*scratch*")
('ibuffer)
(switch-to-buffer "*Ibuffer*")
You will be able to start with the following code if there is not an already-known way to do it:
(defun buffer-in-window-list ()
(let (buffers)
(walk-windows (lambda (window) (push (window-buffer window) buffers)) t t)
buffers))
(defun display-all-buffers ()
(interactive)
(let (buffers-in-window (buffer-in-window-list))
(dolist (buffer (buffer-list))
(when (and (not (string-match "\\`[[:space:]]*\\*" (buffer-name buffer)))
(not (memq buffer buffers-in-window)))
(set-window-buffer (split-window (get-largest-window)) buffer)))
(balance-windows)))
The display-all-buffers command opens a new window for each buffer that is not currently displayed anywhere (including other frames). For usability, it ignores buffers whose names start with * (optionally, prefixed with whitespace characters) because they are usually for internal use only.
Note that Emacs does not allow a user to make too small a window. So, when there are too many buffers to display, the command will display as many buffers as possible in order of most recent display or selection and signal an error.

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)))