-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rework positioning/rendering and enable softwrap/virtual text #5420
Conversation
This PR is a large change to many core components of helix and therefore needs testing before merging. You can enable softwrap either using While virtual text is implemented in this PR, it does not include any functionality that actually makes use of it so it's harder to test altough rebasing #3791 would be neat. |
6d96f54
to
2d100cd
Compare
Is this "fully" implemented and usable for editing? |
yeah unless I missed something. There is nothing intentionally left out |
2d100cd
to
125f2e0
Compare
Thanks a lot for working on this, gonna daily drive it a bit but already looks awesome ! To overcome this, I see the following solutions for the defaults:
I personally prefer the first point, mainly because this was NeoVim's behavior once you had remapped vertical motions to use virtual lines, but I'm curious about your thoughts on this. Thanks ! |
I’m going to daily drive this as well!
Is there a reason for the name of the option to not be the same in both contexts? (both |
It's always |
I don't think we should handle vertical movement differently when a count is specified. That seems inconsistent. I will be adding a command for vertical movement that ignores softwrap later. Users that behaviour could change the |
In the above image, I feel like it would be more readable if whole words were split, rather than being split mid-word. It's a bit distracting. |
That's going to complicate things since you'd need to backtrack and determine where a reasonable break would be. It wouldn't work in cases where you're looking at minified JSON either. |
From what I can tell the github code rendering doesn't support this either but I didn't check other editors |
Is the indentation on wrapped lines deliberate though? I think most editors will wrap to the beginning of the line |
VSCode implements this, and I think it uses a config option to determine maximum word length before wrapping (80 columns). Led also implemented it in that way (though of course it wasn't a major editor). VSCode's wrapping implementation is in https://github.com/microsoft/vscode/blob/82db01c21a0d0d08b156b725a37c39cc3714c238/src/vs/editor/common/viewModel/viewModelLines.ts#L60 |
It is deliberate and there is also a config option for it |
I'm not opposed to it, but it could also be done as a follow-up, depends on what @pascalkuthe thinks |
I would very much like the option to split on words rather than in the middle of them, it is set using 'linebreak' in NeoVim. The following is taken from the NeoVim documentation:
|
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
This assert was added during early development of helix-editor#5420 and makes no sense with the current code. We simply forgot to remove it.
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
The line annotation as implemented in helix-editor#5420 had two shortcomings: * It required the height of virtual text lines to be known ahead time * It checked for line anchors at every grapheme The first problem made the API impractical to use in practice because almost all virtual text needs to be softwrapped. For example inline diagnostics should be softwrapped to avoid cutting off the diagnostic message (as no scrolling is possible). While more complex virtual text like side by side diffs must dynamically calculate the number of empty lines two align two documents (which requires taking account both softwrap and virtual text). To address this, the API has been refactored to use a trait. The second issue caused some performance overhead and unnecessarily complicated the `DocumentFormatter`. It was addressed by only calling the trait mentioned above at line breaks (instead of always). This allows offers additional flexibility to annotations as it offers the flexibility to align lines (needed for side by side diffs).
Supersedes #5008
This PR is a large rework of the core text positioning and rendering code to
remove the assumption that on-screen columns/lines correspond to text columns/
lines. This is required to implement both softwrap and virtual text.
Both of these features are included in this PR, so the original goals outlined
in #5008 can be achieved with this PR.
Implementation
This PR introduces generic
DocFormatter
that positions graphemes from varioussources. This formatter is used both for rendering and for movements/scrolling.
The design is heavily inspired by led, although I streamlined/adjusted some
Details as helix needs are different (especially with the introduction of
virtual text).
The basics of the softwrap implementation from #5008 mostly remains (so indent
carry over, and word splitting are supported) but rendering and positioning are
cleanly separated now and much of the architecture explained in #5008 is not
present anymore as I had to evolve the architecture for the positioning code.
The
DocFormatter
is still a single grapheme iterator (so more efficient)that transverses the entire document. However, it does not deal with syntax
highlighting or rendering at all. It's an iterator that is only concerned witch placing
graphemes from the document and virtual text at the correct position.
Note that the
DocFormatter
does not deal with the contents of line virtual textas that is easily handeled trough the new LineDecoration rendering mechansim.
The
DocFormatter
is constructed from an anchor char index which it backtracksto the last known line breaks. These "known" line breaks are called blocks and
were also described by @cessen in #136. Right now only normal line breaks are
considered as blocks, but the code is written so long lines can be chunked for
improved performance for files with very long text in the future.
I did not implement this logic in this PR as it's only an optimization and can be
implemented later as suggested by @cessen.
I added the functions
visual_offset_from_anchor
/visual_offset_from_block
and
char_idx_at_visual_offset
to theposition
module in helix corethat utilize the
DocFormatter
. These replacevisual_coords_at_pos
andpos_at_visual_cords
respectively. Their names are different to more accuratelyreflect their functions. The old functions have been replaced everywhere except
for the
align_selections
command. I am not sure if virtual text and softwrapshould affect selection alignment and refactoring this function was more
involved, so I left it untouched for now.
I have also updated the vertical movement command to be softwrap aware which
feels much more intuitive than the behavior in other editor like nvim. If there
is demand for vertical movements that ignore softwrap a separate command could
be added that simply sets
softwrap = false
in theTextFormat
before passingit to
move vertically
. Note that many commands (like goto_line_end) are not softwrapaware yet. I am not sure if that is desirable and therefore left it to future work.
The rendering code has been somewhat streamlined and moved to
document.rs
toallow reuse in other views as it had to be rewritten from to utilize
the new
DocFormatter
anyway.Future Work
challenging as the char range of a visual line is only known once rendered, but
the style needs to be applied before the line is rendered
enable sofwrap by default in the future once it had time to mature?