@@ -119,9 +119,6 @@ pub enum KeymapFileLoadResult {
key_bindings: Vec<KeyBinding>,
error_message: MarkdownString,
},
- AllFailedToLoad {
- error_message: MarkdownString,
- },
JsonParseFailure {
error: anyhow::Error,
},
@@ -135,8 +132,7 @@ impl KeymapFile {
pub fn load_asset(asset_path: &str, cx: &AppContext) -> anyhow::Result<Vec<KeyBinding>> {
match Self::load(asset_str::<SettingsAssets>(asset_path).as_ref(), cx) {
KeymapFileLoadResult::Success { key_bindings, .. } => Ok(key_bindings),
- KeymapFileLoadResult::SomeFailedToLoad { error_message, .. }
- | KeymapFileLoadResult::AllFailedToLoad { error_message } => Err(anyhow!(
+ KeymapFileLoadResult::SomeFailedToLoad { error_message, .. } => Err(anyhow!(
"Error loading built-in keymap \"{asset_path}\": {error_message}"
)),
KeymapFileLoadResult::JsonParseFailure { error } => Err(anyhow!(
@@ -151,11 +147,14 @@ impl KeymapFile {
cx: &AppContext,
) -> anyhow::Result<Vec<KeyBinding>> {
match Self::load(asset_str::<SettingsAssets>(asset_path).as_ref(), cx) {
- KeymapFileLoadResult::Success { key_bindings, .. }
- | KeymapFileLoadResult::SomeFailedToLoad { key_bindings, .. } => Ok(key_bindings),
- KeymapFileLoadResult::AllFailedToLoad { error_message } => Err(anyhow!(
+ KeymapFileLoadResult::SomeFailedToLoad {
+ key_bindings,
+ error_message,
+ } if key_bindings.is_empty() => Err(anyhow!(
"Error loading built-in keymap \"{asset_path}\": {error_message}"
)),
+ KeymapFileLoadResult::Success { key_bindings, .. }
+ | KeymapFileLoadResult::SomeFailedToLoad { key_bindings, .. } => Ok(key_bindings),
KeymapFileLoadResult::JsonParseFailure { error } => Err(anyhow!(
"JSON parse error in built-in keymap \"{asset_path}\": {error}"
)),
@@ -166,8 +165,7 @@ impl KeymapFile {
pub fn load_panic_on_failure(content: &str, cx: &AppContext) -> Vec<KeyBinding> {
match Self::load(content, cx) {
KeymapFileLoadResult::Success { key_bindings } => key_bindings,
- KeymapFileLoadResult::SomeFailedToLoad { error_message, .. }
- | KeymapFileLoadResult::AllFailedToLoad { error_message, .. } => {
+ KeymapFileLoadResult::SomeFailedToLoad { error_message, .. } => {
panic!("{error_message}");
}
KeymapFileLoadResult::JsonParseFailure { error } => {
@@ -22,10 +22,10 @@ use feature_flags::FeatureFlagAppExt;
use futures::FutureExt;
use futures::{channel::mpsc, select_biased, StreamExt};
use gpui::{
- actions, point, px, Action, AppContext, AsyncAppContext, Context, DismissEvent, Element,
- FocusableView, KeyBinding, MenuItem, ParentElement, PathPromptOptions, PromptLevel, ReadGlobal,
- SharedString, Styled, Task, TitlebarOptions, View, ViewContext, VisualContext, WindowKind,
- WindowOptions,
+ actions, point, px, Action, AnyWindowHandle, AppContext, AsyncAppContext, Context,
+ DismissEvent, Element, FocusableView, KeyBinding, MenuItem, ParentElement, PathPromptOptions,
+ PromptLevel, ReadGlobal, SharedString, Styled, Task, TitlebarOptions, View, ViewContext,
+ VisualContext, WindowKind, WindowOptions,
};
pub use open_listener::*;
use outline_panel::OutlinePanel;
@@ -1017,6 +1017,16 @@ pub fn handle_keymap_file_changes(
})
.detach();
+ // Need to notify about keymap load errors when new workspaces are created, so that initial
+ // keymap load errors are shown to the user.
+ let (new_workspace_window_tx, mut new_workspace_window_rx) = mpsc::unbounded();
+ cx.observe_new_views(move |_: &mut Workspace, cx| {
+ new_workspace_window_tx
+ .unbounded_send(cx.window_handle())
+ .unwrap();
+ })
+ .detach();
+
let mut current_mapping = settings::get_key_equivalents(cx.keyboard_layout());
cx.on_keyboard_layout_change(move |cx| {
let next_mapping = settings::get_key_equivalents(cx.keyboard_layout());
@@ -1033,68 +1043,102 @@ pub fn handle_keymap_file_changes(
let notification_id = NotificationId::unique::<KeymapParseErrorNotification>();
cx.spawn(move |cx| async move {
- let mut user_key_bindings = Vec::new();
+ let mut user_keymap_content = String::new();
+ enum LastError {
+ None,
+ JsonError(anyhow::Error),
+ LoadError(MarkdownString),
+ }
+ let mut last_load_error = LastError::None;
loop {
- select_biased! {
- _ = base_keymap_rx.next() => {}
- _ = keyboard_layout_rx.next() => {}
- user_keymap_content = user_keymap_file_rx.next() => {
- if let Some(user_keymap_content) = user_keymap_content {
- cx.update(|cx| {
- let load_result = KeymapFile::load(&user_keymap_content, cx);
- match load_result {
- KeymapFileLoadResult::Success { key_bindings } => {
- user_key_bindings = key_bindings;
- dismiss_app_notification(¬ification_id, cx);
- }
- KeymapFileLoadResult::SomeFailedToLoad {
- key_bindings,
- error_message
- } => {
- user_key_bindings = key_bindings;
- show_keymap_file_load_error(notification_id.clone(), error_message, cx);
- }
- KeymapFileLoadResult::AllFailedToLoad {
- error_message
- } => {
- show_keymap_file_load_error(notification_id.clone(), error_message, cx);
- }
- KeymapFileLoadResult::JsonParseFailure { error } => {
- show_keymap_file_json_error(notification_id.clone(), error, cx);
- }
- };
- }).ok();
+ let new_workspace_window = select_biased! {
+ _ = base_keymap_rx.next() => None,
+ _ = keyboard_layout_rx.next() => None,
+ workspace = new_workspace_window_rx.next() => workspace,
+ content = user_keymap_file_rx.next() => {
+ if let Some(content) = content {
+ user_keymap_content = content;
}
+ None
}
- }
- cx.update(|cx| reload_keymaps(cx, user_key_bindings.clone()))
- .ok();
+ };
+ cx.update(|cx| {
+ // No need to reload keymaps when a new workspace is added, just need to send the notification to it.
+ if new_workspace_window.is_none() {
+ let load_result = KeymapFile::load(&user_keymap_content, cx);
+ match load_result {
+ KeymapFileLoadResult::Success { key_bindings } => {
+ reload_keymaps(cx, key_bindings);
+ dismiss_app_notification(¬ification_id, cx);
+ last_load_error = LastError::None;
+ }
+ KeymapFileLoadResult::SomeFailedToLoad {
+ key_bindings,
+ error_message,
+ } => {
+ if !key_bindings.is_empty() {
+ reload_keymaps(cx, key_bindings);
+ }
+ last_load_error = LastError::LoadError(error_message);
+ }
+ KeymapFileLoadResult::JsonParseFailure { error } => {
+ last_load_error = LastError::JsonError(error);
+ }
+ }
+ }
+ match &last_load_error {
+ LastError::None => {}
+ LastError::JsonError(err) => {
+ show_keymap_file_json_error(
+ new_workspace_window,
+ notification_id.clone(),
+ err,
+ cx,
+ );
+ }
+ LastError::LoadError(message) => {
+ show_keymap_file_load_error(
+ new_workspace_window,
+ notification_id.clone(),
+ message.clone(),
+ cx,
+ );
+ }
+ }
+ })
+ .ok();
}
})
.detach();
}
fn show_keymap_file_json_error(
+ new_workspace_window: Option<AnyWindowHandle>,
notification_id: NotificationId,
- error: anyhow::Error,
+ error: &anyhow::Error,
cx: &mut AppContext,
) {
let message: SharedString =
format!("JSON parse error in keymap file. Bindings not reloaded.\n\n{error}").into();
- show_app_notification(notification_id, cx, move |cx| {
- cx.new_view(|_cx| {
- MessageNotification::new(message.clone())
- .with_click_message("Open keymap file")
- .on_click(|cx| {
- cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
- cx.emit(DismissEvent);
- })
- })
- })
- .log_err();
+ show_notification_to_specific_workspace_or_all_workspaces(
+ new_workspace_window,
+ notification_id,
+ cx,
+ move |cx| {
+ cx.new_view(|_cx| {
+ MessageNotification::new(message.clone())
+ .with_click_message("Open keymap file")
+ .on_click(|cx| {
+ cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
+ cx.emit(DismissEvent);
+ })
+ })
+ },
+ );
}
fn show_keymap_file_load_error(
+ new_workspace_window: Option<AnyWindowHandle>,
notification_id: NotificationId,
markdown_error_message: MarkdownString,
cx: &mut AppContext,
@@ -1113,34 +1157,57 @@ fn show_keymap_file_load_error(
cx.spawn(move |cx| async move {
let parsed_markdown = Rc::new(parsed_markdown.await);
cx.update(|cx| {
- show_app_notification(notification_id, cx, move |cx| {
- let workspace_handle = cx.view().downgrade();
- let parsed_markdown = parsed_markdown.clone();
- cx.new_view(move |_cx| {
- MessageNotification::new_from_builder(move |cx| {
- gpui::div()
- .text_xs()
- .child(markdown_preview::markdown_renderer::render_parsed_markdown(
- &parsed_markdown.clone(),
- Some(workspace_handle.clone()),
- cx,
- ))
- .into_any()
- })
- .with_click_message("Open keymap file")
- .on_click(|cx| {
- cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
- cx.emit(DismissEvent);
+ show_notification_to_specific_workspace_or_all_workspaces(
+ new_workspace_window,
+ notification_id,
+ cx,
+ move |cx| {
+ let workspace_handle = cx.view().downgrade();
+ let parsed_markdown = parsed_markdown.clone();
+ cx.new_view(move |_cx| {
+ MessageNotification::new_from_builder(move |cx| {
+ gpui::div()
+ .text_xs()
+ .child(markdown_preview::markdown_renderer::render_parsed_markdown(
+ &parsed_markdown.clone(),
+ Some(workspace_handle.clone()),
+ cx,
+ ))
+ .into_any()
+ })
+ .with_click_message("Open keymap file")
+ .on_click(|cx| {
+ cx.dispatch_action(zed_actions::OpenKeymap.boxed_clone());
+ cx.emit(DismissEvent);
+ })
})
- })
- })
- .log_err();
+ },
+ )
})
- .log_err();
+ .ok();
})
.detach();
}
+fn show_notification_to_specific_workspace_or_all_workspaces<V>(
+ new_workspace_window: Option<AnyWindowHandle>,
+ notification_id: NotificationId,
+ cx: &mut AppContext,
+ build_notification: impl Fn(&mut ViewContext<Workspace>) -> View<V>,
+) where
+ V: workspace::notifications::Notification,
+{
+ if let Some(workspace_window) = new_workspace_window.and_then(|w| w.downcast::<Workspace>()) {
+ workspace_window
+ .update(cx, |workspace, cx| {
+ workspace.show_notification(notification_id, cx, build_notification);
+ })
+ .ok();
+ } else {
+ show_app_notification(notification_id, cx, build_notification).ok();
+ }
+}
+
fn reload_keymaps(cx: &mut AppContext, user_key_bindings: Vec<KeyBinding>) {
cx.clear_key_bindings();
load_default_keymap(cx);