bug-gnu-emacs
[Top][All Lists]
Advanced

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

bug#29279: Sharing the margins


From: Dmitry Gutov
Subject: bug#29279: Sharing the margins
Date: Mon, 13 Nov 2017 19:24:21 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Thunderbird/56.0

On 11/13/17 5:43 PM, Eli Zaretskii wrote:

The second discussion doesn't seem to be relevant, although it touches
this tangentially.  It has nothing useful to add to this discussion,
AFAICT.

Maybe not. Except as a use case. The first one is also not hugely relevant, because it's mostly about workroom-mode, and that one is a rare kind of margin usage conflict.

- Continue to use the text properties to set the contents of the margins (as 
opposed to having a line number based API, which seems to come with its own 
difficulties).

Not sure I understand this.  AFAIK no one proposed to get rid of the
mechanism of specifying margin display, be it via text properties or
overlays.

I meant that the alternative would be a new line-based programmatic API with functions like (add-margin vsual-line-number, width, contents). Which I've briefly considered and discarded, for now.

> The line number display of Emacs 26 avoids using the
> margins, but that wasn't proposed as some more general API for
> anything else.  So I think this is a non-issue, and will just leads us
> astray.

No reference to "native line numbers" intended.

Each element is a cons which specifies some properties of the element, for 
instance:

I think we need to come up with a specific list of properties, because
discussing an example will always be too vague and uncommitted.  So is
this the actual list you propose, or are there other properties you
envision?  If the latter, please add them to the list.

I think that was it, but additions welcome. These ones should be sufficient for the uses I'm thinking of now.

- Priority (so the columns are in the same order on each line)

You mean "order", right?  That is, which part will be rendered first,
which after it, etc., right?  If not, what is the practical meaning of
"priority" here?

Order priority, yes. It's just when I think of an "order" argument, the value is usually something like "user_name ASC", and not a number.

- Minimum width (if we want that, as opposed to just adding strings with that 
length the contents of this margin column)
- Minimum "total" width, which would allow to use this column as padding so 
that the combined width of the margin reaches a given number (hopefully solving the 
writeroom-mode problem).

I don't understand the difference between these two.  Can you
elaborate?

The first one sizes a given column (all the margins contents in this column get padded to at least this width).

The second one helps to size the whole margin, using this column. A way to set the minimum total margin width, IOW.

And what about the maximum width?  I think this is much more important
than the minimum.

I've decided to leave it out, because choosing the places to "cut" is not trivial in advance, until we know of specific situations.

- Padding (whether to pad this column with a space on one side if there is a 
next column)

Doesn't the minimum width already cover this?  If the actual width is
less than the advertised minimum, text should be padded, otherwise it
shouldn't.  Right?

Yes, and padding should be omitted if there is padding on every line of the given column.

But if there is a line which didn't need to be padded, it will look "smushed" together with the next column. For some uses, it might be okay, for others, it will be not as readable.

- Text alignment within the column (left or right)

Why does this need to be a separate property?  We don't have anything
like that for any other kind of text.

IDK, why not? Will we always align to the right? I don't really mind, but if we anticipate the users to want different alignments, the property needs to be here. Otherwise, the callers will have to take care of "minimum width" themselves. And that basically requires them to refresh the margin display specs after every scrolling (but before redisplay, apparently).

