diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 5db0fe748ef6885f697ec71c321814c44e1a9fcb..9453607ce997e5d822f12df8abc529f2f91a3ce7 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -206,13 +206,13 @@ "shift-z shift-q": [ "pane::CloseActiveItem", { - "saveBehavior": "dontSave" + "saveIntent": "skip" } ], "shift-z shift-z": [ "pane::CloseActiveItem", { - "saveBehavior": "promptOnConflict" + "saveIntent": "saveAll" } ], // Count support diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 90c155cdf8caba22d945d3b982cd4ec88b339828..90c448137453fa0be95744958972c6dc63d85405 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -297,7 +297,7 @@ impl PickerDelegate for CommandPaletteDelegate { } } -pub fn humanize_action_name(name: &str) -> String { +fn humanize_action_name(name: &str) -> String { let capacity = name.len() + name.chars().filter(|c| c.is_uppercase()).count(); let mut result = String::with_capacity(capacity); for char in name.chars() { diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index c2d8cc52b26f7483a40982cac04a96c4c8d92bb0..64ef31cd307dc8e5fbf82d01c98f3890d761c1c2 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1528,13 +1528,8 @@ mod tests { let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); active_pane .update(cx, |pane, cx| { - pane.close_active_item( - &workspace::CloseActiveItem { - save_behavior: None, - }, - cx, - ) - .unwrap() + pane.close_active_item(&workspace::CloseActiveItem { save_intent: None }, cx) + .unwrap() }) .await .unwrap(); diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 5b15b5274c60bb835c22e58882f6d7eb95910151..0dc1d1eba437fb67beddbde47bd10bc497ed928a 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -33,7 +33,7 @@ use super::{ #[derive(Clone)] pub struct TestAppContext { - pub cx: Rc>, + cx: Rc>, foreground_platform: Rc, condition_duration: Option, pub function_name: String, diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index b79f655f815a71b3985eb26450b2b5edf9837c26..cd939b5604716a1b6c0f523db53acce735cc9ac1 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -284,12 +284,7 @@ impl TerminalView { pub fn deploy_context_menu(&mut self, position: Vector2F, cx: &mut ViewContext) { let menu_entries = vec![ ContextMenuItem::action("Clear", Clear), - ContextMenuItem::action( - "Close", - pane::CloseActiveItem { - save_behavior: None, - }, - ), + ContextMenuItem::action("Close", pane::CloseActiveItem { save_intent: None }), ]; self.context_menu.update(cx, |menu, cx| { diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index 6bed7f5bb4b995df69bfb055b9b7a6acc64847ca..092d72c2fcd19a4f3dcacf78c0f6487015a0a379 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -50,119 +50,119 @@ pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option ( "write", workspace::Save { - save_behavior: Some(SaveIntent::Save), + save_intent: Some(SaveIntent::Save), } .boxed_clone(), ), "w!" | "wr!" | "wri!" | "writ!" | "write!" => ( "write!", workspace::Save { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), "q" | "qu" | "qui" | "quit" => ( "quit", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Close), + save_intent: Some(SaveIntent::Close), } .boxed_clone(), ), "q!" | "qu!" | "qui!" | "quit!" => ( "quit!", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Skip), + save_intent: Some(SaveIntent::Skip), } .boxed_clone(), ), "wq" => ( "wq", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Save), + save_intent: Some(SaveIntent::Save), } .boxed_clone(), ), "wq!" => ( "wq!", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), "x" | "xi" | "xit" | "exi" | "exit" => ( "exit", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Save), + save_intent: Some(SaveIntent::SaveAll), } .boxed_clone(), ), "x!" | "xi!" | "xit!" | "exi!" | "exit!" => ( "exit!", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), "up" | "upd" | "upda" | "updat" | "update" => ( "update", workspace::Save { - save_behavior: Some(SaveIntent::SaveAll), + save_intent: Some(SaveIntent::SaveAll), } .boxed_clone(), ), "wa" | "wal" | "wall" => ( "wall", workspace::SaveAll { - save_behavior: Some(SaveIntent::SaveAll), + save_intent: Some(SaveIntent::SaveAll), } .boxed_clone(), ), "wa!" | "wal!" | "wall!" => ( "wall!", workspace::SaveAll { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), "qa" | "qal" | "qall" | "quita" | "quital" | "quitall" => ( "quitall", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::Close), + save_intent: Some(SaveIntent::Close), } .boxed_clone(), ), "qa!" | "qal!" | "qall!" | "quita!" | "quital!" | "quitall!" => ( "quitall!", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::Skip), + save_intent: Some(SaveIntent::Skip), } .boxed_clone(), ), "xa" | "xal" | "xall" => ( "xall", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::SaveAll), + save_intent: Some(SaveIntent::SaveAll), } .boxed_clone(), ), "xa!" | "xal!" | "xall!" => ( "xall!", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), "wqa" | "wqal" | "wqall" => ( "wqall", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::SaveAll), + save_intent: Some(SaveIntent::SaveAll), } .boxed_clone(), ), "wqa!" | "wqal!" | "wqall!" => ( "wqall!", workspace::CloseAllItemsAndPanes { - save_behavior: Some(SaveIntent::Overwrite), + save_intent: Some(SaveIntent::Overwrite), } .boxed_clone(), ), @@ -197,7 +197,7 @@ pub fn command_interceptor(mut query: &str, _: &AppContext) -> Option ( "tabclose", workspace::CloseActiveItem { - save_behavior: Some(SaveIntent::Close), + save_intent: Some(SaveIntent::Close), } .boxed_clone(), ), diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index e76da1dfc5dd7dc1d7803c6fe0127a85fb089238..f74625c8b30586ae6a05a637134f9e4c3f1f4191 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -220,53 +220,58 @@ fn replace_command( let replacement = parse_replace_all(&action.query); let pane = workspace.active_pane().clone(); pane.update(cx, |pane, cx| { - if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { - let search = search_bar.update(cx, |search_bar, cx| { - if !search_bar.show(cx) { - return None; - } + let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() else { + return; + }; + let search = search_bar.update(cx, |search_bar, cx| { + if !search_bar.show(cx) { + return None; + } - let mut options = SearchOptions::default(); - if replacement.is_case_sensitive { - options.set(SearchOptions::CASE_SENSITIVE, true) - } - let search = if replacement.search == "" { - search_bar.query(cx) - } else { - replacement.search - }; + let mut options = SearchOptions::default(); + if replacement.is_case_sensitive { + options.set(SearchOptions::CASE_SENSITIVE, true) + } + let search = if replacement.search == "" { + search_bar.query(cx) + } else { + replacement.search + }; - search_bar.set_replacement(Some(&replacement.replacement), cx); - search_bar.activate_search_mode(SearchMode::Regex, cx); - Some(search_bar.search(&search, Some(options), cx)) - }); - let Some(search) = search else { return }; - let search_bar = search_bar.downgrade(); - cx.spawn(|_, mut cx| async move { - search.await?; - search_bar.update(&mut cx, |search_bar, cx| { - if replacement.should_replace_all { - search_bar.select_last_match(cx); - search_bar.replace_all(&Default::default(), cx); - Vim::update(cx, |vim, cx| { - move_cursor( - vim, - Motion::StartOfLine { - display_lines: false, - }, - None, - cx, - ) - }) - } - })?; - anyhow::Ok(()) - }) - .detach_and_log_err(cx); - } + search_bar.set_replacement(Some(&replacement.replacement), cx); + search_bar.activate_search_mode(SearchMode::Regex, cx); + Some(search_bar.search(&search, Some(options), cx)) + }); + let Some(search) = search else { return }; + let search_bar = search_bar.downgrade(); + cx.spawn(|_, mut cx| async move { + search.await?; + search_bar.update(&mut cx, |search_bar, cx| { + if replacement.should_replace_all { + search_bar.select_last_match(cx); + search_bar.replace_all(&Default::default(), cx); + Vim::update(cx, |vim, cx| { + move_cursor( + vim, + Motion::StartOfLine { + display_lines: false, + }, + None, + cx, + ) + }) + } + })?; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); }) } +// convert a vim query into something more usable by zed. +// we don't attempt to fully convert between the two regex syntaxes, +// but we do flip \( and \) to ( and ) (and vice-versa) in the pattern, +// and convert \0..\9 to $0..$9 in the replacement so that common idioms work. fn parse_replace_all(query: &str) -> Replacement { let mut chars = query.chars(); if Some('%') != chars.next() || Some('s') != chars.next() { @@ -284,17 +289,18 @@ fn parse_replace_all(query: &str) -> Replacement { let mut buffer = &mut search; let mut escaped = false; + // 0 - parsing search + // 1 - parsing replacement + // 2 - parsing flags let mut phase = 0; for c in chars { if escaped { escaped = false; if phase == 1 && c.is_digit(10) { - // help vim users discover zed regex syntax - // (though we don't try and fix arbitrary patterns for them) buffer.push('$') + // unescape escaped parens } else if phase == 0 && c == '(' || c == ')' { - // un-escape parens } else if c != delimeter { buffer.push('\\') } @@ -312,6 +318,10 @@ fn parse_replace_all(query: &str) -> Replacement { break; } } else { + // escape unescaped parens + if phase == 0 && c == '(' || c == ')' { + buffer.push('\\') + } buffer.push(c) } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 40e6d36f3bea4419d69be1b6e7dd31b6435cabcd..fbe018409b4008d3675146c44c3cc9f3c81b3784 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -86,13 +86,13 @@ pub struct CloseItemsToTheRightById { #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseActiveItem { - pub save_behavior: Option, + pub save_intent: Option, } #[derive(Clone, PartialEq, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CloseAllItems { - pub save_behavior: Option, + pub save_intent: Option, } actions!( @@ -734,7 +734,7 @@ impl Pane { let active_item_id = self.items[self.active_item_index].id(); Some(self.close_item_by_id( active_item_id, - action.save_behavior.unwrap_or(SaveIntent::Close), + action.save_intent.unwrap_or(SaveIntent::Close), cx, )) } @@ -742,12 +742,10 @@ impl Pane { pub fn close_item_by_id( &mut self, item_id_to_close: usize, - save_behavior: SaveIntent, + save_intent: SaveIntent, cx: &mut ViewContext, ) -> Task> { - self.close_items(cx, save_behavior, move |view_id| { - view_id == item_id_to_close - }) + self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close) } pub fn close_inactive_items( @@ -844,17 +842,17 @@ impl Pane { return None; } - Some(self.close_items( - cx, - action.save_behavior.unwrap_or(SaveIntent::Close), - |_| true, - )) + Some( + self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { + true + }), + ) } pub fn close_items( &mut self, cx: &mut ViewContext, - save_behavior: SaveIntent, + save_intent: SaveIntent, should_close: impl 'static + Fn(usize) -> bool, ) -> Task> { // Find the items to close. @@ -912,7 +910,7 @@ impl Pane { &pane, item_ix, &*item, - save_behavior, + save_intent, &mut cx, ) .await? @@ -1010,14 +1008,14 @@ impl Pane { pane: &WeakViewHandle, item_ix: usize, item: &dyn ItemHandle, - save_behavior: SaveIntent, + save_intent: SaveIntent, cx: &mut AsyncAppContext, ) -> Result { const CONFLICT_MESSAGE: &str = "This file has changed on disk since you started editing it. Do you want to overwrite it?"; const DIRTY_MESSAGE: &str = "This file contains unsaved edits. Do you want to save it?"; - if save_behavior == SaveIntent::Skip { + if save_intent == SaveIntent::Skip { return Ok(true); } @@ -1031,17 +1029,17 @@ impl Pane { }); // when saving a single buffer, we ignore whether or not it's dirty. - if save_behavior == SaveIntent::Save { + if save_intent == SaveIntent::Save { is_dirty = true; } - if save_behavior == SaveIntent::SaveAs { + if save_intent == SaveIntent::SaveAs { is_dirty = true; has_conflict = false; can_save = false; } - if save_behavior == SaveIntent::Overwrite { + if save_intent == SaveIntent::Overwrite { has_conflict = false; } @@ -1060,7 +1058,7 @@ impl Pane { _ => return Ok(false), } } else if is_dirty && (can_save || can_save_as) { - if save_behavior == SaveIntent::Close { + if save_intent == SaveIntent::Close { let will_autosave = cx.read(|cx| { matches!( settings::get::(cx).autosave, @@ -1188,9 +1186,7 @@ impl Pane { vec![ ContextMenuItem::action( "Close Active Item", - CloseActiveItem { - save_behavior: None, - }, + CloseActiveItem { save_intent: None }, ), ContextMenuItem::action("Close Inactive Items", CloseInactiveItems), ContextMenuItem::action("Close Clean Items", CloseCleanItems), @@ -1198,9 +1194,7 @@ impl Pane { ContextMenuItem::action("Close Items To The Right", CloseItemsToTheRight), ContextMenuItem::action( "Close All Items", - CloseAllItems { - save_behavior: None, - }, + CloseAllItems { save_intent: None }, ), ] } else { @@ -1247,9 +1241,7 @@ impl Pane { }), ContextMenuItem::action( "Close All Items", - CloseAllItems { - save_behavior: None, - }, + CloseAllItems { save_intent: None }, ), ] }, @@ -2182,12 +2174,7 @@ mod tests { pane.update(cx, |pane, cx| { assert!(pane - .close_active_item( - &CloseActiveItem { - save_behavior: None - }, - cx - ) + .close_active_item(&CloseActiveItem { save_intent: None }, cx) .is_none()) }); } @@ -2439,12 +2426,7 @@ mod tests { assert_item_labels(&pane, ["A", "B", "1*", "C", "D"], cx); pane.update(cx, |pane, cx| { - pane.close_active_item( - &CloseActiveItem { - save_behavior: None, - }, - cx, - ) + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) }) .unwrap() .await @@ -2455,12 +2437,7 @@ mod tests { assert_item_labels(&pane, ["A", "B", "C", "D*"], cx); pane.update(cx, |pane, cx| { - pane.close_active_item( - &CloseActiveItem { - save_behavior: None, - }, - cx, - ) + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) }) .unwrap() .await @@ -2468,12 +2445,7 @@ mod tests { assert_item_labels(&pane, ["A", "B*", "C"], cx); pane.update(cx, |pane, cx| { - pane.close_active_item( - &CloseActiveItem { - save_behavior: None, - }, - cx, - ) + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) }) .unwrap() .await @@ -2481,12 +2453,7 @@ mod tests { assert_item_labels(&pane, ["A", "C*"], cx); pane.update(cx, |pane, cx| { - pane.close_active_item( - &CloseActiveItem { - save_behavior: None, - }, - cx, - ) + pane.close_active_item(&CloseActiveItem { save_intent: None }, cx) }) .unwrap() .await @@ -2597,12 +2564,7 @@ mod tests { assert_item_labels(&pane, ["A", "B", "C*"], cx); pane.update(cx, |pane, cx| { - pane.close_all_items( - &CloseAllItems { - save_behavior: None, - }, - cx, - ) + pane.close_all_items(&CloseAllItems { save_intent: None }, cx) }) .unwrap() .await diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a853691b7644028e570e7b7b78176d71d2f1aca2..092286973e867007cedbb0b37c94030a6e906fe6 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -163,19 +163,19 @@ pub struct NewFileInDirection(pub SplitDirection); #[derive(Clone, PartialEq, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SaveAll { - pub save_behavior: Option, + pub save_intent: Option, } #[derive(Clone, PartialEq, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Save { - pub save_behavior: Option, + pub save_intent: Option, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseAllItemsAndPanes { - pub save_behavior: Option, + pub save_intent: Option, } #[derive(Deserialize)] @@ -294,7 +294,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { cx.add_action( |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { workspace - .save_active_item(action.save_behavior.unwrap_or(SaveIntent::Save), cx) + .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) .detach_and_log_err(cx); }, ); @@ -1363,7 +1363,7 @@ impl Workspace { cx: &mut ViewContext, ) -> Option>> { let save_all = - self.save_all_internal(action.save_behavior.unwrap_or(SaveIntent::SaveAll), cx); + self.save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx); Some(cx.foreground().spawn(async move { save_all.await?; Ok(()) @@ -1372,7 +1372,7 @@ impl Workspace { fn save_all_internal( &mut self, - save_behaviour: SaveIntent, + save_intent: SaveIntent, cx: &mut ViewContext, ) -> Task> { if self.project.read(cx).is_read_only() { @@ -1407,7 +1407,7 @@ impl Workspace { &pane, ix, &*item, - save_behaviour, + save_intent, &mut cx, ) .await? @@ -1679,7 +1679,7 @@ impl Workspace { pub fn save_active_item( &mut self, - save_behavior: SaveIntent, + save_intent: SaveIntent, cx: &mut ViewContext, ) -> Task> { let project = self.project.clone(); @@ -1690,16 +1690,9 @@ impl Workspace { cx.spawn(|_, mut cx| async move { if let Some(item) = item { - Pane::save_item( - project, - &pane, - item_ix, - item.as_ref(), - save_behavior, - &mut cx, - ) - .await - .map(|_| ()) + Pane::save_item(project, &pane, item_ix, item.as_ref(), save_intent, &mut cx) + .await + .map(|_| ()) } else { Ok(()) } @@ -1719,13 +1712,13 @@ impl Workspace { action: &CloseAllItemsAndPanes, cx: &mut ViewContext, ) -> Option>> { - self.close_all_internal(false, action.save_behavior.unwrap_or(SaveIntent::Close), cx) + self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) } fn close_all_internal( &mut self, retain_active_pane: bool, - save_behavior: SaveIntent, + save_intent: SaveIntent, cx: &mut ViewContext, ) -> Option>> { let current_pane = self.active_pane(); @@ -1748,7 +1741,7 @@ impl Workspace { if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| { pane.close_all_items( &CloseAllItems { - save_behavior: Some(save_behavior), + save_intent: Some(save_intent), }, cx, ) diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index acffbc29abbe1bf4486548e859a397bc7fb3a62d..4e01693dbf6980c10d99c2fc727eeb1ad642b31b 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -38,24 +38,12 @@ pub fn menus() -> Vec> { MenuItem::action("Open Recent...", recent_projects::OpenRecent), MenuItem::separator(), MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject), - MenuItem::action( - "Save", - workspace::Save { - save_behavior: None, - }, - ), + MenuItem::action("Save", workspace::Save { save_intent: None }), MenuItem::action("Save As…", workspace::SaveAs), - MenuItem::action( - "Save All", - workspace::SaveAll { - save_behavior: None, - }, - ), + MenuItem::action("Save All", workspace::SaveAll { save_intent: None }), MenuItem::action( "Close Editor", - workspace::CloseActiveItem { - save_behavior: None, - }, + workspace::CloseActiveItem { save_intent: None }, ), MenuItem::action("Close Window", workspace::CloseWindow), ], diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 5363262e3f610d5a5bd073132ee26df29feb5dae..11e80ffb4a5917f1c1b4f814a48140987ee90633 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1318,6 +1318,7 @@ mod tests { let save_task = workspace.update(cx, |workspace, cx| { workspace.save_active_item(SaveIntent::Save, cx) }); + cx.foreground().run_until_parked(); window.simulate_prompt_answer(0, cx); save_task.await.unwrap(); editor.read_with(cx, |editor, cx| { @@ -1522,9 +1523,7 @@ mod tests { }); cx.dispatch_action( window.into(), - workspace::CloseActiveItem { - save_behavior: None, - }, + workspace::CloseActiveItem { save_intent: None }, ); cx.foreground().run_until_parked(); @@ -1535,9 +1534,7 @@ mod tests { cx.dispatch_action( window.into(), - workspace::CloseActiveItem { - save_behavior: None, - }, + workspace::CloseActiveItem { save_intent: None }, ); cx.foreground().run_until_parked(); window.simulate_prompt_answer(1, cx);