emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: "after" variable watchers


From: martin rudalics
Subject: Re: "after" variable watchers
Date: Mon, 24 May 2021 10:47:29 +0200

>> (1) The desired width of W's left fringe as set by `set-window-fringes'
>>       and stored in a slot w->nominal_left_fringe_width where a value of
>>       -1 stands for "take the width from W's buffer",
>
> Side note: could we please NOT call these "nominal" values?  "Nominal"
> means "standard", whereas these aren't.  How about "desired" or
> "requested"?

I've been using "nominal" in the sense expressed by Wikipedia as "From a
philosophical viewpoint, nominal value represents an accepted condition,
which is a goal or an approximation, as opposed to the real value, which
is always present.".  That sentence expresses well what I'm trying to do
here IMHO.

>> Now suppose I assign a new value to `left-fringe-width' of W's buffer.
>>
>> - If I do not watch `left-fringe-width' at all, the new value will be
>>     picked up by redisplay the next time I call this function for some
>>     other reason, for example, when resizing W, unless it is preceded by a
>>     `set-window-buffer'.  Such an effect could be surprising at least.  It
>>     may lead to inconsistent behavior when W shall be split or resized.
>
> Presumably, we are now at this situation?

Not really.  Capturing the present situation would require to:

- Forget earlier requests to set a specific window's fringe width.  The
  value we currently store for a window's fringe width is agnostic wrt
  from where it was obtained from and thus cannot be restored from any
  nominal value once it has been configured.

- Prevent applying `left-fringe-width' until set_window_buffer is run.

> If so, what are the
> practical problems with what we have now?  We had this since many
> years ago, and I'm not aware of any significant problems with what we
> have.  So what are the reasons for wanting such a significant new
> infrastructure, if we don't have serious problems with what we have?

What we have now is not what we had since "many" years ago.  A window
margin would shrink when dragging the divider between the window and its
neighbor and become sticky after that in Emacs 24.  Try with Emacs 24,
set in *scratch* a `left-margin-width' of 40, do C-x 3 and drag the
divider between the mode lines back and forth.  Eventually, the margins
in both windows will have dropped to 4 columns.

Note that if, in Emacs 24, such a margin is shrunk due to shrinking its
frame, it may disappear entirely.  Try with a left margin width of 40
again, do C-x 3 and drag the frame's right edge to shrink the windows
and expand it again.  The behavior of Emacs 24 in this regard is rather
unpredictable.

Note also that in either case it's technically impossible to re-enlarge
margins after they have shrunk because, as mentioned earlier, the origin
of the configured value has been lost.

With Emacs 25, splitting a window with large margins was inhibited and
this became a constant source of troubles so we added the `min-margins'
crutches which, to give reasonable results, have to be accompanied by an
external application to handle margins widths after a window has been
resized.  So while the behavior of Emacs 25 became more predictable, it
also introduced restrictions people didn't like and still awaits a
solution for handling margins "reasonably" by default.

> If your proposed "after" watcher will calculate a "sanitized" value of
> w->left_fringe_width and store it in the window's structure, then why
> does it have to run "after" the change? the effect of the change will
> not be shown until the next redisplay cycle anyway.

But other parts of our code may want to know the size of the fringes
before that.  Think of someone displaying a buffer in a window, setting
up some decorations and trying to fit that window to its buffer.  If
that function used the old sizes of decorations, things may go wrong
when redisplay decides to change sizes after that.  Similar reasoning
applies to all other functions that try to split or resize windows.

