unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Allowing point to be outside the window?
       [not found] <87ilwd7zaq.fsf.ref@yahoo.com>
@ 2021-11-28  3:07 ` Po Lu
  2021-11-28  8:03   ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-28  3:07 UTC (permalink / raw)
  To: emacs-devel

I wonder what would be involved in allowing point to be outside a
window's visible area.

It would make pixel scrolling much less confusing for many people who
are used to other editors, such as gedit.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28  3:07 ` Allowing point to be outside the window? Po Lu
@ 2021-11-28  8:03   ` Eli Zaretskii
  2021-11-28  8:13     ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28  8:03 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Date: Sun, 28 Nov 2021 11:07:57 +0800
> 
> I wonder what would be involved in allowing point to be outside a
> window's visible area.

Two steps:

  1. Design the feature: how would it work? which operations would
     bring point back into the viewport, and which won't?  For
     example, a simple question: if point is outside of the viewport,
     what is the effect of C-f or C-n on display?

  2. Go over the display-related code and modify all the places which
     assume point is or must be visible.  Source files involved are
     xdisp.c, dispnew.c, window.c, fringe.c, probably also indent.c.

IMO, it's a large job if done cleanly.  Patches welcome.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28  8:03   ` Eli Zaretskii
@ 2021-11-28  8:13     ` Po Lu
  2021-11-28  8:41       ` Eli Zaretskii
  2021-11-28 14:03       ` Alan Mackenzie
  0 siblings, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-11-28  8:13 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Date: Sun, 28 Nov 2021 11:07:57 +0800
>> 
>> I wonder what would be involved in allowing point to be outside a
>> window's visible area.
>
> Two steps:
>
>   1. Design the feature: how would it work? which operations would
>      bring point back into the viewport, and which won't?  For
>      example, a simple question: if point is outside of the viewport,
>      what is the effect of C-f or C-n on display?

The effect would be move point forward, or to the next line, and then to
recenter the window, so point is at the center of the window.

Inserting text should probably recenter the window as well, if point is
outside the visible area (this is also how other programs behave).

This is how other programs behave as well.

> IMO, it's a large job if done cleanly.  Patches welcome.

Thanks, I will take a look at it.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28  8:13     ` Po Lu
@ 2021-11-28  8:41       ` Eli Zaretskii
  2021-11-28 12:47         ` Po Lu
  2021-11-28 14:03       ` Alan Mackenzie
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28  8:41 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 28 Nov 2021 16:13:46 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> From: Po Lu <luangruo@yahoo.com>
> >> Date: Sun, 28 Nov 2021 11:07:57 +0800
> >> 
> >> I wonder what would be involved in allowing point to be outside a
> >> window's visible area.
> >
> > Two steps:
> >
> >   1. Design the feature: how would it work? which operations would
> >      bring point back into the viewport, and which won't?  For
> >      example, a simple question: if point is outside of the viewport,
> >      what is the effect of C-f or C-n on display?
> 
> The effect would be move point forward, or to the next line, and then to
> recenter the window, so point is at the center of the window.

That's one possibility, perhaps the most obvious one.  But it isn't
the only one, and we should understand well what we want in this
regard, because Emacs sometimes moves point by itself, without the
user's say-so.

> Inserting text should probably recenter the window as well, if point is
> outside the visible area (this is also how other programs behave).

This is IMO less obvious, especially if you keep in mind that text can
be inserted by some Emacs command other than an explicit text
insertion by the user (via self-insert-command, C-y, etc.).  It is
quite possible we will need some finer granularity here, where not
_every_ insertion will bring point into the viewport.

> This is how other programs behave as well.

Emacs is much more complex than "other programs", and many
commands/functions are invoked in different contexts, where user
expectations regarding this aspect could be different.  Moreover, we
have a lot of code that disregards the context because the assumption
is that point is always visible, something that is obtained by a
coordinated effort of many parts of Emacs code (so if one of them
doesn't make point visible, some other one will).  Making this
well-orchestrated effort do something different (let alone do it
optionally, since I expect quite a few users to want not to have this
new behavior) will probably require some new protocol.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28  8:41       ` Eli Zaretskii
@ 2021-11-28 12:47         ` Po Lu
  2021-11-28 12:58           ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-28 12:47 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Emacs is much more complex than "other programs", and many
> commands/functions are invoked in different contexts, where user
> expectations regarding this aspect could be different.  Moreover, we
> have a lot of code that disregards the context because the assumption
> is that point is always visible, something that is obtained by a
> coordinated effort of many parts of Emacs code (so if one of them
> doesn't make point visible, some other one will).  Making this
> well-orchestrated effort do something different (let alone do it
> optionally, since I expect quite a few users to want not to have this
> new behavior) will probably require some new protocol.

Hmm, thanks.  One small question though: what should I do in order to
make a window's cursor invisible?

I want to detect if PT is before window start, and in that case refrain
from displaying the phys cursor.  Right now, I test for that condition
in redisplay_window, and the test seems to work, but I have no idea how
to mark the window's cursor as invisible, so to speak.

Or perhaps this is an X/Y question, but I'm not sure.

Thanks in advance.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 12:47         ` Po Lu
@ 2021-11-28 12:58           ` Eli Zaretskii
  2021-11-28 13:10             ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28 12:58 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 28 Nov 2021 20:47:15 +0800
> 
> Hmm, thanks.  One small question though: what should I do in order to
> make a window's cursor invisible?

Prevent display_and_set_cursor from being called, I presume?

There's also set_cursor_from_row, which calculates where to display
the cursor, and something should be done about that to indicate that
the cursor is not shown at all.

> I want to detect if PT is before window start, and in that case refrain
> from displaying the phys cursor.  Right now, I test for that condition
> in redisplay_window, and the test seems to work

display_and_set_cursor is also called from dispnew.c, so I'm not sure
redisplay_window alone will be enough.

And why only "before window start"? what about beyond window's end?

> but I have no idea how to mark the window's cursor as invisible, so
> to speak.

I think you need to add something to the struct window's 'cursor' and
'phys_cursor' members.

> Or perhaps this is an X/Y question, but I'm not sure.

You cannot express coordinates outside of the window as X/Y.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 12:58           ` Eli Zaretskii
@ 2021-11-28 13:10             ` Po Lu
  2021-11-28 13:44               ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-28 13:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> Hmm, thanks.  One small question though: what should I do in order to
>> make a window's cursor invisible?

> Prevent display_and_set_cursor from being called, I presume?

What if the cursor is already displayed?  Is it OK to call
erase_phys_cursor from redisplay_window?

> There's also set_cursor_from_row, which calculates where to display
> the cursor, and something should be done about that to indicate that
> the cursor is not shown at all.

set_cursor_from_row can just immediately return if I indicate that the
cursor is invisible because point is before or after window start,
correct?

> display_and_set_cursor is also called from dispnew.c, so I'm not sure
> redisplay_window alone will be enough.

Hmm, thanks, I think I might put that logic in display_and_set_cursor
instead.

> And why only "before window start"? what about beyond window's end?

I don't know how to get that information inside redisplay_window.
AFAIU, it's not available until redisplay completes.

> I think you need to add something to the struct window's 'cursor' and
> 'phys_cursor' members.

Okay, I'll do that.

> You cannot express coordinates outside of the window as X/Y.

I meant "X/Y" question as in a question that goes somewhat like this:

 - User wants to do X.
 - User doesn't know how to do X, but thinks they can fumble their way
   to a solution if they can just manage to do Y.
 - User doesn't know how to do Y either.
 - User asks for help with Y.
 - Others try to help user with Y, but are confused because Y seems like
   a strange problem to want to solve.
 - After much interaction and wasted time, it finally becomes clear that
   the user really wants help with X, and that Y wasn't even a suitable
   solution for X.

I hope this isn't the case here, but I can't explain what the "X" here
is in terms more specific than "I want the ability to have point outside
the window".

Thanks in advance.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 13:10             ` Po Lu
@ 2021-11-28 13:44               ` Eli Zaretskii
  2021-11-29  1:47                 ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28 13:44 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 28 Nov 2021 21:10:25 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> Hmm, thanks.  One small question though: what should I do in order to
> >> make a window's cursor invisible?
> 
> > Prevent display_and_set_cursor from being called, I presume?
> 
> What if the cursor is already displayed?  Is it OK to call
> erase_phys_cursor from redisplay_window?

Hmm... I guess it would be better to modify display_and_set_cursor so
that it only erases the cursor if needed, but doesn't display it.

> > There's also set_cursor_from_row, which calculates where to display
> > the cursor, and something should be done about that to indicate that
> > the cursor is not shown at all.
> 
> set_cursor_from_row can just immediately return if I indicate that the
> cursor is invisible because point is before or after window start,
> correct?

Yes, but that function is the one that sets the data in window's
'cursor' member, so in this case set_cursor_from_row should somehow
indicate there that the cursor is not visible or something.  I think.
There are several places in the display code which look at that data
and rely on it.

> > And why only "before window start"? what about beyond window's end?
> 
> I don't know how to get that information inside redisplay_window.
> AFAIU, it's not available until redisplay completes.

So you will have to add code somewhere that produces this information
and records it somewhere, right?  It would be not very useful to
support "point outside window, but only before the window" feature.

You will find in try_scrolling code that finds whether point is beyond
the window end.  You can do something similar where you need this
information.

> I hope this isn't the case here, but I can't explain what the "X" here
> is in terms more specific than "I want the ability to have point outside
> the window".

I don't see any way around carefully reading the code and finding all
the places where we assume that point is visible, or make it visible
if it isn't.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28  8:13     ` Po Lu
  2021-11-28  8:41       ` Eli Zaretskii
@ 2021-11-28 14:03       ` Alan Mackenzie
  2021-11-28 14:28         ` Eric S Fraga
                           ` (3 more replies)
  1 sibling, 4 replies; 111+ messages in thread
From: Alan Mackenzie @ 2021-11-28 14:03 UTC (permalink / raw)
  To: Po Lu; +Cc: Eli Zaretskii, emacs-devel

Hello, Po.

On Sun, Nov 28, 2021 at 16:13:46 +0800, Po Lu wrote:
> Eli Zaretskii <eliz@gnu.org> writes:

> >> From: Po Lu <luangruo@yahoo.com>
> >> Date: Sun, 28 Nov 2021 11:07:57 +0800

> >> I wonder what would be involved in allowing point to be outside a
> >> window's visible area.

Why would anybody want this?  Why do you want this?  This isn't a
rhetorical question.  Because other programs do it isn't a satisfactory
reason.

I detest this behaviour, and that Emacs doesn't (currently) do it is a
large part of the reason why I use Emacs.  With these other programs I
experience a constant background stress, that the careless or accidental
depression of a key causes BANG!!! and the text I was reading
disappears for ever, being overwritten by the text around point.

Also, as Eli has asked, how would you bring point back into the
currently displayed window position?  Please assume the user doesn't
have a mouse, or dislikes using it.  It would also be good to have some
means of setting the mark there.

> > Two steps:

> >   1. Design the feature: how would it work? which operations would
> >      bring point back into the viewport, and which won't?  For
> >      example, a simple question: if point is outside of the viewport,
> >      what is the effect of C-f or C-n on display?

> The effect would be move point forward, or to the next line, and then to
> recenter the window, so point is at the center of the window.

In other words, a complete replacement of the window's previously
displayed text by other text some arbitrary distance away.  This is what
I dislike so much, so hopefully this part of the feature would be
optional.

> Inserting text should probably recenter the window as well, if point is
> outside the visible area (this is also how other programs behave).

Again, this doesn't seem a good reason.

> This is how other programs behave as well.

> > IMO, it's a large job if done cleanly.  Patches welcome.

> Thanks, I will take a look at it.

I think Eli was underestimating when he said it would be a large job.
;-)

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:03       ` Alan Mackenzie
@ 2021-11-28 14:28         ` Eric S Fraga
  2021-11-28 14:39           ` Eli Zaretskii
  2021-11-28 14:42         ` dick
                           ` (2 subsequent siblings)
  3 siblings, 1 reply; 111+ messages in thread
From: Eric S Fraga @ 2021-11-28 14:28 UTC (permalink / raw)
  To: emacs-devel

On Sunday, 28 Nov 2021 at 14:03, Alan Mackenzie wrote:
> I detest this behaviour [...]  so hopefully this part of the feature
> would be optional.

+1

Invisible cursor is probably the one aspect of some other editors/word
processors that I hate the most.  And the one aspect of org mode's
folding approach that I dislike.  I would prefer for point to always be
on text that is visible.

-- 
Eric S Fraga with org 9.5 in Emacs 29.0.50 on Debian 11.1




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:28         ` Eric S Fraga
@ 2021-11-28 14:39           ` Eli Zaretskii
  2021-11-28 16:55             ` Eric S Fraga
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28 14:39 UTC (permalink / raw)
  To: Eric S Fraga; +Cc: emacs-devel

> From: Eric S Fraga <e.fraga@ucl.ac.uk>
> Date: Sun, 28 Nov 2021 14:28:39 +0000
> 
> On Sunday, 28 Nov 2021 at 14:03, Alan Mackenzie wrote:
> > I detest this behaviour [...]  so hopefully this part of the feature
> > would be optional.
> 
> +1

Don't worry, this will never be anything _but_ an optional feature.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:03       ` Alan Mackenzie
  2021-11-28 14:28         ` Eric S Fraga
@ 2021-11-28 14:42         ` dick
  2021-11-28 15:39         ` Kévin Le Gouguec
  2021-11-29  0:34         ` Po Lu
  3 siblings, 0 replies; 111+ messages in thread
From: dick @ 2021-11-28 14:42 UTC (permalink / raw)
  To: emacs-devel

AM> Why would anybody want this?  Why do you want this?  This isn't a
AM> rhetorical question.  Because other programs do it isn't a
AM> satisfactory reason.

I used to be involved in a poker game where sometimes I'd split the pot
in a stupid way.  One player would complain, and another player would
say, "Just let him do it the dumb way."

Hundreds of bad ideas float through emacs-devel per year, and for
whatever inexplicable reason, the maintainer entertains them all.  Most
if not all those ideas never see the light of day, or get
compartmentalized in a module never exercised in practice.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:03       ` Alan Mackenzie
  2021-11-28 14:28         ` Eric S Fraga
  2021-11-28 14:42         ` dick
@ 2021-11-28 15:39         ` Kévin Le Gouguec
  2021-11-28 15:45           ` Eli Zaretskii
  2021-11-28 16:59           ` Eric S Fraga
  2021-11-29  0:34         ` Po Lu
  3 siblings, 2 replies; 111+ messages in thread
From: Kévin Le Gouguec @ 2021-11-28 15:39 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Po Lu, Eli Zaretskii, emacs-devel

Alan Mackenzie <acm@muc.de> writes:

>> >> From: Po Lu <luangruo@yahoo.com>
>> >> Date: Sun, 28 Nov 2021 11:07:57 +0800
>
>> >> I wonder what would be involved in allowing point to be outside a
>> >> window's visible area.
>
> Why would anybody want this?  Why do you want this?  This isn't a
> rhetorical question.  Because other programs do it isn't a satisfactory
> reason.

I'll bite: because a user could want to peek a few screenfuls
ahead/behind, while keeping the cursor at a point of interest, where
they can come back with any point movement (e.g. arrow keys) or by just
typing in ("Greetings Mister… *scrolls back up* Mackenzie, RET RET I
trust this Sunday afternoon finds you well…").

Granted, IME I rarely want to check out an arbitrary "full screenfuls";
I usually want to check out the end of the current function (C-M-e) or
look for a pattern (C-s); if that takes me off-screen, C-u C-SPC brings
me right back where I was.

I wouldn't begrudge users who'd prefer the behaviour Po suggests,
though.

> I detest this behaviour, and that Emacs doesn't (currently) do it is a
> large part of the reason why I use Emacs.  With these other programs I
> experience a constant background stress, that the careless or accidental
> depression of a key causes BANG!!! and the text I was reading
> disappears for ever, being overwritten by the text around point.

I don't see how (what I understand of) Po's proposition could lead to
such catastrophic text Armageddon.  And in any case, Emacs's trusty undo
would surely come and save the day.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 15:39         ` Kévin Le Gouguec
@ 2021-11-28 15:45           ` Eli Zaretskii
  2021-11-28 17:14             ` Kévin Le Gouguec
  2021-11-28 16:59           ` Eric S Fraga
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-28 15:45 UTC (permalink / raw)
  To: Kévin Le Gouguec; +Cc: luangruo, acm, emacs-devel

> From: Kévin Le Gouguec <kevin.legouguec@gmail.com>
> Cc: Po Lu <luangruo@yahoo.com>,  Eli Zaretskii <eliz@gnu.org>,
>   emacs-devel@gnu.org
> Date: Sun, 28 Nov 2021 16:39:01 +0100
> 
> > I detest this behaviour, and that Emacs doesn't (currently) do it is a
> > large part of the reason why I use Emacs.  With these other programs I
> > experience a constant background stress, that the careless or accidental
> > depression of a key causes BANG!!! and the text I was reading
> > disappears for ever, being overwritten by the text around point.
> 
> I don't see how (what I understand of) Po's proposition could lead to
> such catastrophic text Armageddon.  And in any case, Emacs's trusty undo
> would surely come and save the day.

Undo doesn't undo display operations, only editing operations.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:39           ` Eli Zaretskii
@ 2021-11-28 16:55             ` Eric S Fraga
  0 siblings, 0 replies; 111+ messages in thread
From: Eric S Fraga @ 2021-11-28 16:55 UTC (permalink / raw)
  To: emacs-devel

On Sunday, 28 Nov 2021 at 16:39, Eli Zaretskii wrote:
> Don't worry, this will never be anything _but_ an optional feature.

Not worried really; just letting Alan know he's not alone! ;-)

Thank you in any case!
-- 
Eric S Fraga with org 9.5 in Emacs 29.0.50 on Debian 11.1




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 15:39         ` Kévin Le Gouguec
  2021-11-28 15:45           ` Eli Zaretskii
@ 2021-11-28 16:59           ` Eric S Fraga
  2021-11-28 17:30             ` Kévin Le Gouguec
  2021-11-29  0:34             ` Dmitry Gutov
  1 sibling, 2 replies; 111+ messages in thread
From: Eric S Fraga @ 2021-11-28 16:59 UTC (permalink / raw)
  To: emacs-devel

On Sunday, 28 Nov 2021 at 16:39, Kévin Le Gouguec wrote:
> I'll bite: because a user could want to peek a few screenfuls
> ahead/behind, while keeping the cursor at a point of interest, where

That's what C-x 2 M-C-v is for? ;-)

Seriously, it's what Emacs does so well that other editors do not: being
able to have multiple views into the same file/buffer.

or, alternatively, set a mark, scroll away, and jump back...

-- 
Eric S Fraga with org 9.5 in Emacs 29.0.50 on Debian 11.1




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 15:45           ` Eli Zaretskii
@ 2021-11-28 17:14             ` Kévin Le Gouguec
  0 siblings, 0 replies; 111+ messages in thread
From: Kévin Le Gouguec @ 2021-11-28 17:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: luangruo, acm, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Kévin Le Gouguec <kevin.legouguec@gmail.com>
>> Cc: Po Lu <luangruo@yahoo.com>,  Eli Zaretskii <eliz@gnu.org>,
>>   emacs-devel@gnu.org
>> Date: Sun, 28 Nov 2021 16:39:01 +0100
>> 
>> > I detest this behaviour, and that Emacs doesn't (currently) do it is a
>> > large part of the reason why I use Emacs.  With these other programs I
>> > experience a constant background stress, that the careless or accidental
>> > depression of a key causes BANG!!! and the text I was reading
>> > disappears for ever, being overwritten by the text around point.
>> 
>> I don't see how (what I understand of) Po's proposition could lead to
>> such catastrophic text Armageddon.  And in any case, Emacs's trusty undo
>> would surely come and save the day.
>
> Undo doesn't undo display operations, only editing operations.

Right; on my first read (and second, and third… I did try to understand
Alan's problem scenario and its consequences, but I obviously failed), I
interpreted "[disappearing] forever, being overwritten" as describing an
editing hazard (akin to erasing the selection in a single keystroke when
delete-selection-mode is on) rather than a "sudden shift of viewport"
issue.

FWIW I find the absence of a blinking cursor conspicuous enough to
remind me that the cursor is actually somewhere else (and that typing in
text or moving around will bring me back to this cursor), but I
understand the concern.

"Keep the feature opt-in" sounds like a good way to avoid surprises;
even for people who might enable the feature, maybe having a fringe
indicator at the top or the bottom of the window (to indicate in which
direction point lies) would help?  (Especially if it blinks according to
blink-cursor-mode)



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 16:59           ` Eric S Fraga
@ 2021-11-28 17:30             ` Kévin Le Gouguec
  2021-11-29  0:34             ` Dmitry Gutov
  1 sibling, 0 replies; 111+ messages in thread
From: Kévin Le Gouguec @ 2021-11-28 17:30 UTC (permalink / raw)
  To: Eric S Fraga; +Cc: emacs-devel

Eric S Fraga <e.fraga@ucl.ac.uk> writes:

> On Sunday, 28 Nov 2021 at 16:39, Kévin Le Gouguec wrote:
>> I'll bite: because a user could want to peek a few screenfuls
>> ahead/behind, while keeping the cursor at a point of interest, where
>
> That's what C-x 2 M-C-v is for? ;-)
>
> Seriously, it's what Emacs does so well that other editors do not: being
> able to have multiple views into the same file/buffer.

Sure, although off the top of my head I can't see a faster way to delete
the temporary window than "C-x o C-x 0" (granted, that's not the most
awkward sequence ever).

(Assuming the user has other windows of interest, so dismissing C-x 1)

> or, alternatively, set a mark, scroll away, and jump back...

Yes, as I described.  As I said, I personally find C-u C-SPC enough for
my purposes, but I can imagine other people liking the proposed
behaviour better.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 16:59           ` Eric S Fraga
  2021-11-28 17:30             ` Kévin Le Gouguec
@ 2021-11-29  0:34             ` Dmitry Gutov
  1 sibling, 0 replies; 111+ messages in thread
From: Dmitry Gutov @ 2021-11-29  0:34 UTC (permalink / raw)
  To: Eric S Fraga, emacs-devel

On 28.11.2021 19:59, Eric S Fraga wrote:
> On Sunday, 28 Nov 2021 at 16:39, Kévin Le Gouguec wrote:
>> I'll bite: because a user could want to peek a few screenfuls
>> ahead/behind, while keeping the cursor at a point of interest, where
> That's what C-x 2 M-C-v is for?;-)

And with the feature in question you would just scroll with the mouse 
however far you like, and then return to the original location with e.g. 
'C-f'. Far fewer keypresses.

I've used this kind of approach often back in another (non-Emacser) 
life, and I've missed that ability for a number of years afterward.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 14:03       ` Alan Mackenzie
                           ` (2 preceding siblings ...)
  2021-11-28 15:39         ` Kévin Le Gouguec
@ 2021-11-29  0:34         ` Po Lu
  2021-12-08  1:45           ` John Ankarström
  3 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-29  0:34 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Eli Zaretskii, emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> Why would anybody want this?  Why do you want this?  This isn't a
> rhetorical question.  Because other programs do it isn't a satisfactory
> reason.

> I detest this behaviour, and that Emacs doesn't (currently) do it is a
> large part of the reason why I use Emacs.  With these other programs I
> experience a constant background stress, that the careless or accidental
> depression of a key causes BANG!!! and the text I was reading
> disappears for ever, being overwritten by the text around point.

> Also, as Eli has asked, how would you bring point back into the
> currently displayed window position?  Please assume the user doesn't
> have a mouse, or dislikes using it.  It would also be good to have some
> means of setting the mark there.

The feature will be optional, of course.  And it will certainly be
disabled by default, because I don't want it either.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-28 13:44               ` Eli Zaretskii
@ 2021-11-29  1:47                 ` Po Lu
  2021-11-29 13:00                   ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-29  1:47 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

Hmm, thanks.  Another question: is the only code that moves either point
or window start in order to constrain point into the window in
redisplay_window, under `recenter' and `try_to_scroll'?

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-29  1:47                 ` Po Lu
@ 2021-11-29 13:00                   ` Eli Zaretskii
  2021-11-29 13:22                     ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-29 13:00 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 29 Nov 2021 09:47:39 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> Hmm, thanks.  Another question: is the only code that moves either point
> or window start in order to constrain point into the window in
> redisplay_window, under `recenter' and `try_to_scroll'?

A $64M question ;-)  Your opinion about my memory is too good.

But in general, I think you are looking at a too-narrow aspect of this
behavior.  First, even in xdisp.c there are several places where we
attempt to redisplay a window, and then reject the results because
point is not fully visible in the window.  "Reject" here sometimes
mean another, fresh attempt of redisplaying the window with some
conditions or variables modified to yield a success.  These places
should be audited in order to decide whether the rejection is
justified under this new feature.

Second, look at the scrolling code in window.c: under some conditions,
it decides up front that point will be invisible, and then either
moves point or modifies the scroll (i.e. the window-start).  That,
too, will have to be revisited and carefully audited.

And there could be other places as well, I simply don't remember.

So I think the answer to your question is, unfortunately, NO.  The
problem is wider and more complex than just the scrolling code in
redisplay_window in these two places.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-29 13:00                   ` Eli Zaretskii
@ 2021-11-29 13:22                     ` Po Lu
  2021-11-29 13:43                       ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-11-29 13:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> A $64M question ;-)  Your opinion about my memory is too good.
>
> But in general, I think you are looking at a too-narrow aspect of this
> behavior.  First, even in xdisp.c there are several places where we
> attempt to redisplay a window, and then reject the results because
> point is not fully visible in the window.  "Reject" here sometimes
> mean another, fresh attempt of redisplaying the window with some
> conditions or variables modified to yield a success.  These places
> should be audited in order to decide whether the rejection is
> justified under this new feature.

Thanks, I will look at it.  One last question though:

> Second, look at the scrolling code in window.c: under some conditions,
> it decides up front that point will be invisible, and then either
> moves point or modifies the scroll (i.e. the window-start).  That,
> too, will have to be revisited and carefully audited.

Is any of that scrolling code used during redisplay (for instance, if
the window start is changed?)  The main use I have for this feature will
be in the pixel scrolling code, which doesn't use any of the normal
scrolling commands.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-29 13:22                     ` Po Lu
@ 2021-11-29 13:43                       ` Eli Zaretskii
  2021-11-30  1:40                         ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-29 13:43 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 29 Nov 2021 21:22:18 +0800
> 
> > Second, look at the scrolling code in window.c: under some conditions,
> > it decides up front that point will be invisible, and then either
> > moves point or modifies the scroll (i.e. the window-start).  That,
> > too, will have to be revisited and carefully audited.
> 
> Is any of that scrolling code used during redisplay (for instance, if
> the window start is changed?)

