unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: martin rudalics <rudalics@gmx.at>
To: Eli Zaretskii <eliz@gnu.org>
Cc: npostavs@gmail.com, emacs-devel@gnu.org
Subject: Re: "after" variable watchers
Date: Tue, 18 May 2021 17:10:24 +0200	[thread overview]
Message-ID: <37077232-fdf9-983c-3a34-a2334a7a8f49@gmx.at> (raw)
In-Reply-To: <83y2cds3g9.fsf@gnu.org>

 > Perhaps taking a single example and explaining how would this work
 > with today's watchers and with the feature you want to install could
 > help me understand.

Maybe the following helps: I propose a function to calculate the desired
width of a window W's left fringe from, in this order

(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",

(2) the value of `left-fringe-width' of W's buffer where anything but a
     non-negative number means to "take the width from W's frame", and

(3) the value of f->left_fringe_width of W's frame as installed by
     gui_set_left_fringe - a non-negative integer.

This is the function:

/** Return configured width of W's left fringe.  */
static int
window_config_left_fringe_width (struct window *w)
{
   int width = (WINDOW_NON_GRAPHICAL_OR_PSEUDO_P (w)
	       ? 0 : w->nominal_left_fringe_width);

   if (width < 0)
     {
       Lisp_Object value = WINDOW_BUFFER_LOCAL_VALUE (Qleft_fringe_width, w);

       width = (RANGED_FIXNUMP (0, value, INT_MAX)
	       ? XFIXNAT (value)
	       : FRAME_LEFT_FRINGE_WIDTH (XFRAME (WINDOW_FRAME (w))));
     }

   return width;
}

window_config_left_fringe_width would be run, among many others,
whenever we

(i) call `set-window-fringes' for W, change `left-fringe-width' of W's
     buffer or change the 'left-fringe' parameter of W's frame, and

(ii) change the size of W.

The value produced by this function is stored in w->left_fringe_width
provided it fits into that window.  Otherwise, a value of 0 is stored.
(In the latter case, all remaining horizontal decorations of W will get
a width of 0 too.) The value stored is called the "realized" value of
W's left fringe width.  The realized value is used to display the
window, to determine its box width and the horizontal position where the
text area starts.

The value produced by this function is _also_ used to determine the
minimum width of W which is needed to tell whether W can be split or
resized and also to tell the window manager what W's frame's minimum
width should be.

Please tell me now if any of the reasoning I've done so far is wrong in
your opinion.  In that case we could try to find a different solution
for such a function or abolish the idea.  Otherwise, please read on.

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.

- If I use a normal "before" watcher, the effect would probably be seen
   with the next redisplay.  Still there's no guarantee that this would
   happen, so a surprising effect can still not be excluded.

So I would either have to explicitly pass the new value of
`left-fringe-width' down to window_config_left_fringe_width, check there
whether it is sane (which amounts to do twice everything the
gui_set_... functions do) and apply it then or, use what I called an
"after" variable watcher which installs the new value before running
window_config_left_fringe_width.

 >> Right.  In the mold of your `add-variable-watcher' at the end of
 >> frame.el.
 >
 > So if I remove those, you will retract this proposal?

It would be a pity to remove them.  And obviously you don't have to
remove any of those if you want me to retract my proposal.

 > And note that the analogy is incomplete at best: the watchers in
 > frame.el watch _variables_ that we used to have forever, so making
 > them modifiable only via accessor functions would be a breaking
 > change.  Whereas you are talking about stuff which is done by
 > functions already.

My window.el has

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

 >> First, it's useful for having a change in a buffer's decorations take
 >> effect immediately.  So we get rid of things like
 >>
 >>     Setting this variable does not take effect until a new buffer is displayed
 >>     in a window.  To make the change take effect, call ‘set-window-buffer’.
 >
 > I think this could be a step in the wrong direction.  It might be
 > easier for users, but it makes it harder for us to develop Emacs in
 > the long run, for example if we want more threading.  That's because
 > all those global and buffer-local variables are part of the huge
 > global state we have, and that gets in the way of some features.

Can you explain how my proposal could be related to threading and what
kind of features it might break there?

 >> And finally we would get rid of the present mixture of errors thrown at
 >> the user and that of changes that are silently ignored whenever a
 >> decoration does not fit.  A user then can set the nominal width of the
 >> right margin to some arbitrary number.  When the window is large enough
 >> to accommodate it, it will be displayed that way.  When the window gets
 >> too small, it will be temporarily clipped or skipped.
 >
 > So we get silent fixes for all of them?  Not sure this is an
 > improvement.  I think we already had complaints about silently
 > "fixing" the decorations as we see fit.

Your last statement is partially ironic and not really nice, in
particular because it inverts the state of affairs.

Emacs 27 explicitly states in set_window_fringes to

       	 Check dimensions of new fringes.  Make changes only if they
	 fit the window's dimensions.

So Emacs 27 silently ignores the value of `left-fringe-width' whenever
it does not fit and keeps on ignoring it when the window gets enlarged
sufficiently to show them afterwards.

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.

martin




  reply	other threads:[~2021-05-18 15:10 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-17  8:27 "after" variable watchers martin rudalics
2021-05-17 10:23 ` Eli Zaretskii
2021-05-17 16:40   ` martin rudalics
2021-05-17 16:57     ` Eli Zaretskii
2021-05-18 15:10       ` martin rudalics [this message]
2021-05-20 13:46         ` Eli Zaretskii
2021-05-24  8:47           ` martin rudalics
2021-05-27 16:51             ` Eli Zaretskii
2021-06-06  7:42               ` martin rudalics
2021-05-17 18:36     ` Stefan Monnier
2021-05-17 18:45       ` Eli Zaretskii
2021-05-17 18:54         ` Stefan Monnier
2021-05-17 18:55           ` Stefan Monnier
2021-05-18 15:10       ` martin rudalics
2021-05-18 15:57         ` Stefan Monnier
2021-05-18 17:01           ` martin rudalics
2021-05-20 13:49             ` Eli Zaretskii
2021-05-24  8:48               ` martin rudalics
2021-05-27 16:53                 ` Eli Zaretskii
2021-05-17 14:57 ` Matt Armstrong
2021-05-17 16:41   ` martin rudalics

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=37077232-fdf9-983c-3a34-a2334a7a8f49@gmx.at \
    --to=rudalics@gmx.at \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=npostavs@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).