From b02681ee8ab04ce5f4ad72d435d2059a8baca601 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 21 Jul 2022 13:41:35 +0200 Subject: [PATCH] Treat NSTextInputClient ranges as UTF-16 --- crates/editor/src/editor.rs | 76 +++++++++++++++--------- crates/editor/src/multi_buffer/anchor.rs | 10 +++- crates/gpui/src/platform/mac/window.rs | 49 +++------------ crates/text/src/offset_utf16.rs | 50 ++++++++++++++++ 4 files changed, 115 insertions(+), 70 deletions(-) create mode 100644 crates/text/src/offset_utf16.rs diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ba5ae4eefea16f54970c577aa740130db827aa53..babc6f3ca4b6697e8375b29fb59509cc968870f8 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -39,15 +39,15 @@ pub use items::MAX_TAB_TITLE_LEN; pub use language::{char_kind, CharKind}; use language::{ BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity, - IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, + IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, TransactionId, }; use link_go_to_definition::LinkGoToDefinitionState; -use multi_buffer::MultiBufferChunks; pub use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, }; +use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; use project::{LocationLink, Project, ProjectPath, ProjectTransaction}; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; @@ -5877,27 +5877,33 @@ impl View for Editor { context } - fn text_for_range(&self, range: Range, cx: &AppContext) -> Option { + fn text_for_range(&self, range_utf16: Range, cx: &AppContext) -> Option { Some( self.buffer .read(cx) .read(cx) - .text_for_range(range) + .text_for_range(OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end)) .collect(), ) } fn selected_text_range(&self, cx: &AppContext) -> Option> { - Some(self.selections.newest(cx).range()) + let range = self.selections.newest::(cx).range(); + Some(range.start.0..range.end.0) } - fn set_selected_text_range(&mut self, range: Range, cx: &mut ViewContext) { - self.change_selections(None, cx, |selections| selections.select_ranges([range])); + fn set_selected_text_range(&mut self, range_utf16: Range, cx: &mut ViewContext) { + self.change_selections(None, cx, |selections| { + selections.select_ranges([OffsetUtf16(range_utf16.start)..OffsetUtf16(range_utf16.end)]) + }); } fn marked_text_range(&self, cx: &AppContext) -> Option> { let range = self.text_highlights::(cx)?.1.get(0)?; - Some(range.to_offset(&*self.buffer.read(cx).read(cx))) + let snapshot = self.buffer.read(cx).read(cx); + let range_utf16 = + range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot); + Some(range_utf16.start.0..range_utf16.end.0) } fn unmark_text(&mut self, cx: &mut ViewContext) { @@ -5906,34 +5912,36 @@ impl View for Editor { fn replace_text_in_range( &mut self, - range: Option>, + range_utf16: Option>, text: &str, cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - if let Some(range) = range { - this.set_selected_text_range(range, cx); + if let Some(range_utf16) = range_utf16.or_else(|| this.marked_text_range(cx)) { + this.set_selected_text_range(range_utf16, cx); } this.handle_input(text, cx); + this.unmark_text(cx); }); } fn replace_and_mark_text_in_range( &mut self, - range: Option>, + range_utf16: Option>, text: &str, - new_selected_range: Option>, + new_selected_range_utf16: Option>, cx: &mut ViewContext, ) { self.transact(cx, |this, cx| { - let range = range.or_else(|| { - let ranges = this.text_highlights::(cx)?.1; - let range = ranges.first()?; - let snapshot = this.buffer.read(cx).read(cx); - Some(range.to_offset(&*snapshot)) - }); - if let Some(range) = range { - this.set_selected_text_range(range, cx); + if let Some(mut marked_range) = this.marked_text_range(cx) { + if let Some(relative_range_utf16) = range_utf16.as_ref() { + marked_range.end = marked_range.start + relative_range_utf16.end; + marked_range.start += relative_range_utf16.start; + } + + this.set_selected_text_range(marked_range, cx); + } else if let Some(range_utf16) = range_utf16 { + this.set_selected_text_range(range_utf16, cx); } let selection = this.selections.newest_anchor(); @@ -5941,16 +5949,28 @@ impl View for Editor { let snapshot = this.buffer.read(cx).read(cx); selection.start.bias_left(&*snapshot)..selection.end.bias_right(&*snapshot) }; - this.highlight_text::( - vec![marked_range], - this.style(cx).composition_mark, - cx, - ); + + if text.is_empty() { + this.unmark_text(cx); + } else { + this.highlight_text::( + vec![marked_range.clone()], + this.style(cx).composition_mark, + cx, + ); + } this.handle_input(text, cx); - if let Some(new_selected_range) = new_selected_range { - this.set_selected_text_range(new_selected_range, cx); + if let Some(new_selected_range) = new_selected_range_utf16 { + let snapshot = this.buffer.read(cx).read(cx); + let insertion_start = marked_range.start.to_offset_utf16(&snapshot).0; + drop(snapshot); + this.set_selected_text_range( + insertion_start + new_selected_range.start + ..insertion_start + new_selected_range.end, + cx, + ); } }); } diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index df080f074cdd5d1295b6a0e4729939819ec71bb0..1340ea814dfe746b3251415ee463d59e6486760b 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -1,10 +1,10 @@ -use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToPoint}; +use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint}; use std::{ cmp::Ordering, ops::{Range, Sub}, }; use sum_tree::Bias; -use text::{rope::TextDimension, Point}; +use text::{rope::TextDimension, OffsetUtf16, Point}; #[derive(Clone, Eq, PartialEq, Debug, Hash)] pub struct Anchor { @@ -89,6 +89,12 @@ impl ToOffset for Anchor { } } +impl ToOffsetUtf16 for Anchor { + fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { + self.summary(snapshot) + } +} + impl ToPoint for Anchor { fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point { self.summary(snapshot) diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 097a53b260ab1045a3fae5b93955ab6f7c7c5ab6..3c4c2804878464a057a69d1a5f951f61309e4c13 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1139,62 +1139,31 @@ extern "C" fn set_marked_text( .to_str() .unwrap(); - let window_state = get_window_state(this); - let mut window_state = window_state.borrow_mut(); - if let Some(pending) = window_state.pending_key_event.as_mut() { - pending.set_marked_text = Some((text.to_string(), selected_range, replacement_range)); - } else { - drop(window_state); - with_input_handler(this, |input_handler| { - input_handler.replace_and_mark_text_in_range( - replacement_range, - text, - selected_range, - ); - }); - } + with_input_handler(this, |input_handler| { + input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range); + }); } } extern "C" fn unmark_text(this: &Object, _: Sel) { - println!("unmark_text"); - let window_state = unsafe { get_window_state(this) }; - let mut window_state = window_state.borrow_mut(); - if let Some(pending) = window_state.pending_key_event.as_mut() { - pending.unmark_text = true; - pending.set_marked_text.take(); - } else { - drop(window_state); - with_input_handler(this, |input_handler| input_handler.finish_composition()); - } + with_input_handler(this, |input_handler| input_handler.finish_composition()); } extern "C" fn attributed_substring_for_proposed_range( this: &Object, _: Sel, range: NSRange, - actual_range: *mut c_void, + _actual_range: *mut c_void, ) -> id { + println!("attributed_substring_for_proposed_range({:?})", range); with_input_handler(this, |input_handler| { - let actual_range = actual_range as *mut NSRange; - if !actual_range.is_null() { - unsafe { *actual_range = NSRange::invalid() }; - } - - let requested_range = range.to_range()?; - if requested_range.is_empty() { - return None; - } - - let selected_range = input_handler.selected_text_range()?; - let intersection = cmp::max(requested_range.start, selected_range.start) - ..cmp::min(requested_range.end, selected_range.end); - if intersection.start >= intersection.end { + let range = range.to_range()?; + if range.is_empty() { return None; } + let selected_text = input_handler.text_for_range(range)?; unsafe { - let selected_text = input_handler.text_for_range(intersection)?; let string: id = msg_send![class!(NSAttributedString), alloc]; let string: id = msg_send![string, initWithString: ns_string(&selected_text)]; Some(string) diff --git a/crates/text/src/offset_utf16.rs b/crates/text/src/offset_utf16.rs new file mode 100644 index 0000000000000000000000000000000000000000..9a52b3c3f900788262696fe136dcc7d5995b3a5a --- /dev/null +++ b/crates/text/src/offset_utf16.rs @@ -0,0 +1,50 @@ +use std::ops::{Add, AddAssign, Sub}; + +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] +pub struct OffsetUtf16(pub usize); + +impl<'a> Add<&'a Self> for OffsetUtf16 { + type Output = Self; + + fn add(self, other: &'a Self) -> Self::Output { + Self(self.0 + other.0) + } +} + +impl Add for OffsetUtf16 { + type Output = Self; + + fn add(self, other: Self) -> Self::Output { + Self(self.0 + other.0) + } +} + +impl<'a> Sub<&'a Self> for OffsetUtf16 { + type Output = Self; + + fn sub(self, other: &'a Self) -> Self::Output { + debug_assert!(*other <= self); + Self(self.0 - other.0) + } +} + +impl Sub for OffsetUtf16 { + type Output = OffsetUtf16; + + fn sub(self, other: Self) -> Self::Output { + debug_assert!(other <= self); + Self(self.0 - other.0) + } +} + +impl<'a> AddAssign<&'a Self> for OffsetUtf16 { + fn add_assign(&mut self, other: &'a Self) { + self.0 += other.0; + } +} + +impl AddAssign for OffsetUtf16 { + fn add_assign(&mut self, other: Self) { + self.0 += other.0; + } +}