# editor: Fix incorrect inlay hint selection behavior \(\#60912\) · gitcafe/zed

[View on GitCafe](https://git.cafe/gitcafe/zed/commit/78c0f2522a420f117cc0ab7629076efc4469b0d9)

Repository: [gitcafe/zed](https://git.cafe/gitcafe/zed)

Visibility: public

Requested revision: 78c0f2522a420f117cc0ab7629076efc4469b0d9

Requested commit: 78c0f2522a420f117cc0ab7629076efc4469b0d9

Commit: 78c0f2522a420f117cc0ab7629076efc4469b0d9

Tree: d5b5279afe9dfe8bcf294d43a34c8cbffa03b2ef

Author: Finn Eitreim

Committer: GitHub

## Message

````
editor: Fix incorrect inlay hint selection behavior (#60912)

# Objective

Fixes #48141

The issue is that there are certain situations where an Inlay hint,
specifically hints that appear to the left of whatever item they are
referring to, e.g. function argument hints, will be selected
incorrectly.


https://github.com/user-attachments/assets/b6d60bad-6999-481b-a1c7-420d3ee113f8

As you can see in the video, in both a modal and non-modal editor, as
soon as you include `(` (or the empty space between the first and second
argument) the hint gets highlighted as well.

## Solution

It is really important to note here that there is no issue with the
actual selection semantics, inlay hints don't get truly selected in the
sense that they don't count as buffer text and if you e.g. copy a
selection with a hint in it it doesn't get copied. A second thing to
note is that a fix that looks good for modal editing might not look good
for regular editing, thats largely what sank the previous attempt to fix
this issue (#51219).

This video is from #51219, but earlier versions of my fix suffered from
the exact same issue.


https://github.com/user-attachments/assets/16452530-1918-47da-9fd4-87c285284959

The cursor behavior for an active selection in a non-modal editor and
when there isn't a selection are different, and right now the behavior
is wired for the non-selection case, the solution to this is to separate
how things display with a selection from the other behavior. !I do not
change the non-selection behavior in any way!

Inlay hints at the boundary of a selection should be excluded, but hints
inside of a selection should be part of the contiguous selection. We can
reuse the `DisplayPointConverter` to do this. My original solution was
to use `isomorphic_display_point_ranges_for_buffer_range`, which maps a
buffer offset range into a `DisplayPoint` range that excludes Inlays,
except it actually maps to potentially several ranges, all the buffer
text *in between* inlay hints. By just taking the start of the first
sub-range, and the end of the last, we get the correct range that we are
looking for. The function I added,
`contiguous_display_point_range_for_buffer_range` basically just does
exactly that, and cut out all the extra work of creating a bunch of
intermediate ranges if it was a large selection with lots of inlays.

Then we can set the cursor head based on this range, this gives the
correct behavior for non-vim mode cursors, the vim mode cursor is
handled by other code after we set that anyway, so vim mode behavior is
unchanged.

These code changes are in `SelectionLayout::new`
```rust 
        let buffer_snapshot = map.buffer_snapshot();
        let point_selection = selection.map(|p| p.to_point(buffer_snapshot));
        let display_selection = point_selection.map(|p| p.to_display_point(map));
        let mut range = display_selection.range();
        let mut head = display_selection.head();
        if !line_mode {
            let offset_range = point_selection.start.to_offset(buffer_snapshot)
                ..point_selection.end.to_offset(buffer_snapshot);
            if let Some(contiguous_range) =
                map.contiguous_display_point_range_for_buffer_range(offset_range)
            {
                range = contiguous_range;
                // Keep the cursor attached to the highlight boundary; the
                // anchor-bias display position may sit on the far side of a
                // boundary inlay the highlight excludes.
                head = if selection.reversed {
                    range.start
                } else {
                    range.end
                };
            }
        }
```

Heres the new behavior:


https://github.com/user-attachments/assets/cbbd53d6-bbea-4c50-91b5-23eaccd5d9d7

## Testing

added `test_selection_layout_around_inlay`, that fails on main currently
and passes with my changes. As far as manual testing the setup is pretty
easy, this is generally pretty language agnostic and can be tested
anywhere with function parameter hints.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- editor: Fixed rendering issues with selections over inlay hints.
````

## Parents

- [5d447d4223f8455db4811ed332c4ad744bb13e40](https://git.cafe/gitcafe/zed/commit/5d447d4223f8455db4811ed332c4ad744bb13e40?format=markdown)

[Source at this commit](https://git.cafe/gitcafe/zed/tree/78c0f2522a420f117cc0ab7629076efc4469b0d9?format=markdown)
