unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* locked narrowing in ELisp
@ 2022-08-16 20:18 Stefan Monnier
  2022-08-17  0:05 ` Dmitry Gutov
  2022-08-17  5:59 ` Po Lu
  0 siblings, 2 replies; 19+ messages in thread
From: Stefan Monnier @ 2022-08-16 20:18 UTC (permalink / raw)
  To: emacs-devel

The new "fast long-lines" code uses a "locked narrowing" to try and
avoid being defeated by naive font-lock (and other) code.

This is a good heuristic to try and make sure Emacs stays responsive
even in the presence of naive packages not designed for such long lines.

Beside the need to override this in those rare packages where we can
provide better behavior with careful coding, the new code also lacks
a way to install such a locked narrowing from ELisp, even though it
seems only natural to use similar heuristics from ELisp code (e.g. when
jit-lock is run from a timer).

But as pointed out by Lars, this is related to the old discussion about
"classifying" narrowing, so as to distinguish narrowings installed by
the user via `C-x n n` from those installed by packages like MMM-mode or
Info-mode (or nowadays those installed by the redisplay).
These distinctions are important because various code may need to look
"outside" of those narrowings in some circumstances, but which "outside"
to use depends on the particular situation.

We've discussed it in the past and I still don't know what would be
a good API, but one particular class of `widen` which is known to be
used within narrowings that should *not* be overridden is those `widen`
calls used in "parsers" that try to understand the context of
an operation.

Those `widen` will sometimes be used in circumstances where they may
actually want to *narrow* rather than widen (e.g. the narrowing might be
needed to make sure the parser only looks at the part of the buffer that
it can understand (e.g. in MMM-mode), or it might be needed in order to
avoid the risk of spending an eternity parsing GBs of text (at the cost
of misparsing in too-large buffers)).

Last time we discussed these issues, we ended up deciding that
indentation (like font-lock before it) should "eagerly widen" (i.e. undo
any user-setup narrowing) before calling the mode-specific code.
This way, the indentation and font-lock code of a major mode should
simply never need to call `widen` and things like the MMM-mode (or the
long-lines code in redisplay) can just setup the narrowing before
calling that major mode code without any need to "label" the narrowing.

This was an elegant solution, but it seems to be too limited, because
the parsing code that want(ed) to `widen` is typically also called from
other parts of the major mode than the indentation or font-lock code.

In the past, we played with having a new widening function (there was
a `prog-widen` briefly in `prog-mode.el`).  Maybe we should
(re)introduce such a thing (or maybe an optional arg to `widen` which
would indicate what "kind" of widening we want to do), coupled with some
kind of `widen-function` which MMM-mode and the redisplay could
setup to filter/tweak those widening.

Within an MMM-mode or "redisplay locked narrowing", we *generally* want
`widen` not to widen past the limits imposed by MMM or by the redisplay,
but there will be cases where we do want to allow it (e.g. in
`nlinum-mode` the widening will usually want to be allowed to override
those narrowings in order to compute the right line number), so we
clearly not all calls to `widen` are the same.


        Stefan




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

* Re: locked narrowing in ELisp
  2022-08-16 20:18 locked narrowing in ELisp Stefan Monnier
@ 2022-08-17  0:05 ` Dmitry Gutov
  2022-08-17  0:55   ` Stefan Monnier
  2022-08-17 11:44   ` Eli Zaretskii
  2022-08-17  5:59 ` Po Lu
  1 sibling, 2 replies; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17  0:05 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

Hi Stefan,

On 16.08.2022 23:18, Stefan Monnier wrote:
> Last time we discussed these issues, we ended up deciding that
> indentation (like font-lock before it) should "eagerly widen" (i.e. undo
> any user-setup narrowing) before calling the mode-specific code.
> This way, the indentation and font-lock code of a major mode should
> simply never need to call `widen` and things like the MMM-mode (or the
> long-lines code in redisplay) can just setup the narrowing before
> calling that major mode code without any need to "label" the narrowing.
> 
> This was an elegant solution, but it seems to be too limited, because
> the parsing code that want(ed) to `widen` is typically also called from
> other parts of the major mode than the indentation or font-lock code.

Have you given any thought to the "soft widen" alternative I voiced 
recently?

https://lists.gnu.org/archive/html/emacs-devel/2022-08/msg00291.html

