diff --git a/crates/gpui_elements/src/editable_text/element.rs b/crates/gpui_elements/src/editable_text/element.rs index b3622ff8bf..13bcaa075e 100644 --- a/crates/gpui_elements/src/editable_text/element.rs +++ b/crates/gpui_elements/src/editable_text/element.rs @@ -688,10 +688,7 @@ impl EditableTextElement { } } - // TODO: doesnt render caret when the caret is at the end of the document. - // contains_position doesnt include the last character because then scroll-to-caret is incorrect, - // but we still need to account for "caret is at the end of the last line" - let is_cursor_in_line = segment.contains_position(caret_pos); + let is_cursor_in_line = segment.contains_position(caret_pos, true); if is_cursor_in_line && let Some(wrapped) = &segment.wrapped_line { let local_offset = caret_pos.saturating_sub(segment.text_range.start); let caret_px = wrapped diff --git a/crates/gpui_elements/src/editable_text/state.rs b/crates/gpui_elements/src/editable_text/state.rs index 96d379ff7e..3cd4b2097d 100644 --- a/crates/gpui_elements/src/editable_text/state.rs +++ b/crates/gpui_elements/src/editable_text/state.rs @@ -18,11 +18,14 @@ pub struct EditableTextState { caret: Entity, /// The utf-8 character range that is currently selected by the user. - /// Valid both when start < end and start > end (which dictates the direction of the selection). Empty when start==end. - /// The start of this range is always the current position of the caret (input cursor). + /// Valid both when start < end and start > end (which dictates the direction of the selection). + /// Empty when start==end. The start of this range is always the current position of the caret (input cursor). + /// This means it breaks the semantics/expectations of the Range type. /// - /// NOTE: because each input has its own selection state, its trivial for users to have multiple selections active across multiple inputs at the same time. - /// This could be considered undesirable behavior, and could prompt the question of whether there should be a mechanism to clear selection when focus is lost. + /// NOTE: because each input has its own selection state, its trivial for users to have + /// multiple selections active across multiple inputs at the same time. + /// This could be considered undesirable behavior, and could prompt the question of + /// whether there should be a mechanism to clear selection when focus is lost. selected_range: Range, /// The utf-8 character range of `storage` which is being composed by IME @@ -85,12 +88,24 @@ impl TextLineSegment { count.unwrap_or_default() + 1 } - pub fn contains_position(&self, pos: usize) -> bool { + pub fn contains_position(&self, pos: usize, include_end: bool) -> bool { if self.text_range.is_empty() { - pos == self.text_range.start - } else { - pos >= self.text_range.start && pos < self.text_range.end + return pos == self.text_range.start; } + + // pos must be >= range-start + if pos < self.text_range.start { + return false; + } + + // pos must be <= range-end + if pos > self.text_range.end { + return false; + } + + // pos must be < range-end + // or == is permitted if explicitly allowed (varies according to usage needs) + pos < self.text_range.end || include_end } } @@ -483,7 +498,7 @@ impl EditableTextState { let line_height = self.layout_data.line_height; let mut row_count = 0; for segment in &self.layout_data.lines { - if !segment.contains_position(character_pos) { + if !segment.contains_position(character_pos, false) { row_count += segment.row_count(); continue; }