The keys in left-margin-columns-alist will be used as alternatives to 
`left-margin' in margin display specs.

I think we need to agree on the model of the display in the margins.
The fact that you use "columns" in your proposal hints that each
"user" of the margin (a Lisp program which displays there) will have a
separate "column" in the margin, and that column will be of the same
pixel width for each screen line.

Yes.

If this is the model, then it makes
little sense to have different display specs regarding the "column"
width for each buffer position where display in the margin is
requested, because the resulting column width will be the same for all
such displays.

The width of the string inside the margin display specs can very, can't it?

If we specify these in each display spec, we are in
effect wasting Lisp storage, and potentially also working against the
fundamental design of the display engine (more about that below).

Sorry, specify what? The margin display spec will continue to be

  ((margin column-name) spec)

where SPEC is a text string, most of the time.

The display engine would scan the contents of the current window, process said 
specs, calculate which lines fit the window and which do not, set the total 
margin width appropriately, and display all columns in it. Some reflowing might 
be required.

This will not work with the current design of the display engine, at
least not without significant pains and at least twofold performance
degradation.  To realize why, you need to remember that when the
display engine is redisplaying a window, it initially has no idea
where the window will end, it normally (but not always) knows only
where it will begin.  This is because the stuff to be displayed in the
window could have changed significantly since the last redisplay
cycle, so any "memory" of where the window ended back then could
easily be invalid (which is why the display engine keeps almost no
memory about the results of the last redisplay).

I think I'm getting the rough idea.

The actual place where the window display ends is the consequence of
the display engine trying to lay out the window starting at
window-start, and going on until the window bottom is reached, at
which point it checks that point is visible in the window, and if not,
chooses a different window-start to bring point into the view, and
retries.

Yes, and choosing a different margin width will obviously affect that, as we as the set of margin specs we encounter in the visible part of the buffer. Which might make a naive implementation restart the process multiple times in certain cases, and maybe even infloop.

(The above description is a simplification: it omits various redisplay
optimizations which refrain from displaying the entire window in the
frequent use cases.  But for the purposes of this discussion, let's
forget about those optimizations, because their semantics is, and must
be, identical to redisplaying the whole window anew each time.)

For this reason, "scanning the contents of the current window" is not
something the display engine can do at the start of a window
redisplay.  It can only do so after one full cycle of window layout.
In addition, changing the dimensions of the margins requires to start
the window display anew, after reallocating the glyph matrices.

So what you suggest can only be implemented by displaying each window
twice.  On top of that, it will disable important redisplay
optimizations, which refrain from examining all of the screen lines
and the corresponding buffer text -- since you require to scan all of
the display specs in the window to dynamically compute the margin
dimensions.

I was hoping that we might consider some parts of redisplay to be "fast enough" by now (posn-at-point is fast enough for ~500 FPS on my machine, for instance), but indeed it should require some smart programming.

The way the current display engine is designed, it makes the layout
decisions either at the start of a window's redisplay, or as it
traverses buffer text one character at a time.  The decisions related
to the dimensions of the canvas are best made at the beginning,
otherwise they will cause the redisplay to be abandoned and restarted
anew, which slows it down.  We have a few cases where we do that, but
they should be rare to provide good user experience.  (These cases
could also complicate the move_it_* functions, which simulate display.
As of now, they don't handle such cases, but won't be able to ignore
the consequences of your proposal to calculate margins dynamically as
part of redisplay.)

Therefore, features that require dynamic recalculation of the window
dimensions as part of redisplay should IMO be avoided at all costs.
We should try to find a way of making these calculations in Lisp, as
part of the program(s) that require display in the margins, so that by
the time redisplay kicks in, the calculation of the margin widths,
including the column width for each "user" of the margin, was already
done and stored in some form that the display engine could use when it
initializes redisplay of a window.

What your description tells me, foremost, is that it should be impossible to "perfectly" make these decisions in Lisp, too.

Like, what can linum-mode do? After scrolling, it would look at the window-start, its height, and calculate the _approximate_ physical line at the end of the window. It can't get the precise number because the layout still hasn't happened, right? Furthermore, after it increases the width of its margin column (suppose we were scrolling up so line numbers increased), it can create a different layout where the last line is not visible anymore. And if its number was 100, that makes the specified margin width inaccurate by 1. Luckily, line numbers' width changes slowly, so this won't be apparent often.

Is this the idea?

If the latter is considered too difficult

I think "unworkable" is a more proper word, unfortunately, because I
don't see anyone who'd step forward to perform a major redesign of the
display engine required for your proposal.  (And if we are redesigning
the display engine, I have a few more important requirements for it ;-)

OK :)

I will post an alternative proposal in a separate email.

we can add "width" as a necessary parameter to the column properties. I think 
that would rule out the possibility of efficiently using the margins for the line numbers 
feature, though, which seems unfortunate. But the other uses of the margin that I'm aware 
of are not as dynamic.

Sorry, I think you lost me here.  What is that "width" parameter,
which "column properties" are being alluded to,

The properties inside left-margin-columns-alist.

So we'll have a WIDTH there (global for each column), instead of MINIMUM-WIDTH, and PADDING and TEXT-ALIGNMENT might become unnecessary.

and why would it
disallow dynamic resizing of the margins?

I guess more accurately, it would disallow dynamic *sizing* of the margins by the display engine.

Column widths will be accessible for manipulation from e.g. pre-redisplay-functions, but like described above, it doesn't seem like choosing margin width precisely is feasible in Lisp, even in the use case of showing line numbers.

The only requirement for a
feature that will allow relatively simple and efficient implementation
is that the necessary total width of each window margin is known, or
can be calculated by accessing some buffer- or window-local variable,
at the beginning of a redisplay cycle.  Is that hard to accomplish?

With a column-global WIDTH property, I don't think it's hard.

Similar feature can be added for the fringes, too (for them, dynamic sizing 
isn't needed at all, probably).

I think fringes are a separate issue.  AFAIK, we current cannot
display more than one bitmap on the fringe at any given screen line.

Technical limitations of some GUI toolkits, no doubt. Hopefully, more easily overcome than display engine complexity.

Fringes have other problems anyway, like inability to use more than 2 colors, vector graphics, etc.





reply via email to

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