Add remaining tests

co-authored-by: Conrad <conrad@zed.dev>
This commit is contained in:
Mikayla 2024-01-10 19:37:53 -08:00
parent a5ca58354d
commit 38396d4281
No known key found for this signature in database
4 changed files with 362 additions and 516 deletions

View file

@ -525,43 +525,4 @@ impl Render for FeedbackModal {
} }
} }
// TODO: Testing of various button states, dismissal prompts, etc. // TODO: Testing of various button states, dismissal prompts, etc. :)
// #[cfg(test)]
// mod test {
// use super::*;
// #[test]
// fn test_invalid_email_addresses() {
// let markdown = markdown.await.log_err();
// let buffer = project.update(&mut cx, |project, cx| {
// project.create_buffer("", markdown, cx)
// })??;
// workspace.update(&mut cx, |workspace, cx| {
// let system_specs = SystemSpecs::new(cx);
// workspace.toggle_modal(cx, move |cx| {
// let feedback_modal = FeedbackModal::new(system_specs, project, buffer, cx);
// assert!(!feedback_modal.can_submit());
// assert!(!feedback_modal.valid_email_address(cx));
// assert!(!feedback_modal.valid_character_count());
// feedback_modal
// .email_address_editor
// .update(cx, |this, cx| this.set_text("a", cx));
// feedback_modal.set_submission_state(cx);
// assert!(!feedback_modal.valid_email_address(cx));
// feedback_modal
// .email_address_editor
// .update(cx, |this, cx| this.set_text("a&b.com", cx));
// feedback_modal.set_submission_state(cx);
// assert!(feedback_modal.valid_email_address(cx));
// });
// })?;
// }
// }

View file

