M-x compile better final state - emacs

When I say M-x compile the make program is typically called. When finished, the buffer *compilation* is often left in an undesirable state: half of the window is left empty (probably expecting some more output). With C-l C-l I can move to last line in the buffer to the bottom of the window - so I see more of the actual compilation process.
Is there a clean way to configure compile such that the window will always show the maximal number of lines - at least at the end?

Here's what I came up with (put it in your .emacs).
(defun compilation-redisplay (proc msg)
"Scroll the current window to fit the tail of the buffer
in. This only fires if `compilation-scroll-output' is true"
(when (memq (process-status proc) '(exit signal))
(let ((buffer (process-buffer proc)))
;; Check that the buffer hasn't already been killed
(unless (null (buffer-name buffer))
(with-current-buffer buffer
;; Check we're at the bottom of the buffer and that we're there
;; because compile.el put us there
(when (and compilation-scroll-output
(= (point) (point-max)))
(with-selected-window (get-buffer-window)
;; This logic is pinched from recenter-top-bottom (window.el)
(recenter
(- -1 (min (max 0 scroll-margin)
(truncate (/ (window-body-height) 4.0))))))))))))
(advice-add 'compilation-sentinel :after #'compilation-redisplay)
The idea is that it runs when the compilation process finishes. It then does a little bit of checking to make sure that there is a sensible buffer, that compilation-scroll-output is set, and that point is at the bottom of the buffer (you don't want stuff jumping around if you have a long compilation process and you've already started poring over the output). Finally, the recenter call is stolen from the code in recenter-top-bottom.

Related

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

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

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)

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