It certainly affects redisplay.  It might also be called by redisplay,
I don't remember.  However, ...

> The main use I have for this feature will be in the pixel scrolling
> code, which doesn't use any of the normal scrolling commands.

... it makes very little sense to me to introduce such a feature only
for the sake of pixel-wise scrolling.  We should allow such a mode
with all the Emacs commands, not just in pixel-wise scrolling.  And
anyway, Emacs might call scrolling commands as part of other commands,
not just because the user types C-v.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-29 13:43                       ` Eli Zaretskii
@ 2021-11-30  1:40                         ` Po Lu
  2021-11-30 16:49                           ` [External] : " Drew Adams
  2021-12-04 11:18                           ` Po Lu
  0 siblings, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-11-30  1:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> It might also be called by redisplay,

That is what I'm worried about, thanks.

> ... it makes very little sense to me to introduce such a feature only
> for the sake of pixel-wise scrolling.  We should allow such a mode
> with all the Emacs commands, not just in pixel-wise scrolling.  And
> anyway, Emacs might call scrolling commands as part of other commands,
> not just because the user types C-v.

Makes sense, I will work on it.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* RE: [External] : Re: Allowing point to be outside the window?
  2021-11-30  1:40                         ` Po Lu
@ 2021-11-30 16:49                           ` Drew Adams
  2021-11-30 17:26                             ` Eli Zaretskii
  2021-12-04 11:18                           ` Po Lu
  1 sibling, 1 reply; 111+ messages in thread
From: Drew Adams @ 2021-11-30 16:49 UTC (permalink / raw)
  To: Po Lu, Eli Zaretskii; +Cc: emacs-devel@gnu.org

A probably OT question that occurred to me.
Ignore if not relevant/helpful.

If we add the ability (as an option) to
let point be off-window, I'm guessing it
might entail the risk that someone uses
`C-w' and is not aware of perhaps killing
a chunk of text (perhaps a big chunk).

Should we maybe consider an (optional)
warning message or an optional confirmation
requirement?

A wild guess is that other editors, which
allow this off-window behavior, somehow
don't have such a risk of cutting lots of
text more or less silently.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 16:49                           ` [External] : " Drew Adams
@ 2021-11-30 17:26                             ` Eli Zaretskii
  2021-11-30 18:10                               ` Lars Ingebrigtsen
  2021-11-30 23:20                               ` Stefan Monnier
  0 siblings, 2 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-30 17:26 UTC (permalink / raw)
  To: Drew Adams; +Cc: luangruo, emacs-devel

> From: Drew Adams <drew.adams@oracle.com>
> CC: "emacs-devel@gnu.org" <emacs-devel@gnu.org>
> Date: Tue, 30 Nov 2021 16:49:46 +0000
> 
> If we add the ability (as an option) to
> let point be off-window, I'm guessing it
> might entail the risk that someone uses
> `C-w' and is not aware of perhaps killing
> a chunk of text (perhaps a big chunk).
> 
> Should we maybe consider an (optional)
> warning message or an optional confirmation
> requirement?

I think kill commands that use point should be disabled when point is
outside of the viewport.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 17:26                             ` Eli Zaretskii
@ 2021-11-30 18:10                               ` Lars Ingebrigtsen
  2021-11-30 18:32                                 ` Eli Zaretskii
                                                   ` (2 more replies)
  2021-11-30 23:20                               ` Stefan Monnier
  1 sibling, 3 replies; 111+ messages in thread
From: Lars Ingebrigtsen @ 2021-11-30 18:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: luangruo, Drew Adams, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> I think kill commands that use point should be disabled when point is
> outside of the viewport.

Perhaps all (well, most) commands should be disabled in this case?
I.e., all commands do nothing except display point?

It's not what (say) Libreoffice does, though -- if you've marked some
text, scroll away with the scroll bar, and then hit DEL, it'll delete
the marked text...

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 18:10                               ` Lars Ingebrigtsen
@ 2021-11-30 18:32                                 ` Eli Zaretskii
  2021-11-30 18:49                                 ` Stefan Kangas
  2021-11-30 23:41                                 ` Daniel Martín
  2 siblings, 0 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-30 18:32 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: luangruo, drew.adams, emacs-devel

> From: Lars Ingebrigtsen <larsi@gnus.org>
> Cc: Drew Adams <drew.adams@oracle.com>,  luangruo@yahoo.com,
>   emacs-devel@gnu.org
> Date: Tue, 30 Nov 2021 19:10:13 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > I think kill commands that use point should be disabled when point is
> > outside of the viewport.
> 
> Perhaps all (well, most) commands should be disabled in this case?
> I.e., all commands do nothing except display point?

I don't know.  I'd need to think about this.  But "all commands"
sounds too many ;-)

> It's not what (say) Libreoffice does, though -- if you've marked some
> text, scroll away with the scroll bar, and then hit DEL, it'll delete
> the marked text...

Those "other applications" only support region operations on
highlighted text, which avoids using (the equivalent of) point.  Emacs
could do that as well (but it will have to preserve point in this
hypothetical mode when highlighting text with point outside of the
viewport).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 18:10                               ` Lars Ingebrigtsen
  2021-11-30 18:32                                 ` Eli Zaretskii
@ 2021-11-30 18:49                                 ` Stefan Kangas
  2021-11-30 19:21                                   ` Eli Zaretskii
  2021-11-30 23:41                                 ` Daniel Martín
  2 siblings, 1 reply; 111+ messages in thread
From: Stefan Kangas @ 2021-11-30 18:49 UTC (permalink / raw)
  To: Lars Ingebrigtsen, Eli Zaretskii; +Cc: luangruo, Drew Adams, emacs-devel

Lars Ingebrigtsen <larsi@gnus.org> writes:

> Perhaps all (well, most) commands should be disabled in this case?
> I.e., all commands do nothing except display point?

Why?  We don't do anything like that when mark is not in the window.

The benefit of this feature for me would be to be able to look around
and then immediately edit where I left point/mark, as I can do in other
text editors.

> It's not what (say) Libreoffice does, though -- if you've marked some
> text, scroll away with the scroll bar, and then hit DEL, it'll delete
> the marked text...

AFAIK, pretty much all graphical text editor and word processors behaves
like LibreOffice in this regard.  Maybe there are some exceptions, but I
can't think of any.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 18:49                                 ` Stefan Kangas
@ 2021-11-30 19:21                                   ` Eli Zaretskii
  2021-11-30 20:57                                     ` Drew Adams
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-11-30 19:21 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: luangruo, larsi, drew.adams, emacs-devel

> From: Stefan Kangas <stefankangas@gmail.com>
> Date: Tue, 30 Nov 2021 10:49:06 -0800
> Cc: luangruo@yahoo.com, Drew Adams <drew.adams@oracle.com>, emacs-devel@gnu.org
> 
> Lars Ingebrigtsen <larsi@gnus.org> writes:
> 
> > Perhaps all (well, most) commands should be disabled in this case?
> > I.e., all commands do nothing except display point?
> 
> Why?  We don't do anything like that when mark is not in the window.

What if neither mark nor point are visible?

Anyway, an alternative would be to warp point back into view if some
editing command is invoked.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* RE: [External] : Re: Allowing point to be outside the window?
  2021-11-30 19:21                                   ` Eli Zaretskii
@ 2021-11-30 20:57                                     ` Drew Adams
  0 siblings, 0 replies; 111+ messages in thread
From: Drew Adams @ 2021-11-30 20:57 UTC (permalink / raw)
  To: Eli Zaretskii, Stefan Kangas
  Cc: luangruo@yahoo.com, larsi@gnus.org, emacs-devel@gnu.org

My main purpose in raising this possible gotcha
was just to point to it.

And yes, the potential problem is wider than just
`C-w'.
___


As for possible remedies -

1. A user option, to specify preferred behavior,
   for sure.

   This could even be per-command, either by way
   of the option value or via a command-symbol
   property - see below, at the end.

2. Some possible choices for the behavior:
   a. Do nothing special (just act, silently).
   b. Show a message that point is off-window.
      (Could also do it even for mark off-window).
   c. Require y/n confirmation, to act.
   d. Don't allow the command (raise an error).
___

For 2c, it might be good, while waiting for
confirmation, to let you use a key to indicate the
region in some way.  Here's one way (similar to
`C-x C-x' behavior):

Move temporarily to point - and when repeated,
move to mark, i.e., toggle between them.  And
highlight the region text, to make clear on
which side of the boundaries the text to be
operated on lies.

Another possibility would be to automatically
bounce to show you the off-window point location.
But I think letting you do that on demand makes
more sense.

IOW, let me tell Emacs to show me point, before
I decide whether to confirm, but also let me
just confirm or not, without showing me point.
___

In any case, by default the feature should be
off: no off-window point.
___

Another possibility might be to force users who
want the ability to act on the region when point
is off-window to use different commands for that.

That is, bind particular (new) commands that
allow it.  This would let you, say, permit some 
operations but not others.  You could even be
able to make a _per-command_ choice like what
the user-option allows.

And each command that lets you act on a region
when point is off-window would be disabled, by
default.  E.g.,

  (put 'kill-region-maybe-offscreen 'disabled)



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 17:26                             ` Eli Zaretskii
  2021-11-30 18:10                               ` Lars Ingebrigtsen
@ 2021-11-30 23:20                               ` Stefan Monnier
  1 sibling, 0 replies; 111+ messages in thread
From: Stefan Monnier @ 2021-11-30 23:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Drew Adams, luangruo, emacs-devel

> I think kill commands that use point should be disabled when point is
> outside of the viewport.

I suspect this would not be worth the trouble and could be annoying in
some cases where the users know very well what they're doing.

Let the users shoot themselves in the foot if they want.
There's `undo` to reattach the foot anyway.


        Stefan




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 18:10                               ` Lars Ingebrigtsen
  2021-11-30 18:32                                 ` Eli Zaretskii
  2021-11-30 18:49                                 ` Stefan Kangas
@ 2021-11-30 23:41                                 ` Daniel Martín
  2021-12-01  8:30                                   ` martin rudalics
  2 siblings, 1 reply; 111+ messages in thread
From: Daniel Martín @ 2021-11-30 23:41 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: Eli Zaretskii, luangruo, Drew Adams, emacs-devel

Lars Ingebrigtsen <larsi@gnus.org> writes:

>
> It's not what (say) Libreoffice does, though -- if you've marked some
> text, scroll away with the scroll bar, and then hit DEL, it'll delete
> the marked text...

It's also how other text editors that implement this feature work; the
text is killed and the window is scrolled to make the point visible
again.

So perhaps users of this feature would not be surprised if Emacs does
the same thing.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-11-30 23:41                                 ` Daniel Martín
@ 2021-12-01  8:30                                   ` martin rudalics
  2021-12-01  9:10                                     ` Juri Linkov
  0 siblings, 1 reply; 111+ messages in thread
From: martin rudalics @ 2021-12-01  8:30 UTC (permalink / raw)
  To: Daniel Martín, Lars Ingebrigtsen
  Cc: luangruo, Eli Zaretskii, Drew Adams, emacs-devel

 >> It's not what (say) Libreoffice does, though -- if you've marked some
 >> text, scroll away with the scroll bar, and then hit DEL, it'll delete
 >> the marked text...
 >
 > It's also how other text editors that implement this feature work; the
 > text is killed and the window is scrolled to make the point visible
 > again.
 >
 > So perhaps users of this feature would not be surprised if Emacs does
 > the same thing.

A GUI Emacs does that already.  With emacs -Q do

(setq truncate-lines t)
(toggle-horizontal-scroll-bar t)
C-x 3

Now put the region on say "To create", use the mouse on the horizontal
scroll bar to drag that region completely out of view and type C-w.

martin



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: [External] : Re: Allowing point to be outside the window?
  2021-12-01  8:30                                   ` martin rudalics
@ 2021-12-01  9:10                                     ` Juri Linkov
  0 siblings, 0 replies; 111+ messages in thread
From: Juri Linkov @ 2021-12-01  9:10 UTC (permalink / raw)
  To: martin rudalics
  Cc: emacs-devel, luangruo, Lars Ingebrigtsen, Daniel Martín,
	Eli Zaretskii, Drew Adams

> A GUI Emacs does that already.  With emacs -Q do
>
> (setq truncate-lines t)
> (toggle-horizontal-scroll-bar t)
> C-x 3
>
> Now put the region on say "To create", use the mouse on the horizontal
> scroll bar to drag that region completely out of view and type C-w.

Interesting, it works even without the horizontal scroll bar,
after typing 'C-x <' (scroll-left).  But it automatically
scrolls back only when using the horizontal scroll bar.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-30  1:40                         ` Po Lu
  2021-11-30 16:49                           ` [External] : " Drew Adams
@ 2021-12-04 11:18                           ` Po Lu
  2021-12-04 12:55                             ` Eli Zaretskii
  2021-12-04 13:00                             ` dick
  1 sibling, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-12-04 11:18 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 743 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>> It might also be called by redisplay,
>
> That is what I'm worried about, thanks.
>
>> ... it makes very little sense to me to introduce such a feature only
>> for the sake of pixel-wise scrolling.  We should allow such a mode
>> with all the Emacs commands, not just in pixel-wise scrolling.  And
>> anyway, Emacs might call scrolling commands as part of other commands,
>> not just because the user types C-v.
>
> Makes sense, I will work on it.

I haven't figured out most of the user level command things, but here's
a patch that allows point to be outside a window.

Some redisplay optimizations are disabled, but I hope to solve that
soon.

WDYT?  Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Make-it-possible-for-point-to-appear-outside-a-windo.patch --]
[-- Type: text/x-patch, Size: 13173 bytes --]

From f2295fbfceff3ef0db91c9c5b9e5155fefe12697 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Sat, 4 Dec 2021 18:33:53 +0800
Subject: [PATCH] Make it possible for point to appear outside a window

* src/window.h (struct window): New field `cursor_visible_p'.

* src/xdisp.c (redisplay_internal, redisplay_window): Allow
point to appear outside the window.

(set_cursor_from_row): Set cursor_visible_p appropriately.
(try_window_id)
(try_cursor_movement): Disable optimization when point is
allowed to be invisible.

(display_and_set_cursor): Erase cursor if invisible.

(try_window): Allow some things.
(syms_of_xdisp) <keep-point-visible>: New variable.
---
 src/window.h |   4 ++
 src/xdisp.c  | 194 ++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 150 insertions(+), 48 deletions(-)

diff --git a/src/window.h b/src/window.h
index 2400c422c1..e9528ff02f 100644
--- a/src/window.h
+++ b/src/window.h
@@ -445,6 +445,10 @@ #define WINDOW_H_INCLUDED
        window.  */
     bool_bf suspend_auto_hscroll : 1;
 
+    /* True if the cursor is supposed to be visible in this
+       window.  */
+    bool_bf cursor_visible_p : 1;
+
     /* Amount by which lines of this window are scrolled in
        y-direction (smooth scrolling).  */
     int vscroll;
diff --git a/src/xdisp.c b/src/xdisp.c
index 7ca3977200..7a0949d514 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16118,6 +16118,8 @@ #define AINC(a,i)							\
   tlbufpos = this_line_start_pos;
   tlendpos = this_line_end_pos;
   if (!consider_all_windows_p
+      /* TODO: enable this optimization.  */
+      && keep_point_visible
       && CHARPOS (tlbufpos) > 0
       && !w->update_mode_line
       && !current_buffer->clip_changed
@@ -16989,6 +16991,8 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
      comes from a text property, not from an overlay.  */
   bool string_from_text_prop = false;
 
+  w->cursor_visible_p = false;
+
   /* Don't even try doing anything if called for a mode-line or
      header-line or tab-line row, since the rest of the code isn't
      prepared to deal with such calamities.  */
@@ -17575,6 +17579,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
 	CHARPOS (this_line_start_pos) = 0;
     }
 
+  w->cursor_visible_p = true;
   return true;
 }
 
@@ -18183,6 +18188,10 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
     return rc;
 #endif
 
+  /* TODO: enable this optimization.  */
+  if (!keep_point_visible)
+    return CURSOR_MOVEMENT_CANNOT_BE_USED;
+
   /* Previously, there was a check for Lisp integer in the
      if-statement below. Now, this field is converted to
      ptrdiff_t, thus zero means invalid position in a buffer.  */
@@ -18770,6 +18779,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       clear_glyph_matrix (w->desired_matrix);
     }
 
+  debug_method_add (w, "real redisplay starts");
+
   /* Otherwise set up data on this window; select its buffer and point
      value.  */
   /* Really select the buffer, for the sake of buffer-local
@@ -18964,34 +18975,39 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
       if (w->cursor.vpos < 0)
 	{
-	  /* If point does not appear, try to move point so it does
-	     appear.  The desired matrix has been built above, so we
-	     can use it here.  First see if point is in invisible
-	     text, and if so, move it to the first visible buffer
-	     position past that.  */
-	  struct glyph_row *r = NULL;
-	  Lisp_Object invprop =
-	    get_char_property_and_overlay (make_fixnum (PT), Qinvisible,
-					   Qnil, NULL);
-
-	  if (TEXT_PROP_MEANS_INVISIBLE (invprop) != 0)
+	  if (!keep_point_visible)
+	    w->cursor_visible_p = false;
+	  else
 	    {
-	      ptrdiff_t alt_pt;
-	      Lisp_Object invprop_end =
-		Fnext_single_char_property_change (make_fixnum (PT), Qinvisible,
-						   Qnil, Qnil);
+	      /* If point does not appear, try to move point so it does
+		 appear.  The desired matrix has been built above, so we
+		 can use it here.  First see if point is in invisible
+		 text, and if so, move it to the first visible buffer
+		 position past that.  */
+	      struct glyph_row *r = NULL;
+	      Lisp_Object invprop =
+		get_char_property_and_overlay (make_fixnum (PT), Qinvisible,
+					       Qnil, NULL);
+
+	      if (TEXT_PROP_MEANS_INVISIBLE (invprop) != 0)
+		{
+		  ptrdiff_t alt_pt;
+		  Lisp_Object invprop_end =
+		    Fnext_single_char_property_change (make_fixnum (PT), Qinvisible,
+						       Qnil, Qnil);
 
-	      if (FIXNATP (invprop_end))
-		alt_pt = XFIXNAT (invprop_end);
-	      else
-		alt_pt = ZV;
-	      r = row_containing_pos (w, alt_pt, w->desired_matrix->rows,
-				      NULL, 0);
+		  if (FIXNATP (invprop_end))
+		    alt_pt = XFIXNAT (invprop_end);
+		  else
+		    alt_pt = ZV;
+		  r = row_containing_pos (w, alt_pt, w->desired_matrix->rows,
+					  NULL, 0);
+		}
+	      if (r)
+		new_vpos = MATRIX_ROW_BOTTOM_Y (r);
+	      else	/* Give up and just move to the middle of the window.  */
+		new_vpos = window_box_height (w) / 2;
 	    }
-	  if (r)
-	    new_vpos = MATRIX_ROW_BOTTOM_Y (r);
-	  else	/* Give up and just move to the middle of the window.  */
-	    new_vpos = window_box_height (w) / 2;
 	}
 
       if (!cursor_row_fully_visible_p (w, false, false, false))
@@ -19223,10 +19239,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  IF_DEBUG (debug_method_add (w, "1"));
 	  clear_glyph_matrix (w->desired_matrix);
 	  if (try_window (window, startp, TRY_WINDOW_CHECK_MARGINS) < 0)
-	    /* -1 means we need to scroll.
-	       0 means we need new matrices, but fonts_changed
-	       is set in that case, so we will detect it below.  */
-	    goto try_to_scroll;
+	    {
+	      /* -1 means we need to scroll.
+		 0 means we need new matrices, but fonts_changed
+		 is set in that case, so we will detect it below.  */
+	      goto try_to_scroll;
+	    }
 	}
 
       if (f->fonts_changed)
@@ -19253,9 +19271,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	clear_glyph_matrix (w->desired_matrix);
     }
 
+  if (!keep_point_visible)
+    goto maybe_try_window;
+
  try_to_scroll:
 
-  /* Redisplay the mode line.  Select the buffer properly for that.  */
+ /* Redisplay the mode line.  Select the buffer properly for that.  */
   if (!update_mode_line)
     {
       update_mode_line = true;
@@ -19317,6 +19338,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   /* Determine the window start relative to point.  */
   init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
   it.current_y = it.last_visible_y;
+
   if (centering_position < 0)
     {
       ptrdiff_t margin_pos = CHARPOS (startp);
@@ -19584,6 +19606,53 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       centering_position = 0;
       goto recenter;
     }
+  goto done;
+
+ maybe_try_window:
+
+  /* Set the window start position here explicitly if it is outside
+     the accessible portion of the buffer.  */
+
+  if (CHARPOS (startp) < BEGV
+      || CHARPOS (startp) > ZV)
+    {
+      if (CHARPOS (startp) < BEGV)
+	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
+      else
+	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
+
+      SET_TEXT_POS_FROM_MARKER (startp, w->start);
+
+      /* Run scroll hooks.  */
+      startp = run_window_scroll_functions (window, startp);
+    }
+
+  /* We invoke try_window and try_window_reusing_current_matrix below,
+     and they manipulate the bidi cache.  Save and restore the cache
+     state of our iterator, so we could continue using it after that.  */
+  itdata = bidi_shelve_cache ();
+
+  /* Redisplay the window.  */
+  use_desired_matrix = false;
+  if (!current_matrix_up_to_date_p
+      || windows_or_buffers_changed
+      || f->cursor_type_changed
+      /* Don't use try_window_reusing_current_matrix in this case
+	 because it can have changed the buffer.  */
+      || !NILP (Vwindow_scroll_functions)
+      || !just_this_one_p
+      || MINI_WINDOW_P (w)
+      || !(used_current_matrix_p
+	   = try_window_reusing_current_matrix (w)))
+    use_desired_matrix = (try_window (window, startp, 0) == 1);
+
+  bidi_unshelve_cache (itdata, false);
+
+  /* If new fonts have been loaded (due to fontsets), give up.  We
+     have to start a new redisplay since we need to re-adjust glyph
+     matrices.  */
+  if (f->fonts_changed)
+    goto need_larger_matrices;
 
  done:
 
@@ -19591,6 +19660,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   w->start_at_line_beg = (CHARPOS (startp) == BEGV
 			  || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n');
 
+  if (!keep_point_visible
+      && (!w->cursor_visible_p
+	  || w->cursor.vpos == -1)
+      && w->phys_cursor_on_p)
+    erase_phys_cursor (w);
+
   /* Display the mode line, header line, and tab-line, if we must.  */
   if ((update_mode_line
        /* If window not full width, must redo its mode line
@@ -19801,6 +19876,8 @@ try_window (Lisp_Object window, struct text_pos pos, int flags)
   struct frame *f = XFRAME (w->frame);
   int cursor_vpos = w->cursor.vpos;
 
+  debug_method_add (w, "try_window");
+
   /* Make POS the new window start.  */
   set_marker_both (w->start, Qnil, CHARPOS (pos), BYTEPOS (pos));
 
@@ -19960,6 +20037,10 @@ try_window_reusing_current_matrix (struct window *w)
     return false;
 #endif
 
+  /* TODO: enable this optimization.  */
+  if (!keep_point_visible)
+    return false;
+
   /* The variable new_start now holds the new window start.  The old
      start `start' can be determined from the current matrix.  */
   SET_TEXT_POS_FROM_MARKER (new_start, w->start);
@@ -20875,6 +20956,8 @@ #define GIVE_UP(X) return 0
 	  row = row_containing_pos (w, PT, r0, NULL, 0);
 	  if (row)
 	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
+	  else
+	    w->cursor_visible_p = false;
 	  return 1;
 	}
     }
@@ -20915,6 +20998,8 @@ #define GIVE_UP(X) return 0
 	  row = row_containing_pos (w, PT, r0, NULL, 0);
 	  if (row)
 	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
+	  else
+	    w->cursor_visible_p = false;
 	  return 2;
 	}
     }
@@ -21152,6 +21237,8 @@ #define GIVE_UP(X) return 0
 				    last_unchanged_at_beg_row + 1, 0);
 	  if (row)
 	    set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
+	  else
+	    w->cursor_visible_p = false;
 	}
 
       /* Start from first_unchanged_at_end_row looking for PT.  */
@@ -21162,6 +21249,8 @@ #define GIVE_UP(X) return 0
 	  if (row)
 	    set_cursor_from_row (w, row, w->current_matrix, delta,
 				 delta_bytes, dy, dvpos);
+	  else
+	    w->cursor_visible_p = false;
 	}
 
       /* Give up if cursor was not found.  */
@@ -21173,24 +21262,25 @@ #define GIVE_UP(X) return 0
     }
 
   /* Don't let the cursor end in the scroll margins.  */
-  {
-    int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
-    int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;
-
-    if ((w->cursor.y < this_scroll_margin
-	 && CHARPOS (start) > BEGV)
-	/* Old redisplay didn't take scroll margin into account at the bottom,
-	   but then global-hl-line-mode doesn't scroll.  KFS 2004-06-14 */
-	|| (w->cursor.y
-	    + (cursor_row_fully_visible_p (w, false, true, true)
-	       ? 1
-	       : cursor_height + this_scroll_margin)) > it.last_visible_y)
-      {
-	w->cursor.vpos = -1;
-	clear_glyph_matrix (w->desired_matrix);
-	return -1;
-      }
-  }
+  if (w->cursor_visible_p)
+    {
+      int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
+      int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;
+
+      if ((w->cursor.y < this_scroll_margin
+	   && CHARPOS (start) > BEGV)
+	  /* Old redisplay didn't take scroll margin into account at the bottom,
+	     but then global-hl-line-mode doesn't scroll.  KFS 2004-06-14 */
+	  || (w->cursor.y
+	      + (cursor_row_fully_visible_p (w, false, true, true)
+		 ? 1
+		 : cursor_height + this_scroll_margin)) > it.last_visible_y)
+	{
+	  w->cursor.vpos = -1;
+	  clear_glyph_matrix (w->desired_matrix);
+	  return -1;
+	}
+    }
 
   /* Scroll the display.  Do it before changing the current matrix so
      that xterm.c doesn't get confused about where the cursor glyph is
@@ -32387,6 +32477,10 @@ display_and_set_cursor (struct window *w, bool on,
 	      && new_cursor_width != w->phys_cursor_width)))
     erase_phys_cursor (w);
 
+  if (!w->cursor_visible_p
+      || w->cursor.vpos == -1)
+    return;
+
   /* Don't check phys_cursor_on_p here because that flag is only set
      to false in some cases where we know that the cursor has been
      completely erased, to avoid the extra work of erasing the cursor
@@ -35491,6 +35585,10 @@ syms_of_xdisp (void)
   x_stretch_cursor_p = 0;
 #endif
 
+  DEFVAR_BOOL ("keep-point-visible", keep_point_visible,
+	       doc: /* Non-nil means to keep the point visible.  */);
+  keep_point_visible = 1;
+
   DEFVAR_LISP ("show-trailing-whitespace", Vshow_trailing_whitespace,
     doc: /* Non-nil means highlight trailing whitespace.
 The face used for trailing whitespace is `trailing-whitespace'.  */);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 11:18                           ` Po Lu
@ 2021-12-04 12:55                             ` Eli Zaretskii
  2021-12-04 13:13                               ` Po Lu
  2021-12-04 13:00                             ` dick
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-04 12:55 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sat, 04 Dec 2021 19:18:38 +0800
> 
> I haven't figured out most of the user level command things, but here's
> a patch that allows point to be outside a window.
> 
> Some redisplay optimizations are disabled, but I hope to solve that
> soon.
> 
> WDYT?  Thanks.