>> (mapc (lambda (var)
>>           (add-variable-watcher var (symbol-function 
'window-update-decorations-for-variable) t))
>>         '(window-scale-body
>>           window-drop-decorations
>>           face-remapping-alist
>>           line-spacing
>>           min-body-width
>>           min-body-height
>>           left-fringe-width
>>           right-fringe-width
>>           fringes-outside-margins
>>           vertical-scroll-bar
>>           horizontal-scroll-bar
>>           scroll-bar-width
>>           scroll-bar-height
>>           left-margin-width
>>           right-margin-width
>>           mode-line-format
>>           header-line-format
>>           tab-line-format))
>>
>> where most are variables that we used to have for quite some time and
>> some figure here as well as in your list.
>
> line-spacing and face-remapping-alist don't really belong there, do
> they?

They do if we want to make windows sensitive to any text scaling of
their buffers.  The new variable `window-scale-body' tells whether to
interpret the specified minimum body height (a number of lines) of a
window in terms of that window's character height - which may include
any line spacing - or in the line height of the window's frame.  This
should fix a number of bugs where people complained that Emacs always
uses the frame's line height in such a case.  BTW, the way to fix the
behavior here is extremely hairy and much more controversial than what
we are discussing in the present thread.

> Likewise mode-line-format and header-line-format, AFAIR.
> AFAIK, the changes in those are immediately shown by the next
> redisplay.  So why are they in the above list?

When a window gets very small, I first want to remove its horizontal
scroll bar, then its tab and header lines and keep its mode line visible
as long as possible.  The redisplay code then will be told to not
redisplay the former.  But maybe you're right here and we can get rid of
these.

> As for the rest: once again, what problems do we have now with changes
> to them?
>
> Note that changes to the variables listed in frame.el do only one
> thing: set a flag in the buffer's structure telling the display code
> this buffer needs to be redisplayed.

.. and at that time take their new values into account ...

> By contrast, you want to run
> complex functions when the variables are modified, and actually affect
> the values stored as result.  That is unheard of in Emacs; it is
> almost like you are asking for having changes in some variables to
> automatically run a function.

Right.  But I do not run a Lisp function.  Though I see no harm in
running a Lisp function from a variable watcher either.

> Reducing the global state means we need to access variables via
> accessor functions.  Your suggestion goes in the exact opposite
> direction: you want a change in a variable's value to have an
> immediate effect, which would mean we'd need more variables to be
> thread-local, or risk causing changes in the wrong buffer/thread.

I have no idea how to impede having "a change in a variable's value have
an immediate effect".  But if you came up with an example how setting a
variable could affect threading detrimentally, I might be able to
understand.

> Ignoring invalid changes, when this is documented, is not a
> catastrophe.

What makes a change invalid?  A fringe width for one and the same buffer
may fit into one window showing that buffer and not fit into another
window showing it.  That's what Emacs 27 does and I won't change that.
The only thing that I intend to change is to make that fringe reappear
in a window whenever that window gets large enough to show it.  So we
would actually do less "silent editing" than with Emacs 27 or at least
not more.

> Maybe I don't understand your plan, but it sounds to me
> like you want to extend such silent "editing" of set values to many
> more widow-related attributes; left-fringe-width was just an example,
> right?

Right.

> and setting it to zero when unfit was also just an example,
> right?

The width of a fringe would always be either the requested value when it
fits or zero when it does not fit.  Window margins OTOH would shrink and
expand dynamically.

>> With my proposal, no settings are ever ignored or fixed silently.
>> Decorations will grow and shrink together with their windows and frames.
>> IMO this is better than, for example, showing the left margin instead of
>> the window's text area when a window has become too narrow.
>
> I don't know if it's better.  I'm mainly worried that we are trying to
> invent significant infrastructure to fix problems that I don't think I

Why do you think that my change to variable watchers would "invent
significant infrastructure".  It hardly does anything that is not
already there.  If an application wants to use a variable's new value
and react to it immediately, it can do that already now, albeit in a
less comfortable way.

> understand sufficiently well, and in fact am not aware they even
> exist.  So maybe we should make a step back and describe and discuss
> those problems first.

Not processing buffer local variables immediately has surprising effects
because it depends on whether `set-window-buffer' gets executed for that
buffer or not.  With emacs -Q consider

(setq left-margin-width 20)
C-x <right>
C-x <left>

or instead of C-x <left>  do C-x k.

It also makes `display-buffer' inconsistent.  If the buffer is already
shown in a window, it does not get the new margin width, otherwise it
will.  Finally, it may render the results of `window-state-put' and
`set-window-configuration' incompatible since the former uses
`set-window-buffer' while the latter doesn't.

All these imply that whenever users set one of these variables, they
should call `set-window-buffer' for all windows showing the buffer right
away.  Just that if the window is too small and the margins don't fit,
users still will not see them later even if they make enough room for
them.  And if they fit and a user shrinks the window, the margins might
continue to occupy the entire display area of that window.

martin



reply via email to

[Prev in Thread] Current Thread [Next in Thread]