diff --git a/Cargo.lock b/Cargo.lock index cfd27bbdd0..87e32ed97d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3157,6 +3157,7 @@ dependencies = [ name = "journal" version = "0.1.0" dependencies = [ + "anyhow", "chrono", "dirs 4.0.0", "editor", diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 748eb48f7e..9d486619d2 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -224,7 +224,7 @@ impl Telemetry { .header("Content-Type", "application/json") .body(json_bytes.into())?; this.http_client.send(request).await?; - Ok(()) + anyhow::Ok(()) } .log_err(), ) @@ -320,7 +320,7 @@ impl Telemetry { .header("Content-Type", "application/json") .body(json_bytes.into())?; this.http_client.send(request).await?; - Ok(()) + anyhow::Ok(()) } .log_err(), ) diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index ff783c6274..3b93414e1f 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -68,7 +68,7 @@ impl PickerDelegate for ContactFinder { this.potential_contacts = potential_contacts.into(); cx.notify(); }); - Ok(()) + anyhow::Ok(()) } .log_err() .await; diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index f92b07da1d..1a23a02efc 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,3 +1,4 @@ +use futures::FutureExt; use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, Text}, @@ -327,12 +328,10 @@ impl InfoPopover { MouseEventHandler::::new(0, cx, |_, cx| { let mut flex = Flex::new(Axis::Vertical).scrollable::(1, None, cx); flex.extend(self.contents.iter().map(|content| { - let project = self.project.read(cx); - if let Some(language) = content - .language - .clone() - .and_then(|language| project.languages().language_for_name(&language)) - { + let languages = self.project.read(cx).languages(); + if let Some(language) = content.language.clone().and_then(|language| { + languages.language_for_name(&language).now_or_never()?.ok() + }) { let runs = language .highlight_text(&content.text.as_str().into(), 0..content.text.len()); diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index e30b85170c..8b12e859e6 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -20,6 +20,7 @@ use postage::prelude::Stream; use project::Project; use serde::Serialize; +use util::ResultExt; use workspace::{ item::{Item, ItemHandle}, searchable::{SearchableItem, SearchableItemHandle}, @@ -200,24 +201,28 @@ impl FeedbackEditor { impl FeedbackEditor { pub fn deploy( system_specs: SystemSpecs, - workspace: &mut Workspace, + _: &mut Workspace, app_state: Arc, cx: &mut ViewContext, ) { - workspace - .with_local_workspace(&app_state, cx, |workspace, cx| { - let project = workspace.project().clone(); - let markdown_language = project.read(cx).languages().language_for_name("Markdown"); - let buffer = project - .update(cx, |project, cx| { - project.create_buffer("", markdown_language, cx) + let markdown = app_state.languages.language_for_name("Markdown"); + cx.spawn(|workspace, mut cx| async move { + let markdown = markdown.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + let project = workspace.project().clone(); + let buffer = project + .update(cx, |project, cx| project.create_buffer("", markdown, cx)) + .expect("creating buffers on a local workspace always succeeds"); + let feedback_editor = cx + .add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx)); + workspace.add_item(Box::new(feedback_editor), cx); }) - .expect("creating buffers on a local workspace always succeeds"); - let feedback_editor = - cx.add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx)); - workspace.add_item(Box::new(feedback_editor), cx); - }) - .detach(); + }) + .await; + }) + .detach(); } } diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index b532397dd1..a2656ad219 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -13,6 +13,7 @@ editor = { path = "../editor" } gpui = { path = "../gpui" } util = { path = "../util" } workspace = { path = "../workspace" } +anyhow = "1.0" chrono = "0.4" dirs = "4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 76a56af93d..1ad97e61b1 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -73,7 +73,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { } } - Ok(()) + anyhow::Ok(()) } .log_err() }) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index e98edbb2d3..3e65ec3120 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -80,31 +80,49 @@ fn test_select_language() { // matching file extension assert_eq!( - registry.language_for_path("zed/lib.rs").map(|l| l.name()), + registry + .language_for_path("zed/lib.rs") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Rust".into()) ); assert_eq!( - registry.language_for_path("zed/lib.mk").map(|l| l.name()), + registry + .language_for_path("zed/lib.mk") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Make".into()) ); // matching filename assert_eq!( - registry.language_for_path("zed/Makefile").map(|l| l.name()), + registry + .language_for_path("zed/Makefile") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Make".into()) ); // matching suffix that is not the full file extension or filename assert_eq!( - registry.language_for_path("zed/cars").map(|l| l.name()), + registry + .language_for_path("zed/cars") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); assert_eq!( - registry.language_for_path("zed/a.cars").map(|l| l.name()), + registry + .language_for_path("zed/a.cars") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); assert_eq!( - registry.language_for_path("zed/sumk").map(|l| l.name()), + registry + .language_for_path("zed/sumk") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); } @@ -666,14 +684,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { moˇd y { - + } } let foo = 1;"}, vec![indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}], @@ -683,7 +701,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y ˇ{ - + } } let foo = 1;"}, @@ -691,14 +709,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}, indoc! {" mod x { mod y «{» - + «}» } let foo = 1;"}, @@ -709,7 +727,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + }ˇ } let foo = 1;"}, @@ -717,14 +735,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}, indoc! {" mod x { mod y «{» - + «}» } let foo = 1;"}, @@ -735,14 +753,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } ˇ} let foo = 1;"}, vec![indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}], @@ -752,7 +770,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } } let fˇoo = 1;"}, @@ -764,7 +782,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } } let foo = 1;ˇ"}, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 983bc58f76..30fc5db563 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -13,8 +13,9 @@ use async_trait::async_trait; use client::http::HttpClient; use collections::HashMap; use futures::{ + channel::oneshot, future::{BoxFuture, Shared}, - FutureExt, TryFutureExt, + FutureExt, TryFutureExt as _, }; use gpui::{executor::Background, MutableAppContext, Task}; use highlight_map::HighlightMap; @@ -43,7 +44,7 @@ use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; use unicase::UniCase; -use util::ResultExt; +use util::{ResultExt, TryFutureExt as _, UnwrapFuture}; #[cfg(any(test, feature = "test-support"))] use futures::channel::mpsc; @@ -484,7 +485,7 @@ impl LanguageRegistry { let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16); Self { language_server_download_dir: None, - languages: Default::default(), + languages: RwLock::new(vec![PLAIN_TEXT.clone()]), available_languages: Default::default(), lsp_binary_statuses_tx, lsp_binary_statuses_rx, @@ -568,12 +569,18 @@ impl LanguageRegistry { self.language_server_download_dir = Some(path.into()); } - pub fn language_for_name(self: &Arc, name: &str) -> Option> { + pub fn language_for_name( + self: &Arc, + name: &str, + ) -> UnwrapFuture>>> { let name = UniCase::new(name); self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name) } - pub fn language_for_name_or_extension(self: &Arc, string: &str) -> Option> { + pub fn language_for_name_or_extension( + self: &Arc, + string: &str, + ) -> UnwrapFuture>>> { let string = UniCase::new(string); self.get_or_load_language(|config| { UniCase::new(config.name.as_ref()) == string @@ -584,7 +591,10 @@ impl LanguageRegistry { }) } - pub fn language_for_path(self: &Arc, path: impl AsRef) -> Option> { + pub fn language_for_path( + self: &Arc, + path: impl AsRef, + ) -> UnwrapFuture>>> { let path = path.as_ref(); let filename = path.file_name().and_then(|name| name.to_str()); let extension = path.extension().and_then(|name| name.to_str()); @@ -600,17 +610,17 @@ impl LanguageRegistry { fn get_or_load_language( self: &Arc, callback: impl Fn(&LanguageConfig) -> bool, - ) -> Option> { + ) -> UnwrapFuture>>> { + let (tx, rx) = oneshot::channel(); + if let Some(language) = self .languages .read() .iter() .find(|language| callback(&language.config)) { - return Some(language.clone()); - } - - if let Some(executor) = self.executor.clone() { + let _ = tx.send(Ok(language.clone())); + } else if let Some(executor) = self.executor.clone() { let mut available_languages = self.available_languages.write(); if let Some(ix) = available_languages.iter().position(|l| callback(&l.config)) { @@ -625,18 +635,29 @@ impl LanguageRegistry { .with_lsp_adapter(language.lsp_adapter) .await; match language.with_queries(queries) { - Ok(language) => this.add(Arc::new(language)), + Ok(language) => { + let language = Arc::new(language); + this.add(language.clone()); + let _ = tx.send(Ok(language)); + } Err(err) => { - log::error!("failed to load language {}: {}", name, err); - return; + let _ = tx.send(Err(anyhow!( + "failed to load language {}: {}", + name, + err + ))); } }; }) .detach(); + } else { + let _ = tx.send(Err(anyhow!("language not found"))); } + } else { + let _ = tx.send(Err(anyhow!("executor does not exist"))); } - None + rx.unwrap() } pub fn to_vec(&self) -> Vec> { diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 5923241955..88de8e690a 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,5 +1,6 @@ use crate::{Grammar, InjectionConfig, Language, LanguageRegistry}; use collections::HashMap; +use futures::FutureExt; use lazy_static::lazy_static; use parking_lot::Mutex; use std::{ @@ -382,11 +383,11 @@ impl SyntaxSnapshot { cursor.next(text); while let Some(layer) = cursor.item() { let SyntaxLayerContent::Pending { language_name } = &layer.content else { unreachable!() }; - if { - let language_registry = ®istry; - language_registry.language_for_name_or_extension(language_name) - } - .is_some() + if registry + .language_for_name_or_extension(language_name) + .now_or_never() + .and_then(|language| language.ok()) + .is_some() { resolved_injection_ranges.push(layer.range.to_offset(text)); } @@ -1116,7 +1117,10 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_registry.language_for_name_or_extension(language_name) + if let Some(language) = language_registry + .language_for_name_or_extension(language_name) + .now_or_never() + .and_then(|language| language.ok()) { combined_injection_ranges.insert(language, Vec::new()); } @@ -1162,10 +1166,10 @@ fn get_injections( }; if let Some(language_name) = language_name { - let language = { - let language_name: &str = &language_name; - language_registry.language_for_name_or_extension(language_name) - }; + let language = language_registry + .language_for_name_or_extension(&language_name) + .now_or_never() + .and_then(|language| language.ok()); let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { if combined { @@ -2522,7 +2526,11 @@ mod tests { registry.add(Arc::new(html_lang())); registry.add(Arc::new(erb_lang())); registry.add(Arc::new(markdown_lang())); - let language = registry.language_for_name(language_name).unwrap(); + let language = registry + .language_for_name(language_name) + .now_or_never() + .unwrap() + .unwrap(); let mut buffer = Buffer::new(0, 0, Default::default()); let mut mutated_syntax_map = SyntaxMap::new(); diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 6982445982..81568b9e3e 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -160,15 +160,13 @@ impl LanguageServer { server: Option, root_path: &Path, cx: AsyncAppContext, - mut on_unhandled_notification: F, + on_unhandled_notification: F, ) -> Self where Stdin: AsyncWrite + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static, F: FnMut(AnyNotification) + 'static + Send, { - let mut stdin = BufWriter::new(stdin); - let mut stdout = BufReader::new(stdout); let (outbound_tx, outbound_rx) = channel::unbounded::>(); let notification_handlers = Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default())); @@ -177,89 +175,19 @@ impl LanguageServer { let input_task = cx.spawn(|cx| { let notification_handlers = notification_handlers.clone(); let response_handlers = response_handlers.clone(); - async move { - let _clear_response_handlers = util::defer({ - let response_handlers = response_handlers.clone(); - move || { - response_handlers.lock().take(); - } - }); - let mut buffer = Vec::new(); - loop { - buffer.clear(); - stdout.read_until(b'\n', &mut buffer).await?; - stdout.read_until(b'\n', &mut buffer).await?; - let message_len: usize = std::str::from_utf8(&buffer)? - .strip_prefix(CONTENT_LEN_HEADER) - .ok_or_else(|| anyhow!("invalid header"))? - .trim_end() - .parse()?; - - buffer.resize(message_len, 0); - stdout.read_exact(&mut buffer).await?; - log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); - - if let Ok(msg) = serde_json::from_slice::(&buffer) { - if let Some(handler) = notification_handlers.lock().get_mut(msg.method) { - handler(msg.id, msg.params.get(), cx.clone()); - } else { - on_unhandled_notification(msg); - } - } else if let Ok(AnyResponse { - id, error, result, .. - }) = serde_json::from_slice(&buffer) - { - if let Some(handler) = response_handlers - .lock() - .as_mut() - .and_then(|handlers| handlers.remove(&id)) - { - if let Some(error) = error { - handler(Err(error)); - } else if let Some(result) = result { - handler(Ok(result.get())); - } else { - handler(Ok("null")); - } - } - } else { - warn!( - "Failed to deserialize message:\n{}", - std::str::from_utf8(&buffer)? - ); - } - - // Don't starve the main thread when receiving lots of messages at once. - smol::future::yield_now().await; - } - } + Self::handle_input( + stdout, + on_unhandled_notification, + notification_handlers, + response_handlers, + cx, + ) .log_err() }); let (output_done_tx, output_done_rx) = barrier::channel(); let output_task = cx.background().spawn({ let response_handlers = response_handlers.clone(); - async move { - let _clear_response_handlers = util::defer({ - let response_handlers = response_handlers.clone(); - move || { - response_handlers.lock().take(); - } - }); - let mut content_len_buffer = Vec::new(); - while let Ok(message) = outbound_rx.recv().await { - log::trace!("outgoing message:{}", String::from_utf8_lossy(&message)); - content_len_buffer.clear(); - write!(content_len_buffer, "{}", message.len()).unwrap(); - stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?; - stdin.write_all(&content_len_buffer).await?; - stdin.write_all("\r\n\r\n".as_bytes()).await?; - stdin.write_all(&message).await?; - stdin.flush().await?; - } - drop(output_done_tx); - Ok(()) - } - .log_err() + Self::handle_output(stdin, outbound_rx, output_done_tx, response_handlers).log_err() }); Self { @@ -278,6 +206,105 @@ impl LanguageServer { } } + async fn handle_input( + stdout: Stdout, + mut on_unhandled_notification: F, + notification_handlers: Arc>>, + response_handlers: Arc>>>, + cx: AsyncAppContext, + ) -> anyhow::Result<()> + where + Stdout: AsyncRead + Unpin + Send + 'static, + F: FnMut(AnyNotification) + 'static + Send, + { + let mut stdout = BufReader::new(stdout); + let _clear_response_handlers = util::defer({ + let response_handlers = response_handlers.clone(); + move || { + response_handlers.lock().take(); + } + }); + let mut buffer = Vec::new(); + loop { + buffer.clear(); + stdout.read_until(b'\n', &mut buffer).await?; + stdout.read_until(b'\n', &mut buffer).await?; + let message_len: usize = std::str::from_utf8(&buffer)? + .strip_prefix(CONTENT_LEN_HEADER) + .ok_or_else(|| anyhow!("invalid header"))? + .trim_end() + .parse()?; + + buffer.resize(message_len, 0); + stdout.read_exact(&mut buffer).await?; + log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); + + if let Ok(msg) = serde_json::from_slice::(&buffer) { + if let Some(handler) = notification_handlers.lock().get_mut(msg.method) { + handler(msg.id, msg.params.get(), cx.clone()); + } else { + on_unhandled_notification(msg); + } + } else if let Ok(AnyResponse { + id, error, result, .. + }) = serde_json::from_slice(&buffer) + { + if let Some(handler) = response_handlers + .lock() + .as_mut() + .and_then(|handlers| handlers.remove(&id)) + { + if let Some(error) = error { + handler(Err(error)); + } else if let Some(result) = result { + handler(Ok(result.get())); + } else { + handler(Ok("null")); + } + } + } else { + warn!( + "Failed to deserialize message:\n{}", + std::str::from_utf8(&buffer)? + ); + } + + // Don't starve the main thread when receiving lots of messages at once. + smol::future::yield_now().await; + } + } + + async fn handle_output( + stdin: Stdin, + outbound_rx: channel::Receiver>, + output_done_tx: barrier::Sender, + response_handlers: Arc>>>, + ) -> anyhow::Result<()> + where + Stdin: AsyncWrite + Unpin + Send + 'static, + { + let mut stdin = BufWriter::new(stdin); + let _clear_response_handlers = util::defer({ + let response_handlers = response_handlers.clone(); + move || { + response_handlers.lock().take(); + } + }); + let mut content_len_buffer = Vec::new(); + while let Ok(message) = outbound_rx.recv().await { + log::trace!("outgoing message:{}", String::from_utf8_lossy(&message)); + content_len_buffer.clear(); + write!(content_len_buffer, "{}", message.len()).unwrap(); + stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?; + stdin.write_all(&content_len_buffer).await?; + stdin.write_all("\r\n\r\n".as_bytes()).await?; + stdin.write_all(&message).await?; + stdin.flush().await?; + } + drop(output_done_tx); + Ok(()) + } + /// Initializes a language server. /// Note that `options` is used directly to construct [`InitializeParams`], /// which is why it is owned. @@ -389,7 +416,7 @@ impl LanguageServer { output_done.recv().await; log::debug!("language server shutdown finished"); drop(tasks); - Ok(()) + anyhow::Ok(()) } .log_err(), ) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2ebea8d07d..003e4dd899 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1838,7 +1838,11 @@ impl Project { ) -> Option<()> { // If the buffer has a language, set it and start the language server if we haven't already. let full_path = buffer.read(cx).file()?.full_path(cx); - let new_language = self.languages.language_for_path(&full_path)?; + let new_language = self + .languages + .language_for_path(&full_path) + .now_or_never()? + .ok()?; buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -2248,8 +2252,14 @@ impl Project { }) .collect(); for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info { - let language = self.languages.language_for_path(&full_path)?; - self.restart_language_server(worktree_id, worktree_abs_path, language, cx); + if let Some(language) = self + .languages + .language_for_path(&full_path) + .now_or_never() + .and_then(|language| language.ok()) + { + self.restart_language_server(worktree_id, worktree_abs_path, language, cx); + } } None @@ -3278,12 +3288,14 @@ impl Project { path: path.into(), }; let signature = this.symbol_signature(&project_path); + let adapter_language = adapter_language.clone(); let language = this .languages .language_for_path(&project_path.path) - .unwrap_or(adapter_language.clone()); + .unwrap_or_else(move |_| adapter_language); let language_server_name = adapter.name.clone(); Some(async move { + let language = language.await; let label = language .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind) .await; @@ -5831,7 +5843,7 @@ impl Project { })?; } - Ok(()) + anyhow::Ok(()) } .log_err(), ) @@ -6060,7 +6072,7 @@ impl Project { worktree_id, path: PathBuf::from(serialized_symbol.path).into(), }; - let language = languages.language_for_path(&path.path); + let language = languages.language_for_path(&path.path).await.log_err(); Ok(Symbol { language_server_name: LanguageServerName( serialized_symbol.language_server_name.into(), diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 5ecf889f9b..3824312a4f 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -124,11 +124,15 @@ pub trait TryFutureExt { fn warn_on_err(self) -> LogErrorFuture where Self: Sized; + fn unwrap(self) -> UnwrapFuture + where + Self: Sized; } -impl TryFutureExt for F +impl TryFutureExt for F where - F: Future>, + F: Future>, + E: std::fmt::Debug, { fn log_err(self) -> LogErrorFuture where @@ -143,17 +147,25 @@ where { LogErrorFuture(self, log::Level::Warn) } + + fn unwrap(self) -> UnwrapFuture + where + Self: Sized, + { + UnwrapFuture(self) + } } pub struct LogErrorFuture(F, log::Level); -impl Future for LogErrorFuture +impl Future for LogErrorFuture where - F: Future>, + F: Future>, + E: std::fmt::Debug, { type Output = Option; - fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let level = self.1; let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; match inner.poll(cx) { @@ -169,6 +181,24 @@ where } } +pub struct UnwrapFuture(F); + +impl Future for UnwrapFuture +where + F: Future>, + E: std::fmt::Debug, +{ + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; + match inner.poll(cx) { + Poll::Ready(result) => Poll::Ready(result.unwrap()), + Poll::Pending => Poll::Pending, + } + } +} + struct Defer(Option); impl Drop for Defer { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2fbac3613e..79a6f67f62 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -167,9 +167,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "licenses.md", "Open Source License Attribution", @@ -192,9 +191,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "keymaps/default.json", "Default Key Bindings", @@ -205,11 +203,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, - _: &OpenDefaultSettings, - cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenDefaultSettings, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "settings/default.json", "Default Settings", @@ -218,32 +213,41 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { ); } }); - cx.add_action( - |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + cx.add_action({ + let app_state = app_state.clone(); + move |_: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + let app_state = app_state.clone(); + let markdown = app_state.languages.language_for_name("JSON"); let content = to_string_pretty(&cx.debug_elements()).unwrap(); - let project = workspace.project().clone(); - let json_language = project - .read(cx) - .languages() - .language_for_name("JSON") - .unwrap(); - if project.read(cx).is_remote() { - cx.propagate_action(); - } else if let Some(buffer) = project - .update(cx, |project, cx| { - project.create_buffer(&content, Some(json_language), cx) - }) - .log_err() - { - workspace.add_item( - Box::new( - cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)), - ), - cx, - ); - } - }, - ); + cx.spawn(|workspace, mut cx| async move { + let markdown = markdown.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, cx, move |workspace, cx| { + let project = workspace.project().clone(); + + let buffer = project + .update(cx, |project, cx| { + project.create_buffer(&content, markdown, cx) + }) + .expect("creating buffers on a local workspace always succeeds"); + let buffer = cx.add_model(|cx| { + MultiBuffer::singleton(buffer, cx) + .with_title("Debug Elements".into()) + }); + workspace.add_item( + Box::new(cx.add_view(|cx| { + Editor::for_multibuffer(buffer, Some(project.clone()), cx) + })), + cx, + ); + }) + }) + .await; + }) + .detach(); + } + }); cx.add_action( |workspace: &mut Workspace, _: &project_panel::ToggleFocus, @@ -628,6 +632,7 @@ fn open_telemetry_log_file( start_offset += newline_offset + 1; } let log_suffix = &log[start_offset..]; + let json = app_state.languages.language_for_name("JSON").await.log_err(); workspace.update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); @@ -635,7 +640,7 @@ fn open_telemetry_log_file( .update(cx, |project, cx| project.create_buffer("", None, cx)) .expect("creating buffers on a local workspace always succeeds"); buffer.update(cx, |buffer, cx| { - buffer.set_language(app_state.languages.language_for_name("JSON"), cx); + buffer.set_language(json, cx); buffer.edit( [( 0..0, @@ -668,35 +673,42 @@ fn open_telemetry_log_file( } fn open_bundled_file( - workspace: &mut Workspace, app_state: Arc, asset_path: &'static str, title: &'static str, language: &'static str, cx: &mut ViewContext, ) { - workspace - .with_local_workspace(&app_state, cx, |workspace, cx| { - let project = workspace.project().clone(); - let buffer = project.update(cx, |project, cx| { - let text = Assets::get(asset_path) - .map(|f| f.data) - .unwrap_or_else(|| Cow::Borrowed(b"File not found")); - let text = str::from_utf8(text.as_ref()).unwrap(); - project - .create_buffer(text, project.languages().language_for_name(language), cx) - .expect("creating buffers on a local workspace always succeeds") - }); - let buffer = - cx.add_model(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into())); - workspace.add_item( - Box::new( - cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), cx)), - ), - cx, - ); - }) - .detach(); + let language = app_state.languages.language_for_name(language); + cx.spawn(|workspace, mut cx| async move { + let language = language.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + let project = workspace.project(); + let buffer = project.update(cx, |project, cx| { + let text = Assets::get(asset_path) + .map(|f| f.data) + .unwrap_or_else(|| Cow::Borrowed(b"File not found")); + let text = str::from_utf8(text.as_ref()).unwrap(); + project + .create_buffer(text, language, cx) + .expect("creating buffers on a local workspace always succeeds") + }); + let buffer = cx.add_model(|cx| { + MultiBuffer::singleton(buffer, cx).with_title(title.into()) + }); + workspace.add_item( + Box::new(cx.add_view(|cx| { + Editor::for_multibuffer(buffer, Some(project.clone()), cx) + })), + cx, + ); + }) + }) + .await; + }) + .detach(); } fn schema_file_match(path: &Path) -> &Path {