This "allows point to be outside a window" in what situations?

In general, it's hard to provide useful feedback without understanding
the main ideas of the changes.  And you didn't add any comments to
help in that.  So I'm left to wonder whether some changes I don't
think I understand are just my misunderstanding of your ideas, or your
misunderstanding of the code.

So just a few random thoughts I had while reading the changes:

> @@ -16989,6 +16991,8 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
>       comes from a text property, not from an overlay.  */
>    bool string_from_text_prop = false;
>  
> +  w->cursor_visible_p = false;
> +
>    /* Don't even try doing anything if called for a mode-line or
>       header-line or tab-line row, since the rest of the code isn't
>       prepared to deal with such calamities.  */
> @@ -17575,6 +17579,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
>  	CHARPOS (this_line_start_pos) = 0;
>      }
>  
> +  w->cursor_visible_p = true;
>    return true;
>  }

Shouldn't set_cursor_from_row be disabled/bypassed in this mode?

And I don't understand the dance with the cursor_visible_p flag: why
do you reset it at entry and set back before exiting?  Isn't the value
of w->cursor enough to tell whether the cursor position was computed
or not?

> +  if (!keep_point_visible)
> +    goto maybe_try_window;
> +
>   try_to_scroll:

I don't understand this change.  Are you forcibly trying to avoid
calling try_scrolling?  If so, why?  That function is only loosely
related to scrolling commands; in fact, if you type C-v in "emacs -Q",
you will never see it called.  This function is an optimization of a
window's redisplay, and that is needed even in this new mode you are
implementing.

> + maybe_try_window:
> +
> +  /* Set the window start position here explicitly if it is outside
> +     the accessible portion of the buffer.  */
> +
> +  if (CHARPOS (startp) < BEGV
> +      || CHARPOS (startp) > ZV)
> +    {
> +      if (CHARPOS (startp) < BEGV)
> +	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
> +      else
> +	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
> +
> +      SET_TEXT_POS_FROM_MARKER (startp, w->start);
> +
> +      /* Run scroll hooks.  */
> +      startp = run_window_scroll_functions (window, startp);
> +    }
> +
> +  /* We invoke try_window and try_window_reusing_current_matrix below,
> +     and they manipulate the bidi cache.  Save and restore the cache
> +     state of our iterator, so we could continue using it after that.  */
> +  itdata = bidi_shelve_cache ();

This and the rest of this hunk is a copy of code we have elsewhere,
and I'm not sure I understand why you needed that.

> @@ -20875,6 +20956,8 @@ #define GIVE_UP(X) return 0
>  	  row = row_containing_pos (w, PT, r0, NULL, 0);
>  	  if (row)
>  	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
> +	  else
> +	    w->cursor_visible_p = false;
>  	  return 1;
>  	}
>      }
> @@ -20915,6 +20998,8 @@ #define GIVE_UP(X) return 0
>  	  row = row_containing_pos (w, PT, r0, NULL, 0);
>  	  if (row)
>  	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
> +	  else
> +	    w->cursor_visible_p = false;
>  	  return 2;
>  	}
>      }
> @@ -21152,6 +21237,8 @@ #define GIVE_UP(X) return 0
>  				    last_unchanged_at_beg_row + 1, 0);
>  	  if (row)
>  	    set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
> +	  else
> +	    w->cursor_visible_p = false;
>  	}
>  
>        /* Start from first_unchanged_at_end_row looking for PT.  */
> @@ -21162,6 +21249,8 @@ #define GIVE_UP(X) return 0
>  	  if (row)
>  	    set_cursor_from_row (w, row, w->current_matrix, delta,
>  				 delta_bytes, dy, dvpos);
> +	  else
> +	    w->cursor_visible_p = false;
>  	}

Are these really needed? why?

> +  if (w->cursor_visible_p)
> +    {
> +      int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
> +      int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;

The condition above doesn't check the keep_point_visible flag, why?

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 11:18                           ` Po Lu
  2021-12-04 12:55                             ` Eli Zaretskii
@ 2021-12-04 13:00                             ` dick
  2021-12-04 13:14                               ` tomas
  2021-12-04 13:19                               ` Po Lu
  1 sibling, 2 replies; 111+ messages in thread
From: dick @ 2021-12-04 13:00 UTC (permalink / raw)
  Cc: emacs-devel

PL> +  if (!keep_point_visible
PL> +      && (!w->cursor_visible_p
PL> +	  || w->cursor.vpos == -1)
PL> +      && w->phys_cursor_on_p)

The display code is already rife with a hundred state variables, all of
which invite laborious combinations of if-clauses to accommodate
(something EZ is rather expert at), all of them sources of FUD and
puzzlement.

cursor_visible_p is yet another state indicator that multiplies the
state space by 2.  I can't oppose it (I can't seem to oppose anything)
if it will revive emacs's fortunes, but the state profileration really
galls.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 12:55                             ` Eli Zaretskii
@ 2021-12-04 13:13                               ` Po Lu
  2021-12-04 16:24                                 ` Eli Zaretskii
  2021-12-04 17:15                                 ` Eli Zaretskii
  0 siblings, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-12-04 13:13 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> This "allows point to be outside a window" in what situations?

If you move point out of the visible area of the window (such as with
`goto-char') or if you set the window start but do not force it.

> In general, it's hard to provide useful feedback without understanding
> the main ideas of the changes.  And you didn't add any comments to
> help in that.  So I'm left to wonder whether some changes I don't
> think I understand are just my misunderstanding of your ideas, or your
> misunderstanding of the code.

I will work on that, sorry.

> So just a few random thoughts I had while reading the changes:

> Shouldn't set_cursor_from_row be disabled/bypassed in this mode?
>
> And I don't understand the dance with the cursor_visible_p flag: why
> do you reset it at entry and set back before exiting?  Isn't the value
> of w->cursor enough to tell whether the cursor position was computed
> or not?

The idea is that if `set_cursor_from_row' returns false (say if the
cursor was not found to be in the row), w->cursor_visible will also be
set to false.

I don't understand why set_cursor_from_row should be disabled though:
isn't it responsible for placing the cursor on the display (and as such,
isn't it required for the cursor to be displayed when point is _inside_
the window?).

>> +  if (!keep_point_visible)
>> +    goto maybe_try_window;
>> +
>>   try_to_scroll:

> I don't understand this change.  Are you forcibly trying to avoid
> calling try_scrolling?  If so, why?  That function is only loosely
> related to scrolling commands; in fact, if you type C-v in "emacs -Q",
> you will never see it called.  This function is an optimization of a
> window's redisplay, and that is needed even in this new mode you are
> implementing.

Doesn't `try_scrolling' try to constrain point inside if it is too near
the end of the window?  (And isn't the optimization that tries to decide
whether or not to blit the display if appropriate `scrolling_window' in
dispextern.c?)

>> + maybe_try_window:
>> +
>> +  /* Set the window start position here explicitly if it is outside
>> +     the accessible portion of the buffer.  */
>> +
>> +  if (CHARPOS (startp) < BEGV
>> +      || CHARPOS (startp) > ZV)
>> +    {
>> +      if (CHARPOS (startp) < BEGV)
>> +	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
>> +      else
>> +	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
>> +
>> +      SET_TEXT_POS_FROM_MARKER (startp, w->start);
>> +
>> +      /* Run scroll hooks.  */
>> +      startp = run_window_scroll_functions (window, startp);
>> +    }
>> +
>> +  /* We invoke try_window and try_window_reusing_current_matrix below,
>> +     and they manipulate the bidi cache.  Save and restore the cache
>> +     state of our iterator, so we could continue using it after that.  */
>> +  itdata = bidi_shelve_cache ();

> This and the rest of this hunk is a copy of code we have elsewhere,
> and I'm not sure I understand why you needed that.

I think I can keep it underneath the recenter label, thanks.

>> @@ -20875,6 +20956,8 @@ #define GIVE_UP(X) return 0
>>  	  row = row_containing_pos (w, PT, r0, NULL, 0);
>>  	  if (row)
>>  	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
>> +	  else
>> +	    w->cursor_visible_p = false;
>>  	  return 1;
>>  	}
>>      }
>> @@ -20915,6 +20998,8 @@ #define GIVE_UP(X) return 0
>>  	  row = row_containing_pos (w, PT, r0, NULL, 0);
>>  	  if (row)
>>  	    set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0);
>> +	  else
>> +	    w->cursor_visible_p = false;
>>  	  return 2;
>>  	}
>>      }
>> @@ -21152,6 +21237,8 @@ #define GIVE_UP(X) return 0
>>  				    last_unchanged_at_beg_row + 1, 0);
>>  	  if (row)
>>  	    set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
>> +	  else
>> +	    w->cursor_visible_p = false;
>>  	}
>>  
>>        /* Start from first_unchanged_at_end_row looking for PT.  */
>> @@ -21162,6 +21249,8 @@ #define GIVE_UP(X) return 0
>>  	  if (row)
>>  	    set_cursor_from_row (w, row, w->current_matrix, delta,
>>  				 delta_bytes, dy, dvpos);
>> +	  else
>> +	    w->cursor_visible_p = false;
>>  	}

> Are these really needed? why?

I think that's not needed anymore.  I will delete it.

>> +  if (w->cursor_visible_p)
>> +    {
>> +      int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
>> +      int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;

> The condition above doesn't check the keep_point_visible flag, why?

w->cursor_visible_p should always be true if keep_point_visible is not
nil by the time we get there.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:00                             ` dick
@ 2021-12-04 13:14                               ` tomas
  2021-12-04 13:19                               ` Po Lu
  1 sibling, 0 replies; 111+ messages in thread
From: tomas @ 2021-12-04 13:14 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 457 bytes --]

On Sat, Dec 04, 2021 at 08:00:16AM -0500, dick wrote:
> PL> +  if (!keep_point_visible
> PL> +      && (!w->cursor_visible_p
> PL> +	  || w->cursor.vpos == -1)
> PL> +      && w->phys_cursor_on_p)
> 
> The display code is already rife [...]
> [...] FUD [...]

> [...]  I can't oppose it [...]

You can oppose, as anyone else. Have you considered working your tone?
Perhaps your opposition might be taken more seriously, then.

Cheers
-- 
t

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:00                             ` dick
  2021-12-04 13:14                               ` tomas
@ 2021-12-04 13:19                               ` Po Lu
  2021-12-04 13:41                                 ` Eli Zaretskii
  2021-12-04 14:17                                 ` dick
  1 sibling, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-12-04 13:19 UTC (permalink / raw)
  To: dick; +Cc: emacs-devel

dick <dick.r.chiang@gmail.com> writes:

> PL> +  if (!keep_point_visible
> PL> +      && (!w->cursor_visible_p
> PL> +	  || w->cursor.vpos == -1)
> PL> +      && w->phys_cursor_on_p)

> The display code is already rife with a hundred state variables, all of
> which invite laborious combinations of if-clauses to accommodate

I am aware of that.

> (something EZ is rather expert at), all of them sources of FUD and
> puzzlement.

Why FUD?  What is the fear, and where is the uncertainty and doubt?

> cursor_visible_p is yet another state indicator that multiplies the
> state space by 2.  I can't oppose it (I can't seem to oppose anything)
> if it will revive emacs's fortunes

You, personally, are free to work on other things that interest you,
just as I will with my interests.  Emacs is free software after all.

If anything, adding the ability to have point outside a window will only
serve positively towards Emacs's fortunes, as this has been a feature
many people have wanted for a while now.

cursor_visible_p is also conceptually simple: if it is false, the cursor
is not inside the window.  I fail to see how it can multiply the state
space by any significant amount.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:19                               ` Po Lu
@ 2021-12-04 13:41                                 ` Eli Zaretskii
  2021-12-05  0:46                                   ` Po Lu
  2021-12-04 14:17                                 ` dick
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-04 13:41 UTC (permalink / raw)
  To: emacs-devel, Po Lu, dick

On December 4, 2021 3:19:57 PM GMT+02:00, Po Lu <luangruo@yahoo.com> wrote:
> dick <dick.r.chiang@gmail.com> writes:
> 
> > PL> +  if (!keep_point_visible
> > PL> +      && (!w->cursor_visible_p
> > PL> +	  || w->cursor.vpos == -1)
> > PL> +      && w->phys_cursor_on_p)
> 
> > The display code is already rife with a hundred state variables, all of
> > which invite laborious combinations of if-clauses to accommodate
> 
> I am aware of that.


But you could perhaps avoid a separate flag if you'd set w->cursor.vpos to a negative value when the cursor is outside of the window.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:19                               ` Po Lu
  2021-12-04 13:41                                 ` Eli Zaretskii
@ 2021-12-04 14:17                                 ` dick
  2021-12-04 16:33                                   ` Eli Zaretskii
  1 sibling, 1 reply; 111+ messages in thread
From: dick @ 2021-12-04 14:17 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> Why FUD?  What is the fear, and where is the uncertainty and doubt?

When you bypass large state-heavy stanzas via "if allow-point-outside"
in central functions like try_cursor_movement() and redisplay_window(),
people (not you or EZ apparently) get scared.

What will likely happen is you'll palm off the new flag
"allow-point-outside" (i.e., !keep-point-visible), which will default
off such that none of your state changes will ever take.  You will be
content with having tallied additional slocs.

The not-so-hidden cost of your change however is obfuscation, which I
suppose is par for the course in the emacs code base.

> just as I will with my interests

You gotta do what you gotta do.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:13                               ` Po Lu
@ 2021-12-04 16:24                                 ` Eli Zaretskii
  2021-12-05  0:40                                   ` Po Lu
  2021-12-04 17:15                                 ` Eli Zaretskii
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-04 16:24 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sat, 04 Dec 2021 21:13:40 +0800
> 
> > This "allows point to be outside a window" in what situations?
> 
> If you move point out of the visible area of the window (such as with
> `goto-char') or if you set the window start but do not force it.

Which commands did you try in addition to goto-char?  What about
scrolling commands?

> > Shouldn't set_cursor_from_row be disabled/bypassed in this mode?
> >
> > And I don't understand the dance with the cursor_visible_p flag: why
> > do you reset it at entry and set back before exiting?  Isn't the value
> > of w->cursor enough to tell whether the cursor position was computed
> > or not?
> 
> The idea is that if `set_cursor_from_row' returns false (say if the
> cursor was not found to be in the row), w->cursor_visible will also be
> set to false.

But you are just wasting cycles that way.  When it is known that point
is outside of the window, that function will end up being called for
every screen line we lay out.  Instead, just set the result without
calling it in those cases.

> I don't understand why set_cursor_from_row should be disabled though:
> isn't it responsible for placing the cursor on the display (and as such,
> isn't it required for the cursor to be displayed when point is _inside_
> the window?).

Yes.  I meant when you know that point is outside of the window.

> > I don't understand this change.  Are you forcibly trying to avoid
> > calling try_scrolling?  If so, why?  That function is only loosely
> > related to scrolling commands; in fact, if you type C-v in "emacs -Q",
> > you will never see it called.  This function is an optimization of a
> > window's redisplay, and that is needed even in this new mode you are
> > implementing.
> 
> Doesn't `try_scrolling' try to constrain point inside if it is too near
> the end of the window?

Among other things, yes.  But its main purpose is not to move point,
it's to find a suitable window-start position.

> (And isn't the optimization that tries to decide whether or not to
> blit the display if appropriate `scrolling_window' in dispextern.c?)

scrolling_window belongs to the second phase of a redisplay cycle,
where we actually deliver glyphs to the glass.  try_scrolling is from
the first phase, where we decide what should be on the glass.

In any case, both try_scrolling and scrolling_window are not about
bringing point back into view, they are about redrawing the window so
that it correctly reflects the buffer contents.  So these functions
cannot and shouldn't be bypassed, even in this mode.

> > The condition above doesn't check the keep_point_visible flag, why?
> 
> w->cursor_visible_p should always be true if keep_point_visible is not
> nil by the time we get there.

There should be a comment there about this, and perhaps also an
assertion.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 14:17                                 ` dick
@ 2021-12-04 16:33                                   ` Eli Zaretskii
  2021-12-04 17:13                                     ` dick
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-04 16:33 UTC (permalink / raw)
  To: dick; +Cc: luangruo, emacs-devel

> From: dick <dick.r.chiang@gmail.com>
> Date: Sat, 04 Dec 2021 09:17:41 -0500
> Cc: emacs-devel@gnu.org
> 
> When you bypass large state-heavy stanzas via "if allow-point-outside"
> in central functions like try_cursor_movement() and redisplay_window(),
> people (not you or EZ apparently) get scared.

Only people who have no clear idea about the overall structure of
redisplay_window can become nervous when they see such bypasses, as a
matter of principle, without considering what exactly is bypassed and
why.  A place to begin is by figuring out what does that "try" in
"try_cursor_movement" means.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 16:33                                   ` Eli Zaretskii
@ 2021-12-04 17:13                                     ` dick
  2021-12-05  0:48                                       ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: dick @ 2021-12-04 17:13 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: luangruo, emacs-devel

> Only people who have no clear idea about the overall structure of

Having spent the last month trying to fix longlines, I believe
I'm among the top five people in the world most entitled to be nervous.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:13                               ` Po Lu
  2021-12-04 16:24                                 ` Eli Zaretskii
@ 2021-12-04 17:15                                 ` Eli Zaretskii
  2021-12-05  0:45                                   ` Po Lu
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-04 17:15 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sat, 04 Dec 2021 21:13:40 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > In general, it's hard to provide useful feedback without understanding
> > the main ideas of the changes.  And you didn't add any comments to
> > help in that.  So I'm left to wonder whether some changes I don't
> > think I understand are just my misunderstanding of your ideas, or your
> > misunderstanding of the code.
> 
> I will work on that, sorry.

Btw, one issue that I couldn't figure out from reading the patch is
how do you intend to distinguish between point going out of the
viewport by an explicit user command (which AFAIU is what we want to
support in this new mode), and point going out of the viewport for
some other reason, for example text insertion before point?
Specifically, the bother is this: if we just disable/bypass all the
places where Emacs brings point back into the window, we could find
point outside of the window without the user meaning that to happen,
and the disabled/bypassed code will then leave point invisible.

So I think a single variable set by the user once and globally is not
enough to control the changes in the display code to support this
mode.  We need some variable that is per window and is set dynamically
depending on what caused point to go out of the viewport.  This will
quite naturally lead to defining the command that we will allow to
create such a situation, because only those commands will be able to
set that dynamic variable, and only for a window where those commands
were invoked.  (I guess some of the scrolling commands will be in that
group.)



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 16:24                                 ` Eli Zaretskii
@ 2021-12-05  0:40                                   ` Po Lu
  0 siblings, 0 replies; 111+ messages in thread
From: Po Lu @ 2021-12-05  0:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Which commands did you try in addition to goto-char?  What about
> scrolling commands?

I haven't worked on any of the scrolling commands yet: they still keep
point inside the window.

>> The idea is that if `set_cursor_from_row' returns false (say if the
>> cursor was not found to be in the row), w->cursor_visible will also be
>> set to false.

> But you are just wasting cycles that way.  When it is known that point
> is outside of the window, that function will end up being called for
> every screen line we lay out.  Instead, just set the result without
> calling it in those cases.

>> I don't understand why set_cursor_from_row should be disabled though:
>> isn't it responsible for placing the cursor on the display (and as such,
>> isn't it required for the cursor to be displayed when point is _inside_
>> the window?).

> Yes.  I meant when you know that point is outside of the window.

Thanks, I understand your point now.

>> Doesn't `try_scrolling' try to constrain point inside if it is too near
>> the end of the window?

> Among other things, yes.  But its main purpose is not to move point,
> it's to find a suitable window-start position.

Thanks.

