Skip to content

Fix inlay hint hover highlight for multi-byte UTF-8 characters#44872

Merged
Veykril merged 1 commit intozed-industries:mainfrom
edlsh:fix/inlay-hint-hover-multibyte-chars
Jan 4, 2026
Merged

Fix inlay hint hover highlight for multi-byte UTF-8 characters#44872
Veykril merged 1 commit intozed-industries:mainfrom
edlsh:fix/inlay-hint-hover-multibyte-chars

Conversation

@edlsh
Copy link
Contributor

@edlsh edlsh commented Dec 15, 2025

Summary

Fixes #44812

The hover highlight for clickable inlay hints was not covering the last character when the label contained multi-byte UTF-8 characters (e.g., ).

Problem

When hovering over an inlay hint like → app/Livewire/UserProfile.php, the highlight/underline stopped one character short, leaving the final character unhighlighted.

Root cause: find_hovered_hint_part() in hover_popover.rs used part.value.chars().count() (character count) but InlayOffset wraps MultiBufferOffset(pub usize) which is byte-based.

For a label like → app/Livewire/UserProfile.php:

  • Byte length: 32 (the character is 3 bytes in UTF-8)
  • Character count: 30

This mismatch caused the calculated highlight range to end 2 bytes short.

Changes

  1. Use byte length instead of character count (line 112):

    // Before (buggy)
    let part_len = part.value.chars().count();
    
    // After (correct)
    let part_len = part.value.len();
  2. Fix boundary condition (line 113): Changed > to >= for correct [start, end) range semantics when hovering at part boundaries.

  3. Rename variable for clarity: hovered_characteroffset_in_hint since it's a byte offset, not a character position.

  4. Add unit test reproducing the exact scenario from the issue with multi-byte UTF-8 characters.

Testing

Added test_find_hovered_hint_part_with_multibyte_characters which:

  • Verifies the label → app/Livewire/UserProfile.php has 32 bytes but 30 characters
  • Tests hovering at the last byte correctly returns the full range
  • Tests boundary behavior with multiple label parts containing multi-byte characters

Release Notes:

  • Fixed inlay hint hover highlight not covering the last character when the label contains multi-byte UTF-8 characters

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Dec 15, 2025
Fixes #44812

The hover highlight for clickable inlay hints was not covering the last
character when the label contained multi-byte UTF-8 characters (e.g., "→").

Root cause: `find_hovered_hint_part()` used `chars().count()` (character
count) but `InlayOffset` is byte-based. For a label like
"→ app/Livewire/UserProfile.php" (32 bytes, 30 chars), the calculated
highlight range ended 2 bytes short.

Changes:
- Use `part.value.len()` (bytes) instead of `chars().count()`
- Change boundary condition from `>` to `>=` for correct [start, end) semantics
- Rename `hovered_character` to `offset_in_hint` for clarity
- Add unit test with multi-byte characters reproducing the reported issue

Release Notes:

- Fixed inlay hint hover highlight not covering the last character when the label contains multi-byte UTF-8 characters (e.g., "→").
@edlsh edlsh force-pushed the fix/inlay-hint-hover-multibyte-chars branch from 18102a0 to ba7fbea Compare December 15, 2025 12:37
Copy link
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@Veykril Veykril merged commit 848d3cc into zed-industries:main Jan 4, 2026
26 checks passed
rtfeldman pushed a commit that referenced this pull request Jan 5, 2026
## Summary

Fixes #44812

The hover highlight for clickable inlay hints was not covering the last
character when the label contained multi-byte UTF-8 characters (e.g.,
`→`).

## Problem

When hovering over an inlay hint like `→ app/Livewire/UserProfile.php`,
the highlight/underline stopped one character short, leaving the final
character unhighlighted.

**Root cause:** `find_hovered_hint_part()` in `hover_popover.rs` used
`part.value.chars().count()` (character count) but `InlayOffset` wraps
`MultiBufferOffset(pub usize)` which is byte-based.

For a label like `→ app/Livewire/UserProfile.php`:
- Byte length: 32 (the `→` character is 3 bytes in UTF-8)
- Character count: 30

This mismatch caused the calculated highlight range to end 2 bytes
short.

## Changes

1. **Use byte length instead of character count** (line 112):
   ```rust
   // Before (buggy)
   let part_len = part.value.chars().count();
   
   // After (correct)
   let part_len = part.value.len();
   ```

2. **Fix boundary condition** (line 113): Changed `>` to `>=` for
correct `[start, end)` range semantics when hovering at part boundaries.