If the user-level narrow/widen commands didn't use _actual_ narrowing, 
but display engine tricks or whatever (example: 
https://github.com/Malabarba/fancy-narrow/), "other parts of the major 
mode" wouldn't have to do the usual (save-restriction (widen) ...) 
dance, which a lot of code is littered with.

Then there would be no need for "hard" or "locked" narrowing to restrict 
those calls to 'widen', because there wouldn't be any. The 
'narrow-to-region' and 'widen' would be restricted to lower-level uses, 
like mmm-mode, Info-mode, and the display engine long-line wrangling magic.

The migration path seems difficult, but the result might be worth it.

> In the past, we played with having a new widening function (there was
> a `prog-widen` briefly in `prog-mode.el`).  Maybe we should
> (re)introduce such a thing (or maybe an optional arg to `widen` which
> would indicate what "kind" of widening we want to do), coupled with some
> kind of `widen-function` which MMM-mode and the redisplay could
> setup to filter/tweak those widening.

'widen-function' can work too. IIRC various 'multi-mode' packages have 
tried to advice 'widen', without much of a reliable success. Hopefully 
the extra indirection doesn't cause too much overhead. But it's a more 
complicated solution either way.



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

* Re: locked narrowing in ELisp
  2022-08-17  0:05 ` Dmitry Gutov
@ 2022-08-17  0:55   ` Stefan Monnier
  2022-08-17  1:00     ` Dmitry Gutov
  2022-08-17 11:44   ` Eli Zaretskii
  1 sibling, 1 reply; 19+ messages in thread
From: Stefan Monnier @ 2022-08-17  0:55 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: emacs-devel

> Have you given any thought to the "soft widen" alternative
> I voiced recently?
>
> https://lists.gnu.org/archive/html/emacs-devel/2022-08/msg00291.html
>
> If the user-level narrow/widen commands didn't use _actual_ narrowing, but
> display engine tricks or whatever (example:
> https://github.com/Malabarba/fancy-narrow/), "other parts of the major mode"
> wouldn't have to do the usual (save-restriction (widen) ...) dance, which
> a lot of code is littered with.
>
> Then there would be no need for "hard" or "locked" narrowing to restrict
> those calls to 'widen', because there wouldn't be any. The
> 'narrow-to-region' and 'widen' would be restricted to lower-level uses, like
> mmm-mode, Info-mode, and the display engine long-line wrangling magic.
>
> The migration path seems difficult, but the result might be worth it.

Such a display-only narrowing might be a good alternative for many uses
of narrowing, but narrowing is also used quite commonly (either by the
end-user or in ELisp code) in order to restrict the effect of an
operation (like search&replace) to a particular region.

> 'widen-function' can work too. IIRC various 'multi-mode' packages have tried
> to advice 'widen', without much of a reliable success.

`widen` is one of those functions that has its own bytecode (I'm far
from convinced that it's used frequently enough to justify it, but that's
another issue), so indeed advising it is unlikely to give good results.


        Stefan




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

* Re: locked narrowing in ELisp
  2022-08-17  0:55   ` Stefan Monnier
@ 2022-08-17  1:00     ` Dmitry Gutov
  2022-08-17 13:03       ` Stefan Monnier
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17  1:00 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On 17.08.2022 03:55, Stefan Monnier wrote:
>> Have you given any thought to the "soft widen" alternative
>> I voiced recently?
>>
>> https://lists.gnu.org/archive/html/emacs-devel/2022-08/msg00291.html
>>
>> If the user-level narrow/widen commands didn't use _actual_ narrowing, but
>> display engine tricks or whatever (example:
>> https://github.com/Malabarba/fancy-narrow/), "other parts of the major mode"
>> wouldn't have to do the usual (save-restriction (widen) ...) dance, which
>> a lot of code is littered with.
>>
>> Then there would be no need for "hard" or "locked" narrowing to restrict
>> those calls to 'widen', because there wouldn't be any. The
>> 'narrow-to-region' and 'widen' would be restricted to lower-level uses, like
>> mmm-mode, Info-mode, and the display engine long-line wrangling magic.
>>
>> The migration path seems difficult, but the result might be worth it.
> 
> Such a display-only narrowing might be a good alternative for many uses
> of narrowing, but narrowing is also used quite commonly (either by the
> end-user or in ELisp code) in order to restrict the effect of an
> operation (like search&replace) to a particular region.

Certain operations could look up the "soft narrowing" bounds and act 
accordingly, if it's implemented in the core and appropriately documented.

To my understanding, there are more commands and facilities that want to 
ignore user-level narrowing, rather than the ones that want to obey it. 
Also a lot of "undecided" ones, waiting for someone to report that they 
should, in fact, ignore narrowing.



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

* Re: locked narrowing in ELisp
  2022-08-16 20:18 locked narrowing in ELisp Stefan Monnier
  2022-08-17  0:05 ` Dmitry Gutov
@ 2022-08-17  5:59 ` Po Lu
  1 sibling, 0 replies; 19+ messages in thread
From: Po Lu @ 2022-08-17  5:59 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

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

> The new "fast long-lines" code uses a "locked narrowing" to try and
> avoid being defeated by naive font-lock (and other) code.
>
> This is a good heuristic to try and make sure Emacs stays responsive
> even in the presence of naive packages not designed for such long lines.
>
> Beside the need to override this in those rare packages where we can
> provide better behavior with careful coding, the new code also lacks
> a way to install such a locked narrowing from ELisp, even though it
> seems only natural to use similar heuristics from ELisp code (e.g. when
> jit-lock is run from a timer).
>
> But as pointed out by Lars, this is related to the old discussion about
> "classifying" narrowing, so as to distinguish narrowings installed by
> the user via `C-x n n` from those installed by packages like MMM-mode or
> Info-mode (or nowadays those installed by the redisplay).
> These distinctions are important because various code may need to look
> "outside" of those narrowings in some circumstances, but which "outside"
> to use depends on the particular situation.
>
> We've discussed it in the past and I still don't know what would be
> a good API, but one particular class of `widen` which is known to be
> used within narrowings that should *not* be overridden is those `widen`
> calls used in "parsers" that try to understand the context of
> an operation.
>
> Those `widen` will sometimes be used in circumstances where they may
> actually want to *narrow* rather than widen (e.g. the narrowing might be
> needed to make sure the parser only looks at the part of the buffer that
> it can understand (e.g. in MMM-mode), or it might be needed in order to
> avoid the risk of spending an eternity parsing GBs of text (at the cost
> of misparsing in too-large buffers)).
>
> Last time we discussed these issues, we ended up deciding that
> indentation (like font-lock before it) should "eagerly widen" (i.e. undo
> any user-setup narrowing) before calling the mode-specific code.
> This way, the indentation and font-lock code of a major mode should
> simply never need to call `widen` and things like the MMM-mode (or the
> long-lines code in redisplay) can just setup the narrowing before
> calling that major mode code without any need to "label" the narrowing.
>
> This was an elegant solution, but it seems to be too limited, because
> the parsing code that want(ed) to `widen` is typically also called from
> other parts of the major mode than the indentation or font-lock code.
>
> In the past, we played with having a new widening function (there was
> a `prog-widen` briefly in `prog-mode.el`).  Maybe we should
> (re)introduce such a thing (or maybe an optional arg to `widen` which
> would indicate what "kind" of widening we want to do), coupled with some
> kind of `widen-function` which MMM-mode and the redisplay could
> setup to filter/tweak those widening.
>
> Within an MMM-mode or "redisplay locked narrowing", we *generally* want
> `widen` not to widen past the limits imposed by MMM or by the redisplay,
> but there will be cases where we do want to allow it (e.g. in
> `nlinum-mode` the widening will usually want to be allowed to override
> those narrowings in order to compute the right line number), so we
> clearly not all calls to `widen` are the same.

FWIW, I've already see a workaround in the wild (well, if my job counts
as "wild"), which is to use an indirect buffer to access contents
outside of the "locked narrowing", should widen fail to widen
completely.



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

* Re: locked narrowing in ELisp
  2022-08-17  0:05 ` Dmitry Gutov
  2022-08-17  0:55   ` Stefan Monnier
@ 2022-08-17 11:44   ` Eli Zaretskii
  2022-08-17 11:54     ` Dmitry Gutov
  1 sibling, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2022-08-17 11:44 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: monnier, emacs-devel

> Date: Wed, 17 Aug 2022 03:05:05 +0300
> From: Dmitry Gutov <dgutov@yandex.ru>
> 
> If the user-level narrow/widen commands didn't use _actual_ narrowing, 
> but display engine tricks or whatever (example: 
> https://github.com/Malabarba/fancy-narrow/), "other parts of the major 
> mode" wouldn't have to do the usual (save-restriction (widen) ...) 
> dance, which a lot of code is littered with.

I don't think this is reliable solution for a general problem such as
this one.  What that package does is prevent _some_ commands and APIs
from escaping the "narrowing".  But in the general case, once you get
low enough into our infrastructure code, there's no way of knowing
what is TRT wrt this "narrowing", because the intent of the caller is
unknown, and quite a few places in our code will happily ignore it.

So I think it is eventually unavoidable to have to define some
property of the narrowing that every piece of Emacs will test in order
to understand whether it should or shouldn't move outside of the
narrowed region.



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

* Re: locked narrowing in ELisp
  2022-08-17 11:44   ` Eli Zaretskii
@ 2022-08-17 11:54     ` Dmitry Gutov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17 11:54 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

On 17.08.2022 14:44, Eli Zaretskii wrote:
>> Date: Wed, 17 Aug 2022 03:05:05 +0300
>> From: Dmitry Gutov <dgutov@yandex.ru>
>>
>> If the user-level narrow/widen commands didn't use _actual_ narrowing,
>> but display engine tricks or whatever (example:
>> https://github.com/Malabarba/fancy-narrow/), "other parts of the major
>> mode" wouldn't have to do the usual (save-restriction (widen) ...)
>> dance, which a lot of code is littered with.
> 
> I don't think this is reliable solution for a general problem such as
> this one.  What that package does is prevent _some_ commands and APIs
> from escaping the "narrowing".  But in the general case, once you get
> low enough into our infrastructure code, there's no way of knowing
> what is TRT wrt this "narrowing", because the intent of the caller is
> unknown, and quite a few places in our code will happily ignore it.

Knowing the intent of the narrowing is indeed the problem. A split 
between user-level ("soft") narrowing and "real" narrowing makes sense 
to me.

Calling some narrowing "hard" doesn't answer the question of intent, OTOH.

> So I think it is eventually unavoidable to have to define some
> property of the narrowing that every piece of Emacs will test in order
> to understand whether it should or shouldn't move outside of the
> narrowed region.

In either approach we'd end up with two kinds of narrowings. The 
question for me is, which of the approaches will require more effort 
from Lisp writers to adapt. That is, how many functions and features 
should specifically handle user-level narrowing.

I'd be happy to be able to remove all the (save-restriction (widen) ...) 
calls littering the code.



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

* Re: locked narrowing in ELisp
  2022-08-17  1:00     ` Dmitry Gutov
@ 2022-08-17 13:03       ` Stefan Monnier
  2022-08-17 13:40         ` Dmitry Gutov
  0 siblings, 1 reply; 19+ messages in thread
From: Stefan Monnier @ 2022-08-17 13:03 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: emacs-devel

>> Such a display-only narrowing might be a good alternative for many uses
>> of narrowing, but narrowing is also used quite commonly (either by the
>> end-user or in ELisp code) in order to restrict the effect of an
>> operation (like search&replace) to a particular region.
>
> Certain operations could look up the "soft narrowing" bounds and act
> accordingly, if it's implemented in the core and appropriately documented.

Could be, but unless we go through the whole C code looking for checks
of BEGV/ZV and updating the code to also check the "soft bounds" this
won't work reliably enough to replace existing uses of narrowing.

> To my understanding, there are more commands and facilities that want to
> ignore user-level narrowing, rather than the ones that want to obey it. Also
> a lot of "undecided" ones, waiting for someone to report that they should,
> in fact, ignore narrowing.

But for backward compatibility reasons, we can't break the cases that
need the bounds to be hard, even if it could fix some cases.


        Stefan




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

* Re: locked narrowing in ELisp
  2022-08-17 13:03       ` Stefan Monnier
@ 2022-08-17 13:40         ` Dmitry Gutov
  2022-08-17 13:55           ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17 13:40 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

On 17.08.2022 16:03, Stefan Monnier wrote:
>>> Such a display-only narrowing might be a good alternative for many uses
>>> of narrowing, but narrowing is also used quite commonly (either by the
>>> end-user or in ELisp code) in order to restrict the effect of an
>>> operation (like search&replace) to a particular region.
>>
>> Certain operations could look up the "soft narrowing" bounds and act
>> accordingly, if it's implemented in the core and appropriately documented.
> 
> Could be, but unless we go through the whole C code looking for checks
> of BEGV/ZV and updating the code to also check the "soft bounds" this
> won't work reliably enough to replace existing uses of narrowing.

The point is to avoid doing that. If we can.

For instance, use two overlays in the current buffer with `invisible' 
property rather than have the display engine refer to the new kind of 
narrowing bounds.

>> To my understanding, there are more commands and facilities that want to
>> ignore user-level narrowing, rather than the ones that want to obey it. Also
>> a lot of "undecided" ones, waiting for someone to report that they should,
>> in fact, ignore narrowing.
> 
> But for backward compatibility reasons, we can't break the cases that
> need the bounds to be hard, even if it could fix some cases.

The "regular" narrowing will play the role of the hard one because there 
will be little need to protect against 'widen' calls all around.

I agree the migration path is non-obvious, though.



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

* Re: locked narrowing in ELisp
  2022-08-17 13:40         ` Dmitry Gutov
@ 2022-08-17 13:55           ` Eli Zaretskii
  2022-08-17 14:03             ` Dmitry Gutov
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2022-08-17 13:55 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: monnier, emacs-devel

> Date: Wed, 17 Aug 2022 16:40:01 +0300
> Cc: emacs-devel@gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> 
> > Could be, but unless we go through the whole C code looking for checks
> > of BEGV/ZV and updating the code to also check the "soft bounds" this
> > won't work reliably enough to replace existing uses of narrowing.
> 
> The point is to avoid doing that. If we can.
> 
> For instance, use two overlays in the current buffer with `invisible' 
> property rather than have the display engine refer to the new kind of 
> narrowing bounds.

That's a time bomb waiting to go off, because invisible text is
handled at a relatively high level in the display engine, and
otherwise the invisible property is largely ignored in Emacs.

Moreover, it will make redisplay slower.  Skipping invisible text is
faster than iterating through it, but it still takes time, whereas not
going beyond BEGV..ZV is instantaneous.

And finally, I don't think I see the benefit of this, even if it'd
work: you want to get rid of (save-restriction (widen)), but you are
willing to have to replace that with tests of overlays and invisible
text all over the place?



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

* Re: locked narrowing in ELisp
  2022-08-17 13:55           ` Eli Zaretskii
@ 2022-08-17 14:03             ` Dmitry Gutov
  2022-08-17 14:20               ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17 14:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

On 17.08.2022 16:55, Eli Zaretskii wrote:
>> Date: Wed, 17 Aug 2022 16:40:01 +0300
>> Cc: emacs-devel@gnu.org
>> From: Dmitry Gutov <dgutov@yandex.ru>
>>
>>> Could be, but unless we go through the whole C code looking for checks
>>> of BEGV/ZV and updating the code to also check the "soft bounds" this
>>> won't work reliably enough to replace existing uses of narrowing.
>>
>> The point is to avoid doing that. If we can.
>>
>> For instance, use two overlays in the current buffer with `invisible'
>> property rather than have the display engine refer to the new kind of
>> narrowing bounds.
> 
> That's a time bomb waiting to go off, because invisible text is
> handled at a relatively high level in the display engine, and
> otherwise the invisible property is largely ignored in Emacs.

User-level features should be implementable in terms of primitives 
allowed in Lisp userland.

> Moreover, it will make redisplay slower.  Skipping invisible text is
> faster than iterating through it, but it still takes time, whereas not
> going beyond BEGV..ZV is instantaneous.

Org, as one example, uses invisible text all the time. Other feature too.

> And finally, I don't think I see the benefit of this, even if it'd
> work: you want to get rid of (save-restriction (widen)), but you are
> willing to have to replace that with tests of overlays and invisible
> text all over the place?

No, I don't think the addition of "tests ... all over the place" will be 
needed. The display engine handles the 'invisible' property already.

A number of features/commands will indeed need to know the bounds of the 
user-level narrowing (and we'll have a buffer-local variable for that), 
but that's probably it.



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

* Re: locked narrowing in ELisp
  2022-08-17 14:03             ` Dmitry Gutov
@ 2022-08-17 14:20               ` Eli Zaretskii
  2022-08-17 23:13                 ` Dmitry Gutov
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2022-08-17 14:20 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: monnier, emacs-devel

> Date: Wed, 17 Aug 2022 17:03:46 +0300
> Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> 
> On 17.08.2022 16:55, Eli Zaretskii wrote:
> >> For instance, use two overlays in the current buffer with `invisible'
> >> property rather than have the display engine refer to the new kind of
> >> narrowing bounds.
> > 
> > That's a time bomb waiting to go off, because invisible text is
> > handled at a relatively high level in the display engine, and
> > otherwise the invisible property is largely ignored in Emacs.
> 
> User-level features should be implementable in terms of primitives 
> allowed in Lisp userland.

I don't see how this is relevant to the concern I raised.  I was
talking about the effects on the display engine.  It doesn't only
display, it also looks at buffer text for various purposes.

> > Moreover, it will make redisplay slower.  Skipping invisible text is
> > faster than iterating through it, but it still takes time, whereas not
> > going beyond BEGV..ZV is instantaneous.
> 
> Org, as one example, uses invisible text all the time. Other feature too.

And Org is indeed relatively slow when you move through a buffer which
has large parts of it hidden by invisible properties.

> > And finally, I don't think I see the benefit of this, even if it'd
> > work: you want to get rid of (save-restriction (widen)), but you are
> > willing to have to replace that with tests of overlays and invisible
> > text all over the place?
> 
> No, I don't think the addition of "tests ... all over the place" will be 
> needed. The display engine handles the 'invisible' property already.
> 
> A number of features/commands will indeed need to know the bounds of the 
> user-level narrowing (and we'll have a buffer-local variable for that), 
> but that's probably it.

I don't think you realize how widespread is use of low-level
primitives and functions in user-level commands.

What you suggest is not a clean design, because it is based on
inaccurate mental model of Emacs internals.  It cannot work reliably,
to the best of my knowledge.



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

* Re: locked narrowing in ELisp
  2022-08-17 14:20               ` Eli Zaretskii
@ 2022-08-17 23:13                 ` Dmitry Gutov
  2022-08-18  1:58                   ` Stefan Monnier
  2022-08-18  6:25                   ` Eli Zaretskii
  0 siblings, 2 replies; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-17 23:13 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

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

On 17.08.2022 17:20, Eli Zaretskii wrote:
>> Date: Wed, 17 Aug 2022 17:03:46 +0300
>> Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org
>> From: Dmitry Gutov <dgutov@yandex.ru>
>>
>> On 17.08.2022 16:55, Eli Zaretskii wrote:
>>>> For instance, use two overlays in the current buffer with `invisible'
>>>> property rather than have the display engine refer to the new kind of
>>>> narrowing bounds.
>>>
>>> That's a time bomb waiting to go off, because invisible text is
>>> handled at a relatively high level in the display engine, and
>>> otherwise the invisible property is largely ignored in Emacs.
>>
>> User-level features should be implementable in terms of primitives
>> allowed in Lisp userland.
> 
> I don't see how this is relevant to the concern I raised.  I was
> talking about the effects on the display engine.  It doesn't only
> display, it also looks at buffer text for various purposes.

I guess I didn't understand your concern, sorry. Invisible is handled 
somewhere on the high level, OK. I did not mistake it for 'intangible'.

>>> Moreover, it will make redisplay slower.  Skipping invisible text is
>>> faster than iterating through it, but it still takes time, whereas not
>>> going beyond BEGV..ZV is instantaneous.
>>
>> Org, as one example, uses invisible text all the time. Other feature too.
> 
> And Org is indeed relatively slow when you move through a buffer which
> has large parts of it hidden by invisible properties.

Org uses different properties, a lot. Not just 'invisible'. So I'd 
rather people test the performance of this first before dismissing.

My limited testing didn't show any particular slowdown.

>>> And finally, I don't think I see the benefit of this, even if it'd
>>> work: you want to get rid of (save-restriction (widen)), but you are
>>> willing to have to replace that with tests of overlays and invisible
>>> text all over the place?
>>
>> No, I don't think the addition of "tests ... all over the place" will be
>> needed. The display engine handles the 'invisible' property already.
>>
>> A number of features/commands will indeed need to know the bounds of the
>> user-level narrowing (and we'll have a buffer-local variable for that),
>> but that's probably it.
> 
> I don't think you realize how widespread is use of low-level
> primitives and functions in user-level commands.

Commands that do want to obey narrowing, can take the soft-narrowing 
bounds and apply the narrowing internally.

> What you suggest is not a clean design, because it is based on
> inaccurate mental model of Emacs internals.  It cannot work reliably,
> to the best of my knowledge.

I'm likely missing a lot of things, since I don't usually use this 
feature interactively. All I know is, about once or twice a year, people 
come and ask to make a certain command ignore narrowing. And nobody 
every comes and asks to revert those changes.

Could someone give a few problem scenarios with this patch? Preferably 
ones that should be hard to fix. Adapting isearch took about 2 lines.

[-- Attachment #2: soft-narrow-and-widen.diff --]
[-- Type: text/x-patch, Size: 3613 bytes --]

diff --git a/lisp/bindings.el b/lisp/bindings.el
index 2e32128274..109a1a411f 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -968,8 +968,8 @@ left-word
 
 (defvar-keymap narrow-map
   :doc "Keymap for narrowing commands."
-  "n" #'narrow-to-region
-  "w" #'widen
+  "n" #'soft-narrow-to-region
+  "w" #'soft-widen
   "g" #'goto-line-relative)
 (define-key ctl-x-map "n" narrow-map)
 
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 31fcf01949..285c92f850 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -1321,9 +1321,10 @@ isearch-mode
   ;; Pushing the initial state used to be before running isearch-mode-hook,
   ;; but a hook might set `isearch-push-state-function' used in
   ;; `isearch-push-state' to save mode-specific initial state.  (Bug#4994)
-  (isearch-push-state)
+  (with-soft-narrow
+   (isearch-push-state)
 
-  (isearch-update)
+   (isearch-update))
 
   (add-hook 'pre-command-hook 'isearch-pre-command-hook)
   (add-hook 'post-command-hook 'isearch-post-command-hook)
@@ -1358,7 +1359,7 @@ isearch-update
 
   (if (and (null unread-command-events)
 	   (null executing-kbd-macro))
-      (progn
+      (with-soft-narrow
         (if (not (input-pending-p))
           (funcall (or isearch-message-function #'isearch-message)))
         (if (and isearch-slow-terminal-mode
diff --git a/lisp/simple.el b/lisp/simple.el
index 1e6e5e11e0..78d0804ee8 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1647,6 +1647,14 @@ goto-line
       (widen))
     (goto-char pos)))
 
+(defmacro with-soft-narrow (&rest body)
+  (declare (indent 1) (debug t))
+  `(let ((bounds (soft-narrow-bounds)))
+     (save-restriction
+       (when bounds
+         (narrow-to-region (car bounds) (cdr bounds)))
+       ,@body)))
+
 (defun goto-line-relative (line &optional buffer)
   "Go to LINE, counting from line at (point-min).
 The line number is relative to the accessible portion of the narrowed
@@ -1654,7 +1662,8 @@ goto-line-relative
   (declare (interactive-only forward-line))
   (interactive (goto-line-read-args t))
   (with-suppressed-warnings ((interactive-only goto-line))
-    (goto-line line buffer t)))
+    (with-soft-narrow
+      (goto-line line buffer t))))
 
 (defun count-words-region (start end &optional arg)
   "Count the number of words in the region.
@@ -10707,6 +10716,40 @@ lax-plist-put
   "Change value in PLIST of PROP to VAL, comparing with `equal'."
   (declare (obsolete plist-put "29.1"))
   (plist-put plist prop val #'equal))
+
+(defvar soft-narrow--overlays nil)
+
+(defun soft-widen ()
+  (interactive)
+  (when soft-narrow--overlays
+    (with-soft-narrow
+      ;; If cursor is after the cdr ovl or before car ovl,
+      ;; move it inside.
+      (delete-overlay (car soft-narrow--overlays))
+      (delete-overlay (cdr soft-narrow--overlays)))
+    (setq soft-narrow--overlays nil)))
+
+(defun soft-narrow-to-region (beg end)
+  (interactive "r")
+  (soft-widen)
+  (let ((o1 (make-overlay (point-min) beg))
+        (o2 (make-overlay end (point-max) nil t t)))
+    (overlay-put o1 'invisible t)
+    (overlay-put o1 'read-only t)
+    (overlay-put o1 'modification-hooks '(soft-narrow--modif))
+    (overlay-put o2 'invisible t)
+    (overlay-put o2 'read-only t)
+    (overlay-put o2 'modification-hooks '(soft-narrow--modif))
+    (setq soft-narrow--overlays (cons o1 o2))))
+
+(defun soft-narrow--modif (&rest _)
+  (user-error "Not allowed"))
+
+(defun soft-narrow-bounds ()
+  (when soft-narrow--overlays
+    (cons (overlay-end (car soft-narrow--overlays))
+          (overlay-start (cdr soft-narrow--overlays)))))
+
 \f
 
 (provide 'simple)

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

* Re: locked narrowing in ELisp
  2022-08-17 23:13                 ` Dmitry Gutov
@ 2022-08-18  1:58                   ` Stefan Monnier
  2022-08-18 21:42                     ` Dmitry Gutov
  2022-08-18  6:25                   ` Eli Zaretskii
  1 sibling, 1 reply; 19+ messages in thread
From: Stefan Monnier @ 2022-08-18  1:58 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: Eli Zaretskii, emacs-devel

> Could someone give a few problem scenarios with this patch? Preferably ones
> that should be hard to fix. Adapting isearch took about 2 lines.

I don't think there'll be much that's hard to fix, no.  The problem is
rather that there'll be a lot to fix, so it'll basically be half-broken
for a long time.

E.g. `C-x h M-x write-region RET` will end up writing something else
than what I'd expect.


        Stefan




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

* Re: locked narrowing in ELisp
  2022-08-17 23:13                 ` Dmitry Gutov
  2022-08-18  1:58                   ` Stefan Monnier
@ 2022-08-18  6:25                   ` Eli Zaretskii
  2022-08-18 23:10                     ` Dmitry Gutov
  1 sibling, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2022-08-18  6:25 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: monnier, emacs-devel

> Date: Thu, 18 Aug 2022 02:13:30 +0300
> Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> 
> On 17.08.2022 17:20, Eli Zaretskii wrote:
> >> Date: Wed, 17 Aug 2022 17:03:46 +0300
> >> Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org
> >> From: Dmitry Gutov <dgutov@yandex.ru>
> >>
> >> On 17.08.2022 16:55, Eli Zaretskii wrote:
> >>>> For instance, use two overlays in the current buffer with `invisible'
> >>>> property rather than have the display engine refer to the new kind of
> >>>> narrowing bounds.
> >>>
> >>> That's a time bomb waiting to go off, because invisible text is
> >>> handled at a relatively high level in the display engine, and
> >>> otherwise the invisible property is largely ignored in Emacs.
> >>
> >> User-level features should be implementable in terms of primitives
> >> allowed in Lisp userland.
> > 
> > I don't see how this is relevant to the concern I raised.  I was
> > talking about the effects on the display engine.  It doesn't only
> > display, it also looks at buffer text for various purposes.
> 
> I guess I didn't understand your concern, sorry. Invisible is handled 
> somewhere on the high level, OK. I did not mistake it for 'intangible'.

The concern is that some parts of the display code will ignore the
invisible text and some won't.  The display code doesn't expect
BEGV..ZV restriction to behave that way, it expects these limits to
affect every part of Emacs.

> >>> Moreover, it will make redisplay slower.  Skipping invisible text is
> >>> faster than iterating through it, but it still takes time, whereas not
> >>> going beyond BEGV..ZV is instantaneous.
> >>
> >> Org, as one example, uses invisible text all the time. Other feature too.
> > 
> > And Org is indeed relatively slow when you move through a buffer which
> > has large parts of it hidden by invisible properties.
> 
> Org uses different properties, a lot. Not just 'invisible'. So I'd 
> rather people test the performance of this first before dismissing.

I'm talking from experience: movement through a large Org buffer with
a lot of text hidden is slower.

> >> A number of features/commands will indeed need to know the bounds of the
> >> user-level narrowing (and we'll have a buffer-local variable for that),
> >> but that's probably it.
> > 
> > I don't think you realize how widespread is use of low-level
> > primitives and functions in user-level commands.
> 
> Commands that do want to obey narrowing, can take the soft-narrowing 
> bounds and apply the narrowing internally.

That would mean all of the commands.  A lot of changes (or a lot of
breakage).  It doesn't sound like a good solution, at least not better
than just biting the bullet and introducing 2 different kinds of
narrowing with some ways of telling Emacs internals which of the two
to obey at any particular moment.  (The latter is actually the tricky
part, IMO.)

> I'm likely missing a lot of things, since I don't usually use this 
> feature interactively. All I know is, about once or twice a year, people 
> come and ask to make a certain command ignore narrowing. And nobody 
> every comes and asks to revert those changes.

Making a specific command ignore narrowing is easy.  Your proposal
implicitly assumes that the number of commands that want to ignore
narrowing is much larger than the other kind.  I don't think it's
true, and you haven't provided any evidence to that effect.  Moreover,
I think it might make sense for some commands to honor or ignore the
narrowing depending on the use case, and that is not solved with your
proposal.

> Could someone give a few problem scenarios with this patch? Preferably 
> ones that should be hard to fix. Adapting isearch took about 2 lines.

That's just one command.  Emacs has thousands of them.



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

* Re: locked narrowing in ELisp
  2022-08-18  1:58                   ` Stefan Monnier
@ 2022-08-18 21:42                     ` Dmitry Gutov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-18 21:42 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Eli Zaretskii, emacs-devel

On 18.08.2022 04:58, Stefan Monnier wrote:
>> Could someone give a few problem scenarios with this patch? Preferably ones
>> that should be hard to fix. Adapting isearch took about 2 lines.
> 
> I don't think there'll be much that's hard to fix, no.  The problem is
> rather that there'll be a lot to fix, so it'll basically be half-broken
> for a long time.

That is a problem indeed. So at the very least the corresponding feature 
branch would need attention from somebody (or some people) who use 
narrowing a lot.

> E.g. `C-x h M-x write-region RET` will end up writing something else
> than what I'd expect.

I see what you mean. Some region-related commands might need direct 
support indeed (region-beginning and region-end changes their return 
values once narrowing it applied).

soft-narrow-to-region might also alter the region, if we decide to 
support its programmatic application.



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

* Re: locked narrowing in ELisp
  2022-08-18  6:25                   ` Eli Zaretskii
@ 2022-08-18 23:10                     ` Dmitry Gutov
  2022-08-19  6:31                       ` Eli Zaretskii
  0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-18 23:10 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

On 18.08.2022 09:25, Eli Zaretskii wrote:

>> I guess I didn't understand your concern, sorry. Invisible is handled
>> somewhere on the high level, OK. I did not mistake it for 'intangible'.
> 
> The concern is that some parts of the display code will ignore the
> invisible text and some won't.  The display code doesn't expect
> BEGV..ZV restriction to behave that way, it expects these limits to
> affect every part of Emacs.

That's not a fundamental problem. I believe it's possible to come 99.9% 
close (and even provide better usability, say, by highlighting the 
narrowing bounds to indicate that there is more to the buffer, but it's 
hidden), but if not, the implementation can go a little lower level:

The redisplay could repeat the trick I showed with Isearch and wrap most 
of its logic in (with-soft-narrow ...) -- which translates the soft 
narrowing into an actual one temporarily. Then the 'invisible' property 
won't even be required.

>>>> A number of features/commands will indeed need to know the bounds of the
>>>> user-level narrowing (and we'll have a buffer-local variable for that),
>>>> but that's probably it.
>>>
>>> I don't think you realize how widespread is use of low-level
>>> primitives and functions in user-level commands.
>>
>> Commands that do want to obey narrowing, can take the soft-narrowing
>> bounds and apply the narrowing internally.
> 
> That would mean all of the commands.  A lot of changes (or a lot of
> breakage).  It doesn't sound like a good solution, at least not better
> than just biting the bullet and introducing 2 different kinds of
> narrowing with some ways of telling Emacs internals which of the two
> to obey at any particular moment.

Two kinds of narrowings is inherently a more complex solution. But it 
might be more backward-compatible, yes. Since it just adds "one more 
thing" on top of the existing system.

> (The latter is actually the tricky
> part, IMO.)

If we just make the "locking" feature to be accessible to Lisp, the 
majority of the code shouldn't care or be aware of it. Just some 
particular features/functions/packages will make use of the locking 
argument, limiting the 'widen' calls in the rest of the Emacs from 
widening past the locked region.

>> I'm likely missing a lot of things, since I don't usually use this
>> feature interactively. All I know is, about once or twice a year, people
>> come and ask to make a certain command ignore narrowing. And nobody
>> every comes and asks to revert those changes.
> 
> Making a specific command ignore narrowing is easy.  Your proposal
> implicitly assumes that the number of commands that want to ignore
> narrowing is much larger than the other kind.

That is indeed the key assumption. I'm inclined to believe that not only 
it is larger, but that the set of commands that should ignore user-level 
narrowing also grows faster.

> I don't think it's
> true, and you haven't provided any evidence to that effect.

My observation is that the number of regular users of interactive 
narrowing is not so big. Perhaps limited to a particular subset of 
old-school Emacsers (and some recently-converted sympathizers).

As a result, a lot of problems with commands _not_ ignoring narrowing 
when they should go unreported for years.

E.g. https://debbugs.gnu.org/21262 or 
https://github.com/dgutov/diff-hl/issues/48 (the packages was 3 years 
old at that point, and its main functionality was broken when narrowed) 
https://github.com/dgutov/diff-hl/pull/109 (the corresponding feature 
was 6 year old at that point).

With that in mind, creating any precise statistics doesn't seem 
possible, even if one decides to try.

On the flip side, though, it doesn't seem like diff-hl needs any support 
for user-level narrowing (ignoring it is fine), and company-mode "just 
worked" when I tested it with soft-narrow-to-region.

Same goes for all other packages I maintain.

> Moreover,
> I think it might make sense for some commands to honor or ignore the
> narrowing depending on the use case, and that is not solved with your
> proposal.

Doesn't seem difficult:

(if something
     (with-soft-narrow
      do-the-thing)
   do-the-thing)

or without the macro:

(save-restriction
   (when something
     (let ((bounds (soft-narrow-bounds)))
       (and bounds
            (narrow-to-region (car bounds) (cdr bounds)))))
   do-the-thing)

>> Could someone give a few problem scenarios with this patch? Preferably
>> ones that should be hard to fix. Adapting isearch took about 2 lines.
> 
> That's just one command.  Emacs has thousands of them.

True. In the current state it only solves Isearch (obviously) and the 
basic movement/editing commands by using 'invisible', 'read-only' and 
'modification-hooks' properties.

Stefan's complaint should be fixable by changing either 
mark-whole-buffer or push-mark directly (I'm not sure yet) and 
soft-narrow-to-region to update the region bounds just in case it was 
called from a Lisp program.

I was thinking the vast majority of use cases should be solved by 
altering some key functions. But I can't guarantee that, of course.



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

* Re: locked narrowing in ELisp
  2022-08-18 23:10                     ` Dmitry Gutov
@ 2022-08-19  6:31                       ` Eli Zaretskii
  2022-08-22  0:59                         ` Dmitry Gutov
  0 siblings, 1 reply; 19+ messages in thread
From: Eli Zaretskii @ 2022-08-19  6:31 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: monnier, emacs-devel

> Date: Fri, 19 Aug 2022 02:10:49 +0300
> Cc: monnier@iro.umontreal.ca, emacs-devel@gnu.org
> From: Dmitry Gutov <dgutov@yandex.ru>
> 
> On 18.08.2022 09:25, Eli Zaretskii wrote:
> 
> >> I guess I didn't understand your concern, sorry. Invisible is handled
> >> somewhere on the high level, OK. I did not mistake it for 'intangible'.
> > 
> > The concern is that some parts of the display code will ignore the
> > invisible text and some won't.  The display code doesn't expect
> > BEGV..ZV restriction to behave that way, it expects these limits to
> > affect every part of Emacs.
> 
> That's not a fundamental problem. I believe it's possible to come 99.9% 
> close (and even provide better usability, say, by highlighting the 
> narrowing bounds to indicate that there is more to the buffer, but it's 
> hidden), but if not, the implementation can go a little lower level:
> 
> The redisplay could repeat the trick I showed with Isearch and wrap most 
> of its logic in (with-soft-narrow ...) -- which translates the soft 
> narrowing into an actual one temporarily. Then the 'invisible' property 
> won't even be required.

I think you still underestimate the magnitude of the problem and its
fundamental nature.  The display engine is not a closed subsystem, it
uses a huge number of Emacs infrastructure code from other subsystems:
buffers, text properties, overlays, markers, character composition,
regular expressions, direct access to buffer text, etc.  All of those
places are also used from other commands and functions, outside of
redisplay.  You cannot "wrap [their] logic in (with-soft-narrow ...)",
because their logic is used in many more use cases, and there's no way
such low-level code can distinguish between the cases, nor should it
(because it will make it too slow).

> > That would mean all of the commands.  A lot of changes (or a lot of
> > breakage).  It doesn't sound like a good solution, at least not better
> > than just biting the bullet and introducing 2 different kinds of
> > narrowing with some ways of telling Emacs internals which of the two
> > to obey at any particular moment.
> 
> Two kinds of narrowings is inherently a more complex solution.

Some problems don't have simple solutions.  The narrowing is a very
fundamental feature in Emacs, it permeates all of our code to the
lowest levels.  Such fundamental features cannot be side-stepped by
tricks and hacks.  The change must be in those same basic levels.

> > (The latter is actually the tricky
> > part, IMO.)
> 
> If we just make the "locking" feature to be accessible to Lisp, the 
> majority of the code shouldn't care or be aware of it. Just some 
> particular features/functions/packages will make use of the locking 
> argument, limiting the 'widen' calls in the rest of the Emacs from 
> widening past the locked region.

I'm not talking about locking!  I'm talking about the basic
requirement to distinguish between "user-level" narrowing and the
other kind.  How do you tell the low-level stuff, such as
set_marker_restricted, which of the two narrowing kinds to apply?
That function is used from every place where we set window-point and
window-start positions (as well as many others), so almost any code
that wants to move one of those will eventually call it.  Commands do
that, but redisplay and other places do it as well.

This is the tricky part of introducing two kinds of narrowing; the
rest is very simple and basically mechanical.

> > Making a specific command ignore narrowing is easy.  Your proposal
> > implicitly assumes that the number of commands that want to ignore
> > narrowing is much larger than the other kind.
> 
> That is indeed the key assumption. I'm inclined to believe that not only 
> it is larger, but that the set of commands that should ignore user-level 
> narrowing also grows faster.

What about commands that apply narrowing as part of their internal
operation -- is that "user-level" narrowing or the other kind?  E.g.,
some/many commands that operate on the region when it's active begin
by narrowing to that region -- which kind is that?

> With that in mind, creating any precise statistics doesn't seem 
> possible, even if one decides to try.

If that's so, then any assumptions about which set is significantly
larger cannot be safely made, either.  We should proceed without any
such assumption, i.e. without adopting any solutions which would be
based on such assumptions and will break if the assumptions are proved
false.

> On the flip side, though, it doesn't seem like diff-hl needs any support 
> for user-level narrowing (ignoring it is fine), and company-mode "just 
> worked" when I tested it with soft-narrow-to-region.
> 
> Same goes for all other packages I maintain.

IME, it is not a good idea to base such fundamental design decisions
on a couple of user commands and their needs.

> > Moreover,
> > I think it might make sense for some commands to honor or ignore the
> > narrowing depending on the use case, and that is not solved with your
> > proposal.
> 
> Doesn't seem difficult:
> 
> (if something
>      (with-soft-narrow
>       do-the-thing)
>    do-the-thing)
> 
> or without the macro:
> 
> (save-restriction
>    (when something
>      (let ((bounds (soft-narrow-bounds)))
>        (and bounds
>             (narrow-to-region (car bounds) (cdr bounds)))))
>    do-the-thing)

We are mis-communicating: the problem is not how to use basic
conditionals in Emacs Lisp, the problem is how you design and
implement that "something", such that we won't need to rewrite every
single command out there.



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

* Re: locked narrowing in ELisp
  2022-08-19  6:31                       ` Eli Zaretskii
@ 2022-08-22  0:59                         ` Dmitry Gutov
  0 siblings, 0 replies; 19+ messages in thread
From: Dmitry Gutov @ 2022-08-22  0:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: monnier, emacs-devel

On 19.08.2022 09:31, Eli Zaretskii wrote:

>> The redisplay could repeat the trick I showed with Isearch and wrap most
>> of its logic in (with-soft-narrow ...) -- which translates the soft
>> narrowing into an actual one temporarily. Then the 'invisible' property
>> won't even be required.
> 
> I think you still underestimate the magnitude of the problem and its
> fundamental nature.  The display engine is not a closed subsystem, it
> uses a huge number of Emacs infrastructure code from other subsystems:
> buffers, text properties, overlays, markers, character composition,
> regular expressions, direct access to buffer text, etc.  All of those
> places are also used from other commands and functions, outside of
> redisplay.  You cannot "wrap [their] logic in (with-soft-narrow ...)",
> because their logic is used in many more use cases, and there's no way
> such low-level code can distinguish between the cases, nor should it
> (because it will make it too slow).

Naturally, some uses of 'goto-char' (for example) need to obey the 
narrowing applied by the user, and some -- do not. That's the situation 
today, and that's not going to change.

The question is, would "do not obey" work as a good default.

>>> That would mean all of the commands.  A lot of changes (or a lot of
>>> breakage).  It doesn't sound like a good solution, at least not better
>>> than just biting the bullet and introducing 2 different kinds of
>>> narrowing with some ways of telling Emacs internals which of the two
>>> to obey at any particular moment.
>>
>> Two kinds of narrowings is inherently a more complex solution.
> 
> Some problems don't have simple solutions.  The narrowing is a very
> fundamental feature in Emacs, it permeates all of our code to the
> lowest levels.  Such fundamental features cannot be side-stepped by
> tricks and hacks.  The change must be in those same basic levels.

I think we're talking about two very similar things here, just looking 
at them from different angles.

But if we were going to separate user-level narrowing from "real" 
narrowing, the result should look (at least at the surface) like the 
patch I posted, and it should face the same problems: how to decide 
which commands should obey user-level locking, and how to minimize the 
number of functions we'll have to alter to support it explicitly.

>>> (The latter is actually the tricky
>>> part, IMO.)
>>
>> If we just make the "locking" feature to be accessible to Lisp, the
>> majority of the code shouldn't care or be aware of it. Just some
>> particular features/functions/packages will make use of the locking
>> argument, limiting the 'widen' calls in the rest of the Emacs from
>> widening past the locked region.
> 
> I'm not talking about locking!  I'm talking about the basic
> requirement to distinguish between "user-level" narrowing and the
> other kind.  How do you tell the low-level stuff, such as
> set_marker_restricted, which of the two narrowing kinds to apply?

The solution I picked is this: lower-level code only obeys the "real" 
narrowing. Perhaps the redisplay loop sprinkles in a bit of support for 
"user-level" narrowing by temporarily applying the "real" narrowing 
around the whole main logic when such "user-level" narrowing is present 
(using its bounds).

As a result, with very rare exceptions (Info-mode, maybe), the only kind 
of "persistent" narrowing would be the "user-level" one, but every 
command (or function, or form) will have the freedom to translate it 
into the "real" one for the duration of its execution. The "real" 
narrowing would be thus relegated to lower-level uses, and not as a 
user's tool.

> That function is used from every place where we set window-point and
> window-start positions (as well as many others), so almost any code
> that wants to move one of those will eventually call it.  Commands do
> that, but redisplay and other places do it as well.
> 
> This is the tricky part of introducing two kinds of narrowing; the
> rest is very simple and basically mechanical.
> 
>>> Making a specific command ignore narrowing is easy.  Your proposal
>>> implicitly assumes that the number of commands that want to ignore
>>> narrowing is much larger than the other kind.
>>
>> That is indeed the key assumption. I'm inclined to believe that not only
>> it is larger, but that the set of commands that should ignore user-level
>> narrowing also grows faster.
> 
> What about commands that apply narrowing as part of their internal
> operation -- is that "user-level" narrowing or the other kind?  E.g.,
> some/many commands that operate on the region when it's active begin
> by narrowing to that region -- which kind is that?

It's the other kind, the "real" one.

Sometime later, new functions might crop up (probably in third-party 
code) that decide to alter the user-level narrowing temporarily instead 
because they know which of their callees obey user-level narrowing and 
which do not, and decide to benefit from that distinction. But that 
approach should remain rare.

>> With that in mind, creating any precise statistics doesn't seem
>> possible, even if one decides to try.
> 
> If that's so, then any assumptions about which set is significantly
> larger cannot be safely made, either.  We should proceed without any
> such assumption, i.e. without adopting any solutions which would be
> based on such assumptions and will break if the assumptions are proved
> false.

We could try to make such analysis anyway, just remain aware that the 
difference might be larger (in one particular direction).

If we just have to adopt a solution which goes against the assumption I 
made, well, then, the approach probably won't work.

>> On the flip side, though, it doesn't seem like diff-hl needs any support
>> for user-level narrowing (ignoring it is fine), and company-mode "just
>> worked" when I tested it with soft-narrow-to-region.
>>
>> Same goes for all other packages I maintain.
> 
> IME, it is not a good idea to base such fundamental design decisions
> on a couple of user commands and their needs.

Several fairly high-profile packages are not "a couple of user 
commands". But it's not the whole ecosystem, sure.

>>> Moreover,
>>> I think it might make sense for some commands to honor or ignore the
>>> narrowing depending on the use case, and that is not solved with your
>>> proposal.
>>
>> Doesn't seem difficult:
>>
>> (if something
>>       (with-soft-narrow
>>        do-the-thing)
>>     do-the-thing)
>>
>> or without the macro:
>>
>> (save-restriction
>>     (when something
>>       (let ((bounds (soft-narrow-bounds)))
>>         (and bounds
>>              (narrow-to-region (car bounds) (cdr bounds)))))
>>     do-the-thing)
> 
> We are mis-communicating: the problem is not how to use basic
> conditionals in Emacs Lisp, the problem is how you design and
> implement that "something", such that we won't need to rewrite every
> single command out there.

I considered which commands should be affected the most: they have to be 
those that act on and affect the part of the buffer that the user sees. 
The user shouldn't be able to modify the text they don't see (1), nor 
(probably) have that text affect the commands they are calling (2).

The (1) is implemented by adding the 'read-only' property to the 
overlays which apply the user-level narrowing. It leaves the 
possibilities of the user being able to jump before the first overlay 
(to bob) or after the last, and insert something there. That seems to be 
mostly handled using the FRONT-ADVANCE and REAR-ADVANCE arguments, as 
well as modification-hooks. But there are other options like 
post-command-hook, or making the overlays longer than 0 chars.

As for (2), though, we can't tell that in advance for each command, and 
most have to choose case-by-case. But Stefan gave a good example of when 
we *do* know: when the command does something with the region. The user 
cannot tell the region spans beyond the accessible/visible part of the 
buffer.

So the way to "fix" a lot of them together is to make sure the region 
cannot span beyond the user-level narrowing currently applied. How to do 
that? Either we have to change all of the commands which change the 
region, or alter 'set-mark' (which might be a fairly invasive change, 
stopping certain code from working), or perform the adjustments in 
post-command-hook: after every command, see if the region spans beyond 
the user-level narrowing, and adjust either or both of its bounds if needed.



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

end of thread, other threads:[~2022-08-22  0:59 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-16 20:18 locked narrowing in ELisp Stefan Monnier
2022-08-17  0:05 ` Dmitry Gutov
2022-08-17  0:55   ` Stefan Monnier
2022-08-17  1:00     ` Dmitry Gutov
2022-08-17 13:03       ` Stefan Monnier
2022-08-17 13:40         ` Dmitry Gutov
2022-08-17 13:55           ` Eli Zaretskii
2022-08-17 14:03             ` Dmitry Gutov
2022-08-17 14:20               ` Eli Zaretskii
2022-08-17 23:13                 ` Dmitry Gutov
2022-08-18  1:58                   ` Stefan Monnier
2022-08-18 21:42                     ` Dmitry Gutov
2022-08-18  6:25                   ` Eli Zaretskii
2022-08-18 23:10                     ` Dmitry Gutov
2022-08-19  6:31                       ` Eli Zaretskii
2022-08-22  0:59                         ` Dmitry Gutov
2022-08-17 11:44   ` Eli Zaretskii
2022-08-17 11:54     ` Dmitry Gutov
2022-08-17  5:59 ` Po Lu

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