@ -170,7 +170,7 @@ impl ActionRegistry {
macro_rules! actions { macro_rules! actions {
($namespace:path, [ $($name:ident),* $(,)? ]) => { ($namespace:path, [ $($name:ident),* $(,)? ]) => {
$( $(
#[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::private::serde_derive::Deserialize)] #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, gpui::private::serde_derive::Deserialize)]
#[serde(crate = "gpui::private::serde")] #[serde(crate = "gpui::private::serde")]
pub struct $name; pub struct $name;

View file

@ -28,11 +28,11 @@ impl KeystrokeMatcher {
/// Pushes a keystroke onto the matcher. /// Pushes a keystroke onto the matcher.
/// The result of the new keystroke is returned: /// The result of the new keystroke is returned:
/// KeyMatch::None => /// - KeyMatch::None =>
/// No match is valid for this key given any pending keystrokes. /// No match is valid for this key given any pending keystrokes.
/// KeyMatch::Pending => /// - KeyMatch::Pending =>
/// There exist bindings which are still waiting for more keys. /// There exist bindings which are still waiting for more keys.
/// KeyMatch::Complete(matches) => /// - KeyMatch::Complete(matches) =>
/// One or more bindings have received the necessary key presses. /// One or more bindings have received the necessary key presses.
/// Bindings added later will take precedence over earlier bindings. /// Bindings added later will take precedence over earlier bindings.
pub fn match_keystroke( pub fn match_keystroke(
@ -77,12 +77,10 @@ impl KeystrokeMatcher {
if let Some(pending_key) = pending_key { if let Some(pending_key) = pending_key {
self.pending_keystrokes.push(pending_key); self.pending_keystrokes.push(pending_key);
}
if self.pending_keystrokes.is_empty() {
KeyMatch::None
} else {
KeyMatch::Pending KeyMatch::Pending
} else {
self.pending_keystrokes.clear();
KeyMatch::None
} }
} }
} }
@ -98,367 +96,374 @@ impl KeyMatch {
pub fn is_some(&self) -> bool { pub fn is_some(&self) -> bool {
matches!(self, KeyMatch::Some(_)) matches!(self, KeyMatch::Some(_))
} }
pub fn matches(self) -> Option<Vec<Box<dyn Action>>> {
match self {
KeyMatch::Some(matches) => Some(matches),
_ => None,
}
}
} }
// #[cfg(test)] impl PartialEq for KeyMatch {
// mod tests { fn eq(&self, other: &Self) -> bool {
// use anyhow::Result; match (self, other) {
// use serde::Deserialize; (KeyMatch::None, KeyMatch::None) => true,
(KeyMatch::Pending, KeyMatch::Pending) => true,
(KeyMatch::Some(a), KeyMatch::Some(b)) => {
if a.len() != b.len() {
return false;
}
// use crate::{actions, impl_actions, keymap_matcher::ActionContext}; for (a, b) in a.iter().zip(b.iter()) {
if !a.partial_eq(b.as_ref()) {
return false;
}
}
// use super::*; true
}
_ => false,
}
}
}
// #[test] #[cfg(test)]
// fn test_keymap_and_view_ordering() -> Result<()> { mod tests {
// actions!(test, [EditorAction, ProjectPanelAction]);
// let mut editor = ActionContext::default(); use serde_derive::Deserialize;
// editor.add_identifier("Editor");
// let mut project_panel = ActionContext::default(); use super::*;
// project_panel.add_identifier("ProjectPanel"); use crate::{self as gpui, KeyBindingContextPredicate, Modifiers};
use crate::{actions, KeyBinding};
// // Editor 'deeper' in than project panel #[test]
// let dispatch_path = vec![(2, editor), (1, project_panel)]; fn test_keymap_and_view_ordering() {
actions!(test, [EditorAction, ProjectPanelAction]);
// // But editor actions 'higher' up in keymap let mut editor = KeyContext::default();
// let keymap = Keymap::new(vec![ editor.add("Editor");
// Binding::new("left", EditorAction, Some("Editor")),
// Binding::new("left", ProjectPanelAction, Some("ProjectPanel")),
// ]);
// let mut matcher = KeymapMatcher::new(keymap); let mut project_panel = KeyContext::default();
project_panel.add("ProjectPanel");
// assert_eq!( // Editor 'deeper' in than project panel
// matcher.match_keystroke(Keystroke::parse("left")?, dispatch_path.clone()), let dispatch_path = vec![project_panel, editor];
// KeyMatch::Matches(vec![
// (2, Box::new(EditorAction)),
// (1, Box::new(ProjectPanelAction)),
// ]),
// );
// Ok(()) // But editor actions 'higher' up in keymap
// } let keymap = Keymap::new(vec![
KeyBinding::new("left", EditorAction, Some("Editor")),
KeyBinding::new("left", ProjectPanelAction, Some("ProjectPanel")),
]);
// #[test] let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
// fn test_push_keystroke() -> Result<()> {
// actions!(test, [B, AB, C, D, DA, E, EF]);
// let mut context1 = ActionContext::default(); let matches = matcher
// context1.add_identifier("1"); .match_keystroke(&Keystroke::parse("left").unwrap(), &dispatch_path)
.matches()
.unwrap();
// let mut context2 = ActionContext::default(); assert!(matches[0].partial_eq(&EditorAction));
// context2.add_identifier("2"); assert!(matches.get(1).is_none());
}
// let dispatch_path = vec![(2, context2), (1, context1)]; #[test]
fn test_multi_keystroke_match() {
actions!(test, [B, AB, C, D, DA, E, EF]);
// let keymap = Keymap::new(vec![ let mut context1 = KeyContext::default();
// Binding::new("a b", AB, Some("1")), context1.add("1");
// Binding::new("b", B, Some("2")),
// Binding::new("c", C, Some("2")),
// Binding::new("d", D, Some("1")),
// Binding::new("d", D, Some("2")),
// Binding::new("d a", DA, Some("2")),
// ]);
// let mut matcher = KeymapMatcher::new(keymap); let mut context2 = KeyContext::default();
context2.add("2");
// // Binding with pending prefix always takes precedence let dispatch_path = vec![context2, context1];
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
// KeyMatch::Pending,
// );
// // B alone doesn't match because a was pending, so AB is returned instead
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("b")?, dispatch_path.clone()),
// KeyMatch::Matches(vec![(1, Box::new(AB))]),
// );
// assert!(!matcher.has_pending_keystrokes());
// // Without an a prefix, B is dispatched like expected let keymap = Keymap::new(vec![
// assert_eq!( KeyBinding::new("a b", AB, Some("1")),
// matcher.match_keystroke(Keystroke::parse("b")?, dispatch_path.clone()), KeyBinding::new("b", B, Some("2")),
// KeyMatch::Matches(vec![(2, Box::new(B))]), KeyBinding::new("c", C, Some("2")),
// ); KeyBinding::new("d", D, Some("1")),
// assert!(!matcher.has_pending_keystrokes()); KeyBinding::new("d", D, Some("2")),
KeyBinding::new("d a", DA, Some("2")),
]);
// // If a is prefixed, C will not be dispatched because there let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
// // was a pending binding for it
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()),
// KeyMatch::Pending,
// );
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("c")?, dispatch_path.clone()),
// KeyMatch::None,
// );
// assert!(!matcher.has_pending_keystrokes());
// // If a single keystroke matches multiple bindings in the tree // Binding with pending prefix always takes precedence
// // all of them are returned so that we can fallback if the action assert_eq!(
// // handler decides to propagate the action matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &dispatch_path),
// assert_eq!( KeyMatch::Pending,
// matcher.match_keystroke(Keystroke::parse("d")?, dispatch_path.clone()), );
// KeyMatch::Matches(vec![(2, Box::new(D)), (1, Box::new(D))]), // B alone doesn't match because a was pending, so AB is returned instead
// ); assert_eq!(
matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &dispatch_path),
KeyMatch::Some(vec![Box::new(AB)]),
);
assert!(!matcher.has_pending_keystrokes());
// // If none of the d action handlers consume the binding, a pending // Without an a prefix, B is dispatched like expected
// // binding may then be used assert_eq!(
// assert_eq!( matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &dispatch_path[0..1]),
// matcher.match_keystroke(Keystroke::parse("a")?, dispatch_path.clone()), KeyMatch::Some(vec![Box::new(B)]),
// KeyMatch::Matches(vec![(2, Box::new(DA))]), );
// ); assert!(!matcher.has_pending_keystrokes());
// assert!(!matcher.has_pending_keystrokes());
// Ok(()) eprintln!("PROBLEM AREA");
// } // If a is prefixed, C will not be dispatched because there
// was a pending binding for it
assert_eq!(
matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &dispatch_path),
KeyMatch::Pending,
);
assert_eq!(
matcher.match_keystroke(&Keystroke::parse("c").unwrap(), &dispatch_path),
KeyMatch::None,
);
assert!(!matcher.has_pending_keystrokes());
// #[test] // If a single keystroke matches multiple bindings in the tree
// fn test_keystroke_parsing() -> Result<()> { // only one of them is returned.
// assert_eq!( assert_eq!(
// Keystroke::parse("ctrl-p")?, matcher.match_keystroke(&Keystroke::parse("d").unwrap(), &dispatch_path),
// Keystroke { KeyMatch::Some(vec![Box::new(D)]),
// key: "p".into(), );
// ctrl: true, }
// alt: false,
// shift: false,
// cmd: false,
// function: false,
// ime_key: None,
// }
// );
// assert_eq!( #[test]
// Keystroke::parse("alt-shift-down")?, fn test_keystroke_parsing() {
// Keystroke { assert_eq!(
// key: "down".into(), Keystroke::parse("ctrl-p").unwrap(),
// ctrl: false, Keystroke {
// alt: true, key: "p".into(),
// shift: true, modifiers: Modifiers {
// cmd: false, control: true,
// function: false, alt: false,
// ime_key: None, shift: false,
// } command: false,
// ); function: false,
},
ime_key: None,
}
);
// assert_eq!( assert_eq!(
// Keystroke::parse("shift-cmd--")?, Keystroke::parse("alt-shift-down").unwrap(),
// Keystroke { Keystroke {
// key: "-".into(), key: "down".into(),
// ctrl: false, modifiers: Modifiers {
// alt: false, control: false,
// shift: true, alt: true,
// cmd: true, shift: true,
// function: false, command: false,
// ime_key: None, function: false,
// } },
// ); ime_key: None,
}
);
// Ok(()) assert_eq!(
// } Keystroke::parse("shift-cmd--").unwrap(),
Keystroke {
key: "-".into(),
modifiers: Modifiers {
control: false,
alt: false,
shift: true,
command: true,
function: false,
},
ime_key: None,
}
);
}
// #[test] #[test]
// fn test_context_predicate_parsing() -> Result<()> { fn test_context_predicate_parsing() {
// use KeymapContextPredicate::*; use KeyBindingContextPredicate::*;
// assert_eq!( assert_eq!(
// KeymapContextPredicate::parse("a && (b == c || d != e)")?, KeyBindingContextPredicate::parse("a && (b == c || d != e)").unwrap(),
// And( And(
// Box::new(Identifier("a".into())), Box::new(Identifier("a".into())),
// Box::new(Or( Box::new(Or(
// Box::new(Equal("b".into(), "c".into())), Box::new(Equal("b".into(), "c".into())),
// Box::new(NotEqual("d".into(), "e".into())), Box::new(NotEqual("d".into(), "e".into())),
// )) ))
// ) )
// ); );
// assert_eq!( assert_eq!(
// KeymapContextPredicate::parse("!a")?, KeyBindingContextPredicate::parse("!a").unwrap(),
// Not(Box::new(Identifier("a".into())),) Not(Box::new(Identifier("a".into())),)
// ); );
}
// Ok(()) #[test]
// } fn test_context_predicate_eval() {
let predicate = KeyBindingContextPredicate::parse("a && b || c == d").unwrap();
// #[test] let mut context = KeyContext::default();
// fn test_context_predicate_eval() { context.add("a");
// let predicate = KeymapContextPredicate::parse("a && b || c == d").unwrap(); assert!(!predicate.eval(&[context]));
// let mut context = ActionContext::default(); let mut context = KeyContext::default();
// context.add_identifier("a"); context.add("a");
// assert!(!predicate.eval(&[context])); context.add("b");
assert!(predicate.eval(&[context]));
// let mut context = ActionContext::default(); let mut context = KeyContext::default();
// context.add_identifier("a"); context.add("a");
// context.add_identifier("b"); context.set("c", "x");
// assert!(predicate.eval(&[context])); assert!(!predicate.eval(&[context]));
// let mut context = ActionContext::default(); let mut context = KeyContext::default();
// context.add_identifier("a"); context.add("a");
// context.add_key("c", "x"); context.set("c", "d");
// assert!(!predicate.eval(&[context])); assert!(predicate.eval(&[context]));
// let mut context = ActionContext::default(); let predicate = KeyBindingContextPredicate::parse("!a").unwrap();
// context.add_identifier("a"); assert!(predicate.eval(&[KeyContext::default()]));
// context.add_key("c", "d"); }
// assert!(predicate.eval(&[context]));
// let predicate = KeymapContextPredicate::parse("!a").unwrap(); #[test]
// assert!(predicate.eval(&[ActionContext::default()])); fn test_context_child_predicate_eval() {
// } let predicate = KeyBindingContextPredicate::parse("a && b > c").unwrap();
let contexts = [
context_set(&["e", "f"]),
context_set(&["c", "d"]), // match this context
context_set(&["a", "b"]),
];
// #[test] assert!(!predicate.eval(&contexts[0..]));
// fn test_context_child_predicate_eval() { assert!(predicate.eval(&contexts[1..]));
// let predicate = KeymapContextPredicate::parse("a && b > c").unwrap(); assert!(!predicate.eval(&contexts[2..]));
// let contexts = [
// context_set(&["e", "f"]),
// context_set(&["c", "d"]), // match this context
// context_set(&["a", "b"]),
// ];
// assert!(!predicate.eval(&contexts[0..])); let predicate = KeyBindingContextPredicate::parse("a && b > c && !d > e").unwrap();
// assert!(predicate.eval(&contexts[1..])); let contexts = [
// assert!(!predicate.eval(&contexts[2..])); context_set(&["f"]),
context_set(&["e"]), // only match this context
context_set(&["c"]),
context_set(&["a", "b"]),
context_set(&["e"]),
context_set(&["c", "d"]),
context_set(&["a", "b"]),
];
// let predicate = KeymapContextPredicate::parse("a && b > c && !d > e").unwrap(); assert!(!predicate.eval(&contexts[0..]));
// let contexts = [ assert!(predicate.eval(&contexts[1..]));
// context_set(&["f"]), assert!(!predicate.eval(&contexts[2..]));
// context_set(&["e"]), // only match this context assert!(!predicate.eval(&contexts[3..]));
// context_set(&["c"]), assert!(!predicate.eval(&contexts[4..]));
// context_set(&["a", "b"]), assert!(!predicate.eval(&contexts[5..]));
// context_set(&["e"]), assert!(!predicate.eval(&contexts[6..]));
// context_set(&["c", "d"]),
// context_set(&["a", "b"]),
// ];
// assert!(!predicate.eval(&contexts[0..])); fn context_set(names: &[&str]) -> KeyContext {
// assert!(predicate.eval(&contexts[1..])); let mut keymap = KeyContext::default();
// assert!(!predicate.eval(&contexts[2..])); names.iter().for_each(|name| keymap.add(name.to_string()));
// assert!(!predicate.eval(&contexts[3..])); keymap
// assert!(!predicate.eval(&contexts[4..])); }
// assert!(!predicate.eval(&contexts[5..])); }
// assert!(!predicate.eval(&contexts[6..]));
// fn context_set(names: &[&str]) -> ActionContext { #[test]
// let mut keymap = ActionContext::new(); fn test_matcher() {
// names #[derive(Clone, Deserialize, PartialEq, Eq, Debug)]
// .iter() pub struct A(pub String);
// .for_each(|name| keymap.add_identifier(name.to_string())); impl_actions!(test, [A]);
// keymap actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]);
// }
// }
// #[test] #[derive(Clone, Debug, Eq, PartialEq)]
// fn test_matcher() -> Result<()> { struct ActionArg {
// #[derive(Clone, Deserialize, PartialEq, Eq, Debug)] a: &'static str,
// pub struct A(pub String); }
// impl_actions!(test, [A]);
// actions!(test, [B, Ab, Dollar, Quote, Ess, Backtick]);
// #[derive(Clone, Debug, Eq, PartialEq)] let keymap = Keymap::new(vec![
// struct ActionArg { KeyBinding::new("a", A("x".to_string()), Some("a")),
// a: &'static str, KeyBinding::new("b", B, Some("a")),
// } KeyBinding::new("a b", Ab, Some("a || b")),
KeyBinding::new("$", Dollar, Some("a")),
KeyBinding::new("\"", Quote, Some("a")),
KeyBinding::new("alt-s", Ess, Some("a")),
KeyBinding::new("ctrl-`", Backtick, Some("a")),
]);
// let keymap = Keymap::new(vec![ let mut context_a = KeyContext::default();
// Binding::new("a", A("x".to_string()), Some("a")), context_a.add("a");
// Binding::new("b", B, Some("a")),
// Binding::new("a b", Ab, Some("a || b")),
// Binding::new("$", Dollar, Some("a")),
// Binding::new("\"", Quote, Some("a")),
// Binding::new("alt-s", Ess, Some("a")),
// Binding::new("ctrl-`", Backtick, Some("a")),
// ]);
// let mut context_a = ActionContext::default(); let mut context_b = KeyContext::default();
// context_a.add_identifier("a"); context_b.add("b");
// let mut context_b = ActionContext::default(); let mut matcher = KeystrokeMatcher::new(Arc::new(Mutex::new(keymap)));
// context_b.add_identifier("b");
// let mut matcher = KeymapMatcher::new(keymap); // Basic match
assert_eq!(
matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_a.clone()]),
KeyMatch::Some(vec![Box::new(A("x".to_string()))])
);
matcher.clear_pending();
// // Basic match // Multi-keystroke match
// assert_eq!( assert_eq!(
// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]), matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_b.clone()]),
// KeyMatch::Matches(vec![(1, Box::new(A("x".to_string())))]) KeyMatch::Pending
// ); );
// matcher.clear_pending(); assert_eq!(
matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &[context_b.clone()]),
KeyMatch::Some(vec![Box::new(Ab)])
);
matcher.clear_pending();
// // Multi-keystroke match // Failed matches don't interfere with matching subsequent keys
// assert_eq!( assert_eq!(
// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]), matcher.match_keystroke(&Keystroke::parse("x").unwrap(), &[context_a.clone()]),
// KeyMatch::Pending KeyMatch::None
// ); );
// assert_eq!( assert_eq!(
// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]), matcher.match_keystroke(&Keystroke::parse("a").unwrap(), &[context_a.clone()]),
// KeyMatch::Matches(vec![(1, Box::new(Ab))]) KeyMatch::Some(vec![Box::new(A("x".to_string()))])
// ); );
// matcher.clear_pending(); matcher.clear_pending();
// // Failed matches don't interfere with matching subsequent keys let mut context_c = KeyContext::default();
// assert_eq!( context_c.add("c");
// matcher.match_keystroke(Keystroke::parse("x")?, vec![(1, context_a.clone())]),
// KeyMatch::None
// );
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_a.clone())]),
// KeyMatch::Matches(vec![(1, Box::new(A("x".to_string())))])
// );
// matcher.clear_pending();
// // Pending keystrokes are cleared when the context changes assert_eq!(
// assert_eq!( matcher.match_keystroke(
// matcher.match_keystroke(Keystroke::parse("a")?, vec![(1, context_b.clone())]), &Keystroke::parse("a").unwrap(),
// KeyMatch::Pending &[context_c.clone(), context_b.clone()]
// ); ),
// assert_eq!( KeyMatch::Pending
// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_a.clone())]), );
// KeyMatch::None assert_eq!(
// ); matcher.match_keystroke(&Keystroke::parse("b").unwrap(), &[context_b.clone()]),
// matcher.clear_pending(); KeyMatch::Some(vec![Box::new(Ab)])
);
// let mut context_c = ActionContext::default(); // handle Czech $ (option + 4 key)
// context_c.add_identifier("c"); assert_eq!(
matcher.match_keystroke(&Keystroke::parse("alt-ç->$").unwrap(), &[context_a.clone()]),
KeyMatch::Some(vec![Box::new(Dollar)])
);
// // Pending keystrokes are maintained per-view // handle Brazillian quote (quote key then space key)
// assert_eq!( assert_eq!(
// matcher.match_keystroke( matcher.match_keystroke(
// Keystroke::parse("a")?, &Keystroke::parse("space->\"").unwrap(),
// vec![(1, context_b.clone()), (2, context_c.clone())] &[context_a.clone()]
// ), ),
// KeyMatch::Pending KeyMatch::Some(vec![Box::new(Quote)])
// ); );
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("b")?, vec![(1, context_b.clone())]),
// KeyMatch::Matches(vec![(1, Box::new(Ab))])
// );
// // handle Czech $ (option + 4 key) // handle ctrl+` on a brazillian keyboard
// assert_eq!( assert_eq!(
// matcher.match_keystroke(Keystroke::parse("alt-ç->$")?, vec![(1, context_a.clone())]), matcher.match_keystroke(&Keystroke::parse("ctrl-->`").unwrap(), &[context_a.clone()]),
// KeyMatch::Matches(vec![(1, Box::new(Dollar))]) KeyMatch::Some(vec![Box::new(Backtick)])
// ); );
// // handle Brazillian quote (quote key then space key) // handle alt-s on a US keyboard
// assert_eq!( assert_eq!(
// matcher.match_keystroke(Keystroke::parse("space->\"")?, vec![(1, context_a.clone())]), matcher.match_keystroke(&Keystroke::parse("alt-s->ß").unwrap(), &[context_a.clone()]),
// KeyMatch::Matches(vec![(1, Box::new(Quote))]) KeyMatch::Some(vec![Box::new(Ess)])
// ); );
}
// // handle ctrl+` on a brazillian keyboard }
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("ctrl-->`")?, vec![(1, context_a.clone())]),
// KeyMatch::Matches(vec![(1, Box::new(Backtick))])
// );
// // handle alt-s on a US keyboard
// assert_eq!(
// matcher.match_keystroke(Keystroke::parse("alt-s->ß")?, vec![(1, context_a.clone())]),
// KeyMatch::Matches(vec![(1, Box::new(Ess))])
// );
// Ok(())
// }
// }

