2

Is there a way to temporarily disable scrolling in an emacs buffer? I would like to "lock" a particular line to be (say) the top of the displayed portion of the buffer, so that if I accidentally try to scroll it off screen I would be prevented from doing so, and it stays visible.

(I'm using org-mode, so if I want to see/type further down than what is currently the bottom of the screen, I want to do it by collapsing sections between the top of the screen instead of accidentally scrolling away.)

As a workaround I can split the buffer into two windows horizontally and work in the bottom buffer (keeping the top one fixed), but I'd rather use one window that just doesn't move.

(Note, in case it matters: My keyboard doesn't have a "Scroll Lock" key, and even when it did I never learned what it would do either in Emacs or in any other application.)

  • I imagine simply redefining next-line to check whether it's going off the screen and aborting would cover most of the use case. – ShreevatsaR Aug 19 '16 at 22:25
  • I played a bit with window-scroll-functions. You can change the scroll under Emacs's nose; you aren't supposed to, but it sort-of works. You have to manually move the point back into the window area if it tries to escape. But when you insert text at the end of the window, I couldn't find a decent behavior. In the end, I couldn't find any advantage compared to narrowing, but many downsides: not built in, doesn't work when the same buffer is shown in multiple windows, really confusing with edits, ... In what case isn't narrowing ok, and what precise behavior do you want instead? – Gilles 'SO- stop being evil' Aug 21 '16 at 19:27
  • @Gilles Let me describe the actual motivation. I was writing a doc in org-mode, where the intro section had a list of elements, and as I was continuing to type future sections, I wanted to always keep this list visible at the top of the screen (so that I can refer to the numbers as I go along). So if I'm typing a later section and it gets too long (so that the list would scroll off screen), I collapse some of the subsections so that the list continues to be visible. So if I try to do anything that makes the list scroll off the top (incl. typing), I'd like to be alerted/prevented from doing so. – ShreevatsaR Aug 22 '16 at 2:33
  • ShreevatsaR: For that case I would just split the window vertically. In the top window have the text you want to keep in view at all times, and then do all your editing in the other window. The approach you're wanting to take sounds like a super-complicated way to achieve something not quite as good. – phils Aug 23 '16 at 4:48
  • @phils Yes you're right, I'm going to just split the window. I don't lose much (a line or so, plus I may be able to wastefully have the same text in both windows which I can solve by narrowing in the lower window), so it's close enough to ideal. – ShreevatsaR Aug 25 '16 at 1:26
2

Scrolling causes the hook window-scroll-functions to be executed. That lets you know when scrolling happens, but as the manual says,

Warning: don’t use this feature to alter the way the window is scrolled. It’s not designed for that, and such use probably won’t work.

It's actually possible to “lock” scrolling by saving window-start and restoring it inside window-scroll-functions, but that doesn't mean you should do it. It can cause the point to get out of the scrolling zone; you can fix that by manually clipping the point and the window point to the window area. But that gets really confusing when inserting text at the end of the window, as it causes the cursor to move backwards while inserting text.

You can't prevent a modification that would cause scrolling, because whether to scroll is determined after the modification is made and after things like line wrapping (whether explicit through auto fill mode or visual with visual line mode) have been processed. Once Emacs gets to the bit where it finds out whether it needs to scroll, the modification has already been carried out and the only solution would be to undo it.

A solution would be to keep two identical buffers, try out the modification in one of them, calculate the effect on scrolling (maybe by maintaining a “shadow” window in a hidden frame?), and carrying out the modification in the original buffer only if no scrolling happens. This sounds difficult for what it's worth.

Narrowing, as suggested by Wilfred Hughes, does most of what you want, but doesn't help with text insertions. You might combine narrowing with an entry in window-scroll-functions, but you need to figure out what you want to do when the text grows too large. You need to have clear requirements before you can start implementing.

I don't think preventing edits makes sense. If you want to maintain a structure overview, do it with two windows: one where you edit text without any length restrictions, and one that shows the structure overview. See How can I get an org-mode outline in a 2nd buffer as a dynamic table of contents?

  • There are a few movements that will not trigger the window-scroll-functions hook because the Emacs team does not consider it to be technically scrolling -- e.g., some functions using goto-char. And, there are a couple of occasions where the WSF will fire, but the values of window-start and window-end win t will be wrong for the same reasons -- e.g., not considered to be scrolling. See discussion by Eli Z. in the context of bug #22637: debbugs.gnu.org/cgi/bugreport.cgi?bug=22637 And, finally, the WSF may be called more than once when point is not fully visible. – lawlist Aug 23 '16 at 2:56
  • Thank you for this answer. As suggested in your last paragraph, I am going to stick with the Emacs-native solution of splitting the window vertically and keeping the text fixed in the upper window and doing all my editing in the lower window, but this is great to know what's possible. Thanks for considering it and looking into it. – ShreevatsaR Aug 25 '16 at 1:24
2

Have you considered narrowing?

(defun wh/narrow-to-visible-screen ()
  "Narrow to the currently visible text in the current buffer."
  (interactive)
  (widen)
  ;; Get position of the beginning of the current line.
  (let ((start-pos (progn (beginning-of-line) (point)))
        ;; Move down one screenful, and get position.
        (end-pos (progn (scroll-up nil) (point))))
    ;; Only show the text between start-pos and end-pos, and forbid
    ;; scrolling outside of that.
    (narrow-to-region start-pos end-pos)
    ;; Ensure the region is positioned correctly in the frame.
    (scroll-down nil)))
  • Narrowing is a nice idea and I use it often, but it doesn't quite help with the case I have in mind. It helps with not scrolling up and also would help if there was a fixed screenful of text. But I am editing the buffer and want to continue typing, except not go beyond the screen. When I type too much in one section and move on to the next section, I want to be able to realize this and collapse (hide) the previous section, instead of scrolling down and going past the screen. – ShreevatsaR Aug 20 '16 at 7:16
  • @ShreevatsaR I think narrowing is the best way to solve this. It solves the problem as long as the window size remains the same and the text doesn't change. What do you want to happen if you resize the window? What do you want to happen if you insert more text than can fit in the window? – Gilles 'SO- stop being evil' Aug 21 '16 at 19:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.