>> (And isn't the optimization that tries to decide whether or not to
>> blit the display if appropriate `scrolling_window' in dispextern.c?)

> scrolling_window belongs to the second phase of a redisplay cycle,
> where we actually deliver glyphs to the glass.  try_scrolling is from
> the first phase, where we decide what should be on the glass.
>
> In any case, both try_scrolling and scrolling_window are not about
> bringing point back into view, they are about redrawing the window so
> that it correctly reflects the buffer contents.  So these functions
> cannot and shouldn't be bypassed, even in this mode.
>
>> > The condition above doesn't check the keep_point_visible flag, why?

>> w->cursor_visible_p should always be true if keep_point_visible is not
>> nil by the time we get there.

> There should be a comment there about this, and perhaps also an
> assertion.

Okay, I will do that.  Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 17:15                                 ` Eli Zaretskii
@ 2021-12-05  0:45                                   ` Po Lu
  2021-12-05  9:03                                     ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-05  0:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Btw, one issue that I couldn't figure out from reading the patch is
> how do you intend to distinguish between point going out of the
> viewport by an explicit user command (which AFAIU is what we want to
> support in this new mode), and point going out of the viewport for
> some other reason, for example text insertion before point?
> Specifically, the bother is this: if we just disable/bypass all the
> places where Emacs brings point back into the window, we could find
> point outside of the window without the user meaning that to happen,
> and the disabled/bypassed code will then leave point invisible.

Hmm, how about not allowing point to _move_ out of the window, instead
only allowing window start to move so that point becomes invisible?

For instance, we could enable the code that scrolls the window to follow
point if PT != w->point (if that makes sense).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 13:41                                 ` Eli Zaretskii
@ 2021-12-05  0:46                                   ` Po Lu
  2021-12-05  7:12                                     ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-05  0:46 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel, dick

Eli Zaretskii <eliz@gnu.org> writes:

> But you could perhaps avoid a separate flag if you'd set
> w->cursor.vpos to a negative value when the cursor is outside of the
> window.

Actually, it now seems that cursor_visible_p is currently totally
redundant: cursor.vpos is already -1 when the cursor is supposed to be
invisible.

Can that value be relied on?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-04 17:13                                     ` dick
@ 2021-12-05  0:48                                       ` Po Lu
  0 siblings, 0 replies; 111+ messages in thread
From: Po Lu @ 2021-12-05  0:48 UTC (permalink / raw)
  To: dick; +Cc: Eli Zaretskii, emacs-devel

dick <dick.r.chiang@gmail.com> writes:

>> Only people who have no clear idea about the overall structure of
>
> Having spent the last month trying to fix longlines, I believe
> I'm among the top five people in the world most entitled to be nervous.

Why are you looking at redisplay_window to solve that problem?

AFAIU, it's caused by the move_it_* functions scanning backwards for
newline characters.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  0:46                                   ` Po Lu
@ 2021-12-05  7:12                                     ` Eli Zaretskii
  2021-12-05  7:16                                       ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-05  7:12 UTC (permalink / raw)
  To: Po Lu; +Cc: dick.r.chiang, emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org,  dick <dick.r.chiang@gmail.com>
> Date: Sun, 05 Dec 2021 08:46:52 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > But you could perhaps avoid a separate flag if you'd set
> > w->cursor.vpos to a negative value when the cursor is outside of the
> > window.
> 
> Actually, it now seems that cursor_visible_p is currently totally
> redundant: cursor.vpos is already -1 when the cursor is supposed to be
> invisible.
> 
> Can that value be relied on?

Depends what for and how you want to rely on it.  It basically says
that the cursor position inside the window is unknown.  The "normal"
redisplay must always calculate some reasonable coordinates and put
them there, otherwise the cursor will not be visible, which is a
display bug.  If that is enough for you, then yes, you can rely on it.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  7:12                                     ` Eli Zaretskii
@ 2021-12-05  7:16                                       ` Po Lu
  2021-12-05  8:48                                         ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-05  7:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: dick.r.chiang, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: emacs-devel@gnu.org,  dick <dick.r.chiang@gmail.com>
>> Date: Sun, 05 Dec 2021 08:46:52 +0800
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> > But you could perhaps avoid a separate flag if you'd set
>> > w->cursor.vpos to a negative value when the cursor is outside of the
>> > window.
>> 
>> Actually, it now seems that cursor_visible_p is currently totally
>> redundant: cursor.vpos is already -1 when the cursor is supposed to be
>> invisible.
>> 
>> Can that value be relied on?
>
> Depends what for and how you want to rely on it.  It basically says
> that the cursor position inside the window is unknown.  The "normal"
> redisplay must always calculate some reasonable coordinates and put
> them there, otherwise the cursor will not be visible, which is a
> display bug.  If that is enough for you, then yes, you can rely on it.

I meant to ask whether or not I can forgo calculating whether point is
outside the window, and just test whether or not cursor.vpos is -1 to
determine if the cursor should be displayed, when point is allowed to be
outside the window.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  7:16                                       ` Po Lu
@ 2021-12-05  8:48                                         ` Eli Zaretskii
  2021-12-05  9:15                                           ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-05  8:48 UTC (permalink / raw)
  To: Po Lu; +Cc: dick.r.chiang, emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
> Date: Sun, 05 Dec 2021 15:16:13 +0800
> 
> >> Can that value be relied on?
> >
> > Depends what for and how you want to rely on it.  It basically says
> > that the cursor position inside the window is unknown.  The "normal"
> > redisplay must always calculate some reasonable coordinates and put
> > them there, otherwise the cursor will not be visible, which is a
> > display bug.  If that is enough for you, then yes, you can rely on it.
> 
> I meant to ask whether or not I can forgo calculating whether point is
> outside the window, and just test whether or not cursor.vpos is -1 to
> determine if the cursor should be displayed, when point is allowed to be
> outside the window.

No.  I think it should be the other way around: first see if point is
outside of the window, then force cursor.vpos to be negative.  That's
because redisplay resets that value to -1 whenever it isn't sure the
cursor will stay in its old position.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  0:45                                   ` Po Lu
@ 2021-12-05  9:03                                     ` Eli Zaretskii
  2021-12-06  2:11                                       ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-05  9:03 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 05 Dec 2021 08:45:14 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Btw, one issue that I couldn't figure out from reading the patch is
> > how do you intend to distinguish between point going out of the
> > viewport by an explicit user command (which AFAIU is what we want to
> > support in this new mode), and point going out of the viewport for
> > some other reason, for example text insertion before point?
> > Specifically, the bother is this: if we just disable/bypass all the
> > places where Emacs brings point back into the window, we could find
> > point outside of the window without the user meaning that to happen,
> > and the disabled/bypassed code will then leave point invisible.
> 
> Hmm, how about not allowing point to _move_ out of the window, instead
> only allowing window start to move so that point becomes invisible?

That'd cover scrolling a window in a way that leaves point outside of
it.  But Emacs also sets the window-start position in other
situations.  Moreover, the display engine itself does that, see
redisplay_window.

So some logic and/or flags will be needed to distinguish between the
cases.

> For instance, we could enable the code that scrolls the window to follow
> point if PT != w->point (if that makes sense).

So any change in the numerical value of point will scroll the window
to bring point into the view?  That'd be fine if that's what users
expect.  But do they?  E.g., what happens if point is below the
window, and you paste (with the mouse) some text into the visible
portion of the window (which doesn't show point)?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  8:48                                         ` Eli Zaretskii
@ 2021-12-05  9:15                                           ` Po Lu
  2021-12-05  9:25                                             ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-05  9:15 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: dick.r.chiang, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
>> Date: Sun, 05 Dec 2021 15:16:13 +0800
>> 
>> >> Can that value be relied on?
>> >
>> > Depends what for and how you want to rely on it.  It basically says
>> > that the cursor position inside the window is unknown.  The "normal"
>> > redisplay must always calculate some reasonable coordinates and put
>> > them there, otherwise the cursor will not be visible, which is a
>> > display bug.  If that is enough for you, then yes, you can rely on it.
>> 
>> I meant to ask whether or not I can forgo calculating whether point is
>> outside the window, and just test whether or not cursor.vpos is -1 to
>> determine if the cursor should be displayed, when point is allowed to be
>> outside the window.
>
> No.  I think it should be the other way around: first see if point is
> outside of the window, then force cursor.vpos to be negative.  That's
> because redisplay resets that value to -1 whenever it isn't sure the
> cursor will stay in its old position.

Thanks.  Another question or two: I can't use the value "-1" for
cursor.vpos to indicate that point is outside the window, as it's
already treated specially by redisplay, correct?

Also, where should I put the code to test if cursor is invisible?  I
think it should be after the label force_start, as forcing the start
could potentially adjust point to be inside the window.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  9:15                                           ` Po Lu
@ 2021-12-05  9:25                                             ` Eli Zaretskii
  2021-12-05  9:31                                               ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-05  9:25 UTC (permalink / raw)
  To: Po Lu; +Cc: dick.r.chiang, emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
> Date: Sun, 05 Dec 2021 17:15:25 +0800
> 
> >> I meant to ask whether or not I can forgo calculating whether point is
> >> outside the window, and just test whether or not cursor.vpos is -1 to
> >> determine if the cursor should be displayed, when point is allowed to be
> >> outside the window.
> >
> > No.  I think it should be the other way around: first see if point is
> > outside of the window, then force cursor.vpos to be negative.  That's
> > because redisplay resets that value to -1 whenever it isn't sure the
> > cursor will stay in its old position.
> 
> Thanks.  Another question or two: I can't use the value "-1" for
> cursor.vpos to indicate that point is outside the window, as it's
> already treated specially by redisplay, correct?

No, you can't.  But the question is why do you need to?  -1 there
means the cursor position is unknown.  Why do you need to know more
than that, plus the fact that point is _allowed_ to be outside of the
window?

> Also, where should I put the code to test if cursor is invisible?  I
> think it should be after the label force_start, as forcing the start
> could potentially adjust point to be inside the window.

It goes back to the same question I asked above: why do you need to
know that point is outside of the window? for what purposes?

IOW, why not just let redisplay do its thing, without bothering to
bring point into the window, when that is allowed?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  9:25                                             ` Eli Zaretskii
@ 2021-12-05  9:31                                               ` Po Lu
  2021-12-05 10:34                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-05  9:31 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: dick.r.chiang, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
>> Date: Sun, 05 Dec 2021 17:15:25 +0800
>> 
>> >> I meant to ask whether or not I can forgo calculating whether point is
>> >> outside the window, and just test whether or not cursor.vpos is -1 to
>> >> determine if the cursor should be displayed, when point is allowed to be
>> >> outside the window.
>> >
>> > No.  I think it should be the other way around: first see if point is
>> > outside of the window, then force cursor.vpos to be negative.  That's
>> > because redisplay resets that value to -1 whenever it isn't sure the
>> > cursor will stay in its old position.
>> 
>> Thanks.  Another question or two: I can't use the value "-1" for
>> cursor.vpos to indicate that point is outside the window, as it's
>> already treated specially by redisplay, correct?
>
> No, you can't.  But the question is why do you need to?  -1 there
> means the cursor position is unknown.  Why do you need to know more
> than that, plus the fact that point is _allowed_ to be outside of the
> window?
>
>> Also, where should I put the code to test if cursor is invisible?  I
>> think it should be after the label force_start, as forcing the start
>> could potentially adjust point to be inside the window.
>
> It goes back to the same question I asked above: why do you need to
> know that point is outside of the window? for what purposes?
>
> IOW, why not just let redisplay do its thing, without bothering to
> bring point into the window, when that is allowed?

I need to erase the phys cursor and to tell display_and_set_cursor to
not draw it: otherwise, the ghost of the cursor remains on-screen even
after point is moved out.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  9:31                                               ` Po Lu
@ 2021-12-05 10:34                                                 ` Eli Zaretskii
  2021-12-05 10:37                                                   ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-05 10:34 UTC (permalink / raw)
  To: Po Lu; +Cc: dick.r.chiang, emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
> Date: Sun, 05 Dec 2021 17:31:03 +0800
> 
> >> Also, where should I put the code to test if cursor is invisible?  I
> >> think it should be after the label force_start, as forcing the start
> >> could potentially adjust point to be inside the window.
> >
> > It goes back to the same question I asked above: why do you need to
> > know that point is outside of the window? for what purposes?
> >
> > IOW, why not just let redisplay do its thing, without bothering to
> > bring point into the window, when that is allowed?
> 
> I need to erase the phys cursor and to tell display_and_set_cursor to
> not draw it: otherwise, the ghost of the cursor remains on-screen even
> after point is moved out.

Which probably means you need to put the code which determines whether
point is inside or outside of the window in update_window or
thereabouts?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05 10:34                                                 ` Eli Zaretskii
@ 2021-12-05 10:37                                                   ` Po Lu
  0 siblings, 0 replies; 111+ messages in thread
From: Po Lu @ 2021-12-05 10:37 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: dick.r.chiang, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: dick.r.chiang@gmail.com,  emacs-devel@gnu.org
>> Date: Sun, 05 Dec 2021 17:31:03 +0800
>> 
>> >> Also, where should I put the code to test if cursor is invisible?  I
>> >> think it should be after the label force_start, as forcing the start
>> >> could potentially adjust point to be inside the window.
>> >
>> > It goes back to the same question I asked above: why do you need to
>> > know that point is outside of the window? for what purposes?
>> >
>> > IOW, why not just let redisplay do its thing, without bothering to
>> > bring point into the window, when that is allowed?
>> 
>> I need to erase the phys cursor and to tell display_and_set_cursor to
>> not draw it: otherwise, the ghost of the cursor remains on-screen even
>> after point is moved out.

> Which probably means you need to put the code which determines whether
> point is inside or outside of the window in update_window or
> thereabouts?

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-05  9:03                                     ` Eli Zaretskii
@ 2021-12-06  2:11                                       ` Po Lu
  2021-12-06 14:13                                         ` Eli Zaretskii
  2021-12-09 11:45                                         ` Eli Zaretskii
  0 siblings, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-12-06  2:11 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1240 bytes --]

Eli Zaretskii <eliz@gnu.org> writes:

> That'd cover scrolling a window in a way that leaves point outside of
> it.  But Emacs also sets the window-start position in other
> situations.  Moreover, the display engine itself does that, see
> redisplay_window.

> So some logic and/or flags will be needed to distinguish between the
> cases.

We should not recenter the display in any case where the start is not
forced and point is not moved.

>> For instance, we could enable the code that scrolls the window to follow
>> point if PT != w->point (if that makes sense).

> So any change in the numerical value of point will scroll the window
> to bring point into the view?  That'd be fine if that's what users
> expect.  But do they?  E.g., what happens if point is below the
> window, and you paste (with the mouse) some text into the visible
> portion of the window (which doesn't show point)?

That's not a problem, as `mouse-yank-primary' and friends already call
mouse-set-point.

WDYT about the attached patch?  It recenters the display whenever PT !=
w->last_point, and it seems to work very well.

It also introduces a new variable `scroll-move-point' that controls if
the scrolling commands will try to move point to stay visible.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: diff.diff --]
[-- Type: text/x-patch, Size: 22133 bytes --]

diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 15cad88d59..814f9d0286 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -250,10 +250,18 @@ Auto Scrolling
 @section Automatic Scrolling
 
 @cindex automatic scrolling
-  Emacs performs @dfn{automatic scrolling} when point moves out of the
-visible portion of the text.  Normally, automatic scrolling centers
-point vertically in the window, but there are several ways to alter
-this behavior.
+  Emacs by default performs @dfn{automatic scrolling} when point moves
+out of the visible portion of the text.  Normally, automatic scrolling
+centers point vertically in the window, but there are several ways to
+alter this behavior.
+
+@vindex keep-point-visible
+  If @code{keep-point-visible} is nil, redisplay will not move recenter
+the display when the window start is changed.
+
+@vindex scroll-move-point
+  If @code{scroll-move-point} is nil, scrolling commands will not move
+point to keep it inside the visible part of the window.
 
 @vindex scroll-conservatively
 @vindex scroll-minibuffer-conservatively
diff --git a/etc/NEWS b/etc/NEWS
index a8b7dc56ba..7154f20ade 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -257,6 +257,15 @@ are met.  The conditions are given by the argument, which can be
 
 * Editing Changes in Emacs 29.1
 
+** New variable 'keep-point-visible'.
+This variable controls if redisplay will try to keep point visible
+inside the window.
+
++++
+** New variable 'scroll-move-point'.
+This variable controls if scrolling moves point to stay inside the
+window.
+
 ---
 ** Indentation of 'cl-flet' and 'cl-labels' has changed.
 These forms now indent like this:
diff --git a/src/window.c b/src/window.c
index e801ff821f..c54f8164d3 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5576,7 +5576,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      something like (scroll-down 1) with PT in the line before
      the partially visible one would recenter.  */
 
-  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
+  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos)
+      && scroll_move_point)
     {
       itdata = bidi_shelve_cache ();
       /* Move backward half the height of the window.  Performance note:
@@ -5657,8 +5658,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
 		  w->start_at_line_beg = true;
 		  wset_update_mode_line (w);
 		  /* Set force_start so that redisplay_window will run the
-		     window-scroll-functions.  */
-		  w->force_start = true;
+		     window-scroll-functions, unless scroll_move_point is false,
+		     in which case forcing the start will cause recentering.  */
+		  w->force_start = scroll_move_point;
 		  return;
 		}
 	    }
@@ -5842,8 +5844,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
       w->start_at_line_beg = (pos == BEGV || FETCH_BYTE (bytepos - 1) == '\n');
       wset_update_mode_line (w);
       /* Set force_start so that redisplay_window will run the
-	 window-scroll-functions.  */
-      w->force_start = true;
+	 window-scroll-functions, unless scroll_move_point is false,
+	 in which case forcing the start will cause recentering.  */
+      w->force_start = scroll_move_point;
     }
 
   /* The rest of this function uses current_y in a nonstandard way,
@@ -5855,157 +5858,161 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      even if there is a header line.  */
   this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
 
-  if (n > 0)
+  if (scroll_move_point)
     {
-      int last_y = it.last_visible_y - this_scroll_margin - 1;
-
-      /* We moved the window start towards ZV, so PT may be now
-	 in the scroll margin at the top.  */
-      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
-      if (IT_CHARPOS (it) == PT
-	  && it.current_y >= this_scroll_margin
-	  && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
-				    - WINDOW_HEADER_LINE_HEIGHT (w)
-	  && (NILP (Vscroll_preserve_screen_position)
-	      || EQ (Vscroll_preserve_screen_position, Qt)))
-	/* We found PT at a legitimate height.  Leave it alone.  */
-	;
-      else
+      if (n > 0)
 	{
-	  if (window_scroll_pixel_based_preserve_y >= 0)
-	    {
-	      /* Don't enter the scroll margin at the end of the window.  */
-	      int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
-
-	      /* If we have a header line, take account of it.  This
-		 is necessary because we set it.current_y to 0, above.  */
-	      move_it_to (&it, -1,
-			  window_scroll_pixel_based_preserve_x,
-			  goal_y - WINDOW_TAB_LINE_HEIGHT (w)
-				 - WINDOW_HEADER_LINE_HEIGHT (w),
-			  -1, MOVE_TO_Y | MOVE_TO_X);
-	    }
+	  int last_y = it.last_visible_y - this_scroll_margin - 1;
 
-	  /* Get out of the scroll margin at the top of the window.  */
-	  while (it.current_y < this_scroll_margin)
+	  /* We moved the window start towards ZV, so PT may be now
+	     in the scroll margin at the top.  */
+	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	  if (IT_CHARPOS (it) == PT
+	      && it.current_y >= this_scroll_margin
+	      && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
+	      - WINDOW_HEADER_LINE_HEIGHT (w)
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT at a legitimate height.  Leave it alone.  */
+	    ;
+	  else
 	    {
-	      int prev = it.current_y;
-	      move_it_by_lines (&it, 1);
-	      if (prev == it.current_y)
-		break;
+	      if (window_scroll_pixel_based_preserve_y >= 0)
+		{
+		  /* Don't enter the scroll margin at the end of the window.  */
+		  int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
+
+		  /* If we have a header line, take account of it.  This
+		     is necessary because we set it.current_y to 0, above.  */
+		  move_it_to (&it, -1,
+			      window_scroll_pixel_based_preserve_x,
+			      goal_y - WINDOW_TAB_LINE_HEIGHT (w)
+			      - WINDOW_HEADER_LINE_HEIGHT (w),
+			      -1, MOVE_TO_Y | MOVE_TO_X);
+		}
+
+	      /* Get out of the scroll margin at the top of the window.  */
+	      while (it.current_y < this_scroll_margin)
+		{
+		  int prev = it.current_y;
+		  move_it_by_lines (&it, 1);
+		  if (prev == it.current_y)
+		    break;
+		}
+	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+	      /* Fix up the Y position to preserve, if it is inside the
+		 scroll margin at the window top.  */
+	      if (window_scroll_pixel_based_preserve_y >= 0
+		  && window_scroll_pixel_based_preserve_y < this_scroll_margin)
+		window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	    }
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	  /* Fix up the Y position to preserve, if it is inside the
-	     scroll margin at the window top.  */
-	  if (window_scroll_pixel_based_preserve_y >= 0
-	      && window_scroll_pixel_based_preserve_y < this_scroll_margin)
-	    window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	}
-    }
-  else if (n < 0)
-    {
-      ptrdiff_t charpos, bytepos;
-      bool partial_p;
-
-      /* Save our position, for the
-	 window_scroll_pixel_based_preserve_y case.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* We moved the window start towards BEGV, so PT may be now
-	 in the scroll margin at the bottom.  */
-      move_it_to (&it, PT, -1,
-		  /* We subtract WINDOW_HEADER_LINE_HEIGHT because
-		     it.y is relative to the bottom of the header
-		     line, see above.  */
-		  (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
-				     - WINDOW_HEADER_LINE_HEIGHT (w)
-		   - partial_line_height (&it) - this_scroll_margin - 1),
-		  -1,
-		  MOVE_TO_POS | MOVE_TO_Y);
-
-      /* Save our position, in case it's correct.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* If PT is in the screen line at the last fully visible line,
-	 move_it_to will stop at X = 0 in that line, because the
-	 required Y coordinate is reached there.  See if we can get to
-	 PT without descending lower in Y, and if we can, it means we
-	 reached PT before the scroll margin.  */
-      if (charpos != PT)
+      else if (n < 0)
 	{
-	  struct it it2;
-	  void *it_data;
+	  ptrdiff_t charpos, bytepos;
+	  bool partial_p;
 
-	  it2 = it;
-	  it_data = bidi_shelve_cache ();
-	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
-	  if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+	  /* Save our position, for the
+	     window_scroll_pixel_based_preserve_y case.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* We moved the window start towards BEGV, so PT may be now
+	     in the scroll margin at the bottom.  */
+	  move_it_to (&it, PT, -1,
+		      /* We subtract WINDOW_HEADER_LINE_HEIGHT because
+			 it.y is relative to the bottom of the header
+			 line, see above.  */
+		      (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
+		       - WINDOW_HEADER_LINE_HEIGHT (w)
+		       - partial_line_height (&it) - this_scroll_margin - 1),
+		      -1,
+		      MOVE_TO_POS | MOVE_TO_Y);
+
+	  /* Save our position, in case it's correct.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* If PT is in the screen line at the last fully visible line,
+	     move_it_to will stop at X = 0 in that line, because the
+	     required Y coordinate is reached there.  See if we can get to
+	     PT without descending lower in Y, and if we can, it means we
+	     reached PT before the scroll margin.  */
+	  if (charpos != PT)
 	    {
-	      charpos = IT_CHARPOS (it);
-	      bytepos = IT_BYTEPOS (it);
-	      bidi_unshelve_cache (it_data, true);
+	      struct it it2;
+	      void *it_data;
+
+	      it2 = it;
+	      it_data = bidi_shelve_cache ();
+	      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	      if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+		{
+		  charpos = IT_CHARPOS (it);
+		  bytepos = IT_BYTEPOS (it);
+		  bidi_unshelve_cache (it_data, true);
+		}
+	      else
+		{
+		  it = it2;
+		  bidi_unshelve_cache (it_data, false);
+		}
 	    }
+
+	  /* See if point is on a partially visible line at the end.  */
+	  if (it.what == IT_EOB)
+	    partial_p =
+	      it.current_y + it.ascent + it.descent
+	      > it.last_visible_y - this_scroll_margin
+	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	  else
 	    {
-	      it = it2;
-	      bidi_unshelve_cache (it_data, false);
+	      move_it_by_lines (&it, 1);
+	      partial_p =
+		it.current_y
+		> it.last_visible_y - this_scroll_margin
+		- WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	    }
-	}
-
-      /* See if point is on a partially visible line at the end.  */
-      if (it.what == IT_EOB)
-	partial_p =
-	  it.current_y + it.ascent + it.descent
-	  > it.last_visible_y - this_scroll_margin
-	  - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-      else
-	{
-	  move_it_by_lines (&it, 1);
-	  partial_p =
-	    it.current_y
-	    > it.last_visible_y - this_scroll_margin
-	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-	}
 
-      if (charpos == PT && !partial_p
-          && (NILP (Vscroll_preserve_screen_position)
-	      || EQ (Vscroll_preserve_screen_position, Qt)))
-	/* We found PT before we found the display margin, so PT is ok.  */
-	;
-      else if (window_scroll_pixel_based_preserve_y >= 0)
-	{
-	  int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
-			    window_scroll_pixel_based_preserve_y);
-
-	  /* Don't let the preserved screen Y coordinate put us inside
-	     any of the two margins.  */
-	  if (goal_y < this_scroll_margin)
-	    goal_y = this_scroll_margin;
-	  SET_TEXT_POS_FROM_MARKER (start, w->start);
-	  start_display (&it, w, start);
-	  /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
-	     here because we called start_display again and did not
-	     alter it.current_y this time.  */
-	  move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
-		      goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	}
-      else
-	{
-	  if (partial_p)
-	    /* The last line was only partially visible, so back up two
-	       lines to make sure we're on a fully visible line.  */
+	  if (charpos == PT && !partial_p
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT before we found the display margin, so PT is ok.  */
+	    ;
+	  else if (window_scroll_pixel_based_preserve_y >= 0)
 	    {
-	      move_it_by_lines (&it, -2);
+	      int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
+				window_scroll_pixel_based_preserve_y);
+
+	      /* Don't let the preserved screen Y coordinate put us inside
+		 any of the two margins.  */
+	      if (goal_y < this_scroll_margin)
+		goal_y = this_scroll_margin;
+	      SET_TEXT_POS_FROM_MARKER (start, w->start);
+	      start_display (&it, w, start);
+	      /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
+		 here because we called start_display again and did not
+		 alter it.current_y this time.  */
+	      move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
+			  goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
 	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
 	    }
 	  else
-	    /* No, the position we saved is OK, so use it.  */
-	    SET_PT_BOTH (charpos, bytepos);
+	    {
+	      if (partial_p)
+		/* The last line was only partially visible, so back up two
+		   lines to make sure we're on a fully visible line.  */
+		{
+		  move_it_by_lines (&it, -2);
+		  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+		}
+	      else
+		/* No, the position we saved is OK, so use it.  */
+		SET_PT_BOTH (charpos, bytepos);
+	    }
 	}
     }
+
   bidi_unshelve_cache (itdata, false);
 
   if (adjust_old_pointm)
@@ -8500,6 +8507,10 @@ syms_of_window (void)
 displayed after a scrolling operation to be somewhat inaccurate.  */);
   fast_but_imprecise_scrolling = false;
 
+  DEFVAR_BOOL ("scroll-move-point", scroll_move_point,
+	       doc: /* If nil, don't move point to fit inside the window when scrolling.  */);
+  scroll_move_point = true;
+
   defsubr (&Sselected_window);
   defsubr (&Sold_selected_window);
   defsubr (&Sminibuffer_window);
diff --git a/src/xdisp.c b/src/xdisp.c
index 0ff6286af7..d0d983ca97 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16118,6 +16118,8 @@ #define AINC(a,i)							\
   tlbufpos = this_line_start_pos;
   tlendpos = this_line_end_pos;
   if (!consider_all_windows_p
+      /* TODO: enable this optimization.  */
+      && keep_point_visible
       && CHARPOS (tlbufpos) > 0
       && !w->update_mode_line
       && !current_buffer->clip_changed
@@ -17768,6 +17770,9 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   else
     scroll_max = 0;
 
+  if (!keep_point_visible)
+    goto out;
+
  too_near_end:
 
   /* Decide whether to scroll down.  */
@@ -18004,6 +18009,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   /* Run window scroll functions.  */
   startp = run_window_scroll_functions (window, startp);
 
+ out:
+
   /* Display the window.  Give up if new fonts are loaded, or if point
      doesn't appear.  */
   if (!try_window (window, startp, 0))
@@ -18183,6 +18190,10 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
     return rc;
 #endif
 
+  /* TODO: enable this optimization.  */
+  if (!keep_point_visible)
+    return CURSOR_MOVEMENT_CANNOT_BE_USED;
+
   /* Previously, there was a check for Lisp integer in the
      if-statement below. Now, this field is converted to
      ptrdiff_t, thus zero means invalid position in a buffer.  */
@@ -18770,6 +18781,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       clear_glyph_matrix (w->desired_matrix);
     }
 
+  debug_method_add (w, "real redisplay starts");
+
   /* Otherwise set up data on this window; select its buffer and point
      value.  */
   /* Really select the buffer, for the sake of buffer-local
@@ -18992,6 +19005,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	    new_vpos = MATRIX_ROW_BOTTOM_Y (r);
 	  else	/* Give up and just move to the middle of the window.  */
 	    new_vpos = window_box_height (w) / 2;
+
 	}
 
       if (!cursor_row_fully_visible_p (w, false, false, false))
@@ -19223,10 +19237,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  IF_DEBUG (debug_method_add (w, "1"));
 	  clear_glyph_matrix (w->desired_matrix);
 	  if (try_window (window, startp, TRY_WINDOW_CHECK_MARGINS) < 0)
-	    /* -1 means we need to scroll.
-	       0 means we need new matrices, but fonts_changed
-	       is set in that case, so we will detect it below.  */
-	    goto try_to_scroll;
+	    {
+	      /* -1 means we need to scroll.
+		 0 means we need new matrices, but fonts_changed
+		 is set in that case, so we will detect it below.  */
+	      goto try_to_scroll;
+	    }
 	}
 
       if (f->fonts_changed)
@@ -19255,7 +19271,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
  try_to_scroll:
 
-  /* Redisplay the mode line.  Select the buffer properly for that.  */
+ /* Redisplay the mode line.  Select the buffer properly for that.  */
   if (!update_mode_line)
     {
       update_mode_line = true;
@@ -19301,6 +19317,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	}
     }
 
+  if (!keep_point_visible && PT == w->last_point)
+    goto maybe_try_window;
+
   /* Finally, just choose a place to start which positions point
      according to user preferences.  */
 
@@ -19317,6 +19336,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   /* Determine the window start relative to point.  */
   init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
   it.current_y = it.last_visible_y;
+
   if (centering_position < 0)
     {
       ptrdiff_t margin_pos = CHARPOS (startp);
@@ -19584,6 +19604,53 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       centering_position = 0;
       goto recenter;
     }
+  goto done;
+
+ maybe_try_window:
+
+  /* Set the window start position here explicitly if it is outside
+     the accessible portion of the buffer.  */
+
+  if (CHARPOS (startp) < BEGV
+      || CHARPOS (startp) > ZV)
+    {
+      if (CHARPOS (startp) < BEGV)
+	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
+      else
+	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
+
+      SET_TEXT_POS_FROM_MARKER (startp, w->start);
+
+      /* Run scroll hooks.  */
+      startp = run_window_scroll_functions (window, startp);
+    }
+
+  /* We invoke try_window and try_window_reusing_current_matrix below,
+     and they manipulate the bidi cache.  Save and restore the cache
+     state of our iterator, so we could continue using it after that.  */
+  itdata = bidi_shelve_cache ();
+
+  /* Redisplay the window.  */
+  use_desired_matrix = false;
+  if (!current_matrix_up_to_date_p
+      || windows_or_buffers_changed
+      || f->cursor_type_changed
+      /* Don't use try_window_reusing_current_matrix in this case
+	 because it can have changed the buffer.  */
+      || !NILP (Vwindow_scroll_functions)
+      || !just_this_one_p
+      || MINI_WINDOW_P (w)
+      || !(used_current_matrix_p
+	   = try_window_reusing_current_matrix (w)))
+    use_desired_matrix = (try_window (window, startp, 0) == 1);
+
+  bidi_unshelve_cache (itdata, false);
+
+  /* If new fonts have been loaded (due to fontsets), give up.  We
+     have to start a new redisplay since we need to re-adjust glyph
+     matrices.  */
+  if (f->fonts_changed)
+    goto need_larger_matrices;
 
  done:
 
@@ -19591,6 +19658,11 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   w->start_at_line_beg = (CHARPOS (startp) == BEGV
 			  || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n');
 
+  if (!keep_point_visible
+      && (w->cursor.vpos == -1)
+      && w->phys_cursor_on_p)
+    erase_phys_cursor (w);
+
   /* Display the mode line, header line, and tab-line, if we must.  */
   if ((update_mode_line
        /* If window not full width, must redo its mode line
@@ -19801,6 +19873,8 @@ try_window (Lisp_Object window, struct text_pos pos, int flags)
   struct frame *f = XFRAME (w->frame);
   int cursor_vpos = w->cursor.vpos;
 
+  debug_method_add (w, "try_window");
+
   /* Make POS the new window start.  */
   set_marker_both (w->start, Qnil, CHARPOS (pos), BYTEPOS (pos));
 
@@ -19960,6 +20034,10 @@ try_window_reusing_current_matrix (struct window *w)
     return false;
 #endif
 
+  /* TODO: enable this optimization.  */
+  if (!keep_point_visible)
+    return false;
+
   /* The variable new_start now holds the new window start.  The old
      start `start' can be determined from the current matrix.  */
   SET_TEXT_POS_FROM_MARKER (new_start, w->start);
@@ -32389,6 +32467,9 @@ display_and_set_cursor (struct window *w, bool on,
 	      && new_cursor_width != w->phys_cursor_width)))
     erase_phys_cursor (w);
 
