5

From the doc of window-end function:

Even if update is non-nil, window-end does not attempt to scroll the display if point has moved off the screen, the way real redisplay would do. It does not alter the window-start value. In effect, it reports where the displayed text will end if scrolling is not required.

Since the window-end only reports where the displayed text will end if scrolling is not required, what should I do to get the correct window-end after I call next-line at the end of the current window, which causes the window to scroll by one line? Do I need something like forcing the window to redisplay before calling window-end function?

Drew
  • 75,699
  • 9
  • 109
  • 225
cutejumper
  • 787
  • 5
  • 12
  • IIUC you just can't (except maybe by forcing a redisplay first, e.g. with `(redisplay)`. Most likely if you explain more of what it is you're trying to do, we can give you a better answer. – Stefan Feb 27 '16 at 14:39
  • @Stefan I am trying to implement [some function](https://github.com/cute-jumper/ace-jump-helm-line/issues/4) in the package. The problem is that I need to know the up-to-date `window-start` and `window-end` to get all the visible lines, but when `next-line` causes the window to scroll, these window bounds are not correct. – cutejumper Feb 27 '16 at 19:06
  • I don't know what "some function" is, and neither do I know why it needs to know the window-start/end and even less why it needs it during the execution of next-line. – Stefan Feb 27 '16 at 20:45
  • It needs to know the `window-start` and `window-end` because I want to label the visual lines. When `next-line` cause the window to scroll, the visual lines are changed, so we need to change the overlay accordingly. – cutejumper Feb 27 '16 at 21:07
  • Then you don't need to know window-start and window-end for that. Instead, you want to use `fontification-functions` which will be called on every part of the buffer that is displayed. Typically this is not used directly but instead it's done via `jit-lock-register`. – Stefan Feb 28 '16 at 23:41
  • @Stefan I have no luck trying to use `jit-lock-register`. After registering a simple function which only prints out its parameters, it doesn't seem to be called when I use `next-line` to scroll the window. Can you point out some code that makes use of it? EDIT: it **is** called multiple times with different parameters when first visiting a buffer. – cutejumper Mar 01 '16 at 01:06
  • See nlinum-mode (in GNU ELPA) for an example. – Stefan Mar 01 '16 at 01:44

1 Answers1

3

The best a user can achieve -- without forcing a redisplay or without modifying the C-source code -- is use the window-end function with the optional second argument set to t in conjunction with the window-scroll-functions hook that takes two arguments -- https://www.gnu.org/software/emacs/manual/html_node/elisp/Window-Hooks.html#Window-Hooks [NOTE: Forcing a redisplay may reveal to the naked eye (for a split second) an unfinished product -- e.g., perhaps the new overlays have not yet been removed/placed.]

CAVEAT:  Beware that the window-scroll-functions hook fires sometimes more than once during each command loop (while redisplay performs its job), and the first values for window-start and window-end win t are not always correct, and the last values are not always correct either -- e.g., when inserting/yanking, or when using goto. [Fn 1.] When the window-scroll-functions hook runs multiple times, the function attached to the hook can cause significant performance issues depending upon the complexity of said function. And, as mentioned, sometimes the user is simply out of luck -- i.e., correct values can never be obtained. Using pos-visible-in-window-p can be helpful as a workaround to set up some checks to prevent the function attached to the hook from fully running its course if point is not yet fully visible; and, a variable can be set up to test for whether the function has already fully run one time during that command loop (so that it doesn't keep running).

I have written a draft proof concept patch to the C-source code that I am using for my own personal setup to always get the correct window-start and window-end during redisplay; however, it may never make its way into the mainstream since I'm not a programmer and am the only person really motivated: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22404 It is called the window-start-end-hook -- but it's only available if a user wants to experiment with the draft patch and build Emacs from source. My draft patch works irrespective of window scrolling, e.g., it works when moving point within the visible window; it works when using goto; it works when inserting/yanking; and, it works when scrolling. Anyone interested in developing that concept should please feel free to submit a post to feature request 22404.


See also these related threads:

How to update window-start without calling redisplay?

https://stackoverflow.com/questions/23923371/emacs-calculating-new-window-start-end-without-redisplay/24216247#24216247


NOTE:  Debugging functions that run during redisplay may require using a feature called trace-redisplay (after launching Emacs from the command line) that is available when building Emacs from source with options such as: ./configure --enable-checking='glyphs'.


[Fn. 1.]:  When working with Bug #22637, Eli Z. on the Emacs development team explained that the reason the window-scroll-functions hook does not yield correct results for inserting/yanking or goto is because it was never designed for those scenarios: "The reason window-scroll-functions aren't run in both of these test cases is that what happens there is not considered 'scrolling'. Scrolling is informally defined as either an explicit call to a function that scrolls the window, or a pseudo-scroll done by the display engine when it detects that some part of the window's previous display is still present, but in a different vertical position. Moving point to an arbitrary location is neither." https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22637#35

lawlist
  • 18,826
  • 5
  • 37
  • 118
  • I don't aware of that @abo-abo has already asked a similar question! Basically I have the same question as him. I've read your post in that thread, and it seems there is no elegant solution yet. `window-start-end-hook` is good, but how can we push forward the feature request? – cutejumper Feb 27 '16 at 19:15
  • Eli Z. is concerned that permitting a user to introduce Lisp into redisplay could cause Emacs to hang or crash; however, he also guided me towards using `safe_call` which I believe resolves that concern because the Lisp function will be canceled if there is an error during redisplay. Eli Z. would prefer a final hook that he calls the proposed `post-redisplay-hook`; however, I believe my solution is better because it permits a user to introduce Lisp that alters the layout (e.g., moves point or increases a font size) and redisplay will recheck the tentative result and do more work if needed. – lawlist Feb 27 '16 at 19:34
  • Other than submitting a contemporaneous e-mail to Eli Z. and also feature 22404 voicing your input, I don't know what else can be done to make the feature a reality. John said he doesn't need to be carbon-copied directly because he reads all the bug e-mail. [I would certainly appreciate receiving a carbon-copy of feature 22404 related emails, if you wouldn't mind (since I don't regularly peruse the latest happenings).] – lawlist Feb 27 '16 at 19:37
  • The `window-start-end-hook` fires completely (i.e., runs all of the code) when point is fully visible, but it has some built-in code that can be altered to fire completely when point is partially visible. Some users may want that partially visible ability -- see the doc-string for the built-in variable `make-cursor-line-fully-visible` which can be set to `nil`, but the default value is `t`. In other words, my draft proof concept is not a final product (it could still use some TLC) -- but I've been using it for a couple of weeks in my own setup and am very happy with it. – lawlist Feb 27 '16 at 19:49
  • From a developer's point of view, I can't see what is blocking this issue to be solved. We should expect the `window-start` and `window-end` can always return the right values -- that is what these functions are supposed to do. I'll try the methods you've mentioned as well as @abo-abo 's approach. – cutejumper Feb 27 '16 at 19:53
  • The problem is that redisplay figures out what the new values for `window-start` and `window-end` will ultimately be -- that is part of its job. Functions such as `window-start` and `window-end` run *before* `redisplay` begins its process (when those new prospective values are still uncertain and/or unknown) -- so a solution at the tail end of redisplay is required. [Lisp error messages are unavailable during redisplay, which is why something like `trace-redisplay` is needed for debugging.] – lawlist Feb 27 '16 at 19:58
  • May I ask why `window-start` and `window-end` run before `redisplay`? It sounds weird because `window-start` and `window-end` should depend on the the current visual area, which is determined by `redisplay`. Why they actually return before the visual area is determined?(I don't know any of the Emacs inner magic...) – cutejumper Feb 27 '16 at 20:06
  • For example, the `window-scroll-functions` hook and the `pre-redisplay-function` can both contain functions attached to them that alter the display -- e.g., move point or increase/decrease font size. So redisplay must do additional work to figure out what the new `window-start` and `window-end` will ultimately be. The new draft concept `window-start-end-hook` can also contain functions that move point or increase/decrease font size, resulting in a change in the values of `window-start` and `window-end`. The normal Lisp functions all run **before** redisplay begins. – lawlist Feb 27 '16 at 20:11
  • Regarding *why* Emacs does what it does, I'm just a programming hobbyist (not by trade) who only recently started learning the language of C for the primary purpose of implementing my own new Emacs features and/or to propose new features and modifications to the Emacs development team. It is clear to me, however, that we definitely need something like the `window-start-end-hook` or a `post-redisplay-hook`. – lawlist Feb 27 '16 at 20:14
  • Thanks! I've heard that the redisplay code of Emacs is **really** complicated. Anyway, your post and @abo-abo 's comments in the related thread give enough hints for a temporary workaround. Should I leave this as it is or mart it as solved? Maybe someone else can also come across similar problems. – cutejumper Feb 27 '16 at 20:25
  • I love attention to this issue, which is a passion of mine. If you mark it solved, people will lose interest. abo-abo left his question open, presumably because he was not satisfied with the answer or because he wanted a new feature or because he wanted more attention to this issue. My personal preference would be to leave this question as not having an accepted answer, so that it draws attention. But the decision is yours -- it's your question, and you have the ultimate say on whether there is a satisfactory solution. I'm in no hurry to receive a check-mark :) – lawlist Feb 27 '16 at 20:28
  • To be honest, I think there **should** be an easy and straightforward way to solve this, and Emacs should provide that. I'm kind of surprised to see how complicated this issue is. So I'll also leave it open. Thanks for your answer and your effort on solving this issue. – cutejumper Feb 27 '16 at 20:34