Scroll-down all the way without error "beginning of buffer" - emacs

I'm looking for some assistance, please, to programmatically scroll a particular window to the very beginning without incurring an error beginning of buffer (which necessarily halts the function). The current (point) is within the visible window, but near the bottom. The number of lines to scroll-down may vary, so I cannot say for sure exactly how many is needed -- but the very beginning of the buffer is what I'm looking for. I don't think (save-excursion (goto-char (point-min))) will do what I want.
scroll-down does not have an argument for a specific window, so if the solution to this question could also include an example to perform the action with a specific window (without switching to it), that would be greatly appreciated. In other words, I'd like to be in a different window while this function is going on in the target window.
(when
(not (pos-visible-in-window-p (point-min)
(get-buffer-window calendar-buffer (selected-frame))))
(scroll-down 20))

You can wrap scroll-down in a condition-case to regain control when an error is signaled:
(condition-case nil
(scroll-down 20)
(error nil))
I wasn't exactly sure if you wanted your last code snippet was part of the function you were referring to, but you can wrap it all in a save-selected-window:
(let ((cal (get-buffer-window calendar-buffer (selected-frame))))
(when cal
(save-selected-window
(select-window cal)
(condition-case nil
(scroll-down 20)
(error nil)))))
EDIT: jpkotta suggested ignore-errors in the comment, which I agree looks cleaner:
(ignore-errors
(scroll-down 20))

(let ((win (get-buffer-window calendar-buffer (selected-frame))))
(when (not (pos-visible-in-window-p (point-min) win))
(set-window-start win (point-min))))
`set-window-start` is a built-in function in `C source code'.
(set-window-start WINDOW POS &optional NOFORCE)
Make display in WINDOW start at position POS in WINDOW's buffer.
WINDOW must be a live window and defaults to the selected one. Return
POS. Optional third arg NOFORCE non-nil inhibits next redisplay from
overriding motion of point in order to display at this exact start.
See also http://www.gnu.org/software/emacs/manual/html_node/elisp/Window-Start-and-End.html
See also http://www.gnu.org/software/emacs/manual/html_node/elisp/Selecting-Windows.html

Related

Stop emacs erc from recentering

Emacs erc keep recentering. It is very hard to keep up with the conversations in a small buffer because of it.
I've tried the followings but none seems to work...
(erc-scrolltobottom-mode)
(require 'erc-goodies)
(setq erc-input-line-position -1)
(erc-add-scroll-to-bottom)
(add-hook 'erc-mode-hook 'erc-add-scroll-to-bottom)
(add-hook 'erc-insert-post-hook 'erc-scroll-to-bottom)
Is there a way to keep erc from recentering?
The issue is that Emacs (and not erc) tries to recenter the screen every time, the cursor moves out of visible portion. See the docstring of scroll-conservatively(C-hvscroll-conservativelyRET)
Scroll up to this many lines, to bring point back on screen. If point
moves off-screen, redisplay will scroll by up to
`scroll-conservatively' lines in order to bring point just barely onto
the screen again. If that cannot be done, then redisplay recenters
point as usual.
If the value is greater than 100, redisplay will never recenter point,
but will always scroll just enough text to bring point into view, even
if you move far away.
A value of zero means always recenter point if it moves off screen.
So setting scroll-conservatively in erc-mode-hook might do the trick
(add-to-list 'erc-mode-hook (lambda ()
(set (make-local-variable 'scroll-conservatively) 100)))
Actually there are a lot of ways to achieve what you want. Have a look at this section of the Emacs manual
SOLUTION # 1:  Comment out line 101 of the Github source code in the following link -- i.e., comment out the line of code that looks like this: (recenter (or erc-input-line-position -1))
https://github.com/emacsmirror/erc/blob/master/erc-goodies.el
(defun erc-scroll-to-bottom (window display-start)
"Recenter WINDOW so that `point' is on the last line.
This is added to `window-scroll-functions' by `erc-add-scroll-to-bottom'.
You can control which line is recentered to by customizing the
variable `erc-input-line-position'.
DISPLAY-START is ignored."
(if (window-live-p window)
;; Temporarily bind resize-mini-windows to nil so that users who have it
;; set to a non-nil value will not suffer from premature minibuffer
;; shrinkage due to the below recenter call. I have no idea why this
;; works, but it solves the problem, and has no negative side effects.
;; (Fran Litterio, 2003/01/07)
(let ((resize-mini-windows nil))
(erc-with-selected-window window
(save-restriction
(widen)
(when (and erc-insert-marker
;; we're editing a line. Scroll.
(> (point) erc-insert-marker))
(save-excursion
(goto-char (point-max))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LINE 101 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (recenter (or erc-input-line-position -1))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(sit-for 0))))))))
SOLUTION # 2:  In your .emacs file, you can create a new function and a defalias like this:
(require 'erc-goodies)
(erc-scrolltobottom-mode)
(setq erc-input-line-position -1)
(erc-add-scroll-to-bottom)
(add-hook 'erc-mode-hook 'erc-add-scroll-to-bottom)
(add-hook 'erc-insert-post-hook 'erc-scroll-to-bottom)
(defun guimobob-erc-scroll-to-bottom (window display-start)
"Recenter WINDOW so that `point' is on the last line.
This is added to `window-scroll-functions' by `erc-add-scroll-to-bottom'.
You can control which line is recentered to by customizing the
variable `erc-input-line-position'.
DISPLAY-START is ignored."
(if (window-live-p window)
;; Temporarily bind resize-mini-windows to nil so that users who have it
;; set to a non-nil value will not suffer from premature minibuffer
;; shrinkage due to the below recenter call. I have no idea why this
;; works, but it solves the problem, and has no negative side effects.
;; (Fran Litterio, 2003/01/07)
(let ((resize-mini-windows nil))
(erc-with-selected-window window
(save-restriction
(widen)
(when (and erc-insert-marker
;; we're editing a line. Scroll.
(> (point) erc-insert-marker))
(save-excursion
(goto-char (point-max))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; (recenter (or erc-input-line-position -1))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(sit-for 0))))))))
(defalias 'erc-scroll-to-bottom 'guimobob-erc-scroll-to-bottom)

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

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)

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

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