+  if (w->cursor.vpos == -1)
+    return;
+
   /* Don't check phys_cursor_on_p here because that flag is only set
      to false in some cases where we know that the cursor has been
      completely erased, to avoid the extra work of erasing the cursor
@@ -32749,6 +32830,9 @@ cursor_in_mouse_face_p (struct window *w)
   int vpos = w->phys_cursor.vpos;
   struct glyph_row *row = MATRIX_ROW (w->current_matrix, vpos);
 
+  if (vpos == -1)
+    return false;
+
   /* When the window is hscrolled, cursor hpos can legitimately be out
      of bounds, but we draw the cursor at the corresponding window
      margin in that case.  */
@@ -35493,6 +35577,10 @@ syms_of_xdisp (void)
   x_stretch_cursor_p = 0;
 #endif
 
+  DEFVAR_BOOL ("keep-point-visible", keep_point_visible,
+	       doc: /* Non-nil means to keep the point visible.  */);
+  keep_point_visible = 1;
+
   DEFVAR_LISP ("show-trailing-whitespace", Vshow_trailing_whitespace,
     doc: /* Non-nil means highlight trailing whitespace.
 The face used for trailing whitespace is `trailing-whitespace'.  */);

[-- Attachment #3: Type: text/plain, Size: 9 bytes --]


Thanks.

^ permalink raw reply related	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-06  2:11                                       ` Po Lu
@ 2021-12-06 14:13                                         ` Eli Zaretskii
  2021-12-07  2:18                                           ` Po Lu
  2021-12-09 11:45                                         ` Eli Zaretskii
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-06 14:13 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 06 Dec 2021 10:11:47 +0800
> 
> > That'd cover scrolling a window in a way that leaves point outside of
> > it.  But Emacs also sets the window-start position in other
> > situations.  Moreover, the display engine itself does that, see
> > redisplay_window.
> 
> > So some logic and/or flags will be needed to distinguish between the
> > cases.
> 
> We should not recenter the display in any case where the start is not
> forced and point is not moved.

Again, what do you mean by "start is forced"?  Emacs sometimes forces
the window-start upon itself, you can clearly see that in the code.

And if point is outside of the window, recentering makes no sense
anyway, because there's nothing to recenter about, there's no point of
reference for it.

> > So any change in the numerical value of point will scroll the window
> > to bring point into the view?  That'd be fine if that's what users
> > expect.  But do they?  E.g., what happens if point is below the
> > window, and you paste (with the mouse) some text into the visible
> > portion of the window (which doesn't show point)?
> 
> That's not a problem, as `mouse-yank-primary' and friends already call
> mouse-set-point.

"Not a problem" because point will be brought back into the window
before pasting?  But is that what users want?

> WDYT about the attached patch?  It recenters the display whenever PT !=
> w->last_point, and it seems to work very well.
> 
> It also introduces a new variable `scroll-move-point' that controls if
> the scrolling commands will try to move point to stay visible.

Thanks, I will look into this later.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-06 14:13                                         ` Eli Zaretskii
@ 2021-12-07  2:18                                           ` Po Lu
  2021-12-07 13:42                                             ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-07  2:18 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Again, what do you mean by "start is forced"?  Emacs sometimes forces
> the window-start upon itself, you can clearly see that in the code.

Whenever w->force_start is true.

> And if point is outside of the window, recentering makes no sense
> anyway, because there's nothing to recenter about, there's no point of
> reference for it.

The window start is moved to a suitable location which will cause point
to be displayed in the center of the screen.

If that isn't possible, the closest location to that is set as window
start.

> "not a problem" because point will be brought back into the window
> before pasting?  But is that what users want?

Yes, that's what users want.

> Thanks, I will look into this later.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-07  2:18                                           ` Po Lu
@ 2021-12-07 13:42                                             ` Eli Zaretskii
  2021-12-08  1:17                                               ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-07 13:42 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 07 Dec 2021 10:18:37 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Again, what do you mean by "start is forced"?  Emacs sometimes forces
> > the window-start upon itself, you can clearly see that in the code.
> 
> Whenever w->force_start is true.

This is sometimes set for Emacs itself, regardless of the user
commands.  So all such uses should be audited.

> > And if point is outside of the window, recentering makes no sense
> > anyway, because there's nothing to recenter about, there's no point of
> > reference for it.
> 
> The window start is moved to a suitable location which will cause point
> to be displayed in the center of the screen.
> 
> If that isn't possible, the closest location to that is set as window
> start.

Yes.  I'm saying that if point is allowed to be outside of the window
during some redisplay cycle, we'd need a different criteria for when
and how to change w->start.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-07 13:42                                             ` Eli Zaretskii
@ 2021-12-08  1:17                                               ` Po Lu
  2021-12-08 17:14                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-08  1:17 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> This is sometimes set for Emacs itself, regardless of the user
> commands.  So all such uses should be audited.

Most of the uses seem fine to me (mostly in the scrolling commands,
which I've already modified as appropriate, and also when the cursor is
obscured and make-cursor-line-fully-visible is non-nil), but I have one
question about the following code:

      /* We used to issue a CHECK_MARGINS argument to try_window here,
	 but this causes scrolling to fail when point begins inside
	 the scroll margin (bug#148) -- cyd  */
      clear_glyph_matrix (w->desired_matrix);
      if (!try_window (window, startp, 0))
	{
	  w->force_start = true;
	  clear_glyph_matrix (w->desired_matrix);
	  goto need_larger_matrices;
	}

Why does it have to force start?  Doesn't need_larger_matrices take care
of resizing the matrix when, for instance, fonts change?

Thanks.

> Yes.  I'm saying that if point is allowed to be outside of the window
> during some redisplay cycle, we'd need a different criteria for when
> and how to change w->start.

Why is that so (for the "how to change w->start" part)?  Making point
display in the center of the screen in that case will be intuitive for
most users.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-11-29  0:34         ` Po Lu
@ 2021-12-08  1:45           ` John Ankarström
  2021-12-08 12:45             ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: John Ankarström @ 2021-12-08  1:45 UTC (permalink / raw)
  To: emacs-devel

Po Lu <luangruo@yahoo.com> writes:

> Alan Mackenzie <acm@muc.de> writes:
>
>> Why would anybody want this?  Why do you want this?  This isn't a
>> rhetorical question.  Because other programs do it isn't a satisfactory
>> reason. [...]
>
> The feature will be optional, of course.  And it will certainly be
> disabled by default, because I don't want it either.

For what it's worth, I think that it would be a good idea to limit this
feature to mouse-based scrolling. That's what I've been doing in my own
Elisp emulation of this kind of thing [1].

I agree with Alan that, if you primarily use the keyboard for scrolling,
it is very jarring if editing/movement commands bring the window view
back to the original position.

But the mouse provdides an easy way of overriding the original position
(by left-clicking on a new position).


[1] http://ankarstrom.se/~john/emacs/scroll-without-point.el




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08  1:45           ` John Ankarström
@ 2021-12-08 12:45             ` Eli Zaretskii
  2021-12-08 13:33               ` John Ankarström
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-08 12:45 UTC (permalink / raw)
  To: John Ankarström; +Cc: emacs-devel

> From: John Ankarström <john@ankarstrom.se>
> Date: Wed, 08 Dec 2021 02:45:02 +0100
> 
> For what it's worth, I think that it would be a good idea to limit this
> feature to mouse-based scrolling.

The display engine doesn't know what kind of user gestures (or even
Lisp program) caused the scroll, so doing that cleanly won't be easy,
if at all feasible.  And Emacs users are likely to dislike a solution
that makes such a distinction, since many of them don't use the mouse.

> I agree with Alan that, if you primarily use the keyboard for scrolling,
> it is very jarring if editing/movement commands bring the window view
> back to the original position.

You'd prefer to have the editing command change the buffer in the
portion that stays invisible?  IMNSHO, that'd be not just more
jarring, it would be appalling.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 12:45             ` Eli Zaretskii
@ 2021-12-08 13:33               ` John Ankarström
  2021-12-08 13:38                 ` Po Lu
                                   ` (3 more replies)
  0 siblings, 4 replies; 111+ messages in thread
From: John Ankarström @ 2021-12-08 13:33 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: John Ankarström <john@ankarstrom.se>
>> Date: Wed, 08 Dec 2021 02:45:02 +0100
>> 
>> For what it's worth, I think that it would be a good idea to limit this
>> feature to mouse-based scrolling.
>
> The display engine doesn't know what kind of user gestures (or even
> Lisp program) caused the scroll, so doing that cleanly won't be easy,
> if at all feasible.  And Emacs users are likely to dislike a solution
> that makes such a distinction, since many of them don't use the mouse.
>
>> I agree with Alan that, if you primarily use the keyboard for scrolling,
>> it is very jarring if editing/movement commands bring the window view
>> back to the original position.
>
> You'd prefer to have the editing command change the buffer in the
> portion that stays invisible?  IMNSHO, that'd be not just more
> jarring, it would be appalling.

No. What I meant is this:

"Point-detached" scrolling is only useful if there is a way to move the
point to the current view.

Let's say the point is at the beginning of the buffer. I scroll down to
look for a specific definition. I find it in the middle of the buffer.
Now, I want to edit that definition. But how do I get the point to the
currently viewed position?

In (all?) "modern" editors, the only way to do this is by left-clicking.

That's why I suggested limiting the whole feature to mouse-based
scrolling. If you are scrolling with the mouse, it is natural to click
to set a new position for the point. But if you are scrolling with, say,
Page Up and Page Down, you would need to move your hand to the mouse to
set a new position for the point.

This makes the whole feature quite useless for any scrolling that isn't
mouse-based. When scrolling with the keyboard, it is annoying if the
only way to move the point to the scrolled-to position is by clicking
the mouse.

The alternative is to provide a keyboard-based way to move the point to
the currently viewed portion of the buffer -- like a reverse C-l.

Come to think of it, that is probably a better solution than limiting
the feature to mouse-based scrolling. I adjust my opinion!




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 13:33               ` John Ankarström
@ 2021-12-08 13:38                 ` Po Lu
  2021-12-08 13:52                   ` John Ankarström
  2021-12-08 14:26                 ` Eli Zaretskii
                                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-08 13:38 UTC (permalink / raw)
  To: John Ankarström; +Cc: emacs-devel

John Ankarström <john@ankarstrom.se> writes:

> No. What I meant is this:

> "Point-detached" scrolling is only useful if there is a way to move the
> point to the current view.

> Let's say the point is at the beginning of the buffer. I scroll down to
> look for a specific definition. I find it in the middle of the buffer.
> Now, I want to edit that definition. But how do I get the point to the
> currently viewed position?

> In (all?) "modern" editors, the only way to do this is by left-clicking.

> That's why I suggested limiting the whole feature to mouse-based
> scrolling. If you are scrolling with the mouse, it is natural to click
> to set a new position for the point. But if you are scrolling with, say,
> Page Up and Page Down, you would need to move your hand to the mouse to
> set a new position for the point.

I'm not saying we shouldn't provide all the knobs to _allow_ limiting
the scope of this feature to mouse wheel (or precision) scrolling, if
that is what you mean by mouse scrolling, but I see no reason to limit
the feature to that by default.

> This makes the whole feature quite useless for any scrolling that isn't
> mouse-based. When scrolling with the keyboard, it is annoying if the
> only way to move the point to the scrolled-to position is by clicking
> the mouse.

If you don't like that behaviour, you will be able to turn it off by
setting `scroll-move-point' to nil.

In fact, it's off by default.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 13:38                 ` Po Lu
@ 2021-12-08 13:52                   ` John Ankarström
  0 siblings, 0 replies; 111+ messages in thread
From: John Ankarström @ 2021-12-08 13:52 UTC (permalink / raw)
  To: emacs-devel

Po Lu <luangruo@yahoo.com> writes:

>> That's why I suggested limiting the whole feature to mouse-based
>> scrolling. If you are scrolling with the mouse, it is natural to click
>> to set a new position for the point. But if you are scrolling with, say,
>> Page Up and Page Down, you would need to move your hand to the mouse to
>> set a new position for the point.
>
> I'm not saying we shouldn't provide all the knobs to _allow_ limiting
> the scope of this feature to mouse wheel (or precision) scrolling, if
> that is what you mean by mouse scrolling, but I see no reason to limit
> the feature to that by default.

Sounds good -- although I think that there should be an included
command, like C-l, that moves the point to the currently viewed portion.

>> This makes the whole feature quite useless for any scrolling that isn't
>> mouse-based. When scrolling with the keyboard, it is annoying if the
>> only way to move the point to the scrolled-to position is by clicking
>> the mouse.
>
> If you don't like that behaviour, you will be able to turn it off by
> setting `scroll-move-point' to nil.
>
> In fact, it's off by default.

Which behavior do you mean? If you're referring to the point-detached
scrolling, I *do* like that behavior. I would just dislike it if, again,
there weren't any way (such as the reverse C-l I suggested) to move the
point to the currently viewed portion.

> Thanks.

Thank you very much for working on this! I have wanted such a feature in
Emacs for a long time :-)




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 13:33               ` John Ankarström
  2021-12-08 13:38                 ` Po Lu
@ 2021-12-08 14:26                 ` Eli Zaretskii
  2021-12-08 16:57                   ` Stefan Monnier
  2021-12-08 19:21                 ` Rudolf Schlatte
  2021-12-08 22:25                 ` Kévin Le Gouguec
  3 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-08 14:26 UTC (permalink / raw)
  To: John Ankarström; +Cc: emacs-devel

> From: John Ankarström <john@ankarstrom.se>
> Date: Wed, 08 Dec 2021 14:33:01 +0100
> 
> The alternative is to provide a keyboard-based way to move the point to
> the currently viewed portion of the buffer -- like a reverse C-l.
> 
> Come to think of it, that is probably a better solution than limiting
> the feature to mouse-based scrolling. I adjust my opinion!

I think I agree with your adjusted opinion.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 14:26                 ` Eli Zaretskii
@ 2021-12-08 16:57                   ` Stefan Monnier
  2021-12-08 19:29                     ` Yuri Khan
  2021-12-09  0:16                     ` Po Lu
  0 siblings, 2 replies; 111+ messages in thread
From: Stefan Monnier @ 2021-12-08 16:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: John Ankarström, emacs-devel

IIUC the way this new feature is expected to work is that it prevents
"scroll to bring point into view" under some conditions.
I'm not sure exactly what are those conditions, but I'd expect it's
something like:
- point was not moved since last time we redisplayed this buffer.
- the buffer was not modified since last time we redisplayed it.
- window-start was set explicitly.

So it should be easy to provide a "move point to view" with something
like:

    (goto-char (window-start))


-- Stefan




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08  1:17                                               ` Po Lu
@ 2021-12-08 17:14                                                 ` Eli Zaretskii
  2021-12-09  0:23                                                   ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-08 17:14 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Wed, 08 Dec 2021 09:17:13 +0800
> 
>       /* We used to issue a CHECK_MARGINS argument to try_window here,
> 	 but this causes scrolling to fail when point begins inside
> 	 the scroll margin (bug#148) -- cyd  */
>       clear_glyph_matrix (w->desired_matrix);
>       if (!try_window (window, startp, 0))
> 	{
> 	  w->force_start = true;
> 	  clear_glyph_matrix (w->desired_matrix);
> 	  goto need_larger_matrices;
> 	}
> 
> Why does it have to force start?

Because the place for the window-start was given to us (this code is
under the 'if (w->force_start)' condition), but we've reset the flag
after checking the condition, so now we are putting the flag back, for
the next redisplay cycle -- since we are abandoning this cycle.

> Doesn't need_larger_matrices take care of resizing the matrix when,
> for instance, fonts change?

need_larger_matrices indeed resizes the glyph matrices, after aborting
the redisplay cycle with the old matrices, but that doesn't mean that
window-start is invalid, it just means the previous glyph matrices
were not large enough, e.g., because some part of the displayed text
now has a different face that uses a smaller font.

Basically, the strategy of redisplay_window, after some initial
bookkeeping, is to go through a series of steps, whereby if a step
succeeds, we are done, otherwise we go to the next step.  The steps
are:

  . if nothing's changed except point, and point is still inside the
    same window, no need to do anything except redisplay the cursor;
  . try using the window-start that someone else told us to use
  . try using the same window-start as the previous redisplay, reusing
    as much of the current glyph matrix as possible
  . try using the same window-start as the previous redisplay, after
    correcting it in small ways (like, for example, to move point out
    of the scroll margin)
  . recompute the window-start anew using point as the reference
  . in each case, redisplay the parts of the window using the
    window-start we found

So, as you see, a lot of the processing is dedicated to finding a good
window-start point, which is why if someone told us where to put it,
we prefer to stick to that.  Besides, that someone could be the user
via one of the scroll commands, and in that case the window-start we
were given is mandatory.

> > Yes.  I'm saying that if point is allowed to be outside of the window
> > during some redisplay cycle, we'd need a different criteria for when
> > and how to change w->start.
> 
> Why is that so (for the "how to change w->start" part)?  Making point
> display in the center of the screen in that case will be intuitive for
> most users.

That's not what I had in mind.  What I had in mind was the situation
where point is outside of the window, and the portion of the buffer
shown in the window changes due to something that Emacs does.  If you
are saying that in all such situation we will bring point back into
the window, then my concern is addressed; but if not, we will not be
able to use the final fallback of recentering the window around point,
because that would move the window instead of redrawing the visible
portion of the buffer.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
       [not found] <9603C99D-97E7-4285-A1C1-022191B6F5CC@univie.ac.at>
@ 2021-12-08 18:43 ` Konrad Podczeck
  2021-12-08 20:47   ` John Ankarström
  0 siblings, 1 reply; 111+ messages in thread
From: Konrad Podczeck @ 2021-12-08 18:43 UTC (permalink / raw)
  To: john; +Cc: emacs-devel

Hi John,

congratulations, the code works great! Do you allow to use it? If yes, what could I try so that, with hl-line-mode on, also the actually highlighted line scrolls out and in of the window, instead of dancing around?

In any case, thanks for your effort, 

Konrad





^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 13:33               ` John Ankarström
  2021-12-08 13:38                 ` Po Lu
  2021-12-08 14:26                 ` Eli Zaretskii
@ 2021-12-08 19:21                 ` Rudolf Schlatte
  2021-12-08 19:56                   ` Juri Linkov
  2021-12-09  0:17                   ` Allowing point to be outside the window? Po Lu
  2021-12-08 22:25                 ` Kévin Le Gouguec
  3 siblings, 2 replies; 111+ messages in thread
From: Rudolf Schlatte @ 2021-12-08 19:21 UTC (permalink / raw)
  To: emacs-devel

John Ankarström <john@ankarstrom.se> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>>> From: John Ankarström <john@ankarstrom.se>
>>> Date: Wed, 08 Dec 2021 02:45:02 +0100
>>> 
>>> For what it's worth, I think that it would be a good idea to limit this
>>> feature to mouse-based scrolling.
[...]
>
> That's why I suggested limiting the whole feature to mouse-based
> scrolling. If you are scrolling with the mouse, it is natural to click
> to set a new position for the point. But if you are scrolling with, say,
> Page Up and Page Down, you would need to move your hand to the mouse to
> set a new position for the point.
>
> This makes the whole feature quite useless for any scrolling that isn't
> mouse-based. When scrolling with the keyboard, it is annoying if the
> only way to move the point to the scrolled-to position is by clicking
> the mouse.

I briefly tested with two contemporary editors (Atom and VS Code), and
their behavior indeed differs between mouse-based and keyboard-based
scrolling: in the former case (scrolling via mouse), the cursor stays
put and moves out of view, while in the latter case (scrolling via the
PgDn key), the cursor changes position to stay on screen.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 16:57                   ` Stefan Monnier
@ 2021-12-08 19:29                     ` Yuri Khan
  2021-12-09  0:16                     ` Po Lu
  1 sibling, 0 replies; 111+ messages in thread
From: Yuri Khan @ 2021-12-08 19:29 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Eli Zaretskii, John Ankarström, Emacs developers

On Wed, 8 Dec 2021 at 23:58, Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> So it should be easy to provide a "move point to view" with something
> like:
>
>     (goto-char (window-start))

A better implementation would bring the point to the line of
(window-start) when point is before (window-start), or to the line of
(window-end) when after (window-end), while striving to keep the
column unchanged or to move to goal-column, as if by (previous-line)
or (next-line).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 19:21                 ` Rudolf Schlatte
@ 2021-12-08 19:56                   ` Juri Linkov
  2021-12-08 20:05                     ` André A. Gomes
  2021-12-08 20:31                     ` Linux console scrollback [ Was: Allowing point to be outside the window? ] Alan Mackenzie
  2021-12-09  0:17                   ` Allowing point to be outside the window? Po Lu
  1 sibling, 2 replies; 111+ messages in thread
From: Juri Linkov @ 2021-12-08 19:56 UTC (permalink / raw)
  To: Rudolf Schlatte; +Cc: emacs-devel

> I briefly tested with two contemporary editors (Atom and VS Code), and
> their behavior indeed differs between mouse-based and keyboard-based
> scrolling: in the former case (scrolling via mouse), the cursor stays
> put and moves out of view, while in the latter case (scrolling via the
> PgDn key), the cursor changes position to stay on screen.

Linux console supported keyboard-based scrolling with Shift-PageDown/PageUp
where the cursor stays on the command line and it scrolls back on a key press,
but recently this feature was removed.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 19:56                   ` Juri Linkov
@ 2021-12-08 20:05                     ` André A. Gomes
  2021-12-08 20:31                     ` Linux console scrollback [ Was: Allowing point to be outside the window? ] Alan Mackenzie
  1 sibling, 0 replies; 111+ messages in thread
From: André A. Gomes @ 2021-12-08 20:05 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Rudolf Schlatte, emacs-devel

Juri Linkov <juri@linkov.net> writes:

>> I briefly tested with two contemporary editors (Atom and VS Code), and
>> their behavior indeed differs between mouse-based and keyboard-based
>> scrolling: in the former case (scrolling via mouse), the cursor stays
>> put and moves out of view, while in the latter case (scrolling via the
>> PgDn key), the cursor changes position to stay on screen.
>
> Linux console supported keyboard-based scrolling with Shift-PageDown/PageUp
> where the cursor stays on the command line and it scrolls back on a key press,
> but recently this feature was removed.

Off-topic: I noticed it in the linux console recently.  Is there a
workaround?  Thanks.


-- 
André A. Gomes
"Free Thought, Free World"



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Linux console scrollback [ Was: Allowing point to be outside the window? ]
  2021-12-08 19:56                   ` Juri Linkov
  2021-12-08 20:05                     ` André A. Gomes
@ 2021-12-08 20:31                     ` Alan Mackenzie
  1 sibling, 0 replies; 111+ messages in thread
From: Alan Mackenzie @ 2021-12-08 20:31 UTC (permalink / raw)
  To: Juri Linkov; +Cc: Rudolf Schlatte, emacs-devel

Hello, Juri.

On Wed, Dec 08, 2021 at 21:56:47 +0200, Juri Linkov wrote:
> > I briefly tested with two contemporary editors (Atom and VS Code), and
> > their behavior indeed differs between mouse-based and keyboard-based
> > scrolling: in the former case (scrolling via mouse), the cursor stays
> > put and moves out of view, while in the latter case (scrolling via the
> > PgDn key), the cursor changes position to stay on screen.

> Linux console supported keyboard-based scrolling with
> Shift-PageDown/PageUp where the cursor stays on the command line and it
> scrolls back on a key press, but recently this feature was removed.

Just as a matter of interest, I've hacked the Linux sources to put this
essential feature back again, in an improved fashion.  Where previously
one scrollback buffer was shared by all the tty's, causing the
scrollback to be lost on moving to a different tty, now each tty has its
own buffer which does not lose its contents by such a move.

I've published the patch on the Gentoo user's list.  It is slightly
different for different kernel major versions.

If anybody is interested in this patch, and can't be bothered to dig it
out of the Gentoo list archive, feel free to send me an email, and I will
reply with the patch and (short) directions for its use.

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 18:43 ` Konrad Podczeck
@ 2021-12-08 20:47   ` John Ankarström
  0 siblings, 0 replies; 111+ messages in thread
From: John Ankarström @ 2021-12-08 20:47 UTC (permalink / raw)
  To: emacs-devel

Konrad Podczeck <konrad.podczeck@univie.ac.at> writes:

> Hi John,
>
> congratulations, the code works great! Do you allow to use it?

Of course!

> If yes, what could I try so that, with hl-line-mode on, also the
> actually highlighted line scrolls out and in of the window, instead of
> dancing around?

I've uploaded an updated version that supports hl-line-mode [1]. It adds
an advice around hl-line-move that disables it while scrolling. The new
version also supports inactive windows.


[1] http://ankarstrom.se/~john/emacs/scroll-without-point.el




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 13:33               ` John Ankarström
                                   ` (2 preceding siblings ...)
  2021-12-08 19:21                 ` Rudolf Schlatte
@ 2021-12-08 22:25                 ` Kévin Le Gouguec
  2021-12-08 23:17                   ` John Ankarström
  3 siblings, 1 reply; 111+ messages in thread
From: Kévin Le Gouguec @ 2021-12-08 22:25 UTC (permalink / raw)
  To: John Ankarström; +Cc: emacs-devel

John Ankarström <john@ankarstrom.se> writes:

> The alternative is to provide a keyboard-based way to move the point to
> the currently viewed portion of the buffer -- like a reverse C-l.

