Cargo.lock 🔗
@@ -3157,6 +3157,7 @@ dependencies = [
name = "journal"
version = "0.1.0"
dependencies = [
+ "anyhow",
"chrono",
"dirs 4.0.0",
"editor",
Antonio Scandurra created
Allow waiting for language to be loaded in LanguageRegistry APIs
Cargo.lock | 1
crates/client/src/telemetry.rs | 4
crates/collab_ui/src/contact_finder.rs | 2
crates/editor/src/hover_popover.rs | 11
crates/feedback/src/feedback_editor.rs | 33 ++--
crates/journal/Cargo.toml | 1
crates/journal/src/journal.rs | 2
crates/language/src/buffer_tests.rs | 54 +++++--
crates/language/src/language.rs | 51 +++++--
crates/language/src/syntax_map.rs | 30 ++-
crates/lsp/src/lsp.rs | 191 +++++++++++++++------------
crates/project/src/project.rs | 24 ++
crates/util/src/util.rs | 40 +++++
crates/zed/src/zed.rs | 126 ++++++++++--------
14 files changed, 352 insertions(+), 218 deletions(-)
@@ -3157,6 +3157,7 @@ dependencies = [
name = "journal"
version = "0.1.0"
dependencies = [
+ "anyhow",
"chrono",
"dirs 4.0.0",
"editor",
@@ -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(),
)
@@ -68,7 +68,7 @@ impl PickerDelegate for ContactFinder {
this.potential_contacts = potential_contacts.into();
cx.notify();
});
- Ok(())
+ anyhow::Ok(())
}
.log_err()
.await;
@@ -1,3 +1,4 @@
+use futures::FutureExt;
use gpui::{
actions,
elements::{Flex, MouseEventHandler, Padding, Text},
@@ -327,12 +328,10 @@ impl InfoPopover {
MouseEventHandler::<InfoPopover>::new(0, cx, |_, cx| {
let mut flex = Flex::new(Axis::Vertical).scrollable::<HoverBlock, _>(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());
@@ -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<AppState>,
cx: &mut ViewContext<Workspace>,
) {
- 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();
}
}
@@ -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"] }
@@ -73,7 +73,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
}
}
- Ok(())
+ anyhow::Ok(())
}
.log_err()
})
@@ -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;ˇ"},
@@ -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<Self>, name: &str) -> Option<Arc<Language>> {
+ pub fn language_for_name(
+ self: &Arc<Self>,
+ name: &str,
+ ) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
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<Self>, string: &str) -> Option<Arc<Language>> {
+ pub fn language_for_name_or_extension(
+ self: &Arc<Self>,
+ string: &str,
+ ) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
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<Self>, path: impl AsRef<Path>) -> Option<Arc<Language>> {
+ pub fn language_for_path(
+ self: &Arc<Self>,
+ path: impl AsRef<Path>,
+ ) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
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<Self>,
callback: impl Fn(&LanguageConfig) -> bool,
- ) -> Option<Arc<Language>> {
+ ) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
+ 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<Arc<Language>> {
@@ -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();
@@ -160,15 +160,13 @@ impl LanguageServer {
server: Option<Child>,
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::<Vec<u8>>();
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::<AnyNotification>(&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, F>(
+ stdout: Stdout,
+ mut on_unhandled_notification: F,
+ notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
+ response_handlers: Arc<Mutex<Option<HashMap<usize, ResponseHandler>>>>,
+ 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::<AnyNotification>(&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: Stdin,
+ outbound_rx: channel::Receiver<Vec<u8>>,
+ output_done_tx: barrier::Sender,
+ response_handlers: Arc<Mutex<Option<HashMap<usize, ResponseHandler>>>>,
+ ) -> 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(),
)
@@ -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(),
@@ -124,11 +124,15 @@ pub trait TryFutureExt {
fn warn_on_err(self) -> LogErrorFuture<Self>
where
Self: Sized;
+ fn unwrap(self) -> UnwrapFuture<Self>
+ where
+ Self: Sized;
}
-impl<F, T> TryFutureExt for F
+impl<F, T, E> TryFutureExt for F
where
- F: Future<Output = anyhow::Result<T>>,
+ F: Future<Output = Result<T, E>>,
+ E: std::fmt::Debug,
{
fn log_err(self) -> LogErrorFuture<Self>
where
@@ -143,17 +147,25 @@ where
{
LogErrorFuture(self, log::Level::Warn)
}
+
+ fn unwrap(self) -> UnwrapFuture<Self>
+ where
+ Self: Sized,
+ {
+ UnwrapFuture(self)
+ }
}
pub struct LogErrorFuture<F>(F, log::Level);
-impl<F, T> Future for LogErrorFuture<F>
+impl<F, T, E> Future for LogErrorFuture<F>
where
- F: Future<Output = anyhow::Result<T>>,
+ F: Future<Output = Result<T, E>>,
+ E: std::fmt::Debug,
{
type Output = Option<T>;
- fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
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>(F);
+
+impl<F, T, E> Future for UnwrapFuture<F>
+where
+ F: Future<Output = Result<T, E>>,
+ E: std::fmt::Debug,
+{
+ type Output = T;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+ 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<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Drop for Defer<F> {
@@ -167,9 +167,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
});
cx.add_action({
let app_state = app_state.clone();
- move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext<Workspace>| {
+ move |_: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext<Workspace>| {
open_bundled_file(
- workspace,
app_state.clone(),
"licenses.md",
"Open Source License Attribution",
@@ -192,9 +191,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
});
cx.add_action({
let app_state = app_state.clone();
- move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
+ move |_: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
open_bundled_file(
- workspace,
app_state.clone(),
"keymaps/default.json",
"Default Key Bindings",
@@ -205,11 +203,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
});
cx.add_action({
let app_state = app_state.clone();
- move |workspace: &mut Workspace,
- _: &OpenDefaultSettings,
- cx: &mut ViewContext<Workspace>| {
+ move |_: &mut Workspace, _: &OpenDefaultSettings, cx: &mut ViewContext<Workspace>| {
open_bundled_file(
- workspace,
app_state.clone(),
"settings/default.json",
"Default Settings",
@@ -218,32 +213,41 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
);
}
});
- cx.add_action(
- |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
+ cx.add_action({
+ let app_state = app_state.clone();
+ move |_: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
+ 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<AppState>,
asset_path: &'static str,
title: &'static str,
language: &'static str,
cx: &mut ViewContext<Workspace>,
) {
- 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 {