Emacs auto-display of things at point in a right window - emacs

I'd like to implement a function it shows relevant code paragraph in its right window when Emacs cursor is prompting on a certain word.
Let's assume that I have my own log format and I have corresponding database which I can find where a log message came from then I have the pointer on a certain log message. If so, then I like to let Emacs open the corresponding source file in a right side window of that log buffer.
Now I can query and get the location of source file thru my own db and emacs. But I still don't know how to control the right window.
If I opened a right window once, then Emacs would open an another one again, I don't want to let it do but want to let it use the previous, existing window.
How could I implement this? Please advise me, or share an example you might have.
Thanks.

In addition to the examples below using the custom function my-display-buffer, keep in mind that BUFFER can be obtained by means other than find-file-noselect; e.g., current-buffer if so desired. In terms of finding your location in the other window, you may find it helpful to select-window or with-selected-window or set-window-point, etc. If the window is selected with the target buffer visible in said window, then simple things like goto-char will suffice to go visually to a particular location. The example my-display-buffer function has a doc-string which describes generally what it was designed to do; i.e., "There are three possibilities ...".
Display buffer to the left:
(let ((buffer (find-file-noselect "~/foo.py")))
(with-current-buffer buffer
(message "major-mode: %s" major-mode))
(my-display-buffer buffer nil 'left))
Display buffer to the right:
(let ((buffer (find-file-noselect "~/foo.py")))
(with-current-buffer buffer
(message "major-mode: %s" major-mode))
(my-display-buffer buffer nil 'right))
Display buffer above:
(let ((buffer (find-file-noselect "~/foo.py")))
(with-current-buffer buffer
(message "major-mode: %s" major-mode))
(my-display-buffer buffer nil 'above))
Display buffer below:
(let ((buffer (find-file-noselect "~/foo.py")))
(with-current-buffer buffer
(message "major-mode: %s" major-mode))
(my-display-buffer buffer nil 'below))
(defun my-display-buffer (buffer-or-name alist direction &optional size pixelwise)
"BUFFER: The buffer that will be displayed.
ALIST: See the doc-string of `display-buffer' for more information.
DIRECTION: Must use one of these symbols: 'left 'right 'below 'above
SIZE: See the doc-string for `split-window'.
PIXELWISE: See the doc-string for `split-window'.
There are three possibilities:
- (1) If a window on the frame already displays the target buffer,
then just reuse the same window.
- (2) If there is already a window in the specified direction in relation
to the selected window, then display the target buffer in said window.
- (3) If there is no window in the specified direction, then create one
in that direction and display the target buffer in said window."
(let* ((buffer
(if (bufferp buffer-or-name)
buffer-or-name
(get-buffer buffer-or-name)))
(window
(cond
((get-buffer-window buffer (selected-frame)))
((window-in-direction direction))
(t
(split-window (selected-window) size direction pixelwise)))))
(window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
window))

Related

Open two files in emacs in "reverse" order?

Say I open two files emacs -nw ./first-file ./second-file. The left buffer is second-file and the right buffer is first-file. Many people are used to it and I can see the argument, but for me, it's not intuitive, as first-file is to the left of second-file in the actual command. I know I switch them afterward, but I was wondering if there was perhaps a line in .emacs that could do this automatically.
Using GNU Emacs 25.3.2
Example usage: /path/to/emacs -nw --eval="(my-one-two \"foo\" \"bar\")"
(defun my-one-two (file-one file-two)
"Display FILE-ONE to the left and FILE-TWO to the right."
(let ((buffer-one (find-file-noselect file-one))
(buffer-two (find-file-noselect file-two)))
(delete-other-windows)
(set-window-buffer (selected-window) buffer-one)
(my-display-buffer buffer-two nil 'right)))
(defun my-display-buffer (buffer-or-name alist direction &optional size pixelwise)
"BUFFER: The buffer that will be displayed.
ALIST: See the doc-string of `display-buffer' for more information.
DIRECTION: Must use one of these symbols: 'left 'right 'below 'above
SIZE: See the doc-string for `split-window'.
PIXELWISE: See the doc-string for `split-window'.
There are three possibilities:
- (1) If a window on the frame already displays the target buffer,
then just reuse the same window.
- (2) If there is already a window in the specified direction in relation
to the selected window, then display the target buffer in said window.
- (3) If there is no window in the specified direction, then create one
in that direction and display the target buffer in said window."
(let* ((buffer
(if (bufferp buffer-or-name)
buffer-or-name
(get-buffer buffer-or-name)))
(window
(cond
((get-buffer-window buffer (selected-frame)))
((window-in-direction direction))
(t
(split-window (selected-window) size direction pixelwise)))))
(window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
window))
Assuming you are running on some kind of linux shell the easiest way I see is to use an alias:
alias myE="emacs -nw $2 $1"
To run emacs now, do: myE file1 file2 and it executes emacs -nw file2 file1.
In order to persist this, put the alias command into your ~/.bashrc.

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.

Make *Buffer List* always appear in horizontal split

I know Emacs tries to be intellectual and opens its helper buffers depending on which dimension of the window is bigger, so it may appear in vertical split window if current width is bigger than height, and in horizontal split otherwise.
But I’d prefer it to open that list always in horizontal split, because there are long paths I can’t see when the buffer is placed in vertical split. How can I do this?
I believe you've got the horizontal/vertical split terminology back to front (I can never remember which is which either), but as your goal was to retain the original window's width, I'm forcing a vertical split.
See C-hf split-window-sensibly RET. It tells you what to do:
You can enforce this function to not split WINDOW horizontally,
by setting (or binding) the variable `split-width-threshold' to
nil. If, in addition, you set `split-height-threshold' to zero,
chances increase that this function does split WINDOW vertically.
So as a permanent setting:
(setq split-width-threshold nil)
(setq split-height-threshold 0)
For just a specific function, you can advise that function (but see Edit 2 below!):
(defadvice list-buffers (around list-buffers-split-vertically)
"Always split vertically when displaying the buffer list.
See `split-window-sensibly'."
(let ((split-width-threshold nil)
(split-height-threshold 0))
ad-do-it))
(ad-activate 'list-buffers)
Edit: Actually, in this instance I suspect you're only concerned with the interactive case, in which case it's preferable to define a function and remap the bindings:
(defun my-list-buffers-vertical-split ()
"`list-buffers', but forcing a vertical split.
See `split-window-sensibly'."
(interactive)
(let ((split-width-threshold nil)
(split-height-threshold 0))
(call-interactively 'list-buffers)))
(global-set-key [remap list-buffers] 'my-list-buffers-vertical-split)
Edit 2: And Stefan points out that display-buffer-alist facilitates such things without advising functions (and of course avoiding unnecessary advice is always a good thing). I believe we still need a custom action, so:
(defun my-display-buffer-pop-up-same-width-window (buffer alist)
"A `display-buffer' ACTION forcing a vertical window split.
See `split-window-sensibly' and `display-buffer-pop-up-window'."
(let ((split-width-threshold nil)
(split-height-threshold 0))
(display-buffer-pop-up-window buffer alist)))
(add-to-list 'display-buffer-alist
'("\\*Buffer List\\*" my-display-buffer-pop-up-same-width-window))
If horizontally or vertically, presently neither split-height-threshold nor split-width-threshold seem reliable WRT to expected kind of split. Which looks like a bug, resp. design issue.
As a work-around call M-x split-window-horizontally resp. -vertically before, or advise the function with it.
You can remove (5) if you prefer not to select the window after it is displayed -- i.e., remove (select-window (get-buffer-window (buffer-name buffer))). I like the bottom window to be reserved for a 3-month calendar, so that's why I have a condition to use the window above (if it exists) -- you can remove that condition if you are so inclined. Actually, it's your function so you can modify everything as you see fit now that you see how it works. The alist would be used like this: '((window-width . 33)) if you wanted to control certain aspects of the target window, etc. I find myself always going back to this document page because it is the only scanty formal example I've found . . . and, of course, the source itself window.el: http://www.gnu.org/software/emacs/manual/html_node/elisp/Display-Action-Functions.html
(defun lawlist-list-buffers-left (&optional arg)
"Display a list of existing buffers.
The list is displayed in a buffer named \"*Buffer List*\".
See `buffer-menu' for a description of the Buffer Menu.
By default, all buffers are listed except those whose names start
with a space (which are for internal use). With prefix argument
ARG, show only buffers that are visiting files."
(interactive "P")
(lawlist-display-buffer-left (list-buffers-noselect arg) nil))
(defun lawlist-list-buffers-right (&optional arg)
"Display a list of existing buffers.
The list is displayed in a buffer named \"*Buffer List*\".
See `buffer-menu' for a description of the Buffer Menu.
By default, all buffers are listed except those whose names start
with a space (which are for internal use). With prefix argument
ARG, show only buffers that are visiting files."
(interactive "P")
(lawlist-display-buffer-right (list-buffers-noselect arg) nil))
(defun lawlist-display-buffer-left (buffer alist)
"(1) If `buffer` is already displayed, then display it again in the same window.
(2) If `buffer` is not already displayed, and if there is a window to the left,
then display that `buffer` in said window. (3) If `buffer` is not already
displayed, and if there is a window to the right, then use the selected window.
(4) If all else fails, then create a new window to the left and display `buffer` there.
(5) Select the target window which displays `buffer`."
(let (
(window
(cond
((get-buffer-window buffer (selected-frame)))
((window-in-direction 'above))
((window-in-direction 'left))
((window-in-direction 'right)
(selected-window))
(t
(split-window (selected-window) nil 'left)))))
(window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
;; OPTIONAL -- uncomment to select the target window.
;; (select-window (get-buffer-window (buffer-name buffer)))
))
(defun lawlist-display-buffer-right (buffer alist)
"(1) If `buffer` is already displayed, then display it again in the same window.
(2) If `buffer` is not already displayed, and if there is a window to the right,
then display that `buffer` in said window. (3) If `buffer` is not already
displayed, and if there is a window to the left, then use the selected window.
(4) If all else fails, then create a new window to the right and display `buffer` there.
(5) Select the target window which displays `buffer`."
(let (
(window
(cond
((get-buffer-window buffer (selected-frame)))
((window-in-direction 'above))
((window-in-direction 'right))
((window-in-direction 'left)
(selected-window))
(t
(split-window (selected-window) nil 'right)))))
(window--display-buffer buffer window 'window alist display-buffer-mark-dedicated)
;; OPTIONAL -- uncomment to select the target window.
;; (select-window (get-buffer-window (buffer-name buffer)))
))

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

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

A smarter alternative to delete-window?

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