View file

@ -592,169 +592,49 @@ impl From<FontStyle> for FontkitStyle {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use super::*; use crate::{font, px, FontRun, MacTextSystem, PlatformTextSystem};
// use crate::AppContext;
// use font_kit::properties::{Style, Weight};
// use platform::FontSystem as _;
// #[crate::test(self, retries = 5)] #[test]
// fn test_layout_str(_: &mut AppContext) { fn test_wrap_line() {
// // This is failing intermittently on CI and we don't have time to figure it out let fonts = MacTextSystem::new();
// let fonts = FontSystem::new(); let font_id = fonts.font_id(&font("Helvetica")).unwrap();
// let menlo = fonts.load_family("Menlo", &Default::default()).unwrap();
// let menlo_regular = RunStyle {
// font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
// color: Default::default(),
// underline: Default::default(),
// };
// let menlo_italic = RunStyle {
// font_id: fonts
// .select_font(&menlo, Properties::new().style(Style::Italic))
// .unwrap(),
// color: Default::default(),
// underline: Default::default(),
// };
// let menlo_bold = RunStyle {
// font_id: fonts
// .select_font(&menlo, Properties::new().weight(Weight::BOLD))
// .unwrap(),
// color: Default::default(),
// underline: Default::default(),
// };
// assert_ne!(menlo_regular, menlo_italic);
// assert_ne!(menlo_regular, menlo_bold);
// assert_ne!(menlo_italic, menlo_bold);
// let line = fonts.layout_line( let line = "one two three four five\n";
// "hello world", let wrap_boundaries = fonts.wrap_line(line, font_id, px(16.), px(64.0));
// 16.0, assert_eq!(wrap_boundaries, &["one two ".len(), "one two three ".len()]);
// &[(2, menlo_bold), (4, menlo_italic), (5, menlo_regular)],
// );
// assert_eq!(line.runs.len(), 3);
// assert_eq!(line.runs[0].font_id, menlo_bold.font_id);
// assert_eq!(line.runs[0].glyphs.len(), 2);
// assert_eq!(line.runs[1].font_id, menlo_italic.font_id);
// assert_eq!(line.runs[1].glyphs.len(), 4);
// assert_eq!(line.runs[2].font_id, menlo_regular.font_id);
// assert_eq!(line.runs[2].glyphs.len(), 5);
// }
// #[test] let line = "aaa ααα ✋✋✋ 🎉🎉🎉\n";
// fn test_glyph_offsets() -> crate::Result<()> { let wrap_boundaries = fonts.wrap_line(line, font_id, px(16.), px(64.0));
// let fonts = FontSystem::new(); assert_eq!(
// let zapfino = fonts.load_family("Zapfino", &Default::default())?; wrap_boundaries,
// let zapfino_regular = RunStyle { &["aaa ααα ".len(), "aaa ααα ✋✋✋ ".len(),]
// font_id: fonts.select_font(&zapfino, &Properties::new())?, );
// color: Default::default(), }
// underline: Default::default(),
// };
// let menlo = fonts.load_family("Menlo", &Default::default())?;
// let menlo_regular = RunStyle {
// font_id: fonts.select_font(&menlo, &Properties::new())?,
// color: Default::default(),
// underline: Default::default(),
// };
// let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈"; #[test]
// let line = fonts.layout_line( fn test_layout_line_bom_char() {
// text, let fonts = MacTextSystem::new();
// 16.0, let font_id = fonts.font_id(&font("Helvetica")).unwrap();
// &[ let line = "\u{feff}";
// (9, zapfino_regular), let mut style = FontRun {
// (13, menlo_regular), font_id,
// (text.len() - 22, zapfino_regular), len: line.len(),
// ], };
// );
// assert_eq!(
// line.runs
// .iter()
// .flat_map(|r| r.glyphs.iter())
// .map(|g| g.index)
// .collect::<Vec<_>>(),
// vec![0, 2, 4, 5, 7, 8, 9, 10, 14, 15, 16, 17, 21, 22, 23, 24, 26, 27, 28, 29, 36, 37],
// );
// Ok(())
// }
// #[test] let layout = fonts.layout_line(line, px(16.), &[style]);
// #[ignore] assert_eq!(layout.len, line.len());
// fn test_rasterize_glyph() { assert!(layout.runs.is_empty());
// use std::{fs::File, io::BufWriter, path::Path};
// let fonts = FontSystem::new(); let line = "a\u{feff}b";
// let font_ids = fonts.load_family("Fira Code", &Default::default()).unwrap(); style.len = line.len();
// let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap(); let layout = fonts.layout_line(line, px(16.), &[style]);
// let glyph_id = fonts.glyph_for_char(font_id, 'G').unwrap(); assert_eq!(layout.len, line.len());
assert_eq!(layout.runs.len(), 1);
// const VARIANTS: usize = 1; assert_eq!(layout.runs[0].glyphs.len(), 2);
// for i in 0..VARIANTS { assert_eq!(layout.runs[0].glyphs[0].id, 68u32.into()); // a
// let variant = i as f32 / VARIANTS as f32; // There's no glyph for \u{feff}
// let (bounds, bytes) = fonts assert_eq!(layout.runs[0].glyphs[1].id, 69u32.into()); // b
// .rasterize_glyph( }
// font_id, }
// 16.0,
// glyph_id,
// vec2f(variant, variant),
// 2.,
// RasterizationOptions::Alpha,
// )
// .unwrap();
// let name = format!("/Users/as-cii/Desktop/twog-{}.png", i);
// let path = Path::new(&name);
// let file = File::create(path).unwrap();
// let w = &mut BufWriter::new(file);
// let mut encoder = png::Encoder::new(w, bounds.width() as u32, bounds.height() as u32);
// encoder.set_color(png::ColorType::Grayscale);
// encoder.set_depth(png::BitDepth::Eight);
// let mut writer = encoder.write_header().unwrap();
// writer.write_image_data(&bytes).unwrap();
// }
// }
// #[test]
// fn test_wrap_line() {
// let fonts = FontSystem::new();
// let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
// let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
// let line = "one two three four five\n";
// let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
// assert_eq!(wrap_boundaries, &["one two ".len(), "one two three ".len()]);
// let line = "aaa ααα ✋✋✋ 🎉🎉🎉\n";
// let wrap_boundaries = fonts.wrap_line(line, font_id, 16., 64.0);
// assert_eq!(
// wrap_boundaries,
// &["aaa ααα ".len(), "aaa ααα ✋✋✋ ".len(),]
// );
// }
// #[test]
// fn test_layout_line_bom_char() {
// let fonts = FontSystem::new();
// let font_ids = fonts.load_family("Helvetica", &Default::default()).unwrap();
// let style = RunStyle {
// font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
// color: Default::default(),
// underline: Default::default(),
// };
// let line = "\u{feff}";
// let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
// assert_eq!(layout.len, line.len());
// assert!(layout.runs.is_empty());
// let line = "a\u{feff}b";
// let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
// assert_eq!(layout.len, line.len());
// assert_eq!(layout.runs.len(), 1);
// assert_eq!(layout.runs[0].glyphs.len(), 2);
// assert_eq!(layout.runs[0].glyphs[0].id, 68); // a
// // There's no glyph for \u{feff}
// assert_eq!(layout.runs[0].glyphs[1].id, 69); // b
// }
// }