Wouldn't the existing M-r (move-to-window-line-top-bottom) fit the bill?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 22:25                 ` Kévin Le Gouguec
@ 2021-12-08 23:17                   ` John Ankarström
  0 siblings, 0 replies; 111+ messages in thread
From: John Ankarström @ 2021-12-08 23:17 UTC (permalink / raw)
  To: emacs-devel

Kévin Le Gouguec <kevin.legouguec@gmail.com> writes:

> John Ankarström <john@ankarstrom.se> writes:
>
>> The alternative is to provide a keyboard-based way to move the point to
>> the currently viewed portion of the buffer -- like a reverse C-l.
>
> Wouldn't the existing M-r (move-to-window-line-top-bottom) fit the bill?

Ah, yes, good point!

I've added support for it in my Elisp implementation and it works well.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 16:57                   ` Stefan Monnier
  2021-12-08 19:29                     ` Yuri Khan
@ 2021-12-09  0:16                     ` Po Lu
  1 sibling, 0 replies; 111+ messages in thread
From: Po Lu @ 2021-12-09  0:16 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Eli Zaretskii, John Ankarström, emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> IIUC the way this new feature is expected to work is that it prevents
> "scroll to bring point into view" under some conditions.
> I'm not sure exactly what are those conditions, but I'd expect it's
> something like:
> - point was not moved since last time we redisplayed this buffer.
> - window-start was set explicitly.

Yes.

> - the buffer was not modified since last time we redisplayed it.

This might not be necessary.

> So it should be easy to provide a "move point to view" with something
> like:

>     (goto-char (window-start))

It would probably have to take into account vscroll, as the cursor
appearing in a partially visible row would be confusing.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 19:21                 ` Rudolf Schlatte
  2021-12-08 19:56                   ` Juri Linkov
@ 2021-12-09  0:17                   ` Po Lu
  1 sibling, 0 replies; 111+ messages in thread
From: Po Lu @ 2021-12-09  0:17 UTC (permalink / raw)
  To: Rudolf Schlatte; +Cc: emacs-devel

Rudolf Schlatte <rudi@constantly.at> writes:

> I briefly tested with two contemporary editors (Atom and VS Code), and
> their behavior indeed differs between mouse-based and keyboard-based
> scrolling: in the former case (scrolling via mouse), the cursor stays
> put and moves out of view, while in the latter case (scrolling via the
> PgDn key), the cursor changes position to stay on screen.

Then I think we would do well to replicate this change.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-08 17:14                                                 ` Eli Zaretskii
@ 2021-12-09  0:23                                                   ` Po Lu
  2021-12-09  8:02                                                     ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-09  0:23 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Po Lu <luangruo@yahoo.com>
>> Cc: emacs-devel@gnu.org
>> Date: Wed, 08 Dec 2021 09:17:13 +0800
>> 
>>       /* We used to issue a CHECK_MARGINS argument to try_window here,
>> 	 but this causes scrolling to fail when point begins inside
>> 	 the scroll margin (bug#148) -- cyd  */
>>       clear_glyph_matrix (w->desired_matrix);
>>       if (!try_window (window, startp, 0))
>> 	{
>> 	  w->force_start = true;
>> 	  clear_glyph_matrix (w->desired_matrix);
>> 	  goto need_larger_matrices;
>> 	}
>> 
>> Why does it have to force start?
>
> Because the place for the window-start was given to us (this code is
> under the 'if (w->force_start)' condition), but we've reset the flag
> after checking the condition, so now we are putting the flag back, for
> the next redisplay cycle -- since we are abandoning this cycle.
>
>> Doesn't need_larger_matrices take care of resizing the matrix when,
>> for instance, fonts change?
>
> need_larger_matrices indeed resizes the glyph matrices, after aborting
> the redisplay cycle with the old matrices, but that doesn't mean that
> window-start is invalid, it just means the previous glyph matrices
> were not large enough, e.g., because some part of the displayed text
> now has a different face that uses a smaller font.
>
> Basically, the strategy of redisplay_window, after some initial
> bookkeeping, is to go through a series of steps, whereby if a step
> succeeds, we are done, otherwise we go to the next step.  The steps
> are:
>
>   . if nothing's changed except point, and point is still inside the
>     same window, no need to do anything except redisplay the cursor;
>   . try using the window-start that someone else told us to use
>   . try using the same window-start as the previous redisplay, reusing
>     as much of the current glyph matrix as possible
>   . try using the same window-start as the previous redisplay, after
>     correcting it in small ways (like, for example, to move point out
>     of the scroll margin)
>   . recompute the window-start anew using point as the reference
>   . in each case, redisplay the parts of the window using the
>     window-start we found
>
> So, as you see, a lot of the processing is dedicated to finding a good
> window-start point, which is why if someone told us where to put it,
> we prefer to stick to that.  Besides, that someone could be the user
> via one of the scroll commands, and in that case the window-start we
> were given is mandatory.

> That's not what I had in mind.  What I had in mind was the situation
> where point is outside of the window, and the portion of the buffer
> shown in the window changes due to something that Emacs does.  If you
> are saying that in all such situation we will bring point back into
> the window, then my concern is addressed; but if not, we will not be
> able to use the final fallback of recentering the window around point,
> because that would move the window instead of redrawing the visible
> portion of the buffer.

If point doesn't change, but something in the visible part of the buffer
does, that part will be redrawn, keeping point and window start in their
original locations.

But maybe we should make it move point back into the window under this
situation.  WDYT?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09  0:23                                                   ` Po Lu
@ 2021-12-09  8:02                                                     ` Eli Zaretskii
  2021-12-09  9:22                                                       ` Po Lu
  2021-12-25  6:45                                                       ` Po Lu
  0 siblings, 2 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-09  8:02 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Thu, 09 Dec 2021 08:23:28 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > That's not what I had in mind.  What I had in mind was the situation
> > where point is outside of the window, and the portion of the buffer
> > shown in the window changes due to something that Emacs does.  If you
> > are saying that in all such situation we will bring point back into
> > the window, then my concern is addressed; but if not, we will not be
> > able to use the final fallback of recentering the window around point,
> > because that would move the window instead of redrawing the visible
> > portion of the buffer.
> 
> If point doesn't change, but something in the visible part of the buffer
> does, that part will be redrawn, keeping point and window start in their
> original locations.

I'm saying that if some part of the window needs to be redrawn, "the
last line of defense" we have there now, which is to center point in
the window, will not necessarily be available, and we need to do
something else if all the other methods in redisplay_window fail to
find a good window-start point.

> But maybe we should make it move point back into the window under this
> situation.  WDYT?

I already provided an example where this could not be what users
expect: some command inserts text before the window-start, which
causes the text shown in the window to move if we use the same
window-start position.  Now, window-start is a marker, so
theoretically we could use the same marker with the buffer position
updated due to the insertion, in which case the stuff displayed in the
window may stay unchanged.  But is this what users will expect?  And
if this is not what users expect, i.e. if we will need to recompute
window-start anew in that case, we cannot use the "recenter around
point" fallback.  Likewise with bringing point into the view: will
this be what users expect in the described situation?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09  8:02                                                     ` Eli Zaretskii
@ 2021-12-09  9:22                                                       ` Po Lu
  2021-12-09 10:02                                                         ` Eli Zaretskii
  2021-12-25  6:45                                                       ` Po Lu
  1 sibling, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-09  9:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Now, window-start is a marker, so theoretically we could use the same
> marker with the buffer position updated due to the insertion, in which
> case the stuff displayed in the window may stay unchanged.

I don't know, because I can't think of what other programs do in this
situation, as they generally don't have commands that insert text
outside the visible area.

Perhaps someone else can chime in at this point.

> But is this what users will expect?  And if this is not what users
> expect, i.e. if we will need to recompute window-start anew in that
> case, we cannot use the "recenter around point" fallback.  Likewise
> with bringing point into the view: will this be what users expect in
> the described situation?

I see what you mean now.  We could have a user option that says one of
the following:

  - If the text before the window start changes, use the current value
    of the window start marker.

  - If the text before the window start changes, bring point into view.

  - If the text before the window start changes, try to move window
    start so that point appears in the middle of the window.  If that is
    not possible, move window start to the location that will cause
    point to appear closest to the middle of the window.

BTW, how would you recommend to determine inside redisplay whether the
text before the start of the window has changed?

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09  9:22                                                       ` Po Lu
@ 2021-12-09 10:02                                                         ` Eli Zaretskii
  0 siblings, 0 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-09 10:02 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Thu, 09 Dec 2021 17:22:52 +0800
> 
> BTW, how would you recommend to determine inside redisplay whether the
> text before the start of the window has changed?

We already do that when needed:

  /* The position of the first and last character that has been changed.  */
  first_changed_charpos = BEG + BEG_UNCHANGED;
  last_changed_charpos  = Z - END_UNCHANGED;

  /* If window starts after a line end, and the last change is in
     front of that newline, then changes don't affect the display.
     This case happens with stealth-fontification.  Note that although
     the display is unchanged, glyph positions in the matrix have to
     be adjusted, of course.  */
  row = MATRIX_ROW (w->current_matrix, w->window_end_vpos);
  if (MATRIX_ROW_DISPLAYS_TEXT_P (row)
      && ((last_changed_charpos < CHARPOS (start)
	   && CHARPOS (start) == BEGV)
	  || (last_changed_charpos < CHARPOS (start) - 1
	      && FETCH_BYTE (BYTEPOS (start) - 1) == '\n')))

The BEG_UNCHANGED and END_UNCHANGED positions are tracked by all the
functions that modify the buffer.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-06  2:11                                       ` Po Lu
  2021-12-06 14:13                                         ` Eli Zaretskii
@ 2021-12-09 11:45                                         ` Eli Zaretskii
  2021-12-09 12:19                                           ` Po Lu
  1 sibling, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-09 11:45 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 06 Dec 2021 10:11:47 +0800
> 
> WDYT about the attached patch?  It recenters the display whenever PT !=
> w->last_point, and it seems to work very well.
> 
> It also introduces a new variable `scroll-move-point' that controls if
> the scrolling commands will try to move point to stay visible.

Once again, it's hard to understand the main ideas behind the
feature.  You explained above when it brings point into the view, but
what I'm looking for is an explanation for how to modify
redisplay_window when this feature is turned ON, without losing too
much of the existing redisplay functionality.

Also, a large part of the patch seems to change mainly whitespace, so
please use the -w switch to show diffs ignoring the whitespace
changes, because otherwise it's very hard to spot the real changes.

A couple of comments below:

> --- a/src/window.c
> +++ b/src/window.c
> @@ -5576,7 +5576,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)

It seems you made changes for GUI scrolling, but not for TTY
scrolling?  Is this feature supposed to be disabled on TTY frames?

> @@ -17768,6 +17770,9 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
>    else
>      scroll_max = 0;
>  
> +  if (!keep_point_visible)
> +    goto out;
> +
>   too_near_end:

I'm confused here.  This bypasses all of try_scrolling's code, which
can select a new window-start because, for example, point moved out of
the visible portion of the window.  Does this mean that when
this feature is turned ON, scrolling of the window in these cases is
no longer supported?  For example, let's say I pressed C-n and that
moved point below the window's end (with the default zero value of
scroll-margin) -- does it mean the window will not scroll, leaving
point invisible in this case?  That brings me back to a question I
already asked: how do users tell Emacs when they deliberately want to
move point outside of the window and when they want Emacs to scroll
the window to make point visible, as before?

> @@ -18183,6 +18190,10 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
>      return rc;
>  #endif
>  
> +  /* TODO: enable this optimization.  */
> +  if (!keep_point_visible)
> +    return CURSOR_MOVEMENT_CANNOT_BE_USED;

Likewise here: you are disabling one of the more important redisplay
optimizations, which minimizes redisplay work when the user just move
point a little ways.  Can you tell why this needs to be disabled under
the new behavior? it seems to be unrelated, because the optimization
only does its thing when point didn't leave the window.

> +  debug_method_add (w, "real redisplay starts");

This will have to go eventually, as this trace is not useful in
general.

> +  /* TODO: enable this optimization.  */
> +  if (!keep_point_visible)
> +    return false;

This optimization also doesn't necessarily have anything to do with
point being outside of the window.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09 11:45                                         ` Eli Zaretskii
@ 2021-12-09 12:19                                           ` Po Lu
  2021-12-09 12:45                                             ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2021-12-09 12:19 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Once again, it's hard to understand the main ideas behind the
> feature.  You explained above when it brings point into the view, but
> what I'm looking for is an explanation for how to modify
> redisplay_window when this feature is turned ON, without losing too
> much of the existing redisplay functionality.

The idea is to prevent redisplay_window from entering `recenter' unless
`w->force_start' or a few other conditions are true, and to not display
the phys cursor if its position is unknown.  A large part of
`try_scrolling' is also disabled, but I think I know how to turn it on
now.

> Also, a large part of the patch seems to change mainly whitespace, so
> please use the -w switch to show diffs ignoring the whitespace
> changes, because otherwise it's very hard to spot the real changes.

Thanks, I will keep that in mind in the future.

> A couple of comments below:

>> --- a/src/window.c
>> +++ b/src/window.c
>> @@ -5576,7 +5576,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)

> It seems you made changes for GUI scrolling, but not for TTY
> scrolling?  Is this feature supposed to be disabled on TTY frames?

No, it's something I plan to do in a while, but not yet.

>> @@ -17768,6 +17770,9 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
>>    else
>>      scroll_max = 0;
>>  
>> +  if (!keep_point_visible)
>> +    goto out;
>> +
>>   too_near_end:

> I'm confused here.  This bypasses all of try_scrolling's code, which
> can select a new window-start because, for example, point moved out of
> the visible portion of the window.  Does this mean that when
> this feature is turned ON, scrolling of the window in these cases is
> no longer supported?  For example, let's say I pressed C-n and that
> moved point below the window's end (with the default zero value of
> scroll-margin) -- does it mean the window will not scroll, leaving
> point invisible in this case?

It does not: it enters the code under the `recenter' label instead,
which causes point to be displayed at the center of the screen.

But the behavior is very different between that and the code in
try_scrolling, so I think it should be turned on if the numeric value of
point has changed, as we do with the recenter label.

>> @@ -18183,6 +18190,10 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp,
>>      return rc;
>>  #endif
>>  
>> +  /* TODO: enable this optimization.  */
>> +  if (!keep_point_visible)
>> +    return CURSOR_MOVEMENT_CANNOT_BE_USED;

> Likewise here: you are disabling one of the more important redisplay
> optimizations, which minimizes redisplay work when the user just move
> point a little ways.  Can you tell why this needs to be disabled under
> the new behavior? it seems to be unrelated, because the optimization
> only does its thing when point didn't leave the window.

I disabled all the redisplay optimizations when first developing this
feature, so I could turn them on one-by-one to see if they would cause
any problems.

This optimization works fine however, so it can be enabled.

> This will have to go eventually, as this trace is not useful in
> general.

Thanks, I'll keep that in mind.

> This optimization also doesn't necessarily have anything to do with
> point being outside of the window.

That's because I haven't tested to see if it works yet.

Thanks.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09 12:19                                           ` Po Lu
@ 2021-12-09 12:45                                             ` Eli Zaretskii
  0 siblings, 0 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-09 12:45 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Thu, 09 Dec 2021 20:19:13 +0800
> 
> > Likewise here: you are disabling one of the more important redisplay
> > optimizations, which minimizes redisplay work when the user just move
> > point a little ways.  Can you tell why this needs to be disabled under
> > the new behavior? it seems to be unrelated, because the optimization
> > only does its thing when point didn't leave the window.
> 
> I disabled all the redisplay optimizations when first developing this
> feature, so I could turn them on one-by-one to see if they would cause
> any problems.

You can disable each of these optimizations individually without
changing any code, by using the corresponding inhibit-* variables
(inhibit-try-window-id etc.).



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
@ 2021-12-09 15:34 Konrad Podczeck
  0 siblings, 0 replies; 111+ messages in thread
From: Konrad Podczeck @ 2021-12-09 15:34 UTC (permalink / raw)
  To: john; +Cc: emacs-devel

Hi John,

the update concerning hl-line-mode works fine. However, I observe a spill-over between different frames. E.g., suppose  I have open two buffers, say A and B, in two different frames. Do M-x scroll-without-point-mode, say in the frame showing A. Scroll in this frame so as to make the cursor there invisible. As soon as I use the mouse to make the frame showing B active, there is a jump back in the frame showing A. Now suppose the frame showing B is the active one, but the mouse is still over the other frame and scroll. Now any command in the frame showing B leads to a jump in frame A. Cam these be avoided?

Konrad


^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-09  8:02                                                     ` Eli Zaretskii
  2021-12-09  9:22                                                       ` Po Lu
@ 2021-12-25  6:45                                                       ` Po Lu
  2021-12-25  7:07                                                         ` Eli Zaretskii
  2022-02-06  7:22                                                         ` Po Lu
  1 sibling, 2 replies; 111+ messages in thread
From: Po Lu @ 2021-12-25  6:45 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> I already provided an example where this could not be what users
> expect: some command inserts text before the window-start, which
> causes the text shown in the window to move if we use the same
> window-start position.  Now, window-start is a marker, so
> theoretically we could use the same marker with the buffer position
> updated due to the insertion, in which case the stuff displayed in the
> window may stay unchanged.

> But is this what users will expect?  And if this is not what users
> expect, i.e. if we will need to recompute window-start anew in that
> case, we cannot use the "recenter around point" fallback.  Likewise
> with bringing point into the view: will this be what users expect in
> the described situation?

To be completely clear, the "recenter around point" fallback you allude
to is the code under the `recenter' label in `redisplay_window',
correct?

Please forgive me if this has been answered before, but I haven't been
working on this in a while, and my memory is already getting rusty.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-25  6:45                                                       ` Po Lu
@ 2021-12-25  7:07                                                         ` Eli Zaretskii
  2022-02-06  7:22                                                         ` Po Lu
  1 sibling, 0 replies; 111+ messages in thread
From: Eli Zaretskii @ 2021-12-25  7:07 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sat, 25 Dec 2021 14:45:59 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > I already provided an example where this could not be what users
> > expect: some command inserts text before the window-start, which
> > causes the text shown in the window to move if we use the same
> > window-start position.  Now, window-start is a marker, so
> > theoretically we could use the same marker with the buffer position
> > updated due to the insertion, in which case the stuff displayed in the
> > window may stay unchanged.
> 
> > But is this what users will expect?  And if this is not what users
> > expect, i.e. if we will need to recompute window-start anew in that
> > case, we cannot use the "recenter around point" fallback.  Likewise
> > with bringing point into the view: will this be what users expect in
> > the described situation?
> 
> To be completely clear, the "recenter around point" fallback you allude
> to is the code under the `recenter' label in `redisplay_window',
> correct?

Yes.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2021-12-25  6:45                                                       ` Po Lu
  2021-12-25  7:07                                                         ` Eli Zaretskii
@ 2022-02-06  7:22                                                         ` Po Lu
  2022-02-06 11:34                                                           ` Eli Zaretskii
  1 sibling, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-06  7:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 484 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> To be completely clear, the "recenter around point" fallback you allude
> to is the code under the `recenter' label in `redisplay_window',
> correct?
>
> Please forgive me if this has been answered before, but I haven't been
> working on this in a while, and my memory is already getting rusty.
>
> Thanks.

How about this: we recenter around the position of the first modified
character?  Point isn't moved at all during that process.

Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: test.diff --]
[-- Type: text/x-patch, Size: 29096 bytes --]

diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 4fcd2a3f7d..fabb53bb3d 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -250,10 +250,18 @@ Auto Scrolling
 @section Automatic Scrolling
 
 @cindex automatic scrolling
-  Emacs performs @dfn{automatic scrolling} when point moves out of the
-visible portion of the text.  Normally, automatic scrolling centers
-point vertically in the window, but there are several ways to alter
-this behavior.
+  Emacs by default performs @dfn{automatic scrolling} when point moves
+out of the visible portion of the text.  Normally, automatic scrolling
+centers point vertically in the window, but there are several ways to
+alter this behavior.
+
+@vindex keep-point-visible
+  If @code{keep-point-visible} is nil, redisplay will not move recenter
+the display when the window start is changed.
+
+@vindex scroll-move-point
+  If @code{scroll-move-point} is nil, scrolling commands will not move
+point to keep it inside the visible part of the window.
 
 @vindex scroll-conservatively
 @vindex scroll-minibuffer-conservatively
diff --git a/etc/NEWS b/etc/NEWS
index 6c5aeacb7b..a696a1dffc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -392,6 +392,15 @@ are met.  The conditions are given by the argument, which can be
 
 * Editing Changes in Emacs 29.1
 
+** New variable 'keep-point-visible'.
+This variable controls if redisplay will try to keep point visible
+inside the window.
+
++++
+** New variable 'scroll-move-point'.
+This variable controls if scrolling moves point to stay inside the
+window.
+
 ---
 ** Indentation of 'cl-flet' and 'cl-labels' has changed.
 These forms now indent like this:
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 54724ca328..8f07e58428 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -507,20 +507,22 @@ pixel-scroll-precision-scroll-down-page
          (edges (window-edges nil t))
          (usable-height (- (nth 3 edges)
                            (nth 1 edges)))
