emacs-devel
[Top][All Lists]
Advanced

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

Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functi


From: Jimmy Yuen Ho Wong
Subject: Re: Need `truncate-string-to-pixel-width` and `glyph-pixel-width` functions in C
Date: Thu, 24 Oct 2024 22:11:36 +0100

Why would you need to truncate? and which part needs to be truncated?

My suggestion is to align the <Type>s part so that it and the
following pseudo-scrollbar end exactly at the window edge.  With this
arrangement, you shouldn't need to truncate at least the <Type>s part.
This can be dome by using :align-to which counts from 'right', which
is the right edge of the text area. 
 

Actually, I don't understand why you'd need to truncate anything,
since the dimensions of the child frame can be calculated such that
the longest completion candidate will still fit without truncation.
Or what am I missing?

The :align-to of the padding between candidate and annotation can only be calculated by adding the prefix column width, calculated from the longest prefix width, the candidate width, calculated the same way, and the suffix column width minus the pixel width of the suffix. The reason for this is that `string-pixel-width` will be called on all of these concatenated strings, and the maximum of which will be given as the width to a function to render the child frame. This also answers your next question, yes the pop up width is already calculated dynamically, but against a max width, this is what I mean by "confined", hence the need for truncation before blitzing the strings to a child frame. :align-to (- right suffix-width) will lead `string-pixel-width` to return a ridiculously large number that, funnily enough, is calculated from the current window's text area width, instead of the child frame's window, of which the size is unknown at this time.

As to the reason for a max width, let's just say there exists language servers such as Rust Analyzer, that will return the signature of a function as the completion annotation, which can be quite long. Nobody wants their completion pop up box taking up half the screen.

Of course, one can also adjusts the child frame parameters to let it deal with min/max size, margins and allow the user to resize, but the beautifully emulated scroll bar will have to be sacrificed because either I delete it from Corfu or the window will start truncating from the scroll bar when the user resizes the window to be smaller than the content width.
 
> There are 2 options - `truncate-string-to-width`, which is
> bugged, or set the `truncate-line` buffer local to t and let Emacs' window system do its magic, which will be
> performant and correct, and I can even replace the arrow in an ellipsis, but truncation will start to truncate off
> the emulated scroll bar.

You assumed something I didn't say and didn't have in mind.  There
should be no need to use truncate-lines.
 
I didn't assume anything. It's just a logical conclusion. Do you have it in mind now?

This is not about refusal.  It isn't like what you are asking is easy
to do, and the "Emacs devs" are just a lazy bunch.  I looked at the
code involved, and I currently have no idea how to implement what you
have in mind so that it works in all the cases, those which you
mentioned and those you didn't, and works in a way that will be useful
for Lisp programs.  You may think it's easy, but it isn't.  Some
things are even conceptually not simple, for example whether it is
okay or not to break a string between U+1F3C2 and the following
U+1F3FF, and if so, how to discover that subject to how the current
character-composition machinery in Emacs was designed and implemented.

You already have `string-glyph-split` which works correctly, (a job well done BTW, even more comformant than Chrome and Webkit). There are only 3 things you need to do for a hypothetical `glyph-pixel-width`:
1. With no faces, find the width of a character glyph from the default font or whatever `char-displayable-p` returns. This is easy, I can already do this from Elisp reasonably fast by going straight to the terminal using `composite-get-gstring`.
2. With faces, resolve the font from the faces, go to 1. Still no need to deal with windows. Can be done correctly in Elisp, but incredibly slowly due to anon faces, face lists and having to go through the recursive `face-attribute-merged-with`. The logic in `face-attribute-merged-with` can be duplicated and modified to deal with anon faces, and run iteratively, but still a few orders of magnitude slower than it needs to be.
3. Display properties is when you start to need a window, as a first try, you can delegate this back to the current slow path via `window-text-pixel-size`.

1 and 2 will be the vast vast majority of the use case. It's okay to be slower for 3 due to the involvement of a window.

As a point of reference, this benchmark finishes in ~0.10s in my MacBook Pro M1 Pro, this is 6 FPS for measuring 10k glyph widths.

```elisp
(let ((s "1")) 'face 'completions-common-part)))
    (benchmark-run 10000 (string-pixel-width s)))
```

The following finishes in ~0.9s. Using the procedure described in case 1 above already cuts the time down by half.

```elisp
(benchmark-run 10000 (string-pixel-width "1"))
```

Getting boths cases down 1 order of magnitude is all I'm hoping for.
 
I'm not saying emacs-devs are lazy, I'm just saying after 14 years, Emacs finally can split strings with emojis correctly, can we go one step further and give Emacs users the ability to layout in pixel precision in a more performant manner please?

reply via email to

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