Merge pull request #1419 from zed-industries/ime-regressions

Fix IME regressions
This commit is contained in:
Antonio Scandurra
2022-07-27 12:37:37 +02:00
committed by GitHub
10 changed files with 125 additions and 76 deletions
+15 -15
View File
@@ -14,30 +14,30 @@
"k": "vim::Up",
"l": "vim::Right",
"0": "vim::StartOfLine",
"shift-$": "vim::EndOfLine",
"shift-G": "vim::EndOfDocument",
"$": "vim::EndOfLine",
"shift-g": "vim::EndOfDocument",
"w": "vim::NextWordStart",
"shift-W": [
"shift-w": [
"vim::NextWordStart",
{
"ignorePunctuation": true
}
],
"e": "vim::NextWordEnd",
"shift-E": [
"shift-e": [
"vim::NextWordEnd",
{
"ignorePunctuation": true
}
],
"b": "vim::PreviousWordStart",
"shift-B": [
"shift-b": [
"vim::PreviousWordStart",
{
"ignorePunctuation": true
}
],
"shift-%": "vim::Matching",
"%": "vim::Matching",
"escape": "editor::Cancel"
}
},
@@ -48,12 +48,12 @@
"vim::PushOperator",
"Change"
],
"shift-C": "vim::ChangeToEndOfLine",
"shift-c": "vim::ChangeToEndOfLine",
"d": [
"vim::PushOperator",
"Delete"
],
"shift-D": "vim::DeleteToEndOfLine",
"shift-d": "vim::DeleteToEndOfLine",
"y": [
"vim::PushOperator",
"Yank"
@@ -62,14 +62,14 @@
"vim::SwitchMode",
"Insert"
],
"shift-I": "vim::InsertFirstNonWhitespace",
"shift-i": "vim::InsertFirstNonWhitespace",
"a": "vim::InsertAfter",
"shift-A": "vim::InsertEndOfLine",
"shift-a": "vim::InsertEndOfLine",
"x": "vim::DeleteRight",
"shift-X": "vim::DeleteLeft",
"shift-^": "vim::FirstNonWhitespace",
"shift-x": "vim::DeleteLeft",
"^": "vim::FirstNonWhitespace",
"o": "vim::InsertLineBelow",
"shift-O": "vim::InsertLineAbove",
"shift-o": "vim::InsertLineAbove",
"v": [
"vim::SwitchMode",
{
@@ -78,7 +78,7 @@
}
}
],
"shift-V": [
"shift-v": [
"vim::SwitchMode",
{
"Visual": {
@@ -113,7 +113,7 @@
"context": "Editor && vim_operator == c",
"bindings": {
"w": "vim::ChangeWord",
"shift-W": [
"shift-w": [
"vim::ChangeWord",
{
"ignorePunctuation": true
+7
View File
@@ -41,6 +41,7 @@ pub struct Keystroke {
pub alt: bool,
pub shift: bool,
pub cmd: bool,
pub function: bool,
pub key: String,
}
@@ -277,6 +278,7 @@ impl Keystroke {
let mut alt = false;
let mut shift = false;
let mut cmd = false;
let mut function = false;
let mut key = None;
let mut components = source.split("-").peekable();
@@ -286,6 +288,7 @@ impl Keystroke {
"alt" => alt = true,
"shift" => shift = true,
"cmd" => cmd = true,
"fn" => function = true,
_ => {
if let Some(component) = components.peek() {
if component.is_empty() && source.ends_with('-') {
@@ -306,6 +309,7 @@ impl Keystroke {
alt,
shift,
cmd,
function,
key: key.unwrap(),
})
}
@@ -464,6 +468,7 @@ mod tests {
alt: false,
shift: false,
cmd: false,
function: false,
}
);
@@ -475,6 +480,7 @@ mod tests {
alt: true,
shift: true,
cmd: false,
function: false,
}
);
@@ -486,6 +492,7 @@ mod tests {
alt: false,
shift: true,
cmd: true,
function: false,
}
);
+13 -7
View File
@@ -210,19 +210,24 @@ impl Event {
unsafe fn parse_keystroke(native_event: id) -> Keystroke {
use cocoa::appkit::*;
let modifiers = native_event.modifierFlags();
let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let mut shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
let mut chars_ignoring_modifiers =
CStr::from_ptr(native_event.charactersIgnoringModifiers().UTF8String() as *mut c_char)
.to_str()
.unwrap();
let first_char = chars_ignoring_modifiers.chars().next().map(|ch| ch as u16);
let modifiers = native_event.modifierFlags();
let ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
let alt = modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask);
let mut shift = modifiers.contains(NSEventModifierFlags::NSShiftKeyMask);
let cmd = modifiers.contains(NSEventModifierFlags::NSCommandKeyMask);
let function = modifiers.contains(NSEventModifierFlags::NSFunctionKeyMask)
&& first_char.map_or(true, |ch| {
ch < NSUpArrowFunctionKey || ch > NSModeSwitchFunctionKey
});
#[allow(non_upper_case_globals)]
let key = match chars_ignoring_modifiers.chars().next().map(|ch| ch as u16) {
let key = match first_char {
Some(SPACE_KEY) => "space",
Some(BACKSPACE_KEY) => "backspace",
Some(ENTER_KEY) | Some(NUMPAD_ENTER_KEY) => "enter",
@@ -282,6 +287,7 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke {
alt,
shift,
cmd,
function,
key: key.into(),
}
}
+1
View File
@@ -184,6 +184,7 @@ impl MacForegroundPlatform {
(keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
(keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
(keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
(keystroke.shift, NSEventModifierFlags::NSShiftKeyMask),
] {
if *modifier {
mask |= *flag;
+63 -29
View File
@@ -154,6 +154,10 @@ unsafe fn build_classes() {
sel!(performKeyEquivalent:),
handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(keyDown:),
handle_key_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseDown:),
handle_view_event as extern "C" fn(&Object, Sel, id),
@@ -275,7 +279,8 @@ struct WindowState {
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
close_callback: Option<Box<dyn FnOnce()>>,
input_handler: Option<Box<dyn InputHandler>>,
pending_key_down_event: Option<KeyDownEvent>,
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
performed_key_equivalent: bool,
synthetic_drag_counter: usize,
executor: Rc<executor::Foreground>,
scene_to_render: Option<Scene>,
@@ -287,6 +292,11 @@ struct WindowState {
previous_modifiers_changed_event: Option<Event>,
}
struct InsertText {
replacement_range: Option<Range<usize>>,
text: String,
}
impl Window {
pub fn open(
id: usize,
@@ -359,7 +369,8 @@ impl Window {
close_callback: None,
activate_callback: None,
input_handler: None,
pending_key_down_event: None,
pending_key_down: None,
performed_key_equivalent: false,
synthetic_drag_counter: 0,
executor,
scene_to_render: Default::default(),
@@ -689,13 +700,28 @@ extern "C" fn dealloc_view(this: &Object, _: Sel) {
}
extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
handle_key_event(this, native_event, true)
}
extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
handle_key_event(this, native_event, false);
}
extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
let window_state = unsafe { get_window_state(this) };
let mut window_state_borrow = window_state.as_ref().borrow_mut();
let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
if let Some(event) = event {
window_state_borrow.pending_key_down_event = match event {
if key_equivalent {
window_state_borrow.performed_key_equivalent = true;
} else if window_state_borrow.performed_key_equivalent {
return NO;
}
let function_is_held;
window_state_borrow.pending_key_down = match event {
Event::KeyDown(event) => {
let keydown = event.keystroke.clone();
// Ignore events from held-down keys after some of the initially-pressed keys
@@ -708,19 +734,23 @@ extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) ->
window_state_borrow.last_fresh_keydown = Some(keydown);
}
Some(event)
function_is_held = event.keystroke.function;
Some((event, None))
}
_ => return NO,
};
drop(window_state_borrow);
unsafe {
let input_context: id = msg_send![this, inputContext];
let _: BOOL = msg_send![input_context, handleEvent: native_event];
if !function_is_held {
unsafe {
let input_context: id = msg_send![this, inputContext];
let _: BOOL = msg_send![input_context, handleEvent: native_event];
}
}
let mut handled = false;
let mut window_state_borrow = window_state.borrow_mut();
if let Some(event) = window_state_borrow.pending_key_down_event.take() {
if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
if let Some(mut callback) = window_state_borrow.event_callback.take() {
drop(window_state_borrow);
@@ -729,14 +759,26 @@ extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) ->
.flatten()
.is_some();
if !is_composing {
callback(Event::KeyDown(event));
handled = callback(Event::KeyDown(event));
}
if !handled {
if let Some(insert) = insert_text {
handled = true;
with_input_handler(this, |input_handler| {
input_handler
.replace_text_in_range(insert.replacement_range, &insert.text)
});
}
}
window_state.borrow_mut().event_callback = Some(callback);
}
} else {
handled = true;
}
YES
handled as BOOL
} else {
NO
}
@@ -819,6 +861,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
ctrl: false,
alt: false,
shift: false,
function: false,
key: ".".into(),
};
let event = Event::KeyDown(KeyDownEvent {
@@ -837,6 +880,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
unsafe {
let () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
get_window_state(this).borrow_mut().performed_key_equivalent = false;
}
}
@@ -1042,7 +1086,7 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
unsafe {
let window_state = get_window_state(this);
let mut window_state_borrow = window_state.borrow_mut();
let pending_key_down_event = window_state_borrow.pending_key_down_event.take();
let pending_key_down = window_state_borrow.pending_key_down.take();
drop(window_state_borrow);
let is_attributed_string: BOOL =
@@ -1062,24 +1106,17 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
.flatten()
.is_some();
if is_composing || text.chars().count() > 1 || pending_key_down_event.is_none() {
if is_composing || text.chars().count() > 1 || pending_key_down.is_none() {
with_input_handler(this, |input_handler| {
input_handler.replace_text_in_range(replacement_range, text)
});
} else {
let mut handled = false;
let event_callback = window_state.borrow_mut().event_callback.take();
if let Some(mut event_callback) = event_callback {
handled = event_callback(Event::KeyDown(pending_key_down_event.unwrap()));
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)
});
}
let mut pending_key_down = pending_key_down.unwrap();
pending_key_down.1 = Some(InsertText {
replacement_range,
text: text.to_string(),
});
window_state.borrow_mut().pending_key_down = Some(pending_key_down);
}
}
}
@@ -1092,10 +1129,7 @@ extern "C" fn set_marked_text(
replacement_range: NSRange,
) {
unsafe {
get_window_state(this)
.borrow_mut()
.pending_key_down_event
.take();
get_window_state(this).borrow_mut().pending_key_down.take();
let is_attributed_string: BOOL =
msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
+1
View File
@@ -311,6 +311,7 @@ mod test {
alt: false,
shift: false,
cmd: false,
function: false,
key: "🖖🏻".to_string(), //2 char string
};
assert_eq!(to_esc_str(&ks, &TermMode::NONE), None);
+11 -11
View File
@@ -427,7 +427,7 @@ mod test {
#[gpui::test]
async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-$"]);
let mut cx = cx.binding(["$"]);
cx.assert("T|est test", "Test tes|t");
cx.assert("Test tes|t", "Test tes|t");
cx.assert(
@@ -471,7 +471,7 @@ mod test {
#[gpui::test]
async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-G"]);
let mut cx = cx.binding(["shift-g"]);
cx.assert(
indoc! {"
@@ -561,7 +561,7 @@ mod test {
);
for cursor_offset in cursor_offsets {
cx.simulate_keystroke("shift-W");
cx.simulate_keystroke("shift-w");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
}
}
@@ -607,7 +607,7 @@ mod test {
Mode::Normal,
);
for cursor_offset in cursor_offsets {
cx.simulate_keystroke("shift-E");
cx.simulate_keystroke("shift-e");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
}
}
@@ -653,7 +653,7 @@ mod test {
Mode::Normal,
);
for cursor_offset in cursor_offsets.into_iter().rev() {
cx.simulate_keystroke("shift-B");
cx.simulate_keystroke("shift-b");
cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
}
}
@@ -740,7 +740,7 @@ mod test {
#[gpui::test]
async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-A"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["shift-a"]).mode_after(Mode::Insert);
cx.assert("The q|uick", "The quick|");
cx.assert("The q|uick ", "The quick |");
cx.assert("|", "|");
@@ -765,7 +765,7 @@ mod test {
#[gpui::test]
async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-^"]);
let mut cx = cx.binding(["^"]);
cx.assert("The q|uick", "|The quick");
cx.assert(" The q|uick", " |The quick");
cx.assert("|", "|");
@@ -792,7 +792,7 @@ mod test {
#[gpui::test]
async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-I"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["shift-i"]).mode_after(Mode::Insert);
cx.assert("The q|uick", "|The quick");
cx.assert(" The q|uick", " |The quick");
cx.assert("|", "|");
@@ -817,7 +817,7 @@ mod test {
#[gpui::test]
async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-D"]);
let mut cx = cx.binding(["shift-d"]);
cx.assert(
indoc! {"
The q|uick
@@ -858,7 +858,7 @@ mod test {
#[gpui::test]
async fn test_delete_left(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-X"]);
let mut cx = cx.binding(["shift-x"]);
cx.assert("Te|st", "T|st");
cx.assert("T|est", "|est");
cx.assert("|Test", "|Test");
@@ -956,7 +956,7 @@ mod test {
#[gpui::test]
async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-O"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["shift-o"]).mode_after(Mode::Insert);
cx.assert(
"|",
+5 -5
View File
@@ -139,7 +139,7 @@ mod test {
test"},
);
let mut cx = cx.binding(["c", "shift-W"]);
let mut cx = cx.binding(["c", "shift-w"]);
cx.assert("Test te|st-test test", "Test te| test");
}
@@ -174,7 +174,7 @@ mod test {
test"},
);
let mut cx = cx.binding(["c", "shift-E"]);
let mut cx = cx.binding(["c", "shift-e"]);
cx.assert("Test te|st-test test", "Test te| test");
}
@@ -204,14 +204,14 @@ mod test {
test"},
);
let mut cx = cx.binding(["c", "shift-B"]);
let mut cx = cx.binding(["c", "shift-b"]);
cx.assert("Test test-test |test", "Test |test");
}
#[gpui::test]
async fn test_change_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "shift-$"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["c", "$"]).mode_after(Mode::Insert);
cx.assert(
indoc! {"
The q|uick
@@ -347,7 +347,7 @@ mod test {
#[gpui::test]
async fn test_change_end_of_document(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["c", "shift-G"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["c", "shift-g"]).mode_after(Mode::Insert);
cx.assert(
indoc! {"
The quick
+5 -5
View File
@@ -109,7 +109,7 @@ mod test {
test"},
);
let mut cx = cx.binding(["d", "shift-W"]);
let mut cx = cx.binding(["d", "shift-w"]);
cx.assert("Test te|st-test test", "Test te|test");
}
@@ -144,7 +144,7 @@ mod test {
test"},
);
let mut cx = cx.binding(["d", "shift-E"]);
let mut cx = cx.binding(["d", "shift-e"]);
cx.assert("Test te|st-test test", "Test te| test");
}
@@ -176,14 +176,14 @@ mod test {
test"},
);
let mut cx = cx.binding(["d", "shift-B"]);
let mut cx = cx.binding(["d", "shift-b"]);
cx.assert("Test test-test |test", "Test |test");
}
#[gpui::test]
async fn test_delete_end_of_line(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "shift-$"]);
let mut cx = cx.binding(["d", "$"]);
cx.assert(
indoc! {"
The q|uick
@@ -304,7 +304,7 @@ mod test {
#[gpui::test]
async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["d", "shift-G"]);
let mut cx = cx.binding(["d", "shift-g"]);
cx.assert(
indoc! {"
The quick
+4 -4
View File
@@ -422,7 +422,7 @@ mod test {
#[gpui::test]
async fn test_visual_line_delete(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-V", "x"]);
let mut cx = cx.binding(["shift-v", "x"]);
cx.assert(
indoc! {"
The qu|ick brown
@@ -457,7 +457,7 @@ mod test {
The quick brown
fox ju|mps over"},
);
let mut cx = cx.binding(["shift-V", "j", "x"]);
let mut cx = cx.binding(["shift-v", "j", "x"]);
cx.assert(
indoc! {"
The qu|ick brown
@@ -558,7 +558,7 @@ mod test {
#[gpui::test]
async fn test_visual_line_change(cx: &mut gpui::TestAppContext) {
let cx = VimTestContext::new(cx, true).await;
let mut cx = cx.binding(["shift-V", "c"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["shift-v", "c"]).mode_after(Mode::Insert);
cx.assert(
indoc! {"
The qu|ick brown
@@ -597,7 +597,7 @@ mod test {
fox jumps over
|"},
);
let mut cx = cx.binding(["shift-V", "j", "c"]).mode_after(Mode::Insert);
let mut cx = cx.binding(["shift-v", "j", "c"]).mode_after(Mode::Insert);
cx.assert(
indoc! {"
The qu|ick brown