-         (next-pos (save-excursion
-                     (goto-char desired-start)
-                     (when (zerop (vertical-motion (1+ scroll-margin)))
-                       (set-window-start nil desired-start)
-                       (signal 'end-of-buffer nil))
-                     (while (when-let ((posn (posn-at-point)))
-                              (< (cdr (posn-x-y posn)) delta))
-                       (when (zerop (vertical-motion 1))
+         (next-pos (when keep-point-visible
+                     (save-excursion
+                       (goto-char desired-start)
+                       (when (zerop (vertical-motion (1+ scroll-margin)))
                          (set-window-start nil desired-start)
-                         (signal 'end-of-buffer nil)))
-                     (point)))
+                         (signal 'end-of-buffer nil))
+                       (while (when-let ((posn (posn-at-point)))
+                                (< (cdr (posn-x-y posn)) delta))
+                         (when (zerop (vertical-motion 1))
+                           (set-window-start nil desired-start)
+                           (signal 'end-of-buffer nil)))
+                       (point))))
          (scroll-preserve-screen-position nil)
          (auto-window-vscroll nil))
-    (when (and (or (< (point) next-pos))
+    (when (and keep-point-visible
+               (or (< (point) next-pos))
                (let ((pos-visibility (pos-visible-in-window-p next-pos nil t)))
                  (and pos-visibility
                       (or (eq (length pos-visibility) 2)
@@ -554,15 +556,17 @@ pixel-scroll-precision-scroll-up-page
          (max-y (- (nth 3 edges)
                    (nth 1 edges)))
          (usable-height max-y)
-         (posn (posn-at-x-y 0 (+ (window-tab-line-height)
-                                 (window-header-line-height)
-                                 (- max-y delta))))
-         (point (posn-point posn))
-         (up-point (save-excursion
-                     (goto-char point)
-                     (vertical-motion (- (1+ scroll-margin)))
-                     (point))))
-    (when (> (point) up-point)
+         (posn (when keep-point-visible
+                 (posn-at-x-y 0 (+ (window-tab-line-height)
+                                   (window-header-line-height)
+                                   (- max-y delta)))))
+         (point (when posn (posn-point posn)))
+         (up-point (when point
+                     (save-excursion
+                       (goto-char point)
+                       (vertical-motion (- (1+ scroll-margin)))
+                       (point)))))
+    (when (and keep-point-visible (> (point) up-point))
       (when (let ((pos-visible (pos-visible-in-window-p up-point nil t)))
               (or (eq (length pos-visible) 2)
                   (when-let* ((posn (posn-at-point up-point))
diff --git a/src/window.c b/src/window.c
index 449f2b0cc5..b2fd76652f 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5578,7 +5578,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      something like (scroll-down 1) with PT in the line before
      the partially visible one would recenter.  */
 
-  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
+  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos)
+      && scroll_move_point)
     {
       itdata = bidi_shelve_cache ();
       /* Move backward half the height of the window.  Performance note:
@@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
 		  w->start_at_line_beg = true;
 		  wset_update_mode_line (w);
 		  /* Set force_start so that redisplay_window will run the
-		     window-scroll-functions.  */
-		  w->force_start = true;
+		     window-scroll-functions, unless scroll_move_point is false,
+		     in which case forcing the start will cause recentering.  */
+		  w->force_start = scroll_move_point;
 		  return;
 		}
 	    }
@@ -5844,8 +5846,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
       w->start_at_line_beg = (pos == BEGV || FETCH_BYTE (bytepos - 1) == '\n');
       wset_update_mode_line (w);
       /* Set force_start so that redisplay_window will run the
-	 window-scroll-functions.  */
-      w->force_start = true;
+	 window-scroll-functions, unless scroll_move_point is false,
+	 in which case forcing the start will cause recentering.  */
+      w->force_start = scroll_move_point;
     }
 
   /* The rest of this function uses current_y in a nonstandard way,
@@ -5857,7 +5860,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      even if there is a header line.  */
   this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
 
-  if (n > 0)
+  if (scroll_move_point)
     {
       int last_y = it.last_visible_y - this_scroll_margin - 1;
 
@@ -5873,142 +5876,159 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
 	      || EQ (Vscroll_preserve_screen_position, Qt)))
 	/* We found PT at a legitimate height.  Leave it alone.  */
 	;
-      else
+      else if (n > 0)
 	{
-	  if (window_scroll_pixel_based_preserve_y >= 0)
-	    {
-	      /* Don't enter the scroll margin at the end of the window.  */
-	      int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
-
-	      /* If we have a header line, take account of it.  This
-		 is necessary because we set it.current_y to 0, above.  */
-	      move_it_to (&it, -1,
-			  window_scroll_pixel_based_preserve_x,
-			  goal_y - WINDOW_TAB_LINE_HEIGHT (w)
-				 - WINDOW_HEADER_LINE_HEIGHT (w),
-			  -1, MOVE_TO_Y | MOVE_TO_X);
-	    }
+	  int last_y = it.last_visible_y - this_scroll_margin - 1;
 
-	  /* Get out of the scroll margin at the top of the window.  */
-	  while (it.current_y < this_scroll_margin)
+	  /* We moved the window start towards ZV, so PT may be now
+	     in the scroll margin at the top.  */
+	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	  if (IT_CHARPOS (it) == PT
+	      && it.current_y >= this_scroll_margin
+	      && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
+	      - WINDOW_HEADER_LINE_HEIGHT (w)
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT at a legitimate height.  Leave it alone.  */
+	    ;
+	  else
 	    {
-	      int prev = it.current_y;
-	      move_it_by_lines (&it, 1);
-	      if (prev == it.current_y)
-		break;
+	      if (window_scroll_pixel_based_preserve_y >= 0)
+		{
+		  /* Don't enter the scroll margin at the end of the window.  */
+		  int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
+
+		  /* If we have a header line, take account of it.  This
+		     is necessary because we set it.current_y to 0, above.  */
+		  move_it_to (&it, -1,
+			      window_scroll_pixel_based_preserve_x,
+			      goal_y - WINDOW_TAB_LINE_HEIGHT (w)
+			      - WINDOW_HEADER_LINE_HEIGHT (w),
+			      -1, MOVE_TO_Y | MOVE_TO_X);
+		}
+
+	      /* Get out of the scroll margin at the top of the window.  */
+	      while (it.current_y < this_scroll_margin)
+		{
+		  int prev = it.current_y;
+		  move_it_by_lines (&it, 1);
+		  if (prev == it.current_y)
+		    break;
+		}
+	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+	      /* Fix up the Y position to preserve, if it is inside the
+		 scroll margin at the window top.  */
+	      if (window_scroll_pixel_based_preserve_y >= 0
+		  && window_scroll_pixel_based_preserve_y < this_scroll_margin)
+		window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	    }
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	  /* Fix up the Y position to preserve, if it is inside the
-	     scroll margin at the window top.  */
-	  if (window_scroll_pixel_based_preserve_y >= 0
-	      && window_scroll_pixel_based_preserve_y < this_scroll_margin)
-	    window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	}
-    }
-  else if (n < 0)
-    {
-      ptrdiff_t charpos, bytepos;
-      bool partial_p;
-
-      /* Save our position, for the
-	 window_scroll_pixel_based_preserve_y case.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* We moved the window start towards BEGV, so PT may be now
-	 in the scroll margin at the bottom.  */
-      move_it_to (&it, PT, -1,
-		  /* We subtract WINDOW_HEADER_LINE_HEIGHT because
-		     it.y is relative to the bottom of the header
-		     line, see above.  */
-		  (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
-				     - WINDOW_HEADER_LINE_HEIGHT (w)
-		   - partial_line_height (&it) - this_scroll_margin - 1),
-		  -1,
-		  MOVE_TO_POS | MOVE_TO_Y);
-
-      /* Save our position, in case it's correct.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* If PT is in the screen line at the last fully visible line,
-	 move_it_to will stop at X = 0 in that line, because the
-	 required Y coordinate is reached there.  See if we can get to
-	 PT without descending lower in Y, and if we can, it means we
-	 reached PT before the scroll margin.  */
-      if (charpos != PT)
+      else if (n < 0)
 	{
-	  struct it it2;
-	  void *it_data;
+	  ptrdiff_t charpos, bytepos;
+	  bool partial_p;
 
-	  it2 = it;
-	  it_data = bidi_shelve_cache ();
-	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
-	  if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+	  /* Save our position, for the
+	     window_scroll_pixel_based_preserve_y case.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* We moved the window start towards BEGV, so PT may be now
+	     in the scroll margin at the bottom.  */
+	  move_it_to (&it, PT, -1,
+		      /* We subtract WINDOW_HEADER_LINE_HEIGHT because
+			 it.y is relative to the bottom of the header
+			 line, see above.  */
+		      (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
+		       - WINDOW_HEADER_LINE_HEIGHT (w)
+		       - partial_line_height (&it) - this_scroll_margin - 1),
+		      -1,
+		      MOVE_TO_POS | MOVE_TO_Y);
+
+	  /* Save our position, in case it's correct.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* If PT is in the screen line at the last fully visible line,
+	     move_it_to will stop at X = 0 in that line, because the
+	     required Y coordinate is reached there.  See if we can get to
+	     PT without descending lower in Y, and if we can, it means we
+	     reached PT before the scroll margin.  */
+	  if (charpos != PT)
 	    {
-	      charpos = IT_CHARPOS (it);
-	      bytepos = IT_BYTEPOS (it);
-	      bidi_unshelve_cache (it_data, true);
+	      struct it it2;
+	      void *it_data;
+
+	      it2 = it;
+	      it_data = bidi_shelve_cache ();
+	      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	      if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+		{
+		  charpos = IT_CHARPOS (it);
+		  bytepos = IT_BYTEPOS (it);
+		  bidi_unshelve_cache (it_data, true);
+		}
+	      else
+		{
+		  it = it2;
+		  bidi_unshelve_cache (it_data, false);
+		}
 	    }
+
+	  /* See if point is on a partially visible line at the end.  */
+	  if (it.what == IT_EOB)
+	    partial_p =
+	      it.current_y + it.ascent + it.descent
+	      > it.last_visible_y - this_scroll_margin
+	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	  else
 	    {
-	      it = it2;
-	      bidi_unshelve_cache (it_data, false);
+	      move_it_by_lines (&it, 1);
+	      partial_p =
+		it.current_y
+		> it.last_visible_y - this_scroll_margin
+		- WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	    }
-	}
-
-      /* See if point is on a partially visible line at the end.  */
-      if (it.what == IT_EOB)
-	partial_p =
-	  it.current_y + it.ascent + it.descent
-	  > it.last_visible_y - this_scroll_margin
-	  - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-      else
-	{
-	  move_it_by_lines (&it, 1);
-	  partial_p =
-	    it.current_y
-	    > it.last_visible_y - this_scroll_margin
-	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-	}
 
-      if (charpos == PT && !partial_p
-          && (NILP (Vscroll_preserve_screen_position)
-	      || EQ (Vscroll_preserve_screen_position, Qt)))
-	/* We found PT before we found the display margin, so PT is ok.  */
-	;
-      else if (window_scroll_pixel_based_preserve_y >= 0)
-	{
-	  int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
-			    window_scroll_pixel_based_preserve_y);
-
-	  /* Don't let the preserved screen Y coordinate put us inside
-	     any of the two margins.  */
-	  if (goal_y < this_scroll_margin)
-	    goal_y = this_scroll_margin;
-	  SET_TEXT_POS_FROM_MARKER (start, w->start);
-	  start_display (&it, w, start);
-	  /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
-	     here because we called start_display again and did not
-	     alter it.current_y this time.  */
-	  move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
-		      goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	}
-      else
-	{
-	  if (partial_p)
-	    /* The last line was only partially visible, so back up two
-	       lines to make sure we're on a fully visible line.  */
+	  if (charpos == PT && !partial_p
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT before we found the display margin, so PT is ok.  */
+	    ;
+	  else if (window_scroll_pixel_based_preserve_y >= 0)
 	    {
-	      move_it_by_lines (&it, -2);
+	      int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
+				window_scroll_pixel_based_preserve_y);
+
+	      /* Don't let the preserved screen Y coordinate put us inside
+		 any of the two margins.  */
+	      if (goal_y < this_scroll_margin)
+		goal_y = this_scroll_margin;
+	      SET_TEXT_POS_FROM_MARKER (start, w->start);
+	      start_display (&it, w, start);
+	      /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
+		 here because we called start_display again and did not
+		 alter it.current_y this time.  */
+	      move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
+			  goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
 	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
 	    }
 	  else
-	    /* No, the position we saved is OK, so use it.  */
-	    SET_PT_BOTH (charpos, bytepos);
+	    {
+	      if (partial_p)
+		/* The last line was only partially visible, so back up two
+		   lines to make sure we're on a fully visible line.  */
+		{
+		  move_it_by_lines (&it, -2);
+		  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+		}
+	      else
+		/* No, the position we saved is OK, so use it.  */
+		SET_PT_BOTH (charpos, bytepos);
+	    }
 	}
     }
+
   bidi_unshelve_cache (itdata, false);
 
   if (adjust_old_pointm)
@@ -8513,6 +8533,10 @@ syms_of_window (void)
 displayed after a scrolling operation to be somewhat inaccurate.  */);
   fast_but_imprecise_scrolling = false;
 
+  DEFVAR_BOOL ("scroll-move-point", scroll_move_point,
+	       doc: /* If nil, don't move point to fit inside the window when scrolling.  */);
+  scroll_move_point = true;
+
   defsubr (&Sselected_window);
   defsubr (&Sold_selected_window);
   defsubr (&Sminibuffer_window);
diff --git a/src/xdisp.c b/src/xdisp.c
index db9bc512a9..1f882f6996 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16224,6 +16224,8 @@ #define AINC(a,i)							\
   tlbufpos = this_line_start_pos;
   tlendpos = this_line_end_pos;
   if (!consider_all_windows_p
+      /* TODO: enable this optimization.  */
+      && keep_point_visible
       && CHARPOS (tlbufpos) > 0
       && !w->update_mode_line
       && !current_buffer->clip_changed
@@ -17874,6 +17876,9 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   else
     scroll_max = 0;
 
+  if (!keep_point_visible && PT == w->last_point)
+    goto out;
+
  too_near_end:
 
   /* Decide whether to scroll down.  */
@@ -18110,6 +18115,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   /* Run window scroll functions.  */
   startp = run_window_scroll_functions (window, startp);
 
+ out:
+
   /* Display the window.  Give up if new fonts are loaded, or if point
      doesn't appear.  */
   if (!try_window (window, startp, 0))
@@ -18844,6 +18851,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   int frame_line_height, margin;
   bool use_desired_matrix;
   void *itdata = NULL;
+  bool need_recenter_even_if_point_can_be_invisible = false;
+  ptrdiff_t that_recentering_position;
+  ptrdiff_t that_recentering_byte;
 
   SET_TEXT_POS (lpoint, PT, PT_BYTE);
   opoint = lpoint;
@@ -19022,6 +19032,36 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
   SET_TEXT_POS_FROM_MARKER (startp, w->start);
 
+  if (!keep_point_visible && window_outdated (w))
+    {
+      /* If some text changed between window start, then recenter the
+	 display around the first character that changed, to avoid
+	 confusing the user by not updating the display to reflect the
+	 changes.  */
+      ptrdiff_t last_changed_charpos, first_changed_charpos;
+
+      /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
+	 only if buffer has really changed.  The reason is that the gap is
+	 initially at Z for freshly visited files.  The code below would
+	 set end_unchanged to 0 in that case.  */
+      if (GPT - BEG < BEG_UNCHANGED)
+	BEG_UNCHANGED = GPT - BEG;
+      if (Z - GPT < END_UNCHANGED)
+	END_UNCHANGED = Z - GPT;
+
+      /* The position of the first and last character that has been changed.  */
+      first_changed_charpos = BEG + BEG_UNCHANGED;
+      last_changed_charpos  = Z - END_UNCHANGED;
+
+      if (last_changed_charpos < CHARPOS (startp))
+	{
+	  that_recentering_position = first_changed_charpos;
+	  that_recentering_byte = buf_charpos_to_bytepos (current_buffer,
+							  first_changed_charpos);
+	  need_recenter_even_if_point_can_be_invisible = true;
+	}
+    }
+
   /* If someone specified a new starting point but did not insist,
      check whether it can be used.  */
   if ((w->optional_new_start || window_frozen_p (w))
@@ -19146,6 +19186,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	    new_vpos = MATRIX_ROW_BOTTOM_Y (r);
 	  else	/* Give up and just move to the middle of the window.  */
 	    new_vpos = window_box_height (w) / 2;
+
 	}
 
       if (!cursor_row_fully_visible_p (w, false, false, false))
@@ -19393,10 +19434,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  IF_DEBUG (debug_method_add (w, "1"));
 	  clear_glyph_matrix (w->desired_matrix);
 	  if (try_window (window, startp, TRY_WINDOW_CHECK_MARGINS) < 0)
-	    /* -1 means we need to scroll.
-	       0 means we need new matrices, but fonts_changed
-	       is set in that case, so we will detect it below.  */
-	    goto try_to_scroll;
+	    {
+	      /* -1 means we need to scroll.
+		 0 means we need new matrices, but fonts_changed
+		 is set in that case, so we will detect it below.  */
+	      goto try_to_scroll;
+	    }
 	}
 
       if (f->fonts_changed)
@@ -19425,7 +19468,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
  try_to_scroll:
 
-  /* Redisplay the mode line.  Select the buffer properly for that.  */
+ /* Redisplay the mode line.  Select the buffer properly for that.  */
   if (!update_mode_line)
     {
       update_mode_line = true;
@@ -19471,6 +19514,13 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	}
     }
 
+  if (!keep_point_visible
+      && !need_recenter_even_if_point_can_be_invisible)
+    {
+      if (PT == w->last_point)
+	goto maybe_try_window;
+    }
+
   /* Finally, just choose a place to start which positions point
      according to user preferences.  */
 
@@ -19484,9 +19534,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   if (!buffer_unchanged_p)
     w->base_line_number = 0;
 
-  /* Determine the window start relative to point.  */
-  init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
+  /* Determine the window start relative to where we want to recenter
+     to.  */
+
+  if (need_recenter_even_if_point_can_be_invisible)
+    init_iterator (&it, w, that_recentering_position,
+		   that_recentering_byte, NULL, DEFAULT_FACE_ID);
+  else
+    init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
   it.current_y = it.last_visible_y;
+
   if (centering_position < 0)
     {
       ptrdiff_t margin_pos = CHARPOS (startp);
@@ -19513,7 +19570,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  margin_pos = IT_CHARPOS (it1);
 	  RESTORE_IT (&it, &it, it1data);
 	}
-      scrolling_up = PT > margin_pos;
+      scrolling_up = (need_recenter_even_if_point_can_be_invisible
+		      ? that_recentering_position
+		      : PT) > margin_pos;
       aggressive =
 	scrolling_up
 	? BVAR (current_buffer, scroll_up_aggressively)
@@ -19754,6 +19813,53 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       centering_position = 0;
       goto recenter;
     }
+  goto done;
+
+ maybe_try_window:
+
+  /* Set the window start position here explicitly if it is outside
+     the accessible portion of the buffer.  */
+
+  if (CHARPOS (startp) < BEGV
+      || CHARPOS (startp) > ZV)
+    {
+      if (CHARPOS (startp) < BEGV)
+	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
+      else
+	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
+
+      SET_TEXT_POS_FROM_MARKER (startp, w->start);
+
+      /* Run scroll hooks.  */
+      startp = run_window_scroll_functions (window, startp);
+    }
+
+  /* We invoke try_window and try_window_reusing_current_matrix below,
+     and they manipulate the bidi cache.  Save and restore the cache
+     state of our iterator, so we could continue using it after that.  */
+  itdata = bidi_shelve_cache ();
+
+  /* Redisplay the window.  */
+  use_desired_matrix = false;
+  if (!current_matrix_up_to_date_p
+      || windows_or_buffers_changed
+      || f->cursor_type_changed
+      /* Don't use try_window_reusing_current_matrix in this case
+	 because it can have changed the buffer.  */
+      || !NILP (Vwindow_scroll_functions)
+      || !just_this_one_p
+      || MINI_WINDOW_P (w)
+      || !(used_current_matrix_p
+	   = try_window_reusing_current_matrix (w)))
+    use_desired_matrix = (try_window (window, startp, 0) == 1);
+
+  bidi_unshelve_cache (itdata, false);
+
+  /* If new fonts have been loaded (due to fontsets), give up.  We
+     have to start a new redisplay since we need to re-adjust glyph
+     matrices.  */
+  if (f->fonts_changed)
+    goto need_larger_matrices;
 
  done:
 
@@ -19761,6 +19867,11 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   w->start_at_line_beg = (CHARPOS (startp) == BEGV
 			  || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n');
 
+  if (!keep_point_visible
+      && (w->cursor.vpos == -1)
+      && w->phys_cursor_on_p)
+    erase_phys_cursor (w);
+
   /* Display the mode line, header line, and tab-line, if we must.  */
   if ((update_mode_line
        /* If window not full width, must redo its mode line
@@ -28638,6 +28749,7 @@ fill_composite_glyph_string (struct glyph_string *s, struct face *base_face,
 
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28690,6 +28802,7 @@ fill_gstring_glyph_string (struct glyph_string *s, int face_id,
   s->cmp_to = glyph->slice.cmp.to + 1;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28759,6 +28872,7 @@ fill_glyphless_glyph_string (struct glyph_string *s, int face_id,
   s->font = s->face->font ? s->face->font : FRAME_FONT (s->f);
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28834,6 +28948,7 @@ fill_glyph_string (struct glyph_string *s, int face_id,
 
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28879,6 +28994,7 @@ fill_image_glyph_string (struct glyph_string *s)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28905,6 +29021,7 @@ fill_xwidget_glyph_string (struct glyph_string *s)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28942,6 +29059,7 @@ fill_stretch_glyph_string (struct glyph_string *s, int start, int end)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -29231,6 +29349,7 @@ set_glyph_string_background_width (struct glyph_string *s, int start, int last_x
 #ifdef HAVE_WINDOW_SYSTEM
       if (FRAME_WINDOW_P (s->f)
 	  && s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w))
@@ -32608,6 +32727,9 @@ display_and_set_cursor (struct window *w, bool on,
 	      && new_cursor_width != w->phys_cursor_width)))
     erase_phys_cursor (w);
 
+  if (w->cursor.vpos == -1)
+    return;
+
   /* Don't check phys_cursor_on_p here because that flag is only set
      to false in some cases where we know that the cursor has been
      completely erased, to avoid the extra work of erasing the cursor
@@ -32968,6 +33090,9 @@ cursor_in_mouse_face_p (struct window *w)
   int vpos = w->phys_cursor.vpos;
   struct glyph_row *row = MATRIX_ROW (w->current_matrix, vpos);
 
+  if (vpos == -1)
+    return false;
+
   /* When the window is hscrolled, cursor hpos can legitimately be out
      of bounds, but we draw the cursor at the corresponding window
      margin in that case.  */
@@ -35717,6 +35842,10 @@ syms_of_xdisp (void)
   x_stretch_cursor_p = 0;
 #endif
 
+  DEFVAR_BOOL ("keep-point-visible", keep_point_visible,
+	       doc: /* Non-nil means to keep the point visible.  */);
+  keep_point_visible = 1;
+
   DEFVAR_LISP ("show-trailing-whitespace", Vshow_trailing_whitespace,
     doc: /* Non-nil means highlight trailing whitespace.
 The face used for trailing whitespace is `trailing-whitespace'.  */);

^ permalink raw reply related	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06  7:22                                                         ` Po Lu
@ 2022-02-06 11:34                                                           ` Eli Zaretskii
  2022-02-06 11:46                                                             ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-06 11:34 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 06 Feb 2022 15:22:57 +0800
> 
> Po Lu <luangruo@yahoo.com> writes:
> 
> > To be completely clear, the "recenter around point" fallback you allude
> > to is the code under the `recenter' label in `redisplay_window',
> > correct?
> >
> > Please forgive me if this has been answered before, but I haven't been
> > working on this in a while, and my memory is already getting rusty.
> >
> > Thanks.
> 
> How about this: we recenter around the position of the first modified
> character?  Point isn't moved at all during that process.

It's very hard to keep a discussion with such long pauses.  I don't
even remember what we were discussing the last time and what issues
remained unresolved.

> +@vindex keep-point-visible
> +  If @code{keep-point-visible} is nil, redisplay will not move recenter
> +the display when the window start is changed.
> +
> +@vindex scroll-move-point
> +  If @code{scroll-move-point} is nil, scrolling commands will not move
> +point to keep it inside the visible part of the window.

Why do we need 2 flags?  Are they indeed orthogonal, or can we have a
single variable (perhaps with more than 2 states)?

> +** New variable 'keep-point-visible'.
> +This variable controls if redisplay will try to keep point visible
> +inside the window.
> +
> ++++
> +** New variable 'scroll-move-point'.
> +This variable controls if scrolling moves point to stay inside the
> +window.

This is waaaay too terse for such a significant change...

> --- a/lisp/pixel-scroll.el
> +++ b/lisp/pixel-scroll.el

Why do we need to have changes in pixel-scroll.el be part of this
changeset?  It makes the changes harder to review, and is not really
related to the changes in the display code.

> --- a/src/window.c
> +++ b/src/window.c
> @@ -5578,7 +5578,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>       something like (scroll-down 1) with PT in the line before
>       the partially visible one would recenter.  */
>  
> -  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
> +  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos)
> +      && scroll_move_point)

Don't you need to test keep_point_visible as well here?  If not, why
not?

> @@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>  		  w->start_at_line_beg = true;
>  		  wset_update_mode_line (w);
>  		  /* Set force_start so that redisplay_window will run the
> -		     window-scroll-functions.  */
> -		  w->force_start = true;
> +		     window-scroll-functions, unless scroll_move_point is false,
> +		     in which case forcing the start will cause recentering.  */
> +		  w->force_start = scroll_move_point;

Should the logic of whether and how to obey the force_start flag be
confined to the display code, instead of having part of it here?  What
does it mean when you set the w->start point, but do NOT set the
w->force_start flag?

> @@ -5844,8 +5846,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>        w->start_at_line_beg = (pos == BEGV || FETCH_BYTE (bytepos - 1) == '\n');
>        wset_update_mode_line (w);
>        /* Set force_start so that redisplay_window will run the
> -	 window-scroll-functions.  */
> -      w->force_start = true;
> +	 window-scroll-functions, unless scroll_move_point is false,
> +	 in which case forcing the start will cause recentering.  */
> +      w->force_start = scroll_move_point;

Same here.

> @@ -5857,7 +5860,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>       even if there is a header line.  */
>    this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
>  
> -  if (n > 0)
> +  if (scroll_move_point)

This and the rest of changes in window_scroll_pixel_based are
impossible to review: you replaced the "n > 0" condition with a
different one, and now the rest of the diffs are completely
unreadable.

I also don't understand how come the exact same code which was
previously run only for n > 0 is now run for any value of 'n', and
_by_default_ (since scroll_move_point is non-zero by default)?  How
can that be TRT??

> +  if (!keep_point_visible && window_outdated (w))
> +    {
> +      /* If some text changed between window start, then recenter the
> +	 display around the first character that changed, to avoid
> +	 confusing the user by not updating the display to reflect the
> +	 changes.  */
> +      ptrdiff_t last_changed_charpos, first_changed_charpos;
> +
> +      /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
> +	 only if buffer has really changed.  The reason is that the gap is
> +	 initially at Z for freshly visited files.  The code below would
> +	 set end_unchanged to 0 in that case.  */
> +      if (GPT - BEG < BEG_UNCHANGED)
> +	BEG_UNCHANGED = GPT - BEG;
> +      if (Z - GPT < END_UNCHANGED)
> +	END_UNCHANGED = Z - GPT;

I'm not sure I understand this part.  Why do you need to change the
values of BEG_UNCHANGED and END_UNCHANGED -- those are supposed to be
changed only by code that modifies the buffer text.

> -	    /* -1 means we need to scroll.
> -	       0 means we need new matrices, but fonts_changed
> -	       is set in that case, so we will detect it below.  */
> -	    goto try_to_scroll;
> +	    {
> +	      /* -1 means we need to scroll.
> +		 0 means we need new matrices, but fonts_changed
> +		 is set in that case, so we will detect it below.  */
> +	      goto try_to_scroll;
> +	    }

These braces are redundant.

> +  /* Determine the window start relative to where we want to recenter
> +     to.  */
> +
> +  if (need_recenter_even_if_point_can_be_invisible)
> +    init_iterator (&it, w, that_recentering_position,
> +		   that_recentering_byte, NULL, DEFAULT_FACE_ID);
> +  else
> +    init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
>    it.current_y = it.last_visible_y;
> +

You want to display window around the change, but not bring point
there?  Is that a good idea?

> + maybe_try_window:

Why do we need this maybe_try_window stuff?  It seems to repeat
existing code, doesn't it?

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06 11:34                                                           ` Eli Zaretskii
@ 2022-02-06 11:46                                                             ` Po Lu
  2022-02-06 11:55                                                               ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-06 11:46 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> It's very hard to keep a discussion with such long pauses.  I don't
> even remember what we were discussing the last time and what issues
> remained unresolved.

Sorry for that, and I don't quite remember the problems either.  IIRC,
the one problem that remained unsolved was to find a better solution for
the case where some text changed behind window start than to recenter
the window around point, which I think I've found.

>> +@vindex keep-point-visible
>> +  If @code{keep-point-visible} is nil, redisplay will not move recenter
>> +the display when the window start is changed.
>> +
>> +@vindex scroll-move-point
>> +  If @code{scroll-move-point} is nil, scrolling commands will not move
>> +point to keep it inside the visible part of the window.

> Why do we need 2 flags?  Are they indeed orthogonal, or can we have a
> single variable (perhaps with more than 2 states)?

The first flag controls the behaviour of redisplay, while the second
controls that of the scrolling commands, so I think they are orthogonal.

> This is waaaay too terse for such a significant change...

Agreed, though I can't think of anything better.

> Why do we need to have changes in pixel-scroll.el be part of this
> changeset?  It makes the changes harder to review, and is not really
> related to the changes in the display code.

It allows pixel-scroll to take advantage of the new feature.  You can
just ignore it for now, I simply forgot to remove it.

>> -  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
>> +  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos)
>> +      && scroll_move_point)

> Don't you need to test keep_point_visible as well here?  If not, why
> not?

Thanks for catching that.

>> @@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>>  		  w->start_at_line_beg = true;
>>  		  wset_update_mode_line (w);
>>  		  /* Set force_start so that redisplay_window will run the
>> -		     window-scroll-functions.  */
>> -		  w->force_start = true;
>> +		     window-scroll-functions, unless scroll_move_point is false,
>> +		     in which case forcing the start will cause recentering.  */
>> +		  w->force_start = scroll_move_point;

> What does it mean when you set the w->start point, but do NOT set the
> w->force_start flag?

w->force_start will result in point being constrained inside window
start by the code under force_start: in redisplay_window.

>> @@ -5857,7 +5860,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>>       even if there is a header line.  */
>>    this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
>>  
>> -  if (n > 0)
>> +  if (scroll_move_point)

> This and the rest of changes in window_scroll_pixel_based are
> impossible to review: you replaced the "n > 0" condition with a
> different one, and now the rest of the diffs are completely
> unreadable.

> I also don't understand how come the exact same code which was
> previously run only for n > 0 is now run for any value of 'n', and
> _by_default_ (since scroll_move_point is non-zero by default)?  How
> can that be TRT??

??? That seems to be a problem which came up after I rebased the changes
onto master from some version of master around late December, not
before.  Thanks for catching it.

>> +  if (!keep_point_visible && window_outdated (w))
>> +    {
>> +      /* If some text changed between window start, then recenter the
>> +	 display around the first character that changed, to avoid
>> +	 confusing the user by not updating the display to reflect the
>> +	 changes.  */
>> +      ptrdiff_t last_changed_charpos, first_changed_charpos;
>> +
>> +      /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
>> +	 only if buffer has really changed.  The reason is that the gap is
>> +	 initially at Z for freshly visited files.  The code below would
>> +	 set end_unchanged to 0 in that case.  */
>> +      if (GPT - BEG < BEG_UNCHANGED)
>> +	BEG_UNCHANGED = GPT - BEG;
>> +      if (Z - GPT < END_UNCHANGED)
>> +	END_UNCHANGED = Z - GPT;

> I'm not sure I understand this part.  Why do you need to change the
> values of BEG_UNCHANGED and END_UNCHANGED -- those are supposed to be
> changed only by code that modifies the buffer text.

I saw the code do that in try_window_id, so even though I didn't really
understand it I thought it would be safer to do that here as well.

>> -	    /* -1 means we need to scroll.
>> -	       0 means we need new matrices, but fonts_changed
>> -	       is set in that case, so we will detect it below.  */
>> -	    goto try_to_scroll;
>> +	    {
>> +	      /* -1 means we need to scroll.
>> +		 0 means we need new matrices, but fonts_changed
>> +		 is set in that case, so we will detect it below.  */
>> +	      goto try_to_scroll;
>> +	    }
>
> These braces are redundant.

Thanks.

>> +  /* Determine the window start relative to where we want to recenter
>> +     to.  */
>> +
>> +  if (need_recenter_even_if_point_can_be_invisible)
>> +    init_iterator (&it, w, that_recentering_position,
>> +		   that_recentering_byte, NULL, DEFAULT_FACE_ID);
>> +  else
>> +    init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
>>    it.current_y = it.last_visible_y;
>> +

> You want to display window around the change, but not bring point
> there?  Is that a good idea?

Yes, IMO.  If the change is actually at point, then the window will be
recentered around point by the `PT != w->last_point' conditional.

Otherwise, such surprising changes to point would be unwanted.

> Why do we need this maybe_try_window stuff?  It seems to repeat
> existing code, doesn't it?

It does.  There was a reason I put it there last December, but I can't
remember it right now.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06 11:46                                                             ` Po Lu
@ 2022-02-06 11:55                                                               ` Eli Zaretskii
  2022-02-06 12:21                                                                 ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-06 11:55 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 06 Feb 2022 19:46:15 +0800
> 
> >> +@vindex keep-point-visible
> >> +  If @code{keep-point-visible} is nil, redisplay will not move recenter
> >> +the display when the window start is changed.
> >> +
> >> +@vindex scroll-move-point
> >> +  If @code{scroll-move-point} is nil, scrolling commands will not move
> >> +point to keep it inside the visible part of the window.
> 
> > Why do we need 2 flags?  Are they indeed orthogonal, or can we have a
> > single variable (perhaps with more than 2 states)?
> 
> The first flag controls the behaviour of redisplay, while the second
> controls that of the scrolling commands, so I think they are orthogonal.

Since scrolling is basically implemented in redisplay, I don't think
this answers my question.

Let's look at this from another aspect: which combinations of values
of these two variables make sense, and what would be the behavior
under each one of these reasonable combinations?

> >> @@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
> >>  		  w->start_at_line_beg = true;
> >>  		  wset_update_mode_line (w);
> >>  		  /* Set force_start so that redisplay_window will run the
> >> -		     window-scroll-functions.  */
> >> -		  w->force_start = true;
> >> +		     window-scroll-functions, unless scroll_move_point is false,
> >> +		     in which case forcing the start will cause recentering.  */
> >> +		  w->force_start = scroll_move_point;
> 
> > What does it mean when you set the w->start point, but do NOT set the
> > w->force_start flag?
> 
> w->force_start will result in point being constrained inside window
> start by the code under force_start: in redisplay_window.

That doesn't really answer my question.  I was asking what is the
semantics of setting the window-start, but not the force_start flag?
What is expected from redisplay in this case? should it obey
window-start?

> >> +      /* Make sure beg_unchanged and end_unchanged are up to date.  Do it
> >> +	 only if buffer has really changed.  The reason is that the gap is
> >> +	 initially at Z for freshly visited files.  The code below would
> >> +	 set end_unchanged to 0 in that case.  */
> >> +      if (GPT - BEG < BEG_UNCHANGED)
> >> +	BEG_UNCHANGED = GPT - BEG;
> >> +      if (Z - GPT < END_UNCHANGED)
> >> +	END_UNCHANGED = Z - GPT;
> 
> > I'm not sure I understand this part.  Why do you need to change the
> > values of BEG_UNCHANGED and END_UNCHANGED -- those are supposed to be
> > changed only by code that modifies the buffer text.
> 
> I saw the code do that in try_window_id, so even though I didn't really
> understand it I thought it would be safer to do that here as well.

But you aren't going to do anything that try_window_id does, are you?

> > You want to display window around the change, but not bring point
> > there?  Is that a good idea?
> 
> Yes, IMO.  If the change is actually at point, then the window will be
> recentered around point by the `PT != w->last_point' conditional.
> 
> Otherwise, such surprising changes to point would be unwanted.

No, I mean don't we want to force point to move to the locus of these
changes?  I thought other applications did that.

> > Why do we need this maybe_try_window stuff?  It seems to repeat
> > existing code, doesn't it?
> 
> It does.  There was a reason I put it there last December, but I can't
> remember it right now.

Too bad.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06 11:55                                                               ` Eli Zaretskii
@ 2022-02-06 12:21                                                                 ` Po Lu
  2022-02-06 16:15                                                                   ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-06 12:21 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Since scrolling is basically implemented in redisplay, I don't think
> this answers my question.
>
> Let's look at this from another aspect: which combinations of values
> of these two variables make sense, and what would be the behavior
> under each one of these reasonable combinations?

It makes sense to have `scroll-move-point' to t, and
`keep-point-visible' set to nil or t, and `scroll-move-point' set to nil
when `keep-point-visible' is nil.

The behaviour under the first and combinations would be that scrolling
moves point to fit inside the visible portion of the buffer.  With the
third, scrolling will not move the point at all.

>> >> @@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
>> >>  		  w->start_at_line_beg = true;
>> >>  		  wset_update_mode_line (w);
>> >>  		  /* Set force_start so that redisplay_window will run the
>> >> -		     window-scroll-functions.  */
>> >> -		  w->force_start = true;
>> >> +		     window-scroll-functions, unless scroll_move_point is false,
>> >> +		     in which case forcing the start will cause recentering.  */
>> >> +		  w->force_start = scroll_move_point;

> That doesn't really answer my question.  I was asking what is the
> semantics of setting the window-start, but not the force_start flag?
> What is expected from redisplay in this case? should it obey
> window-start?

It's expected that it will obey window-start (unless some text changed
before it, in which case it will recenter around the first change
position.)

> But you aren't going to do anything that try_window_id does, are you?

No, so it's probably safe to remove then.  Thanks.

> No, I mean don't we want to force point to move to the locus of these
> changes?  I thought other applications did that.

They provide no way to make changes without point already being at the
locus of the change.  (In which case it will be caught either by the `PT
!= w->last_point' conditional, and redisplay will recenter around point
as usual, or if point did not move, then it will recenter around the
change, which would be near the point in that case.)

IOW, there's no way in most other editors to do something like this:

  (save-excursion
    (goto-char (point-min))
    (insert "foo"))



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06 12:21                                                                 ` Po Lu
@ 2022-02-06 16:15                                                                   ` Eli Zaretskii
  2022-02-07  1:21                                                                     ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-06 16:15 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Sun, 06 Feb 2022 20:21:11 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Since scrolling is basically implemented in redisplay, I don't think
> > this answers my question.
> >
> > Let's look at this from another aspect: which combinations of values
> > of these two variables make sense, and what would be the behavior
> > under each one of these reasonable combinations?
> 
> It makes sense to have `scroll-move-point' to t, and
> `keep-point-visible' set to nil or t, and `scroll-move-point' set to nil
> when `keep-point-visible' is nil.
> 
> The behaviour under the first and combinations would be that scrolling
> moves point to fit inside the visible portion of the buffer.  With the
> third, scrolling will not move the point at all.

So we have at most 3 states, and could have a single tri-state
variable.

And I question the validity of the combination scroll-move-point = nil
with keep-point-visible = t.  The problem with your implementation is
that scroll-move-point = nil disables scrolling in redisplay_window,
but scrolling there is used not necessarily as result of scrolling
commands, but also when redisplay_window decides that the optimal
method of updating a window is to scroll its previous contents.  Thus,
disabling scrolling on that level will cause confusing results,
because users will expect that to affect only scrolling commands: they
are unaware that redisplay sometimes scrolls the window for other
reasons.

> >> >> @@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
> >> >>  		  w->start_at_line_beg = true;
> >> >>  		  wset_update_mode_line (w);
> >> >>  		  /* Set force_start so that redisplay_window will run the
> >> >> -		     window-scroll-functions.  */
> >> >> -		  w->force_start = true;
> >> >> +		     window-scroll-functions, unless scroll_move_point is false,
> >> >> +		     in which case forcing the start will cause recentering.  */
> >> >> +		  w->force_start = scroll_move_point;
> 
> > That doesn't really answer my question.  I was asking what is the
> > semantics of setting the window-start, but not the force_start flag?
> > What is expected from redisplay in this case? should it obey
> > window-start?
> 
> It's expected that it will obey window-start (unless some text changed
> before it, in which case it will recenter around the first change
> position.)

For that, you must set the force_start flag.  If you don't set it,
redisplay cannot know whether the value of w->start is just the result
of the previous redisplay cycle or was set by some user command which
wants to scroll the window.

Perhaps you want to explore using w->optional_new_start instead?

> > No, I mean don't we want to force point to move to the locus of these
> > changes?  I thought other applications did that.
> 
> They provide no way to make changes without point already being at the
> locus of the change.  (In which case it will be caught either by the `PT
> != w->last_point' conditional, and redisplay will recenter around point
> as usual, or if point did not move, then it will recenter around the
> change, which would be near the point in that case.)
> 
> IOW, there's no way in most other editors to do something like this:
> 
>   (save-excursion
>     (goto-char (point-min))
>     (insert "foo"))

Maybe so should we, at least as an option.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-06 16:15                                                                   ` Eli Zaretskii
@ 2022-02-07  1:21                                                                     ` Po Lu
  2022-02-07  7:21                                                                       ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-07  1:21 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> So we have at most 3 states, and could have a single tri-state
> variable.

Indeed.

> And I question the validity of the combination scroll-move-point = nil
> with keep-point-visible = t.  The problem with your implementation is
> that scroll-move-point = nil disables scrolling in redisplay_window,
> but scrolling there is used not necessarily as result of scrolling
> commands, but also when redisplay_window decides that the optimal
> method of updating a window is to scroll its previous contents.  Thus,
> disabling scrolling on that level will cause confusing results,
> because users will expect that to affect only scrolling commands: they
> are unaware that redisplay sometimes scrolls the window for other
> reasons.

Perhaps we could have redisplay bind `scroll-move-point' to a reasonable
value before calling the scrolling commands?

> For that, you must set the force_start flag.  If you don't set it,
> redisplay cannot know whether the value of w->start is just the result
> of the previous redisplay cycle or was set by some user command which
> wants to scroll the window.
>
> Perhaps you want to explore using w->optional_new_start instead?

Yes, it seems to be perfect for what I need.  Thanks.

> Maybe so should we, at least as an option.

Sure, an option would be nice.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-07  1:21                                                                     ` Po Lu
@ 2022-02-07  7:21                                                                       ` Po Lu
  2022-02-07 13:41                                                                         ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-07  7:21 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Po Lu <luangruo@yahoo.com> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>> So we have at most 3 states, and could have a single tri-state
>> variable.
>
> Indeed.
>
>> And I question the validity of the combination scroll-move-point = nil
>> with keep-point-visible = t.  The problem with your implementation is
>> that scroll-move-point = nil disables scrolling in redisplay_window,
>> but scrolling there is used not necessarily as result of scrolling
>> commands, but also when redisplay_window decides that the optimal
>> method of updating a window is to scroll its previous contents.  Thus,
>> disabling scrolling on that level will cause confusing results,
>> because users will expect that to affect only scrolling commands: they
>> are unaware that redisplay sometimes scrolls the window for other
>> reasons.
>
> Perhaps we could have redisplay bind `scroll-move-point' to a reasonable
> value before calling the scrolling commands?

I don't see where the scrolling commands in window.c get called from
`redisplay_window'.  Is it try_scrolling?  If so, I see nothing there.

Thanks.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-07  7:21                                                                       ` Po Lu
@ 2022-02-07 13:41                                                                         ` Eli Zaretskii
  2022-02-07 13:57                                                                           ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-07 13:41 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 07 Feb 2022 15:21:49 +0800
> 
> Po Lu <luangruo@yahoo.com> writes:
> 
> > Eli Zaretskii <eliz@gnu.org> writes:
> >
> >> So we have at most 3 states, and could have a single tri-state
> >> variable.
> >
> > Indeed.
> >
> >> And I question the validity of the combination scroll-move-point = nil
> >> with keep-point-visible = t.  The problem with your implementation is
> >> that scroll-move-point = nil disables scrolling in redisplay_window,
> >> but scrolling there is used not necessarily as result of scrolling
> >> commands, but also when redisplay_window decides that the optimal
> >> method of updating a window is to scroll its previous contents.  Thus,
> >> disabling scrolling on that level will cause confusing results,
> >> because users will expect that to affect only scrolling commands: they
> >> are unaware that redisplay sometimes scrolls the window for other
> >> reasons.
> >
> > Perhaps we could have redisplay bind `scroll-move-point' to a reasonable
> > value before calling the scrolling commands?
> 
> I don't see where the scrolling commands in window.c get called from
> `redisplay_window'.

I didn't say redisplay calls the scrolling commands: you did.

The truth is that redisplay doesn't call any scrolling commands.

> Is it try_scrolling?

try_scrolling is not a scrolling command, it's one of the methods used
by redisplay to update a window's display by reusing parts of the
existing display.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-07 13:41                                                                         ` Eli Zaretskii
@ 2022-02-07 13:57                                                                           ` Po Lu
  2022-02-07 14:24                                                                             ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-07 13:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> >> And I question the validity of the combination scroll-move-point = nil
>> >> with keep-point-visible = t.  The problem with your implementation is
>> >> that scroll-move-point = nil disables scrolling in redisplay_window,
>> >> but scrolling there is used not necessarily as result of scrolling
>> >> commands, but also when redisplay_window decides that the optimal
>> >> method of updating a window is to scroll its previous contents.  Thus,
>> >> disabling scrolling on that level will cause confusing results,
>> >> because users will expect that to affect only scrolling commands: they
>> >> are unaware that redisplay sometimes scrolls the window for other
>> >> reasons.

> I didn't say redisplay calls the scrolling commands: you did.
>
> The truth is that redisplay doesn't call any scrolling commands.

`scroll-move-point' doesn't affect redisplay; it only affects the
scrolling commands in window.c.

I thought what you wrote was a typo, and that you meant to say "disables
scrolling in window.c" or something else similar.

Thanks.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-07 13:57                                                                           ` Po Lu
@ 2022-02-07 14:24                                                                             ` Eli Zaretskii
  2022-02-08  0:58                                                                               ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-07 14:24 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Mon, 07 Feb 2022 21:57:50 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> >> And I question the validity of the combination scroll-move-point = nil
> >> >> with keep-point-visible = t.  The problem with your implementation is
> >> >> that scroll-move-point = nil disables scrolling in redisplay_window,
> >> >> but scrolling there is used not necessarily as result of scrolling
> >> >> commands, but also when redisplay_window decides that the optimal
> >> >> method of updating a window is to scroll its previous contents.  Thus,
> >> >> disabling scrolling on that level will cause confusing results,
> >> >> because users will expect that to affect only scrolling commands: they
> >> >> are unaware that redisplay sometimes scrolls the window for other
> >> >> reasons.
> 
> > I didn't say redisplay calls the scrolling commands: you did.
> >
> > The truth is that redisplay doesn't call any scrolling commands.
> 
> `scroll-move-point' doesn't affect redisplay; it only affects the
> scrolling commands in window.c.

It does affect redisplay: by not setting the force_start flag of the
window.

> I thought what you wrote was a typo, and that you meant to say "disables
> scrolling in window.c" or something else similar.

No, the problematic word was "scrolling": I meant the large block in
redisplay_window conditioned by w->force_start being non-zero.  That
includes the code under try_to_scroll.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-07 14:24                                                                             ` Eli Zaretskii
@ 2022-02-08  0:58                                                                               ` Po Lu
  2022-02-08 17:08                                                                                 ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-08  0:58 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> `scroll-move-point' doesn't affect redisplay; it only affects the
>> scrolling commands in window.c.
>
> It does affect redisplay: by not setting the force_start flag of the
> window.

What about modifying the code under force_start to not move point if
`keep-point-visible' is nil?  Since the point isn't required to be
visible anymore, there is no need to move the cursor there, I think.

> No, the problematic word was "scrolling": I meant the large block in
> redisplay_window conditioned by w->force_start being non-zero.  That
> includes the code under try_to_scroll.

Thanks for explaining that.




^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-08  0:58                                                                               ` Po Lu
@ 2022-02-08 17:08                                                                                 ` Eli Zaretskii
  2022-02-09  1:57                                                                                   ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-08 17:08 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Tue, 08 Feb 2022 08:58:22 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> `scroll-move-point' doesn't affect redisplay; it only affects the
> >> scrolling commands in window.c.
> >
> > It does affect redisplay: by not setting the force_start flag of the
> > window.
> 
> What about modifying the code under force_start to not move point if
> `keep-point-visible' is nil?  Since the point isn't required to be
> visible anymore, there is no need to move the cursor there, I think.

I don't think you can do that indiscriminately, because some of the
places that set the window's force_start flag are unrelated to this
feature.  One example is set_window_buffer and its callers.  Another
example is the code in redisplay_window that ends with "goto
force_start".  And there are probably more.

So if you want to do that, you'll need to carefully audit all those
places, and make sure you avoid moving point only when force_start was
set by scrolling commands or in some similar situations.  Otherwise,
users will complain that point goes out of view when they didn't
expect it to.  For example, "C-x 2" goes through that code, and I'm
quite sure we don't want point to be left outside of the window in
that case.



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-08 17:08                                                                                 ` Eli Zaretskii
@ 2022-02-09  1:57                                                                                   ` Po Lu
  2022-02-10 13:04                                                                                     ` Eli Zaretskii
  0 siblings, 1 reply; 111+ messages in thread
From: Po Lu @ 2022-02-09  1:57 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> I don't think you can do that indiscriminately, because some of the
> places that set the window's force_start flag are unrelated to this
> feature.  One example is set_window_buffer and its callers.  Another
> example is the code in redisplay_window that ends with "goto
> force_start".  And there are probably more.
>
> So if you want to do that, you'll need to carefully audit all those
> places, and make sure you avoid moving point only when force_start was
> set by scrolling commands or in some similar situations.  Otherwise,
> users will complain that point goes out of view when they didn't
> expect it to.  For example, "C-x 2" goes through that code, and I'm
> quite sure we don't want point to be left outside of the window in
> that case.

Would it make sense to introduce a new flag that split-window-below and
other such commands set that makes force_start move point regardless of
the value of `keep-point-visible'?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-09  1:57                                                                                   ` Po Lu
@ 2022-02-10 13:04                                                                                     ` Eli Zaretskii
  2022-02-10 13:09                                                                                       ` Po Lu
  0 siblings, 1 reply; 111+ messages in thread
From: Eli Zaretskii @ 2022-02-10 13:04 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

> From: Po Lu <luangruo@yahoo.com>
> Cc: emacs-devel@gnu.org
> Date: Wed, 09 Feb 2022 09:57:45 +0800
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > I don't think you can do that indiscriminately, because some of the
> > places that set the window's force_start flag are unrelated to this
> > feature.  One example is set_window_buffer and its callers.  Another
> > example is the code in redisplay_window that ends with "goto
> > force_start".  And there are probably more.
> >
> > So if you want to do that, you'll need to carefully audit all those
> > places, and make sure you avoid moving point only when force_start was
> > set by scrolling commands or in some similar situations.  Otherwise,
> > users will complain that point goes out of view when they didn't
> > expect it to.  For example, "C-x 2" goes through that code, and I'm
> > quite sure we don't want point to be left outside of the window in
> > that case.
> 
> Would it make sense to introduce a new flag that split-window-below and
> other such commands set that makes force_start move point regardless of
> the value of `keep-point-visible'?

Commands cannot bind variables to force redisplay do something,
because redisplay runs when the command already exited.

So what you mean is probably a flag in the window object.  Is that the
intent?



^ permalink raw reply	[flat|nested] 111+ messages in thread

* Re: Allowing point to be outside the window?
  2022-02-10 13:04                                                                                     ` Eli Zaretskii
@ 2022-02-10 13:09                                                                                       ` Po Lu
  0 siblings, 0 replies; 111+ messages in thread
From: Po Lu @ 2022-02-10 13:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Commands cannot bind variables to force redisplay do something,
> because redisplay runs when the command already exited.
>
> So what you mean is probably a flag in the window object.  Is that the
> intent?

Yes, I should have been clearer.



^ permalink raw reply	[flat|nested] 111+ messages in thread

end of thread, other threads:[~2022-02-10 13:09 UTC | newest]

Thread overview: 111+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <87ilwd7zaq.fsf.ref@yahoo.com>
2021-11-28  3:07 ` Allowing point to be outside the window? Po Lu
2021-11-28  8:03   ` Eli Zaretskii
2021-11-28  8:13     ` Po Lu
2021-11-28  8:41       ` Eli Zaretskii
2021-11-28 12:47         ` Po Lu
2021-11-28 12:58           ` Eli Zaretskii
2021-11-28 13:10             ` Po Lu
2021-11-28 13:44               ` Eli Zaretskii
2021-11-29  1:47                 ` Po Lu
2021-11-29 13:00                   ` Eli Zaretskii
2021-11-29 13:22                     ` Po Lu
2021-11-29 13:43                       ` Eli Zaretskii
2021-11-30  1:40                         ` Po Lu
2021-11-30 16:49                           ` [External] : " Drew Adams
2021-11-30 17:26                             ` Eli Zaretskii
2021-11-30 18:10                               ` Lars Ingebrigtsen
2021-11-30 18:32                                 ` Eli Zaretskii
2021-11-30 18:49                                 ` Stefan Kangas
2021-11-30 19:21                                   ` Eli Zaretskii
2021-11-30 20:57                                     ` Drew Adams
2021-11-30 23:41                                 ` Daniel Martín
2021-12-01  8:30                                   ` martin rudalics
2021-12-01  9:10                                     ` Juri Linkov
2021-11-30 23:20                               ` Stefan Monnier
2021-12-04 11:18                           ` Po Lu
2021-12-04 12:55                             ` Eli Zaretskii
2021-12-04 13:13                               ` Po Lu
2021-12-04 16:24                                 ` Eli Zaretskii
2021-12-05  0:40                                   ` Po Lu
2021-12-04 17:15                                 ` Eli Zaretskii
2021-12-05  0:45                                   ` Po Lu
2021-12-05  9:03                                     ` Eli Zaretskii
2021-12-06  2:11                                       ` Po Lu
2021-12-06 14:13                                         ` Eli Zaretskii
2021-12-07  2:18                                           ` Po Lu
2021-12-07 13:42                                             ` Eli Zaretskii
2021-12-08  1:17                                               ` Po Lu
2021-12-08 17:14                                                 ` Eli Zaretskii
2021-12-09  0:23                                                   ` Po Lu
2021-12-09  8:02                                                     ` Eli Zaretskii
2021-12-09  9:22                                                       ` Po Lu
2021-12-09 10:02                                                         ` Eli Zaretskii
2021-12-25  6:45                                                       ` Po Lu
2021-12-25  7:07                                                         ` Eli Zaretskii
2022-02-06  7:22                                                         ` Po Lu
2022-02-06 11:34                                                           ` Eli Zaretskii
2022-02-06 11:46                                                             ` Po Lu
2022-02-06 11:55                                                               ` Eli Zaretskii
2022-02-06 12:21                                                                 ` Po Lu
2022-02-06 16:15                                                                   ` Eli Zaretskii
2022-02-07  1:21                                                                     ` Po Lu
2022-02-07  7:21                                                                       ` Po Lu
2022-02-07 13:41                                                                         ` Eli Zaretskii
2022-02-07 13:57                                                                           ` Po Lu
2022-02-07 14:24                                                                             ` Eli Zaretskii
2022-02-08  0:58                                                                               ` Po Lu
2022-02-08 17:08                                                                                 ` Eli Zaretskii
2022-02-09  1:57                                                                                   ` Po Lu
2022-02-10 13:04                                                                                     ` Eli Zaretskii
2022-02-10 13:09                                                                                       ` Po Lu
2021-12-09 11:45                                         ` Eli Zaretskii
2021-12-09 12:19                                           ` Po Lu
2021-12-09 12:45                                             ` Eli Zaretskii
2021-12-04 13:00                             ` dick
2021-12-04 13:14                               ` tomas
2021-12-04 13:19                               ` Po Lu
2021-12-04 13:41                                 ` Eli Zaretskii
2021-12-05  0:46                                   ` Po Lu
2021-12-05  7:12                                     ` Eli Zaretskii
2021-12-05  7:16                                       ` Po Lu
2021-12-05  8:48                                         ` Eli Zaretskii
2021-12-05  9:15                                           ` Po Lu
2021-12-05  9:25                                             ` Eli Zaretskii
2021-12-05  9:31                                               ` Po Lu
2021-12-05 10:34                                                 ` Eli Zaretskii
2021-12-05 10:37                                                   ` Po Lu
2021-12-04 14:17                                 ` dick
2021-12-04 16:33                                   ` Eli Zaretskii
2021-12-04 17:13                                     ` dick
2021-12-05  0:48                                       ` Po Lu
2021-11-28 14:03       ` Alan Mackenzie
2021-11-28 14:28         ` Eric S Fraga
2021-11-28 14:39           ` Eli Zaretskii
2021-11-28 16:55             ` Eric S Fraga
2021-11-28 14:42         ` dick
2021-11-28 15:39         ` Kévin Le Gouguec
2021-11-28 15:45           ` Eli Zaretskii
2021-11-28 17:14             ` Kévin Le Gouguec
2021-11-28 16:59           ` Eric S Fraga
2021-11-28 17:30             ` Kévin Le Gouguec
2021-11-29  0:34             ` Dmitry Gutov
2021-11-29  0:34         ` Po Lu
2021-12-08  1:45           ` John Ankarström
2021-12-08 12:45             ` Eli Zaretskii
2021-12-08 13:33               ` John Ankarström
2021-12-08 13:38                 ` Po Lu
2021-12-08 13:52                   ` John Ankarström
2021-12-08 14:26                 ` Eli Zaretskii
2021-12-08 16:57                   ` Stefan Monnier
2021-12-08 19:29                     ` Yuri Khan
2021-12-09  0:16                     ` Po Lu
2021-12-08 19:21                 ` Rudolf Schlatte
2021-12-08 19:56                   ` Juri Linkov
2021-12-08 20:05                     ` André A. Gomes
2021-12-08 20:31                     ` Linux console scrollback [ Was: Allowing point to be outside the window? ] Alan Mackenzie
2021-12-09  0:17                   ` Allowing point to be outside the window? Po Lu
2021-12-08 22:25                 ` Kévin Le Gouguec
2021-12-08 23:17                   ` John Ankarström
     [not found] <9603C99D-97E7-4285-A1C1-022191B6F5CC@univie.ac.at>
2021-12-08 18:43 ` Konrad Podczeck
2021-12-08 20:47   ` John Ankarström
2021-12-09 15:34 Konrad Podczeck

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).