Restructure IME composition to not follow Chromium so closely

Antonio Scandurra created

Change summary

crates/gpui/src/platform/mac/window.rs | 145 +++++++++------------------
1 file changed, 48 insertions(+), 97 deletions(-)

Detailed changes

crates/gpui/src/platform/mac/window.rs 🔗

@@ -37,7 +37,6 @@ use smol::Timer;
 use std::{
     any::Any,
     cell::{Cell, RefCell},
-    cmp,
     convert::TryInto,
     ffi::{c_void, CStr},
     mem,
@@ -274,7 +273,7 @@ struct WindowState {
     should_close_callback: Option<Box<dyn FnMut() -> bool>>,
     close_callback: Option<Box<dyn FnOnce()>>,
     input_handler: Option<Box<dyn InputHandler>>,
-    pending_key_event: Option<PendingKeyEvent>,
+    pending_keydown_event: Option<KeyDownEvent>,
     synthetic_drag_counter: usize,
     executor: Rc<executor::Foreground>,
     scene_to_render: Option<Scene>,
@@ -286,13 +285,6 @@ struct WindowState {
     previous_modifiers_changed_event: Option<Event>,
 }
 
-#[derive(Default, Debug)]
-struct PendingKeyEvent {
-    set_marked_text: Option<(String, Option<Range<usize>>, Option<Range<usize>>)>,
-    unmark_text: bool,
-    insert_text: Option<String>,
-}
-
 impl Window {
     pub fn open(
         id: usize,
@@ -369,7 +361,7 @@ impl Window {
                 close_callback: None,
                 activate_callback: None,
                 input_handler: None,
-                pending_key_event: None,
+                pending_keydown_event: None,
                 synthetic_drag_counter: 0,
                 executor,
                 scene_to_render: Default::default(),
@@ -705,7 +697,7 @@ extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) ->
 
     let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
     if let Some(event) = event {
-        let mut event = match event {
+        window_state_borrow.pending_keydown_event = match event {
             Event::KeyDown(event) => {
                 let keydown = (event.keystroke.clone(), event.input.clone());
                 // Ignore events from held-down keys after some of the initially-pressed keys
@@ -718,97 +710,30 @@ extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) ->
                     window_state_borrow.last_fresh_keydown = Some(keydown);
                 }
 
-                event
+                Some(event)
             }
             _ => return NO,
         };
-
-        // TODO: handle "live conversion"
-        window_state_borrow.pending_key_event = Some(Default::default());
         drop(window_state_borrow);
-        let had_marked_text =
-            with_input_handler(this, |input_handler| input_handler.marked_text_range())
-                .flatten()
-                .is_some();
 
-        // TODO:
-        // Since Mac Eisu Kana keys cannot be handled by interpretKeyEvents to enable/
-        // disable an IME, we need to pass the event to processInputKeyBindings.
-        // processInputKeyBindings is available at least on 10.11-11.0.
-        // if (keyCode == kVK_JIS_Eisu || keyCode == kVK_JIS_Kana) {
-        //     if ([NSTextInputContext
-        //         respondsToSelector:@selector(processInputKeyBindings:)]) {
-        //             [NSTextInputContext performSelector:@selector(processInputKeyBindings:)
-        //                 withObject:theEvent];
-        //         }
-        // } else {
         unsafe {
             let input_context: id = msg_send![this, inputContext];
             let _: BOOL = msg_send![input_context, handleEvent: native_event];
         }
-        // }
-
-        let pending_event = window_state.borrow_mut().pending_key_event.take().unwrap();
-
-        dbg!(&pending_event);
-
-        let mut inserted_text = false;
-        let has_marked_text = pending_event.set_marked_text.is_some()
-            || with_input_handler(this, |input_handler| input_handler.marked_text_range())
-                .flatten()
-                .is_some();
-        if let Some(text) = pending_event.insert_text.as_ref() {
-            if !text.is_empty() {
-                with_input_handler(this, |input_handler| {
-                    input_handler.replace_text_in_range(None, &text)
-                });
-                inserted_text = true;
-            }
-        }
-
-        with_input_handler(this, |input_handler| {
-            if let Some((text, new_selected_range, replacement_range)) =
-                pending_event.set_marked_text
-            {
-                input_handler.replace_and_mark_text_in_range(
-                    replacement_range,
-                    &text,
-                    new_selected_range,
-                )
-            } else if had_marked_text && !has_marked_text && !inserted_text {
-                if pending_event.unmark_text {
-                    input_handler.finish_composition();
-                } else {
-                    input_handler.cancel_composition();
-                }
-            }
-        });
 
-        if has_marked_text {
-            YES
-        } else {
-            let mut handled = false;
-            let mut window_state_borrow = window_state.borrow_mut();
+        let mut handled = false;
+        let mut window_state_borrow = window_state.borrow_mut();
+        if let Some(event) = window_state_borrow.pending_keydown_event.take() {
             if let Some(mut callback) = window_state_borrow.event_callback.take() {
                 drop(window_state_borrow);
-
-                if inserted_text {
-                    handled = true;
-                } else {
-                    if let Some(text) = pending_event.insert_text {
-                        if text.chars().count() == 1 {
-                            event.keystroke.key = text;
-                        }
-                    }
-
-                    handled = callback(Event::KeyDown(event.clone()));
-                }
-
+                handled = callback(Event::KeyDown(event.clone()));
                 window_state.borrow_mut().event_callback = Some(callback);
             }
-
-            handled as BOOL
+        } else {
+            handled = true;
         }
+
+        handled as BOOL
     } else {
         NO
     }
@@ -1090,6 +1015,11 @@ extern "C" fn first_rect_for_character_range(_: &Object, _: Sel, _: NSRange, _:
 
 extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
     unsafe {
+        let window_state = get_window_state(this);
+        let mut window_state_borrow = window_state.borrow_mut();
+        let pending_keydown_event = window_state_borrow.pending_keydown_event.take();
+        drop(window_state_borrow);
+
         let is_attributed_string: BOOL =
             msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
         let text: id = if is_attributed_string == YES {
@@ -1100,20 +1030,36 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
         let text = CStr::from_ptr(text.UTF8String() as *mut c_char)
             .to_str()
             .unwrap();
+        let replacement_range = replacement_range.to_range();
 
-        let window_state = get_window_state(this);
-        let mut window_state = window_state.borrow_mut();
-        if window_state.pending_key_event.is_some() && !replacement_range.is_valid() {
-            window_state.pending_key_event.as_mut().unwrap().insert_text = Some(text.to_string());
-            drop(window_state);
-        } else {
-            drop(window_state);
+        let is_composing =
+            with_input_handler(this, |input_handler| input_handler.marked_text_range())
+                .flatten()
+                .is_some();
+
+        if is_composing || text.chars().count() > 1 || pending_keydown_event.is_none() {
             with_input_handler(this, |input_handler| {
-                input_handler.replace_text_in_range(replacement_range.to_range(), text);
+                input_handler.replace_text_in_range(replacement_range, text)
             });
-        }
+        } else {
+            let mut pending_keydown_event = pending_keydown_event.unwrap();
+            pending_keydown_event.keystroke.key = text.into();
+            let mut window_state_borrow = window_state.borrow_mut();
+            let event_callback = window_state_borrow.event_callback.take();
+            drop(window_state_borrow);
 
-        with_input_handler(this, |input_handler| input_handler.unmark_text());
+            let mut handled = false;
+            if let Some(mut event_callback) = event_callback {
+                handled = event_callback(Event::KeyDown(pending_keydown_event));
+                window_state.borrow_mut().event_callback = Some(event_callback);
+            }
+
+            if !handled {
+                with_input_handler(this, |input_handler| {
+                    input_handler.replace_text_in_range(replacement_range, text)
+                });
+            }
+        }
     }
 }
 
@@ -1126,6 +1072,11 @@ extern "C" fn set_marked_text(
 ) {
     println!("set_marked_text");
     unsafe {
+        get_window_state(this)
+            .borrow_mut()
+            .pending_keydown_event
+            .take();
+
         let is_attributed_string: BOOL =
             msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
         let text: id = if is_attributed_string == YES {