3. **Rename variable** for clarity: `hovered_character` →
`offset_in_hint` since it's a byte offset, not a character position.

4. **Add unit test** reproducing the exact scenario from the issue with
multi-byte UTF-8 characters.

## Testing

Added `test_find_hovered_hint_part_with_multibyte_characters` which:
- Verifies the label `→ app/Livewire/UserProfile.php` has 32 bytes but
30 characters
- Tests hovering at the last byte correctly returns the full range
- Tests boundary behavior with multiple label parts containing
multi-byte characters

Release Notes:

- Fixed inlay hint hover highlight not covering the last character when
the label contains multi-byte UTF-8 characters
LivioGama pushed a commit to LivioGama/zed that referenced this pull request Jan 20, 2026
…ndustries#44872)

## Summary

Fixes zed-industries#44812

The hover highlight for clickable inlay hints was not covering the last
character when the label contained multi-byte UTF-8 characters (e.g.,
`→`).

## Problem

When hovering over an inlay hint like `→ app/Livewire/UserProfile.php`,
the highlight/underline stopped one character short, leaving the final
character unhighlighted.

**Root cause:** `find_hovered_hint_part()` in `hover_popover.rs` used
`part.value.chars().count()` (character count) but `InlayOffset` wraps
`MultiBufferOffset(pub usize)` which is byte-based.

For a label like `→ app/Livewire/UserProfile.php`:
- Byte length: 32 (the `→` character is 3 bytes in UTF-8)
- Character count: 30

This mismatch caused the calculated highlight range to end 2 bytes
short.

## Changes

1. **Use byte length instead of character count** (line 112):
   ```rust
   // Before (buggy)
   let part_len = part.value.chars().count();
   
   // After (correct)
   let part_len = part.value.len();
   ```

2. **Fix boundary condition** (line 113): Changed `>` to `>=` for
correct `[start, end)` range semantics when hovering at part boundaries.

3. **Rename variable** for clarity: `hovered_character` →
`offset_in_hint` since it's a byte offset, not a character position.

4. **Add unit test** reproducing the exact scenario from the issue with
multi-byte UTF-8 characters.

## Testing

Added `test_find_hovered_hint_part_with_multibyte_characters` which:
- Verifies the label `→ app/Livewire/UserProfile.php` has 32 bytes but
30 characters
- Tests hovering at the last byte correctly returns the full range
- Tests boundary behavior with multiple label parts containing
multi-byte characters

Release Notes:

- Fixed inlay hint hover highlight not covering the last character when
the label contains multi-byte UTF-8 characters
LivioGama pushed a commit to LivioGama/zed that referenced this pull request Jan 20, 2026
…ndustries#44872)

## Summary

Fixes zed-industries#44812

The hover highlight for clickable inlay hints was not covering the last
character when the label contained multi-byte UTF-8 characters (e.g.,
`→`).

## Problem

When hovering over an inlay hint like `→ app/Livewire/UserProfile.php`,
the highlight/underline stopped one character short, leaving the final
character unhighlighted.

**Root cause:** `find_hovered_hint_part()` in `hover_popover.rs` used
`part.value.chars().count()` (character count) but `InlayOffset` wraps
`MultiBufferOffset(pub usize)` which is byte-based.

For a label like `→ app/Livewire/UserProfile.php`:
- Byte length: 32 (the `→` character is 3 bytes in UTF-8)
- Character count: 30

This mismatch caused the calculated highlight range to end 2 bytes
short.

## Changes

1. **Use byte length instead of character count** (line 112):
   ```rust
   // Before (buggy)
   let part_len = part.value.chars().count();
   
   // After (correct)
   let part_len = part.value.len();
   ```

2. **Fix boundary condition** (line 113): Changed `>` to `>=` for
correct `[start, end)` range semantics when hovering at part boundaries.

3. **Rename variable** for clarity: `hovered_character` →
`offset_in_hint` since it's a byte offset, not a character position.

4. **Add unit test** reproducing the exact scenario from the issue with
multi-byte UTF-8 characters.

## Testing

Added `test_find_hovered_hint_part_with_multibyte_characters` which:
- Verifies the label `→ app/Livewire/UserProfile.php` has 32 bytes but
30 characters
- Tests hovering at the last byte correctly returns the full range
- Tests boundary behavior with multiple label parts containing
multi-byte characters

Release Notes:

- Fixed inlay hint hover highlight not covering the last character when
the label contains multi-byte UTF-8 characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inlay hint hover highlight doesn't cover the last character of the label

2 participants