diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 93c0954b1710312b1f3f27b0c43fb25564c6046e..f2988b907c07cddba5c1646178762e8b28a508a5 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -920,6 +920,39 @@ impl AssistantPanel { self.editors.get(self.active_editor_index?) } + fn render_api_key_editor( + &self, + editor: &View, + cx: &mut ViewContext, + ) -> impl IntoElement { + let settings = ThemeSettings::get_global(cx); + let text_style = TextStyle { + color: if editor.read(cx).read_only(cx) { + cx.theme().colors().text_disabled + } else { + cx.theme().colors().text + }, + font_family: settings.ui_font.family.clone(), + font_features: settings.ui_font.features, + font_size: rems(0.875).into(), + font_weight: FontWeight::NORMAL, + font_style: FontStyle::Normal, + line_height: relative(1.3).into(), + background_color: None, + underline: None, + white_space: WhiteSpace::Normal, + }; + EditorElement::new( + &editor, + EditorStyle { + background: cx.theme().colors().editor_background, + local_player: cx.theme().players().local(), + text: text_style, + ..Default::default() + }, + ) + } + fn render_hamburger_button(cx: &mut ViewContext) -> impl IntoElement { IconButton::new("hamburger_button", IconName::Menu) .on_click(cx.listener(|this, _event, cx| { @@ -1091,28 +1124,42 @@ fn build_api_key_editor(cx: &mut ViewContext) -> View { impl Render for AssistantPanel { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { if let Some(api_key_editor) = self.api_key_editor.clone() { + const INSTRUCTIONS: [&'static str; 5] = [ + "To use the assistant panel or inline assistant, you need to add your OpenAI API key.", + " - You can create an API key at: platform.openai.com/api-keys", + " - Having a subscription for another service like GitHub Copilot won't work.", + " ", + "Paste your OpenAI API key and press Enter to use the assistant:" + ]; + v_flex() + .p_4() + .size_full() .on_action(cx.listener(AssistantPanel::save_credentials)) .track_focus(&self.focus_handle) - .child(Label::new( - "To use the assistant panel or inline assistant, you need to add your OpenAI api key.", - )) - .child(Label::new( - " - Having a subscription for another service like GitHub Copilot won't work." - )) - .child(Label::new( - " - You can create a api key at: platform.openai.com/api-keys" - )) - .child(Label::new( - " " - )) - .child(Label::new( - "Paste your OpenAI API key and press Enter to use the assistant" - )) - .child(api_key_editor) - .child(Label::new( - "Click on the Z button in the status bar to close this panel." - )) + .children( + INSTRUCTIONS.map(|instruction| Label::new(instruction).size(LabelSize::Small)), + ) + .child( + h_flex() + .w_full() + .my_2() + .px_2() + .py_1() + .bg(cx.theme().colors().editor_background) + .rounded_md() + .child(self.render_api_key_editor(&api_key_editor, cx)), + ) + .child( + h_flex() + .gap_2() + .child(Label::new("Click on").size(LabelSize::Small)) + .child(Icon::new(IconName::Ai).size(IconSize::XSmall)) + .child( + Label::new("in the status bar to close this panel.") + .size(LabelSize::Small), + ), + ) } else { let header = TabBar::new("assistant_header") .start_child( diff --git a/crates/vim/src/editor_events.rs b/crates/vim/src/editor_events.rs index e4057792796cad86d7fe31ff01b78ea9434ae102..e325c529a7d70ff340ff92ea788552a3197bacae 100644 --- a/crates/vim/src/editor_events.rs +++ b/crates/vim/src/editor_events.rs @@ -1,6 +1,6 @@ -use crate::Vim; +use crate::{insert::NormalBefore, Vim}; use editor::{Editor, EditorEvent}; -use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext}; +use gpui::{Action, AppContext, Entity, EntityId, View, ViewContext, WindowContext}; pub fn init(cx: &mut AppContext) { cx.observe_new_views(|_, cx: &mut ViewContext| { @@ -34,8 +34,7 @@ fn focused(editor: View, cx: &mut WindowContext) { fn blurred(editor: View, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| { - vim.workspace_state.recording = false; - vim.workspace_state.recorded_actions.clear(); + vim.stop_recording_immediately(NormalBefore.boxed_clone()); if let Some(previous_editor) = vim.active_editor.clone() { if previous_editor .upgrade() diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index a2587c6f003a57f0199dfd3b62bb6d6a1d1c2139..796cfce7a3c5bb3091996d0a8c72d76c4379abfa 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -493,4 +493,17 @@ mod test { cx.simulate_keystrokes(["escape"]); cx.assert_state("ˇjhello\n", Mode::Normal); } + + #[gpui::test] + async fn test_repeat_over_blur(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + cx.set_shared_state("ˇhello hello hello\n").await; + cx.simulate_shared_keystrokes(["c", "f", "o", "x", "escape"]) + .await; + cx.assert_shared_state("ˇx hello hello\n").await; + cx.simulate_shared_keystrokes([":", "escape"]).await; + cx.simulate_shared_keystrokes(["."]).await; + cx.assert_shared_state("ˇx hello\n").await; + } } diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index fe5c5db62f831a3725e753ef4df3d448c28c4e68..384722286cdfa3768320c0810655c4ca06c9b012 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -62,6 +62,8 @@ pub struct NeovimBackedTestContext { impl NeovimBackedTestContext { pub async fn new(cx: &mut gpui::TestAppContext) -> NeovimBackedTestContext { + #[cfg(feature = "neovim")] + cx.executor().allow_parking(); // rust stores the name of the test on the current thread. // We use this to automatically name a file that will store // the neovim connection's requests/responses so that we can diff --git a/crates/vim/test_data/test_repeat_over_blur.json b/crates/vim/test_data/test_repeat_over_blur.json new file mode 100644 index 0000000000000000000000000000000000000000..3929711e49327db2e47c0c6fdcbe56ebff5dbd09 --- /dev/null +++ b/crates/vim/test_data/test_repeat_over_blur.json @@ -0,0 +1,11 @@ +{"Put":{"state":"ˇhello hello hello\n"}} +{"Key":"c"} +{"Key":"f"} +{"Key":"o"} +{"Key":"x"} +{"Key":"escape"} +{"Get":{"state":"ˇx hello hello\n","mode":"Normal"}} +{"Key":":"} +{"Key":"escape"} +{"Key":"."} +{"Get":{"state":"ˇx hello\n","mode":"Normal"}}