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)
Related
I am a historian trying to configure Emacs to efficiently write academic papers. With that aim in mind, I decided to add margins to the Emacs buffer. The end result looks like this, and was achieved with this code:
;; Margins function
(defun my-set-margins ()
"Set margins in current buffer."
(setq left-margin-width 26)
(setq right-margin-width 26))
;; Add margins by default to a mode
(add-hook 'org-mode-hook 'my-set-margins)
The problem is that when I split the screen vertically, the margins make the text unreadable. So, I came to the conclusion that it would be necessary to either (1) have Emacs remove the margins automatically during vertical split screen and put them back when in single screen; or (2) create a keyboard shortcut to toggle the margins. Any elips ideas on how to do either? Thanks in advance!
The documentation states that setting the left-margin-width or right-margin-width does not immediately affect the window. These variables are checked when a new buffer is displayed in the window. Thus, you can make changes take effect by calling set-window-buffer. https://www.gnu.org/software/emacs/manual/html_node/elisp/Display-Margins.html
The following function checks to see whether either margin is greater than 0, and toggles either on or off based thereon. I bound the interactive function to the global-map with the f5 key.
(defun my-toggle-margins ()
"Set margins in current buffer."
(interactive)
(if (or (> left-margin-width 0) (> right-margin-width 0))
(progn
(setq left-margin-width 0)
(setq right-margin-width 0)
(set-window-buffer (selected-window) (current-buffer)))
(setq left-margin-width 26)
(setq right-margin-width 26)
(set-window-buffer (selected-window) (current-buffer))))
(global-set-key [f5] 'my-toggle-margins)
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
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)
I've written this little minor mode to get the TAB key to continue indenting after the major mode has performed the initial indent behaviour and/or to force indentation when the major mode thinks no indent is necessary.
It has been pointed out to me that the combination setq and make-local-variable can probably be simplified into a let scope. Given that this needs to work across multiple buffers concurrently, how would one change this to use let instead of make-local-variable?
;;; dwim-tab.el --- minor mode to force indentation when TAB would otherwise stall
; internal tracking variables
(setq dwim-tab-point-before nil)
(setq dwim-tab-point-after nil)
(defun dwim-tab ()
"Indents normally once, then switches to tab-to-tab-stop if invoked again.
Always performs tab-to-tab-stop if the first TAB press does not cause the
point to move."
(interactive)
(setq dwim-tab-point-before (point))
(if (eq dwim-tab-point-before dwim-tab-point-after) ; pressed TAB again
(tab-to-tab-stop)
(indent-for-tab-command))
(if (eq (point) dwim-tab-point-before) ; point didn't move
(tab-to-tab-stop))
(setq dwim-tab-point-after (point)))
(define-minor-mode dwim-tab-mode
"Toggle dwim-tab-mode.
With a non-nil argument, turns on dwim-tab-mode. With a nil argument, turns it
off.
When dwim-tab-mode is enabled, pressing the TAB key once will behave as normal,
but pressing it subsequent times, will continue to indent, using
tab-to-tab-stop.
If dwim-tab determines that the first TAB key press resulted in no movement of
the point, it will indent according to tab-to-tab-stop instead."
:init-value nil
:lighter " DWIM"
:keymap '(("\t" . dwim-tab))
(make-local-variable 'dwim-tab-point-before)
(make-local-variable 'dwim-tab-point-after))
(provide 'dwim-tab)
Does this do what you want? Look ma, no variables!
(defun tab-dwim ()
(interactive)
(when (or (eq last-command this-command)
(= (point) (progn
(indent-for-tab-command)
(point))))
(tab-to-tab-stop)))
The last-command check isn't strictly necessary assuming indent-for-tab-command won't magically start indenting again. But it's slightly more CPU efficient.
I just read Emacs :TODO indicator at left side, and tried it out. It seems intriguing. The little indicator triangles appear, but I'm getting a weird side effect: the text itself is being altered. Characters are being deleted.
Before:
After:
The mode-line does indicate that the buffer has been altered after running annotate-todo.
What explains this?
(I'm using emacs 22.2.1 on Windows)
Ahhh... I see the error of my ways earlier. Here's a new version.
(defun annotate-todo ()
"put fringe marker on TODO: lines in the curent buffer"
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "TODO:" nil t)
(let ((overlay (make-overlay (- (point) 5) (point))))
(overlay-put overlay 'before-string (propertize (format "A")
'display '(left-fringe right-triangle)))))))
The first solution used a the 'display text property, which changes how the specified text is displayed, in this case it was replaced by the triangle in the left fringe. What I needed to do was to use a 'before-string overlay instead. Which doesn't change the string being displayed.
Another advantage, the cut/paste of the code annotated by this does not carry the markup.
I've updated the code in the original question to reflect this change as well.