From 7a67ec57436eb048f10b2febb3b0220f3a7d1fa0 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 15 Aug 2023 12:48:30 -0400 Subject: [PATCH 01/27] Add support for querying multiple language servers for completions --- crates/project/src/project.rs | 82 ++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1aa2a2dd40c2d01b98860d39420c6ec4ab894728..f85460770d9d55e2d1d47b9380101ddf47977bed 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3861,7 +3861,7 @@ impl Project { let file = File::from_dyn(buffer.file())?; let buffer_abs_path = file.as_local().map(|f| f.abs_path(cx)); let server = self - .primary_language_servers_for_buffer(buffer, cx) + .primary_language_server_for_buffer(buffer, cx) .map(|s| s.1.clone()); Some((buffer_handle, buffer_abs_path, server)) }) @@ -4166,7 +4166,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetDefinition { position }, cx) + self.request_primary_lsp(buffer.clone(), GetDefinition { position }, cx) } pub fn type_definition( @@ -4176,7 +4176,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetTypeDefinition { position }, cx) + self.request_primary_lsp(buffer.clone(), GetTypeDefinition { position }, cx) } pub fn references( @@ -4186,7 +4186,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetReferences { position }, cx) + self.request_primary_lsp(buffer.clone(), GetReferences { position }, cx) } pub fn document_highlights( @@ -4196,7 +4196,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetDocumentHighlights { position }, cx) + self.request_primary_lsp(buffer.clone(), GetDocumentHighlights { position }, cx) } pub fn symbols(&self, query: &str, cx: &mut ModelContext) -> Task>> { @@ -4424,7 +4424,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetHover { position }, cx) + self.request_primary_lsp(buffer.clone(), GetHover { position }, cx) } pub fn completions( @@ -4434,7 +4434,29 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer.clone(), GetCompletions { position }, cx) + let server_ids: Vec<_> = self + .language_servers_for_buffer(buffer.read(cx), cx) + .map(|(_, server)| server.server_id()) + .collect(); + + let buffer = buffer.clone(); + cx.spawn(|this, mut cx| async move { + let mut completions = Vec::new(); + + for server_id in server_ids { + let new_completions = this + .update(&mut cx, |this, cx| { + this.request_lsp(buffer.clone(), server_id, GetCompletions { position }, cx) + }) + .await; + + if let Ok(new_completions) = new_completions { + completions.extend_from_slice(&new_completions); + } + } + + Ok(completions) + }) } pub fn apply_additional_edits_for_completion( @@ -4448,7 +4470,7 @@ impl Project { let buffer_id = buffer.remote_id(); if self.is_local() { - let lang_server = match self.primary_language_servers_for_buffer(buffer, cx) { + let lang_server = match self.primary_language_server_for_buffer(buffer, cx) { Some((_, server)) => server.clone(), _ => return Task::ready(Ok(Default::default())), }; @@ -4545,7 +4567,7 @@ impl Project { ) -> Task>> { let buffer = buffer_handle.read(cx); let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end); - self.request_lsp(buffer_handle.clone(), GetCodeActions { range }, cx) + self.request_primary_lsp(buffer_handle.clone(), GetCodeActions { range }, cx) } pub fn apply_code_action( @@ -4901,7 +4923,7 @@ impl Project { cx: &mut ModelContext, ) -> Task>>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp(buffer, PrepareRename { position }, cx) + self.request_primary_lsp(buffer, PrepareRename { position }, cx) } pub fn perform_rename( @@ -4913,7 +4935,7 @@ impl Project { cx: &mut ModelContext, ) -> Task> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_lsp( + self.request_primary_lsp( buffer, PerformRename { position, @@ -4940,7 +4962,7 @@ impl Project { .tab_size, ) }); - self.request_lsp( + self.request_primary_lsp( buffer.clone(), OnTypeFormatting { position, @@ -4967,7 +4989,7 @@ impl Project { let lsp_request = InlayHints { range }; if self.is_local() { - let lsp_request_task = self.request_lsp(buffer_handle.clone(), lsp_request, cx); + let lsp_request_task = self.request_primary_lsp(buffer_handle.clone(), lsp_request, cx); cx.spawn(|_, mut cx| async move { buffer_handle .update(&mut cx, |buffer, _| { @@ -5223,10 +5245,28 @@ impl Project { } } - // TODO: Wire this up to allow selecting a server? + fn request_primary_lsp( + &self, + buffer_handle: ModelHandle, + request: R, + cx: &mut ModelContext, + ) -> Task> + where + ::Result: Send, + { + let buffer = buffer_handle.read(cx); + let server_id = match self.primary_language_server_for_buffer(buffer, cx) { + Some((_, server)) => server.server_id(), + None => return Task::ready(Ok(Default::default())), + }; + + self.request_lsp(buffer_handle, server_id, request, cx) + } + fn request_lsp( &self, buffer_handle: ModelHandle, + server_id: LanguageServerId, request: R, cx: &mut ModelContext, ) -> Task> @@ -5236,10 +5276,11 @@ impl Project { let buffer = buffer_handle.read(cx); if self.is_local() { let file = File::from_dyn(buffer.file()).and_then(File::as_local); - if let Some((file, language_server)) = file.zip( - self.primary_language_servers_for_buffer(buffer, cx) - .map(|(_, server)| server.clone()), - ) { + let language_server = self + .language_server_for_buffer(buffer, server_id, cx) + .map(|(_, server)| server.clone()); + + if let (Some(file), Some(language_server)) = (file, language_server) { let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx); return cx.spawn(|this, cx| async move { if !request.check_capabilities(language_server.capabilities()) { @@ -5294,6 +5335,7 @@ impl Project { } }); } + Task::ready(Ok(Default::default())) } @@ -6842,7 +6884,7 @@ impl Project { let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version()); let response = this .update(&mut cx, |this, cx| { - this.request_lsp(buffer_handle, request, cx) + this.request_primary_lsp(buffer_handle, request, cx) }) .await?; this.update(&mut cx, |this, cx| { @@ -7558,7 +7600,7 @@ impl Project { }) } - fn primary_language_servers_for_buffer( + fn primary_language_server_for_buffer( &self, buffer: &Buffer, cx: &AppContext, From 40ce099780f51b7863bb45d8237f754b8195e40b Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 15 Aug 2023 16:34:15 -0400 Subject: [PATCH 02/27] Use originating language server to resolve additional completion edits --- crates/language/src/buffer.rs | 1 + crates/language/src/proto.rs | 2 ++ crates/project/src/lsp_command.rs | 3 ++- crates/project/src/project.rs | 3 ++- crates/rpc/proto/zed.proto | 3 ++- crates/rpc/src/rpc.rs | 2 +- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 0b10432a9f4747d93ff974ac72ddbbb6783fe676..ec5f9541f58fde77287e43436d1c9cce1f84bd54 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -149,6 +149,7 @@ pub struct Completion { pub old_range: Range, pub new_text: String, pub label: CodeLabel, + pub server_id: LanguageServerId, pub lsp_completion: lsp::CompletionItem, } diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 0de3f704c7f29e4d1cff8f3fe371d0fad36d1f42..c463c7a01c481a2211431d865d00b0124fb2fda6 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -433,6 +433,7 @@ pub fn serialize_completion(completion: &Completion) -> proto::Completion { old_start: Some(serialize_anchor(&completion.old_range.start)), old_end: Some(serialize_anchor(&completion.old_range.end)), new_text: completion.new_text.clone(), + server_id: completion.server_id.0 as u64, lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(), } } @@ -465,6 +466,7 @@ pub async fn deserialize_completion( lsp_completion.filter_text.as_deref(), ) }), + server_id: LanguageServerId(completion.server_id as usize), lsp_completion, }) } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index a8692257d8032fdca5667c2089249e806b241e34..ad5b63ae2a65809f1e7f5d64e501c9016562406b 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1338,7 +1338,7 @@ impl LspCommand for GetCompletions { completions: Option, _: ModelHandle, buffer: ModelHandle, - _: LanguageServerId, + server_id: LanguageServerId, cx: AsyncAppContext, ) -> Result> { let completions = if let Some(completions) = completions { @@ -1425,6 +1425,7 @@ impl LspCommand for GetCompletions { lsp_completion.filter_text.as_deref(), ) }), + server_id, lsp_completion, } }) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f85460770d9d55e2d1d47b9380101ddf47977bed..7986d6faecc392298a0ccaf3cf60dd2a2f643526 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4470,7 +4470,8 @@ impl Project { let buffer_id = buffer.remote_id(); if self.is_local() { - let lang_server = match self.primary_language_server_for_buffer(buffer, cx) { + let server_id = completion.server_id; + let lang_server = match self.language_server_for_buffer(buffer, server_id, cx) { Some((_, server)) => server.clone(), _ => return Task::ready(Ok(Default::default())), }; diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index a0b98372b10f05248e1c49fc14844b9de61274f9..2bc31dccc32cc5d6db8dbc812f7491172b0e026a 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -630,7 +630,8 @@ message Completion { Anchor old_start = 1; Anchor old_end = 2; string new_text = 3; - bytes lsp_completion = 4; + uint64 server_id = 4; + bytes lsp_completion = 5; } message GetCodeActions { diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 6b430d90e46072a6f885b60a4e912978ed26c6a2..3cb8b6bffa2ca1549ca854db39e46ef8fc8634a7 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 59; +pub const PROTOCOL_VERSION: u32 = 60; From 8839b07a25911197763a63f5ff32af66f30d6daf Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Aug 2023 11:53:05 -0400 Subject: [PATCH 03/27] Add broken Tailwind language server --- crates/zed/src/languages.rs | 7 +- crates/zed/src/languages/tailwind.rs | 126 +++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 crates/zed/src/languages/tailwind.rs diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index eb31c08dd238d64595d7a0dc4c5b1760951d88a9..0f8699aff262381992555bb00532178bee3bc787 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -18,6 +18,7 @@ mod python; mod ruby; mod rust; mod svelte; +mod tailwind; mod typescript; mod yaml; @@ -116,7 +117,11 @@ pub fn init(languages: Arc, node_runtime: Arc) { language( "html", tree_sitter_html::language(), - vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))], + vec![ + // Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())), + // Arc::new(emmet::EmmetLspAdapter::new(node_runtime.clone())), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), + ], ); language( "ruby", diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs new file mode 100644 index 0000000000000000000000000000000000000000..79f65eb5788ecda20dcb890e0ba9d6bb830c2188 --- /dev/null +++ b/crates/zed/src/languages/tailwind.rs @@ -0,0 +1,126 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::StreamExt; +use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use lsp::LanguageServerBinary; +use node_runtime::NodeRuntime; +use serde_json::json; +use smol::fs; +use std::{ + any::Any, + ffi::OsString, + path::{Path, PathBuf}, + sync::Arc, +}; +use util::ResultExt; + +const SERVER_PATH: &'static str = "node_modules/.bin/tailwindcss-language-server"; + +fn server_binary_arguments(server_path: &Path) -> Vec { + vec![server_path.into(), "--stdio".into()] +} + +pub struct TailwindLspAdapter { + node: Arc, +} + +impl TailwindLspAdapter { + pub fn new(node: Arc) -> Self { + TailwindLspAdapter { node } + } +} + +#[async_trait] +impl LspAdapter for TailwindLspAdapter { + async fn name(&self) -> LanguageServerName { + LanguageServerName("tailwindcss-language-server".into()) + } + + async fn fetch_latest_server_version( + &self, + _: &dyn LspAdapterDelegate, + ) -> Result> { + Ok(Box::new( + self.node + .npm_package_latest_version("@tailwindcss/language-server") + .await?, + ) as Box<_>) + } + + async fn fetch_server_binary( + &self, + version: Box, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + let version = version.downcast::().unwrap(); + let server_path = container_dir.join(SERVER_PATH); + + if fs::metadata(&server_path).await.is_err() { + dbg!(&container_dir, version.as_str()); + self.node + .npm_install_packages( + &container_dir, + [("@tailwindcss/language-server", version.as_str())], + ) + .await?; + } + + Ok(LanguageServerBinary { + path: self.node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } + + async fn cached_server_binary( + &self, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await + } + + async fn initialization_options(&self) -> Option { + Some(json!({ + "provideFormatter": true + })) + } +} + +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} From e54f16f37205a75b6b9ff37eda63c8315eece50c Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Aug 2023 21:25:17 -0400 Subject: [PATCH 04/27] Register initial request handlers before launching server --- crates/lsp/src/lsp.rs | 2 +- crates/project/src/project.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index e0ae64d8069c08b12e11b8b12155892dc974ae0d..66ef33418b80dccd81fcf661255b84946a161989 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -282,7 +282,7 @@ impl LanguageServer { stdout.read_exact(&mut buffer).await?; if let Ok(message) = str::from_utf8(&buffer) { - log::trace!("incoming message:{}", message); + log::trace!("incoming message: {}", message); for handler in io_handlers.lock().values_mut() { handler(true, message); } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7986d6faecc392298a0ccaf3cf60dd2a2f643526..4e16ee3da2478817fd9d33369a71412b4958f64f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2748,10 +2748,8 @@ impl Project { ) -> Result>> { let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await; let language_server = match pending_server.task.await? { - Some(server) => server.initialize(initialization_options).await?, - None => { - return Ok(None); - } + Some(server) => server, + None => return Ok(None), }; language_server @@ -2909,7 +2907,9 @@ impl Project { ) .ok(); - Ok(Some(language_server)) + Ok(Some( + language_server.initialize(initialization_options).await?, + )) } fn insert_newly_running_language_server( From 4f0fa21c04db0aa61074732bce1789c2c7e3955e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 17 Aug 2023 15:02:33 +0300 Subject: [PATCH 05/27] Provide more data to tailwind langserver Tailwind needs user languages and language-to-language-id mappings to start providing completions for those languages. And also it has emmet completions disabled by default, enable them. --- crates/zed/src/languages/tailwind.rs | 35 +++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index 79f65eb5788ecda20dcb890e0ba9d6bb830c2188..0290bf33342d9bb7e649301934fd7f5b724c90ba 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -1,10 +1,15 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; -use futures::StreamExt; +use collections::HashMap; +use futures::{ + future::{self, BoxFuture}, + FutureExt, StreamExt, +}; +use gpui::AppContext; use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; -use serde_json::json; +use serde_json::{json, Value}; use smol::fs; use std::{ any::Any, @@ -89,9 +94,33 @@ impl LspAdapter for TailwindLspAdapter { async fn initialization_options(&self) -> Option { Some(json!({ - "provideFormatter": true + "provideFormatter": true, + "userLanguages": { + "html": "html", + "css": "css", + "javascript": "javascript", + }, })) } + + fn workspace_configuration(&self, _: &mut AppContext) -> Option> { + Some( + future::ready(json!({ + "tailwindCSS": { + "emmetCompletions": true, + } + })) + .boxed(), + ) + } + + async fn language_ids(&self) -> HashMap { + HashMap::from([ + ("HTML".to_string(), "html".to_string()), + ("CSS".to_string(), "css".to_string()), + ("JavaScript".to_string(), "javascript".to_string()), + ]) + } } async fn get_cached_server_binary( From a979e3212762a9ed67a43cee3ef281361cf83ab0 Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 17 Aug 2023 21:57:39 -0400 Subject: [PATCH 06/27] Utilize LSP completion `itemDefaults` a bit Tailwind likes to throw a lot of completion data at us, this gets it to send less. Previously it would respond to a completion with 2.5 MB JSON blob, now it is more like 0.8 MB. Relies on a local copy of lsp-types with the `itemDefaults` field added. I don't have write perms to push to our fork of the crate atm, sorry :) --- Cargo.lock | 4 +- crates/lsp/Cargo.toml | 2 +- crates/lsp/src/lsp.rs | 8 ++++ crates/project/src/lsp_command.rs | 69 +++++++++++++++++++++++-------- 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ff9981a6a0bd52e9d1ac24169fbc428db1ff0ce..4e1d0b53a6102df67ab2855a38c28c166d8061d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4185,9 +4185,7 @@ dependencies = [ [[package]] name = "lsp-types" -version = "0.94.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" +version = "0.94.1" dependencies = [ "bitflags 1.3.2", "serde", diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 47e0995c852ab49417bdfafe714123c85db3d223..c3ff37dc61bf5ca8f22f842eb7883481e7c65da1 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -20,7 +20,7 @@ anyhow.workspace = true async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true } futures.workspace = true log.workspace = true -lsp-types = "0.94" +lsp-types = { path = "../../../lsp-types" } parking_lot.workspace = true postage.workspace = true serde.workspace = true diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 66ef33418b80dccd81fcf661255b84946a161989..f39d97aeb56ae2ceaa919a59d6874a31e9328351 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -423,6 +423,14 @@ impl LanguageServer { }), ..Default::default() }), + completion_list: Some(CompletionListCapability { + item_defaults: Some(vec![ + "commitCharacters".to_owned(), + "editRange".to_owned(), + "insertTextMode".to_owned(), + "data".to_owned(), + ]), + }), ..Default::default() }), rename: Some(RenameClientCapabilities { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index ad5b63ae2a65809f1e7f5d64e501c9016562406b..c1bb890d496b771b680084d0cc708d27655f2988 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -15,7 +15,10 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped, }; -use lsp::{DocumentHighlightKind, LanguageServer, LanguageServerId, ServerCapabilities}; +use lsp::{ + CompletionListItemDefaultsEditRange, DocumentHighlightKind, LanguageServer, LanguageServerId, + ServerCapabilities, +}; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions { @@ -1341,10 +1344,16 @@ impl LspCommand for GetCompletions { server_id: LanguageServerId, cx: AsyncAppContext, ) -> Result> { + let mut response_list = None; let completions = if let Some(completions) = completions { match completions { lsp::CompletionResponse::Array(completions) => completions, - lsp::CompletionResponse::List(list) => list.items, + + lsp::CompletionResponse::List(mut list) => { + let items = std::mem::take(&mut list.items); + response_list = Some(list); + items + } } } else { Default::default() @@ -1354,6 +1363,7 @@ impl LspCommand for GetCompletions { let language = buffer.language().cloned(); let snapshot = buffer.snapshot(); let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left); + let mut range_for_token = None; completions .into_iter() @@ -1374,6 +1384,7 @@ impl LspCommand for GetCompletions { edit.new_text.clone(), ) } + // If the language server does not provide a range, then infer // the range based on the syntax tree. None => { @@ -1381,27 +1392,51 @@ impl LspCommand for GetCompletions { log::info!("completion out of expected range"); return None; } - let Range { start, end } = range_for_token - .get_or_insert_with(|| { - let offset = self.position.to_offset(&snapshot); - let (range, kind) = snapshot.surrounding_word(offset); - if kind == Some(CharKind::Word) { - range - } else { - offset..offset - } - }) - .clone(); + + let default_edit_range = response_list + .as_ref() + .and_then(|list| list.item_defaults.as_ref()) + .and_then(|defaults| defaults.edit_range.as_ref()) + .and_then(|range| match range { + CompletionListItemDefaultsEditRange::Range(r) => Some(r), + _ => None, + }); + + let range = if let Some(range) = default_edit_range { + let range = range_from_lsp(range.clone()); + let start = snapshot.clip_point_utf16(range.start, Bias::Left); + let end = snapshot.clip_point_utf16(range.end, Bias::Left); + if start != range.start.0 || end != range.end.0 { + log::info!("completion out of expected range"); + return None; + } + + snapshot.anchor_before(start)..snapshot.anchor_after(end) + } else { + range_for_token + .get_or_insert_with(|| { + let offset = self.position.to_offset(&snapshot); + let (range, kind) = snapshot.surrounding_word(offset); + let range = if kind == Some(CharKind::Word) { + range + } else { + offset..offset + }; + + snapshot.anchor_before(range.start) + ..snapshot.anchor_after(range.end) + }) + .clone() + }; + let text = lsp_completion .insert_text .as_ref() .unwrap_or(&lsp_completion.label) .clone(); - ( - snapshot.anchor_before(start)..snapshot.anchor_after(end), - text, - ) + (range, text) } + Some(lsp::CompletionTextEdit::InsertAndReplace(_)) => { log::info!("unsupported insert/replace completion"); return None; From c842e8707971e0f3f77a846a0fe04be7a6fe6fea Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 18 Aug 2023 11:57:19 -0400 Subject: [PATCH 07/27] Use updated lsp-types fork branch --- Cargo.lock | 1 + crates/lsp/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 4e1d0b53a6102df67ab2855a38c28c166d8061d2..e27b5756aa9f197a8aac1659ad6e26720a4b100d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4186,6 +4186,7 @@ dependencies = [ [[package]] name = "lsp-types" version = "0.94.1" +source = "git+https://github.com/zed-industries/lsp-types?branch=updated-completion-list-item-defaults#90a040a1d195687bd19e1df47463320a44e93d7a" dependencies = [ "bitflags 1.3.2", "serde", diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index c3ff37dc61bf5ca8f22f842eb7883481e7c65da1..653c25b7bb21de87b793b4b9a94e5e73dc79e1c0 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -20,7 +20,7 @@ anyhow.workspace = true async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553", optional = true } futures.workspace = true log.workspace = true -lsp-types = { path = "../../../lsp-types" } +lsp-types = { git = "https://github.com/zed-industries/lsp-types", branch = "updated-completion-list-item-defaults" } parking_lot.workspace = true postage.workspace = true serde.workspace = true From 007d1b09ac970cfc27736dd3d798a96041d84387 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:35:20 +0200 Subject: [PATCH 08/27] Z 2819 (#2872) This PR adds new config option to language config called `word_boundaries` that controls which characters should be recognised as word boundary for a given language. This will improve our UX for languages such as PHP and Tailwind. Release Notes: - Improved completions for PHP [#1820](https://github.com/zed-industries/community/issues/1820) --------- Co-authored-by: Julia Risley --- crates/editor/src/editor.rs | 1 - crates/editor/src/items.rs | 29 +++++++++++++---------- crates/editor/src/movement.rs | 30 +++++++++++++++++++----- crates/editor/src/multi_buffer.rs | 11 +++++---- crates/language/src/buffer.rs | 25 +++++++++++++------- crates/language/src/language.rs | 5 +++- crates/project/src/project.rs | 2 +- crates/project/src/search.rs | 27 ++++++++++++++++----- crates/vim/src/motion.rs | 18 ++++++++------ crates/vim/src/normal/change.rs | 9 ++++--- crates/vim/src/object.rs | 21 ++++++++++------- crates/zed/src/languages/php/config.toml | 1 + 12 files changed, 120 insertions(+), 59 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 256ef2284cd6c3ffbb6f7e9746ddfb8e75a4bdfd..9e24e56efeaa915e766866f7c4427dfd2105ea30 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2654,7 +2654,6 @@ impl Editor { false }); } - fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { let offset = position.to_offset(buffer); let (word_range, kind) = buffer.surrounding_word(offset); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index b99977a60eb45dc3a0a616169067063e6c4e691f..4a2b03bbdf15716b16a8aa989c302d102b88d575 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1028,7 +1028,7 @@ impl SearchableItem for Editor { if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() { ranges.extend( query - .search(excerpt_buffer.as_rope()) + .search(excerpt_buffer, None) .await .into_iter() .map(|range| { @@ -1038,17 +1038,22 @@ impl SearchableItem for Editor { } else { for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) { let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer); - let rope = excerpt.buffer.as_rope().slice(excerpt_range.clone()); - ranges.extend(query.search(&rope).await.into_iter().map(|range| { - let start = excerpt - .buffer - .anchor_after(excerpt_range.start + range.start); - let end = excerpt - .buffer - .anchor_before(excerpt_range.start + range.end); - buffer.anchor_in_excerpt(excerpt.id.clone(), start) - ..buffer.anchor_in_excerpt(excerpt.id.clone(), end) - })); + ranges.extend( + query + .search(&excerpt.buffer, Some(excerpt_range.clone())) + .await + .into_iter() + .map(|range| { + let start = excerpt + .buffer + .anchor_after(excerpt_range.start + range.start); + let end = excerpt + .buffer + .anchor_before(excerpt_range.start + range.end); + buffer.anchor_in_excerpt(excerpt.id.clone(), start) + ..buffer.anchor_in_excerpt(excerpt.id.clone(), end) + }), + ); } } ranges diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index f70436abeb45f248ee8c306613a220655313797f..5917b8b3bdbe9f9f97e89f5567196b0e23226280 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -176,14 +176,21 @@ pub fn line_end( } pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { + let raw_point = point.to_point(map); + let language = map.buffer_snapshot.language_at(raw_point); + find_preceding_boundary(map, point, |left, right| { - (char_kind(left) != char_kind(right) && !right.is_whitespace()) || left == '\n' + (char_kind(language, left) != char_kind(language, right) && !right.is_whitespace()) + || left == '\n' }) } pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { + let raw_point = point.to_point(map); + let language = map.buffer_snapshot.language_at(raw_point); find_preceding_boundary(map, point, |left, right| { - let is_word_start = char_kind(left) != char_kind(right) && !right.is_whitespace(); + let is_word_start = + char_kind(language, left) != char_kind(language, right) && !right.is_whitespace(); let is_subword_start = left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase(); is_word_start || is_subword_start || left == '\n' @@ -191,14 +198,20 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis } pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { + let raw_point = point.to_point(map); + let language = map.buffer_snapshot.language_at(raw_point); find_boundary(map, point, |left, right| { - (char_kind(left) != char_kind(right) && !left.is_whitespace()) || right == '\n' + (char_kind(language, left) != char_kind(language, right) && !left.is_whitespace()) + || right == '\n' }) } pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { + let raw_point = point.to_point(map); + let language = map.buffer_snapshot.language_at(raw_point); find_boundary(map, point, |left, right| { - let is_word_end = (char_kind(left) != char_kind(right)) && !left.is_whitespace(); + let is_word_end = + (char_kind(language, left) != char_kind(language, right)) && !left.is_whitespace(); let is_subword_end = left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase(); is_word_end || is_subword_end || right == '\n' @@ -385,10 +398,15 @@ pub fn find_boundary_in_line( } pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool { + let raw_point = point.to_point(map); + let language = map.buffer_snapshot.language_at(raw_point); let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left); let text = &map.buffer_snapshot; - let next_char_kind = text.chars_at(ix).next().map(char_kind); - let prev_char_kind = text.reversed_chars_at(ix).next().map(char_kind); + let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(language, c)); + let prev_char_kind = text + .reversed_chars_at(ix) + .next() + .map(|c| char_kind(language, c)); prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word)) } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 8417c411f243938661afdd2756b1621f60507fb8..d4061f25dc6a22b8a3abd79ae0471c811e2b08e0 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1865,13 +1865,16 @@ impl MultiBufferSnapshot { let mut end = start; let mut next_chars = self.chars_at(start).peekable(); let mut prev_chars = self.reversed_chars_at(start).peekable(); + + let language = self.language_at(start); + let kind = |c| char_kind(language, c); let word_kind = cmp::max( - prev_chars.peek().copied().map(char_kind), - next_chars.peek().copied().map(char_kind), + prev_chars.peek().copied().map(kind), + next_chars.peek().copied().map(kind), ); for ch in prev_chars { - if Some(char_kind(ch)) == word_kind && ch != '\n' { + if Some(kind(ch)) == word_kind && ch != '\n' { start -= ch.len_utf8(); } else { break; @@ -1879,7 +1882,7 @@ impl MultiBufferSnapshot { } for ch in next_chars { - if Some(char_kind(ch)) == word_kind && ch != '\n' { + if Some(kind(ch)) == word_kind && ch != '\n' { end += ch.len_utf8(); } else { break; diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index ec5f9541f58fde77287e43436d1c9cce1f84bd54..eff95460c41beac3b5b096a65160765ce7f98fd3 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2174,13 +2174,16 @@ impl BufferSnapshot { let mut end = start; let mut next_chars = self.chars_at(start).peekable(); let mut prev_chars = self.reversed_chars_at(start).peekable(); + + let language = self.language_at(start); + let kind = |c| char_kind(language, c); let word_kind = cmp::max( - prev_chars.peek().copied().map(char_kind), - next_chars.peek().copied().map(char_kind), + prev_chars.peek().copied().map(kind), + next_chars.peek().copied().map(kind), ); for ch in prev_chars { - if Some(char_kind(ch)) == word_kind && ch != '\n' { + if Some(kind(ch)) == word_kind && ch != '\n' { start -= ch.len_utf8(); } else { break; @@ -2188,7 +2191,7 @@ impl BufferSnapshot { } for ch in next_chars { - if Some(char_kind(ch)) == word_kind && ch != '\n' { + if Some(kind(ch)) == word_kind && ch != '\n' { end += ch.len_utf8(); } else { break; @@ -2985,14 +2988,18 @@ pub fn contiguous_ranges( }) } -pub fn char_kind(c: char) -> CharKind { +pub fn char_kind(language: Option<&Arc>, c: char) -> CharKind { if c.is_whitespace() { - CharKind::Whitespace + return CharKind::Whitespace; } else if c.is_alphanumeric() || c == '_' { - CharKind::Word - } else { - CharKind::Punctuation + return CharKind::Word; + } + if let Some(language) = language { + if language.config.word_characters.contains(&c) { + return CharKind::Word; + } } + CharKind::Punctuation } /// Find all of the ranges of whitespace that occur at the ends of lines diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 223f5679aed05c8a5fee158f3a14959133c9151e..82245d67ca0487adb33aac8ec124f658948c3509 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -11,7 +11,7 @@ mod buffer_tests; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; -use collections::HashMap; +use collections::{HashMap, HashSet}; use futures::{ channel::oneshot, future::{BoxFuture, Shared}, @@ -344,6 +344,8 @@ pub struct LanguageConfig { pub block_comment: Option<(Arc, Arc)>, #[serde(default)] pub overrides: HashMap, + #[serde(default)] + pub word_characters: HashSet, } #[derive(Debug, Default)] @@ -411,6 +413,7 @@ impl Default for LanguageConfig { block_comment: Default::default(), overrides: Default::default(), collapsed_placeholder: Default::default(), + word_characters: Default::default(), } } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4e16ee3da2478817fd9d33369a71412b4958f64f..4b3c80c08a639217fd62be176335644969afe2ac 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5193,7 +5193,7 @@ impl Project { snapshot.file().map(|file| file.path().as_ref()), ) { query - .search(snapshot.as_rope()) + .search(&snapshot, None) .await .iter() .map(|range| { diff --git a/crates/project/src/search.rs b/crates/project/src/search.rs index 71a0b70b81b7a6f08a2584064a55a07743096b2c..bfa34c04228017e836afafae127a9d593b2d4fa5 100644 --- a/crates/project/src/search.rs +++ b/crates/project/src/search.rs @@ -3,7 +3,7 @@ use anyhow::{Context, Result}; use client::proto; use globset::{Glob, GlobMatcher}; use itertools::Itertools; -use language::{char_kind, Rope}; +use language::{char_kind, BufferSnapshot}; use regex::{Regex, RegexBuilder}; use smol::future::yield_now; use std::{ @@ -23,6 +23,7 @@ pub enum SearchQuery { files_to_include: Vec, files_to_exclude: Vec, }, + Regex { regex: Regex, query: Arc, @@ -193,12 +194,24 @@ impl SearchQuery { } } - pub async fn search(&self, rope: &Rope) -> Vec> { + pub async fn search( + &self, + buffer: &BufferSnapshot, + subrange: Option>, + ) -> Vec> { const YIELD_INTERVAL: usize = 20000; if self.as_str().is_empty() { return Default::default(); } + let language = buffer.language_at(0); + let rope = if let Some(range) = subrange { + buffer.as_rope().slice(range) + } else { + buffer.as_rope().clone() + }; + + let kind = |c| char_kind(language, c); let mut matches = Vec::new(); match self { @@ -215,10 +228,10 @@ impl SearchQuery { let mat = mat.unwrap(); if *whole_word { - let prev_kind = rope.reversed_chars_at(mat.start()).next().map(char_kind); - let start_kind = char_kind(rope.chars_at(mat.start()).next().unwrap()); - let end_kind = char_kind(rope.reversed_chars_at(mat.end()).next().unwrap()); - let next_kind = rope.chars_at(mat.end()).next().map(char_kind); + let prev_kind = rope.reversed_chars_at(mat.start()).next().map(kind); + let start_kind = kind(rope.chars_at(mat.start()).next().unwrap()); + let end_kind = kind(rope.reversed_chars_at(mat.end()).next().unwrap()); + let next_kind = rope.chars_at(mat.end()).next().map(kind); if Some(start_kind) == prev_kind || Some(end_kind) == next_kind { continue; } @@ -226,6 +239,7 @@ impl SearchQuery { matches.push(mat.start()..mat.end()) } } + Self::Regex { regex, multiline, .. } => { @@ -263,6 +277,7 @@ impl SearchQuery { } } } + matches } diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index acf9d46ad3e66ff992ce717d00fbc39bd85bbd1b..1defee70da3e33502b8247e20e75554ff0f19bbc 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -439,11 +439,12 @@ pub(crate) fn next_word_start( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { + let language = map.buffer_snapshot.language_at(point.to_point(map)); for _ in 0..times { let mut crossed_newline = false; point = movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); let at_newline = right == '\n'; let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) @@ -463,11 +464,12 @@ fn next_word_end( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { + let language = map.buffer_snapshot.language_at(point.to_point(map)); for _ in 0..times { *point.column_mut() += 1; point = movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); @@ -493,12 +495,13 @@ fn previous_word_start( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { + let language = map.buffer_snapshot.language_at(point.to_point(map)); for _ in 0..times { // This works even though find_preceding_boundary is called for every character in the line containing // cursor because the newline is checked only once. point = movement::find_preceding_boundary(map, point, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); (left_kind != right_kind && !right.is_whitespace()) || left == '\n' }); @@ -508,6 +511,7 @@ fn previous_word_start( fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoint { let mut last_point = DisplayPoint::new(from.row(), 0); + let language = map.buffer_snapshot.language_at(from.to_point(map)); for (ch, point) in map.chars_at(last_point) { if ch == '\n' { return from; @@ -515,7 +519,7 @@ fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoi last_point = point; - if char_kind(ch) != CharKind::Whitespace { + if char_kind(language, ch) != CharKind::Whitespace { break; } } diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index d226c704108b6bd036c08f1700cbb7812a3c1c51..50bc049a3aa96d37ae9acce6a1505369333bf534 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -82,16 +82,19 @@ fn expand_changed_word_selection( ignore_punctuation: bool, ) -> bool { if times.is_none() || times.unwrap() == 1 { + let language = map + .buffer_snapshot + .language_at(selection.start.to_point(map)); let in_word = map .chars_at(selection.head()) .next() - .map(|(c, _)| char_kind(c) != CharKind::Whitespace) + .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace) .unwrap_or_default(); if in_word { selection.end = movement::find_boundary(map, selection.end, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index 85e6eab6927a8370ea9646fa43baa383bf13e8b0..d0bcad36c22a5e8775418d571c46d5bf7ab38883 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -122,17 +122,18 @@ fn in_word( ignore_punctuation: bool, ) -> Option> { // Use motion::right so that we consider the character under the cursor when looking for the start + let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); let start = movement::find_preceding_boundary_in_line( map, right(map, relative_to, 1), |left, right| { - char_kind(left).coerce_punctuation(ignore_punctuation) - != char_kind(right).coerce_punctuation(ignore_punctuation) + char_kind(language, left).coerce_punctuation(ignore_punctuation) + != char_kind(language, right).coerce_punctuation(ignore_punctuation) }, ); let end = movement::find_boundary_in_line(map, relative_to, |left, right| { - char_kind(left).coerce_punctuation(ignore_punctuation) - != char_kind(right).coerce_punctuation(ignore_punctuation) + char_kind(language, left).coerce_punctuation(ignore_punctuation) + != char_kind(language, right).coerce_punctuation(ignore_punctuation) }); Some(start..end) @@ -155,10 +156,11 @@ fn around_word( relative_to: DisplayPoint, ignore_punctuation: bool, ) -> Option> { + let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); let in_word = map .chars_at(relative_to) .next() - .map(|(c, _)| char_kind(c) != CharKind::Whitespace) + .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace) .unwrap_or(false); if in_word { @@ -182,20 +184,21 @@ fn around_next_word( relative_to: DisplayPoint, ignore_punctuation: bool, ) -> Option> { + let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); // Get the start of the word let start = movement::find_preceding_boundary_in_line( map, right(map, relative_to, 1), |left, right| { - char_kind(left).coerce_punctuation(ignore_punctuation) - != char_kind(right).coerce_punctuation(ignore_punctuation) + char_kind(language, left).coerce_punctuation(ignore_punctuation) + != char_kind(language, right).coerce_punctuation(ignore_punctuation) }, ); let mut word_found = false; let end = movement::find_boundary(map, relative_to, |left, right| { - let left_kind = char_kind(left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n'; diff --git a/crates/zed/src/languages/php/config.toml b/crates/zed/src/languages/php/config.toml index 19acb949e25c66be9890f9931f49a8e82f8bb972..60dd2335551bc746ec6b26f2afe039b377e18442 100644 --- a/crates/zed/src/languages/php/config.toml +++ b/crates/zed/src/languages/php/config.toml @@ -10,3 +10,4 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, ] collapsed_placeholder = "/* ... */" +word_characters = ["$"] From a35b3f39c5c692a5d6ab1df7c1f26d933f3fb601 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 22 Aug 2023 12:41:59 +0300 Subject: [PATCH 09/27] Expand word characters for html and css --- crates/zed/src/languages/css/config.toml | 1 + crates/zed/src/languages/html/config.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/zed/src/languages/css/config.toml b/crates/zed/src/languages/css/config.toml index ba9660c4ed1f9b2de49558fc919e521d3f2f16f1..05de4be8a3fbe3b7e321ad6e3d335fbf7a1917c2 100644 --- a/crates/zed/src/languages/css/config.toml +++ b/crates/zed/src/languages/css/config.toml @@ -8,3 +8,4 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] }, { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, ] +word_characters = ["-"] diff --git a/crates/zed/src/languages/html/config.toml b/crates/zed/src/languages/html/config.toml index 077a421ce1b0e0375b56d2fc8cac48a601e03a65..164e095cee9a71f62531fc576c44f40a1c4e9260 100644 --- a/crates/zed/src/languages/html/config.toml +++ b/crates/zed/src/languages/html/config.toml @@ -10,3 +10,4 @@ brackets = [ { start = "<", end = ">", close = true, newline = true, not_in = ["comment", "string"] }, { start = "!--", end = " --", close = true, newline = false, not_in = ["comment", "string"] }, ] +word_characters = ["-"] From 814896de3f5655eb9c529e1e39b497be73cf9a0b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 22 Aug 2023 12:51:14 +0300 Subject: [PATCH 10/27] Reenable html, remove emmet due to the lack of the code --- crates/zed/src/languages.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 0f8699aff262381992555bb00532178bee3bc787..2cdd540e8a26fe55a4ed4a3947541e49f863d4c8 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -118,8 +118,7 @@ pub fn init(languages: Arc, node_runtime: Arc) { "html", tree_sitter_html::language(), vec![ - // Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())), - // Arc::new(emmet::EmmetLspAdapter::new(node_runtime.clone())), + Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())), Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ], ); From affb73d651bff6e21116da894ff1c42708ba6ba3 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 22 Aug 2023 23:36:04 -0400 Subject: [PATCH 11/27] Only generate workspace/configuration for relevant adapter --- crates/language/src/language.rs | 46 ++---------------- crates/project/src/project.rs | 64 +++++++++++++------------- crates/zed/src/languages/json.rs | 45 +++++++++--------- crates/zed/src/languages/tailwind.rs | 29 ++++++------ crates/zed/src/languages/typescript.rs | 22 ++++----- crates/zed/src/languages/yaml.rs | 23 +++++---- 6 files changed, 95 insertions(+), 134 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 82245d67ca0487adb33aac8ec124f658948c3509..58732355a55992e2831389853e89b4e7bb205ff6 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -46,7 +46,7 @@ use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; use unicase::UniCase; use util::{http::HttpClient, paths::PathExt}; -use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture}; +use util::{post_inc, ResultExt, TryFutureExt as _, UnwrapFuture}; #[cfg(any(test, feature = "test-support"))] use futures::channel::mpsc; @@ -175,10 +175,7 @@ impl CachedLspAdapter { self.adapter.code_action_kinds() } - pub fn workspace_configuration( - &self, - cx: &mut AppContext, - ) -> Option> { + pub fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { self.adapter.workspace_configuration(cx) } @@ -287,8 +284,8 @@ pub trait LspAdapter: 'static + Send + Sync { None } - fn workspace_configuration(&self, _: &mut AppContext) -> Option> { - None + fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + futures::future::ready(serde_json::json!({})).boxed() } fn code_action_kinds(&self) -> Option> { @@ -685,41 +682,6 @@ impl LanguageRegistry { result } - pub fn workspace_configuration(&self, cx: &mut AppContext) -> Task { - let lsp_adapters = { - let state = self.state.read(); - state - .available_languages - .iter() - .filter(|l| !l.loaded) - .flat_map(|l| l.lsp_adapters.clone()) - .chain( - state - .languages - .iter() - .flat_map(|language| &language.adapters) - .map(|adapter| adapter.adapter.clone()), - ) - .collect::>() - }; - - let mut language_configs = Vec::new(); - for adapter in &lsp_adapters { - if let Some(language_config) = adapter.workspace_configuration(cx) { - language_configs.push(language_config); - } - } - - cx.background().spawn(async move { - let mut config = serde_json::json!({}); - let language_configs = futures::future::join_all(language_configs).await; - for language_config in language_configs { - merge_json_value_into(language_config, &mut config); - } - config - }) - } - pub fn add(&self, language: Arc) { self.state.write().add(language); } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 4b3c80c08a639217fd62be176335644969afe2ac..63b6786d8c51ec1046f91a2a25464ee0d1f5626a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -603,7 +603,7 @@ impl Project { cx.observe_global::(Self::on_settings_changed) ], _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx), - _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), + _maintain_workspace_config: Self::maintain_workspace_config(cx), active_entry: None, languages, client, @@ -673,7 +673,7 @@ impl Project { collaborators: Default::default(), join_project_response_message_id: response.message_id, _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx), - _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx), + _maintain_workspace_config: Self::maintain_workspace_config(cx), languages, user_store: user_store.clone(), fs, @@ -2441,35 +2441,42 @@ impl Project { }) } - fn maintain_workspace_config( - languages: Arc, - cx: &mut ModelContext, - ) -> Task<()> { + fn maintain_workspace_config(cx: &mut ModelContext) -> Task<()> { let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel(); let _ = postage::stream::Stream::try_recv(&mut settings_changed_rx); let settings_observation = cx.observe_global::(move |_, _| { *settings_changed_tx.borrow_mut() = (); }); + cx.spawn_weak(|this, mut cx| async move { while let Some(_) = settings_changed_rx.next().await { - let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await; - if let Some(this) = this.upgrade(&cx) { - this.read_with(&cx, |this, _| { - for server_state in this.language_servers.values() { - if let LanguageServerState::Running { server, .. } = server_state { - server - .notify::( - lsp::DidChangeConfigurationParams { - settings: workspace_config.clone(), - }, - ) - .ok(); - } - } - }) - } else { + let Some(this) = this.upgrade(&cx) else { break; + }; + + let servers: Vec<_> = this.read_with(&cx, |this, _| { + this.language_servers + .values() + .filter_map(|state| match state { + LanguageServerState::Starting(_) => None, + LanguageServerState::Running { + adapter, server, .. + } => Some((adapter.clone(), server.clone())), + }) + .collect() + }); + + for (adapter, server) in servers { + let workspace_config = + cx.update(|cx| adapter.workspace_configuration(cx)).await; + server + .notify::( + lsp::DidChangeConfigurationParams { + settings: workspace_config.clone(), + }, + ) + .ok(); } } @@ -2584,7 +2591,6 @@ impl Project { let state = LanguageServerState::Starting({ let adapter = adapter.clone(); let server_name = adapter.name.0.clone(); - let languages = self.languages.clone(); let language = language.clone(); let key = key.clone(); @@ -2594,7 +2600,6 @@ impl Project { initialization_options, pending_server, adapter.clone(), - languages, language.clone(), server_id, key, @@ -2698,7 +2703,6 @@ impl Project { initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, - languages: Arc, language: Arc, server_id: LanguageServerId, key: (WorktreeId, LanguageServerName), @@ -2709,7 +2713,6 @@ impl Project { initialization_options, pending_server, adapter.clone(), - languages, server_id, cx, ); @@ -2742,11 +2745,10 @@ impl Project { initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, - languages: Arc, server_id: LanguageServerId, cx: &mut AsyncAppContext, ) -> Result>> { - let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await; + let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx)).await; let language_server = match pending_server.task.await? { Some(server) => server, None => return Ok(None), @@ -2788,12 +2790,12 @@ impl Project { language_server .on_request::({ - let languages = languages.clone(); + let adapter = adapter.clone(); move |params, mut cx| { - let languages = languages.clone(); + let adapter = adapter.clone(); async move { let workspace_config = - cx.update(|cx| languages.workspace_configuration(cx)).await; + cx.update(|cx| adapter.workspace_configuration(cx)).await; Ok(params .items .into_iter() diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index b7e4ab4ba7b32491bbbb8aa025cab543dde113af..225cea0e92064c25600fad240b5d772eb9e76bc3 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -102,7 +102,7 @@ impl LspAdapter for JsonLspAdapter { fn workspace_configuration( &self, cx: &mut AppContext, - ) -> Option> { + ) -> BoxFuture<'static, serde_json::Value> { let action_names = cx.all_action_names().collect::>(); let staff_mode = cx.default_global::().0; let language_names = &self.languages.language_names(); @@ -113,29 +113,28 @@ impl LspAdapter for JsonLspAdapter { }, cx, ); - Some( - future::ready(serde_json::json!({ - "json": { - "format": { - "enable": true, + + future::ready(serde_json::json!({ + "json": { + "format": { + "enable": true, + }, + "schemas": [ + { + "fileMatch": [ + schema_file_match(&paths::SETTINGS), + &*paths::LOCAL_SETTINGS_RELATIVE_PATH, + ], + "schema": settings_schema, }, - "schemas": [ - { - "fileMatch": [ - schema_file_match(&paths::SETTINGS), - &*paths::LOCAL_SETTINGS_RELATIVE_PATH, - ], - "schema": settings_schema, - }, - { - "fileMatch": [schema_file_match(&paths::KEYMAP)], - "schema": KeymapFile::generate_json_schema(&action_names), - } - ] - } - })) - .boxed(), - ) + { + "fileMatch": [schema_file_match(&paths::KEYMAP)], + "schema": KeymapFile::generate_json_schema(&action_names), + } + ] + } + })) + .boxed() } async fn language_ids(&self) -> HashMap { diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index 0290bf33342d9bb7e649301934fd7f5b724c90ba..9a32f69e43a52179996a94ed063ebfb18e65f68d 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -103,23 +103,24 @@ impl LspAdapter for TailwindLspAdapter { })) } - fn workspace_configuration(&self, _: &mut AppContext) -> Option> { - Some( - future::ready(json!({ - "tailwindCSS": { - "emmetCompletions": true, - } - })) - .boxed(), - ) + fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + future::ready(json!({ + "tailwindCSS": { + "emmetCompletions": true, + } + })) + .boxed() } async fn language_ids(&self) -> HashMap { - HashMap::from([ - ("HTML".to_string(), "html".to_string()), - ("CSS".to_string(), "css".to_string()), - ("JavaScript".to_string(), "javascript".to_string()), - ]) + HashMap::from_iter( + [ + ("HTML".to_string(), "html".to_string()), + ("CSS".to_string(), "css".to_string()), + ("JavaScript".to_string(), "javascript".to_string()), + ] + .into_iter(), + ) } } diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 0a47d365b598aa41df1c1fa50aedd7d718aceb87..b7e4438e1f71855c269a45d795f9ddabbd578772 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -202,18 +202,16 @@ impl EsLintLspAdapter { #[async_trait] impl LspAdapter for EsLintLspAdapter { - fn workspace_configuration(&self, _: &mut AppContext) -> Option> { - Some( - future::ready(json!({ - "": { - "validate": "on", - "rulesCustomizations": [], - "run": "onType", - "nodePath": null, - } - })) - .boxed(), - ) + fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + future::ready(json!({ + "": { + "validate": "on", + "rulesCustomizations": [], + "run": "onType", + "nodePath": null, + } + })) + .boxed() } async fn name(&self) -> LanguageServerName { diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index b57c6f5699498706de0b1c91655fb15848b1a2c6..48d7a3cf877602705ccf7037e23c1f98e0c16190 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -86,21 +86,20 @@ impl LspAdapter for YamlLspAdapter { ) -> Option { get_cached_server_binary(container_dir, &self.node).await } - fn workspace_configuration(&self, cx: &mut AppContext) -> Option> { + fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { let tab_size = all_language_settings(None, cx) .language(Some("YAML")) .tab_size; - Some( - future::ready(serde_json::json!({ - "yaml": { - "keyOrdering": false - }, - "[yaml]": { - "editor.tabSize": tab_size, - } - })) - .boxed(), - ) + + future::ready(serde_json::json!({ + "yaml": { + "keyOrdering": false + }, + "[yaml]": { + "editor.tabSize": tab_size, + } + })) + .boxed() } } From 68408f38382163291b23e6aec2985f858d2a381c Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 22 Aug 2023 23:50:40 -0400 Subject: [PATCH 12/27] Add VSCode CSS language server & add Tailwind to .css files --- crates/zed/src/languages.rs | 10 ++- crates/zed/src/languages/css.rs | 126 ++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 crates/zed/src/languages/css.rs diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 2cdd540e8a26fe55a4ed4a3947541e49f863d4c8..42b62cdfa96aef5f2eaf25419a9e5a34bb8c66ec 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -6,6 +6,7 @@ use std::{borrow::Cow, str, sync::Arc}; use util::asset_str; mod c; +mod css; mod elixir; mod go; mod html; @@ -52,7 +53,14 @@ pub fn init(languages: Arc, node_runtime: Arc) { tree_sitter_cpp::language(), vec![Arc::new(c::CLspAdapter)], ); - language("css", tree_sitter_css::language(), vec![]); + language( + "css", + tree_sitter_css::language(), + vec![ + Arc::new(css::CssLspAdapter::new(node_runtime.clone())), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), + ], + ); language( "elixir", tree_sitter_elixir::language(), diff --git a/crates/zed/src/languages/css.rs b/crates/zed/src/languages/css.rs new file mode 100644 index 0000000000000000000000000000000000000000..51db8b8ab8dac4b8fac0985bdbd08a8d2e4faa3c --- /dev/null +++ b/crates/zed/src/languages/css.rs @@ -0,0 +1,126 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::StreamExt; +use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use lsp::LanguageServerBinary; +use node_runtime::NodeRuntime; +use serde_json::json; +use smol::fs; +use std::{ + any::Any, + ffi::OsString, + path::{Path, PathBuf}, + sync::Arc, +}; +use util::ResultExt; + +const SERVER_PATH: &'static str = + "node_modules/vscode-langservers-extracted/bin/vscode-css-language-server"; + +fn server_binary_arguments(server_path: &Path) -> Vec { + vec![server_path.into(), "--stdio".into()] +} + +pub struct CssLspAdapter { + node: Arc, +} + +impl CssLspAdapter { + pub fn new(node: Arc) -> Self { + CssLspAdapter { node } + } +} + +#[async_trait] +impl LspAdapter for CssLspAdapter { + async fn name(&self) -> LanguageServerName { + LanguageServerName("vscode-css-language-server".into()) + } + + async fn fetch_latest_server_version( + &self, + _: &dyn LspAdapterDelegate, + ) -> Result> { + Ok(Box::new( + self.node + .npm_package_latest_version("vscode-langservers-extracted") + .await?, + ) as Box<_>) + } + + async fn fetch_server_binary( + &self, + version: Box, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + let version = version.downcast::().unwrap(); + let server_path = container_dir.join(SERVER_PATH); + + if fs::metadata(&server_path).await.is_err() { + self.node + .npm_install_packages( + &container_dir, + [("vscode-langservers-extracted", version.as_str())], + ) + .await?; + } + + Ok(LanguageServerBinary { + path: self.node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } + + async fn cached_server_binary( + &self, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await + } + + async fn initialization_options(&self) -> Option { + Some(json!({ + "provideFormatter": true + })) + } +} + +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} From a394aaa52461c1d581badc8648dd558b872b811f Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Aug 2023 00:11:15 -0400 Subject: [PATCH 13/27] Add Tailwind server to JS/TS --- crates/zed/src/languages.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 42b62cdfa96aef5f2eaf25419a9e5a34bb8c66ec..8aaa11e1cd9d13c0ae9a429cc0bc7f3e34f2e39f 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -112,6 +112,7 @@ pub fn init(languages: Arc, node_runtime: Arc) { vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ], ); language( @@ -120,6 +121,7 @@ pub fn init(languages: Arc, node_runtime: Arc) { vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ], ); language( From fc457d45f5f529fa4d0ade52c48b62516baa388c Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 25 Aug 2023 18:46:30 -0400 Subject: [PATCH 14/27] Add `word_characters` to language overrides & use for more things Use word_characters to feed completion trigger characters as well and also recognize kebab as a potential sub-word splitter. This is fine for non-kebab-case languages because we'd only ever attempt to split a word with a kebab in it in language scopes which are kebab-cased Co-Authored-By: Max Brunsfeld --- crates/editor/src/editor.rs | 2 + crates/editor/src/editor_tests.rs | 96 ++++++++++++++++++- crates/editor/src/movement.rs | 22 ++--- crates/editor/src/multi_buffer.rs | 10 +- .../src/test/editor_lsp_test_context.rs | 2 +- crates/language/src/buffer.rs | 16 ++-- crates/language/src/language.rs | 9 ++ .../LiveKitBridge/Package.resolved | 4 +- crates/project/src/search.rs | 8 +- crates/vim/src/motion.rs | 22 ++--- crates/vim/src/normal/change.rs | 10 +- crates/vim/src/object.rs | 30 +++--- .../zed/src/languages/javascript/config.toml | 3 + 13 files changed, 178 insertions(+), 56 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9e24e56efeaa915e766866f7c4427dfd2105ea30..bfa804c56ce9ff4515f91e3a1d413227987af258 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2654,6 +2654,7 @@ impl Editor { false }); } + fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { let offset = position.to_offset(buffer); let (word_range, kind) = buffer.surrounding_word(offset); @@ -8878,6 +8879,7 @@ pub fn split_words<'a>(text: &'a str) -> impl std::iter::Iterator(move |_, _| async move { + Ok(Some(lsp::CompletionResponse::Array(vec![ + lsp::CompletionItem { + label: "bg-blue".into(), + ..Default::default() + }, + lsp::CompletionItem { + label: "bg-red".into(), + ..Default::default() + }, + lsp::CompletionItem { + label: "bg-yellow".into(), + ..Default::default() + }, + ]))) + }); + + cx.set_state(r#"

"#); + + // Trigger completion when typing a dash, because the dash is an extra + // word character in the 'element' scope, which contains the cursor. + cx.simulate_keystroke("-"); + cx.foreground().run_until_parked(); + cx.update_editor(|editor, _| { + if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + assert_eq!( + menu.matches.iter().map(|m| &m.string).collect::>(), + &["bg-red", "bg-blue", "bg-yellow"] + ); + } else { + panic!("expected completion menu to be open"); + } + }); + + cx.simulate_keystroke("l"); + cx.foreground().run_until_parked(); + cx.update_editor(|editor, _| { + if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + assert_eq!( + menu.matches.iter().map(|m| &m.string).collect::>(), + &["bg-blue", "bg-yellow"] + ); + } else { + panic!("expected completion menu to be open"); + } + }); + + // When filtering completions, consider the character after the '-' to + // be the start of a subword. + cx.set_state(r#"

"#); + cx.simulate_keystroke("l"); + cx.foreground().run_until_parked(); + cx.update_editor(|editor, _| { + if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + assert_eq!( + menu.matches.iter().map(|m| &m.string).collect::>(), + &["bg-blue", "bg-yellow"] + ); + } else { + panic!("expected completion menu to be open"); + } + }); +} + fn empty_range(row: usize, column: usize) -> Range { let point = DisplayPoint::new(row as u32, column as u32); point..point diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 5917b8b3bdbe9f9f97e89f5567196b0e23226280..d55b2a464f7c2f6ab5894dcaea69f39ab5389304 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -177,20 +177,20 @@ pub fn line_end( pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { let raw_point = point.to_point(map); - let language = map.buffer_snapshot.language_at(raw_point); + let scope = map.buffer_snapshot.language_scope_at(raw_point); find_preceding_boundary(map, point, |left, right| { - (char_kind(language, left) != char_kind(language, right) && !right.is_whitespace()) + (char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace()) || left == '\n' }) } pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { let raw_point = point.to_point(map); - let language = map.buffer_snapshot.language_at(raw_point); + let scope = map.buffer_snapshot.language_scope_at(raw_point); find_preceding_boundary(map, point, |left, right| { let is_word_start = - char_kind(language, left) != char_kind(language, right) && !right.is_whitespace(); + char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace(); let is_subword_start = left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase(); is_word_start || is_subword_start || left == '\n' @@ -199,19 +199,19 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { let raw_point = point.to_point(map); - let language = map.buffer_snapshot.language_at(raw_point); + let scope = map.buffer_snapshot.language_scope_at(raw_point); find_boundary(map, point, |left, right| { - (char_kind(language, left) != char_kind(language, right) && !left.is_whitespace()) + (char_kind(&scope, left) != char_kind(&scope, right) && !left.is_whitespace()) || right == '\n' }) } pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { let raw_point = point.to_point(map); - let language = map.buffer_snapshot.language_at(raw_point); + let scope = map.buffer_snapshot.language_scope_at(raw_point); find_boundary(map, point, |left, right| { let is_word_end = - (char_kind(language, left) != char_kind(language, right)) && !left.is_whitespace(); + (char_kind(&scope, left) != char_kind(&scope, right)) && !left.is_whitespace(); let is_subword_end = left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase(); is_word_end || is_subword_end || right == '\n' @@ -399,14 +399,14 @@ pub fn find_boundary_in_line( pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool { let raw_point = point.to_point(map); - let language = map.buffer_snapshot.language_at(raw_point); + let scope = map.buffer_snapshot.language_scope_at(raw_point); let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left); let text = &map.buffer_snapshot; - let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(language, c)); + let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(&scope, c)); let prev_char_kind = text .reversed_chars_at(ix) .next() - .map(|c| char_kind(language, c)); + .map(|c| char_kind(&scope, c)); prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word)) } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index d4061f25dc6a22b8a3abd79ae0471c811e2b08e0..99dcbd189c8ec87baba7e64139bee1c72352978d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1360,11 +1360,13 @@ impl MultiBuffer { return false; } - if char.is_alphanumeric() || char == '_' { + let snapshot = self.snapshot(cx); + let position = position.to_offset(&snapshot); + let scope = snapshot.language_scope_at(position); + if char_kind(&scope, char) == CharKind::Word { return true; } - let snapshot = self.snapshot(cx); let anchor = snapshot.anchor_before(position); anchor .buffer_id @@ -1866,8 +1868,8 @@ impl MultiBufferSnapshot { let mut next_chars = self.chars_at(start).peekable(); let mut prev_chars = self.reversed_chars_at(start).peekable(); - let language = self.language_at(start); - let kind = |c| char_kind(language, c); + let scope = self.language_scope_at(start); + let kind = |c| char_kind(&scope, c); let word_kind = cmp::max( prev_chars.peek().copied().map(kind), next_chars.peek().copied().map(kind), diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 83aaa3b703491b5f5334d173ca8d90e731aeb5e8..a2a7d71dce7d6796d5c9c079d2f71a586c3deb26 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -50,7 +50,7 @@ impl<'a> EditorLspTestContext<'a> { language .path_suffixes() .first() - .unwrap_or(&"txt".to_string()) + .expect("language must have a path suffix for EditorLspTestContext") ); let mut fake_servers = language diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index eff95460c41beac3b5b096a65160765ce7f98fd3..44ee87079783b72376e4ba77f89f781aad894b34 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -2175,8 +2175,8 @@ impl BufferSnapshot { let mut next_chars = self.chars_at(start).peekable(); let mut prev_chars = self.reversed_chars_at(start).peekable(); - let language = self.language_at(start); - let kind = |c| char_kind(language, c); + let scope = self.language_scope_at(start); + let kind = |c| char_kind(&scope, c); let word_kind = cmp::max( prev_chars.peek().copied().map(kind), next_chars.peek().copied().map(kind), @@ -2988,17 +2988,21 @@ pub fn contiguous_ranges( }) } -pub fn char_kind(language: Option<&Arc>, c: char) -> CharKind { +pub fn char_kind(scope: &Option, c: char) -> CharKind { if c.is_whitespace() { return CharKind::Whitespace; } else if c.is_alphanumeric() || c == '_' { return CharKind::Word; } - if let Some(language) = language { - if language.config.word_characters.contains(&c) { - return CharKind::Word; + + if let Some(scope) = scope { + if let Some(characters) = scope.word_characters() { + if characters.contains(&c) { + return CharKind::Word; + } } } + CharKind::Punctuation } diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 58732355a55992e2831389853e89b4e7bb205ff6..ccc19370326d977fa11d6856f63b83fc44456470 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -370,6 +370,8 @@ pub struct LanguageConfigOverride { pub block_comment: Override<(Arc, Arc)>, #[serde(skip_deserializing)] pub disabled_bracket_ixs: Vec, + #[serde(default)] + pub word_characters: Override>, } #[derive(Clone, Deserialize, Debug)] @@ -1557,6 +1559,13 @@ impl LanguageScope { .map(|e| (&e.0, &e.1)) } + pub fn word_characters(&self) -> Option<&HashSet> { + Override::as_option( + self.config_override().map(|o| &o.word_characters), + Some(&self.language.config.word_characters), + ) + } + pub fn brackets(&self) -> impl Iterator { let mut disabled_ids = self .config_override() diff --git a/crates/live_kit_client/LiveKitBridge/Package.resolved b/crates/live_kit_client/LiveKitBridge/Package.resolved index 85ae0885652d9054356ca19b9be6d5fd8642d16d..b925bc8f0d5ef290993fa0d49adcf221dd3570f6 100644 --- a/crates/live_kit_client/LiveKitBridge/Package.resolved +++ b/crates/live_kit_client/LiveKitBridge/Package.resolved @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/apple/swift-protobuf.git", "state": { "branch": null, - "revision": "0af9125c4eae12a4973fb66574c53a54962a9e1e", - "version": "1.21.0" + "revision": "ce20dc083ee485524b802669890291c0d8090170", + "version": "1.22.1" } } ] diff --git a/crates/project/src/search.rs b/crates/project/src/search.rs index bfa34c04228017e836afafae127a9d593b2d4fa5..c0ba5a609e789e456778eb9403482d68c75678e2 100644 --- a/crates/project/src/search.rs +++ b/crates/project/src/search.rs @@ -204,15 +204,14 @@ impl SearchQuery { if self.as_str().is_empty() { return Default::default(); } - let language = buffer.language_at(0); + + let range_offset = subrange.as_ref().map(|r| r.start).unwrap_or(0); let rope = if let Some(range) = subrange { buffer.as_rope().slice(range) } else { buffer.as_rope().clone() }; - let kind = |c| char_kind(language, c); - let mut matches = Vec::new(); match self { Self::Text { @@ -228,6 +227,9 @@ impl SearchQuery { let mat = mat.unwrap(); if *whole_word { + let scope = buffer.language_scope_at(range_offset + mat.start()); + let kind = |c| char_kind(&scope, c); + let prev_kind = rope.reversed_chars_at(mat.start()).next().map(kind); let start_kind = kind(rope.chars_at(mat.start()).next().unwrap()); let end_kind = kind(rope.reversed_chars_at(mat.end()).next().unwrap()); diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 1defee70da3e33502b8247e20e75554ff0f19bbc..243653680b13f263d747dee585cc81f55f33b50f 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -439,12 +439,12 @@ pub(crate) fn next_word_start( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { - let language = map.buffer_snapshot.language_at(point.to_point(map)); + let scope = map.buffer_snapshot.language_scope_at(point.to_point(map)); for _ in 0..times { let mut crossed_newline = false; point = movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); let at_newline = right == '\n'; let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) @@ -464,12 +464,12 @@ fn next_word_end( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { - let language = map.buffer_snapshot.language_at(point.to_point(map)); + let scope = map.buffer_snapshot.language_scope_at(point.to_point(map)); for _ in 0..times { *point.column_mut() += 1; point = movement::find_boundary(map, point, |left, right| { - let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); @@ -495,13 +495,13 @@ fn previous_word_start( ignore_punctuation: bool, times: usize, ) -> DisplayPoint { - let language = map.buffer_snapshot.language_at(point.to_point(map)); + let scope = map.buffer_snapshot.language_scope_at(point.to_point(map)); for _ in 0..times { // This works even though find_preceding_boundary is called for every character in the line containing // cursor because the newline is checked only once. point = movement::find_preceding_boundary(map, point, |left, right| { - let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); (left_kind != right_kind && !right.is_whitespace()) || left == '\n' }); @@ -511,7 +511,7 @@ fn previous_word_start( fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoint { let mut last_point = DisplayPoint::new(from.row(), 0); - let language = map.buffer_snapshot.language_at(from.to_point(map)); + let scope = map.buffer_snapshot.language_scope_at(from.to_point(map)); for (ch, point) in map.chars_at(last_point) { if ch == '\n' { return from; @@ -519,7 +519,7 @@ fn first_non_whitespace(map: &DisplaySnapshot, from: DisplayPoint) -> DisplayPoi last_point = point; - if char_kind(language, ch) != CharKind::Whitespace { + if char_kind(&scope, ch) != CharKind::Whitespace { break; } } diff --git a/crates/vim/src/normal/change.rs b/crates/vim/src/normal/change.rs index 50bc049a3aa96d37ae9acce6a1505369333bf534..1c9aa48951e6ffb5fe46035b6dfc690a501c8739 100644 --- a/crates/vim/src/normal/change.rs +++ b/crates/vim/src/normal/change.rs @@ -82,19 +82,19 @@ fn expand_changed_word_selection( ignore_punctuation: bool, ) -> bool { if times.is_none() || times.unwrap() == 1 { - let language = map + let scope = map .buffer_snapshot - .language_at(selection.start.to_point(map)); + .language_scope_at(selection.start.to_point(map)); let in_word = map .chars_at(selection.head()) .next() - .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace) + .map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace) .unwrap_or_default(); if in_word { selection.end = movement::find_boundary(map, selection.end, |left, right| { - let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index d0bcad36c22a5e8775418d571c46d5bf7ab38883..a6cc91d7bde94881ce7c91175c395699c4ccac1c 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -122,18 +122,20 @@ fn in_word( ignore_punctuation: bool, ) -> Option> { // Use motion::right so that we consider the character under the cursor when looking for the start - let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); + let scope = map + .buffer_snapshot + .language_scope_at(relative_to.to_point(map)); let start = movement::find_preceding_boundary_in_line( map, right(map, relative_to, 1), |left, right| { - char_kind(language, left).coerce_punctuation(ignore_punctuation) - != char_kind(language, right).coerce_punctuation(ignore_punctuation) + char_kind(&scope, left).coerce_punctuation(ignore_punctuation) + != char_kind(&scope, right).coerce_punctuation(ignore_punctuation) }, ); let end = movement::find_boundary_in_line(map, relative_to, |left, right| { - char_kind(language, left).coerce_punctuation(ignore_punctuation) - != char_kind(language, right).coerce_punctuation(ignore_punctuation) + char_kind(&scope, left).coerce_punctuation(ignore_punctuation) + != char_kind(&scope, right).coerce_punctuation(ignore_punctuation) }); Some(start..end) @@ -156,11 +158,13 @@ fn around_word( relative_to: DisplayPoint, ignore_punctuation: bool, ) -> Option> { - let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); + let scope = map + .buffer_snapshot + .language_scope_at(relative_to.to_point(map)); let in_word = map .chars_at(relative_to) .next() - .map(|(c, _)| char_kind(language, c) != CharKind::Whitespace) + .map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace) .unwrap_or(false); if in_word { @@ -184,21 +188,23 @@ fn around_next_word( relative_to: DisplayPoint, ignore_punctuation: bool, ) -> Option> { - let language = map.buffer_snapshot.language_at(relative_to.to_point(map)); + let scope = map + .buffer_snapshot + .language_scope_at(relative_to.to_point(map)); // Get the start of the word let start = movement::find_preceding_boundary_in_line( map, right(map, relative_to, 1), |left, right| { - char_kind(language, left).coerce_punctuation(ignore_punctuation) - != char_kind(language, right).coerce_punctuation(ignore_punctuation) + char_kind(&scope, left).coerce_punctuation(ignore_punctuation) + != char_kind(&scope, right).coerce_punctuation(ignore_punctuation) }, ); let mut word_found = false; let end = movement::find_boundary(map, relative_to, |left, right| { - let left_kind = char_kind(language, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(language, right).coerce_punctuation(ignore_punctuation); + let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); + let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); let found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n'; diff --git a/crates/zed/src/languages/javascript/config.toml b/crates/zed/src/languages/javascript/config.toml index c23ddcd6e7957dfb6444fe9031482ff236f5b46e..7f6c6931e4a25f6e500c1d1872e10c617d9d9e06 100644 --- a/crates/zed/src/languages/javascript/config.toml +++ b/crates/zed/src/languages/javascript/config.toml @@ -17,3 +17,6 @@ brackets = [ [overrides.element] line_comment = { remove = true } block_comment = ["{/* ", " */}"] + +[overrides.string] +word_characters = ["-"] From ded6decb29d04b1bc56a723ee48127558e1e1b63 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 28 Aug 2023 11:27:45 -0400 Subject: [PATCH 15/27] Initial unstyled language server short name in completions Co-Authored-By: Kirill Bulatov --- crates/editor/src/editor.rs | 61 ++++++++++++++++++++------ crates/language/src/language.rs | 9 ++++ crates/zed/src/languages/c.rs | 4 ++ crates/zed/src/languages/css.rs | 4 ++ crates/zed/src/languages/elixir.rs | 4 ++ crates/zed/src/languages/go.rs | 4 ++ crates/zed/src/languages/html.rs | 4 ++ crates/zed/src/languages/json.rs | 4 ++ crates/zed/src/languages/lua.rs | 4 ++ crates/zed/src/languages/php.rs | 4 ++ crates/zed/src/languages/python.rs | 4 ++ crates/zed/src/languages/ruby.rs | 4 ++ crates/zed/src/languages/rust.rs | 4 ++ crates/zed/src/languages/svelte.rs | 4 ++ crates/zed/src/languages/tailwind.rs | 4 ++ crates/zed/src/languages/typescript.rs | 8 ++++ crates/zed/src/languages/yaml.rs | 4 ++ 17 files changed, 121 insertions(+), 13 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index bfa804c56ce9ff4515f91e3a1d413227987af258..a188a47e350393b54dfb92daddb0470dbe505892 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -810,6 +810,7 @@ struct CompletionsMenu { id: CompletionId, initial_position: Anchor, buffer: ModelHandle, + project: Option>, completions: Arc<[Completion]>, match_candidates: Vec, matches: Arc<[StringMatch]>, @@ -853,6 +854,26 @@ impl CompletionsMenu { fn render(&self, style: EditorStyle, cx: &mut ViewContext) -> AnyElement { enum CompletionTag {} + let language_servers = self.project.as_ref().map(|project| { + project + .read(cx) + .language_servers_for_buffer(self.buffer.read(cx), cx) + .map(|(adapter, server)| (server.server_id(), format!("{}: ", adapter.short_name))) + .collect::>() + }); + let get_server_name = move |lookup_server_id: lsp::LanguageServerId| -> Option { + language_servers + .iter() + .flatten() + .find_map(|(server_id, server_name)| { + if *server_id == lookup_server_id { + Some(server_name.clone()) + } else { + None + } + }) + }; + let completions = self.completions.clone(); let matches = self.matches.clone(); let selected_item = self.selected_item; @@ -879,19 +900,31 @@ impl CompletionsMenu { style.autocomplete.item }; - Text::new(completion.label.text.clone(), style.text.clone()) - .with_soft_wrap(false) - .with_highlights(combine_syntax_and_fuzzy_match_highlights( - &completion.label.text, - style.text.color.into(), - styled_runs_for_code_label( - &completion.label, - &style.syntax, - ), - &mat.positions, - )) - .contained() - .with_style(item_style) + let completion_label = + Text::new(completion.label.text.clone(), style.text.clone()) + .with_soft_wrap(false) + .with_highlights( + combine_syntax_and_fuzzy_match_highlights( + &completion.label.text, + style.text.color.into(), + styled_runs_for_code_label( + &completion.label, + &style.syntax, + ), + &mat.positions, + ), + ); + + if let Some(server_name) = get_server_name(completion.server_id) { + Flex::row() + .with_child(Text::new(server_name, style.text.clone())) + .with_child(completion_label) + .into_any() + } else { + completion_label.into_any() + } + .contained() + .with_style(item_style) }, ) .with_cursor_style(CursorStyle::PointingHand) @@ -2850,6 +2883,7 @@ impl Editor { }); let id = post_inc(&mut self.next_completion_id); + let project = self.project.clone(); let task = cx.spawn(|this, mut cx| { async move { let menu = if let Some(completions) = completions.await.log_err() { @@ -2868,6 +2902,7 @@ impl Editor { }) .collect(), buffer, + project, completions: completions.into(), matches: Vec::new().into(), selected_item: 0, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index ccc19370326d977fa11d6856f63b83fc44456470..a2d02ecd96b3059c5c2820d696e684f67cfba8ae 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -90,6 +90,7 @@ pub struct LanguageServerName(pub Arc); /// once at startup, and caches the results. pub struct CachedLspAdapter { pub name: LanguageServerName, + pub short_name: &'static str, pub initialization_options: Option, pub disk_based_diagnostic_sources: Vec, pub disk_based_diagnostics_progress_token: Option, @@ -100,6 +101,7 @@ pub struct CachedLspAdapter { impl CachedLspAdapter { pub async fn new(adapter: Arc) -> Arc { let name = adapter.name().await; + let short_name = adapter.short_name(); let initialization_options = adapter.initialization_options().await; let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources().await; let disk_based_diagnostics_progress_token = @@ -108,6 +110,7 @@ impl CachedLspAdapter { Arc::new(CachedLspAdapter { name, + short_name, initialization_options, disk_based_diagnostic_sources, disk_based_diagnostics_progress_token, @@ -216,6 +219,8 @@ pub trait LspAdapterDelegate: Send + Sync { pub trait LspAdapter: 'static + Send + Sync { async fn name(&self) -> LanguageServerName; + fn short_name(&self) -> &'static str; + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, @@ -1696,6 +1701,10 @@ impl LspAdapter for Arc { LanguageServerName(self.name.into()) } + fn short_name(&self) -> &'static str { + "FakeLspAdapter" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 47aa2b739c3773fe701552a0ac17477c15ee963b..ad5a68f8dd722348d71ddba48bef0ef26b292cf2 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -19,6 +19,10 @@ impl super::LspAdapter for CLspAdapter { LanguageServerName("clangd".into()) } + fn short_name(&self) -> &'static str { + "clangd" + } + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/css.rs b/crates/zed/src/languages/css.rs index 51db8b8ab8dac4b8fac0985bdbd08a8d2e4faa3c..f2103050f3cc1fcb3306cc60fe1dff97f6535907 100644 --- a/crates/zed/src/languages/css.rs +++ b/crates/zed/src/languages/css.rs @@ -37,6 +37,10 @@ impl LspAdapter for CssLspAdapter { LanguageServerName("vscode-css-language-server".into()) } + fn short_name(&self) -> &'static str { + "css" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index c32927e15cfc177fe8c4611f8b5de686ebaffed6..b166feda766ba547250adf89cdbbc0bb73ed080c 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -27,6 +27,10 @@ impl LspAdapter for ElixirLspAdapter { LanguageServerName("elixir-ls".into()) } + fn short_name(&self) -> &'static str { + "elixir-ls" + } + fn will_start_server( &self, delegate: &Arc, diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index d7982f7bdb471d6b0a154d60094f6e206618f0b1..19b7013709420774e3533b346877df79f21d9c7b 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -37,6 +37,10 @@ impl super::LspAdapter for GoLspAdapter { LanguageServerName("gopls".into()) } + fn short_name(&self) -> &'static str { + "gopls" + } + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs index ecc839fca6875e54f0a0bf4d0671c675123418ef..cfb6a5dde92469299fdd87cbd40de6b7d7479227 100644 --- a/crates/zed/src/languages/html.rs +++ b/crates/zed/src/languages/html.rs @@ -37,6 +37,10 @@ impl LspAdapter for HtmlLspAdapter { LanguageServerName("vscode-html-language-server".into()) } + fn short_name(&self) -> &'static str { + "html" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 225cea0e92064c25600fad240b5d772eb9e76bc3..f7e8f874925265f2b61ce47f1aa5d59cc218c596 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -43,6 +43,10 @@ impl LspAdapter for JsonLspAdapter { LanguageServerName("json-language-server".into()) } + fn short_name(&self) -> &'static str { + "json" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/lua.rs b/crates/zed/src/languages/lua.rs index 7c5c7179d019ee9c8c90e069fec55f297ceda2d5..ee6d0f8579a2063e8cb13e7d026062c9ff3e1ab1 100644 --- a/crates/zed/src/languages/lua.rs +++ b/crates/zed/src/languages/lua.rs @@ -29,6 +29,10 @@ impl super::LspAdapter for LuaLspAdapter { LanguageServerName("lua-language-server".into()) } + fn short_name(&self) -> &'static str { + "lua" + } + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/php.rs b/crates/zed/src/languages/php.rs index 6a01d00300997862532178f24cc6ad518c2c093b..73bb4b019cc04bca9dc4566dbefb72ce2a39caac 100644 --- a/crates/zed/src/languages/php.rs +++ b/crates/zed/src/languages/php.rs @@ -41,6 +41,10 @@ impl LspAdapter for IntelephenseLspAdapter { LanguageServerName("intelephense".into()) } + fn short_name(&self) -> &'static str { + "php" + } + async fn fetch_latest_server_version( &self, _delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 41ad28ba862e38e04b52ec5e4e1b77e87b183200..023bbab13f65e3ec84016fc4978aac3a2a9fd40c 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -35,6 +35,10 @@ impl LspAdapter for PythonLspAdapter { LanguageServerName("pyright".into()) } + fn short_name(&self) -> &'static str { + "pyright" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs index 358441352a7090084c46e2f6fd1d06b2aed8c68c..3890b90dbd7a48e99713d05ba391114004cf7b7b 100644 --- a/crates/zed/src/languages/ruby.rs +++ b/crates/zed/src/languages/ruby.rs @@ -12,6 +12,10 @@ impl LspAdapter for RubyLanguageServer { LanguageServerName("solargraph".into()) } + fn short_name(&self) -> &'static str { + "solargraph" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 3c7f84fec7dced7f8241ff7009160b0d748191f4..bf8ad00293f3a93f62a6e50793db717952bb8fbf 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -22,6 +22,10 @@ impl LspAdapter for RustLspAdapter { LanguageServerName("rust-analyzer".into()) } + fn short_name(&self) -> &'static str { + "rust" + } + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/svelte.rs b/crates/zed/src/languages/svelte.rs index 8416859f5a6093e091b5afb9d306d5c9ba979ac6..35665e864fadd2f481f5213af205f36a420c06c1 100644 --- a/crates/zed/src/languages/svelte.rs +++ b/crates/zed/src/languages/svelte.rs @@ -36,6 +36,10 @@ impl LspAdapter for SvelteLspAdapter { LanguageServerName("svelte-language-server".into()) } + fn short_name(&self) -> &'static str { + "svelte" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index 9a32f69e43a52179996a94ed063ebfb18e65f68d..d7c11f0c7395fef1f7916f12717b501eb1f1628b 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -41,6 +41,10 @@ impl LspAdapter for TailwindLspAdapter { LanguageServerName("tailwindcss-language-server".into()) } + fn short_name(&self) -> &'static str { + "tailwind" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index b7e4438e1f71855c269a45d795f9ddabbd578772..e3bb0aae95150e1c398130a40e89e4cce97b33be 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -56,6 +56,10 @@ impl LspAdapter for TypeScriptLspAdapter { LanguageServerName("typescript-language-server".into()) } + fn short_name(&self) -> &'static str { + "tsserver" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, @@ -218,6 +222,10 @@ impl LspAdapter for EsLintLspAdapter { LanguageServerName("eslint".into()) } + fn short_name(&self) -> &'static str { + "eslint" + } + async fn fetch_latest_server_version( &self, delegate: &dyn LspAdapterDelegate, diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index 48d7a3cf877602705ccf7037e23c1f98e0c16190..21155cc2316c727e134f7d763ee785425e4fceb8 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -40,6 +40,10 @@ impl LspAdapter for YamlLspAdapter { LanguageServerName("yaml-language-server".into()) } + fn short_name(&self) -> &'static str { + "yaml" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate, From 35b7787e02c1b544fd8cfb94551382543f69da11 Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 28 Aug 2023 11:56:44 -0400 Subject: [PATCH 16/27] Add Tailwind server to TSX --- crates/zed/src/languages.rs | 2 +- crates/zed/src/languages/tailwind.rs | 2 ++ crates/zed/src/languages/tsx/config.toml | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 8aaa11e1cd9d13c0ae9a429cc0bc7f3e34f2e39f..f0b8a1444acfb9a288a0952b83d017df0f70203e 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -104,6 +104,7 @@ pub fn init(languages: Arc, node_runtime: Arc) { vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ], ); language( @@ -112,7 +113,6 @@ pub fn init(languages: Arc, node_runtime: Arc) { vec![ Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), - Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())), ], ); language( diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index d7c11f0c7395fef1f7916f12717b501eb1f1628b..1b7c271d10584983244c2f14800bffee241c67eb 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -103,6 +103,7 @@ impl LspAdapter for TailwindLspAdapter { "html": "html", "css": "css", "javascript": "javascript", + "typescriptreact": "typescriptreact", }, })) } @@ -122,6 +123,7 @@ impl LspAdapter for TailwindLspAdapter { ("HTML".to_string(), "html".to_string()), ("CSS".to_string(), "css".to_string()), ("JavaScript".to_string(), "javascript".to_string()), + ("TSX".to_string(), "typescriptreact".to_string()), ] .into_iter(), ) diff --git a/crates/zed/src/languages/tsx/config.toml b/crates/zed/src/languages/tsx/config.toml index 234dc6b01326a8398b5ab5dfbdfec93a37d910ad..2f676f67107dad1e69e403288d1d3ef43fe7bfb7 100644 --- a/crates/zed/src/languages/tsx/config.toml +++ b/crates/zed/src/languages/tsx/config.toml @@ -16,3 +16,6 @@ brackets = [ [overrides.element] line_comment = { remove = true } block_comment = ["{/* ", " */}"] + +[overrides.string] +word_characters = ["-"] From 15628af04b727d6729f20eceac7a8ce2ca0685e1 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 29 Aug 2023 11:21:02 -0400 Subject: [PATCH 17/27] Style language server name in completion menu Omit in buffers with one or zero running language servers with the capability to provide completions Co-Authored-By: Antonio Scandurra --- crates/editor/src/editor.rs | 118 ++++++++++++++++++++++++-------- crates/theme/src/theme.rs | 3 + styles/src/style_tree/editor.ts | 3 + 3 files changed, 96 insertions(+), 28 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a188a47e350393b54dfb92daddb0470dbe505892..ab2be13a25366643b9904b67407fdc9af514fd34 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -44,7 +44,7 @@ use gpui::{ elements::*, executor, fonts::{self, HighlightStyle, TextStyle}, - geometry::vector::Vector2F, + geometry::vector::{vec2f, Vector2F}, impl_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton}, @@ -858,21 +858,43 @@ impl CompletionsMenu { project .read(cx) .language_servers_for_buffer(self.buffer.read(cx), cx) - .map(|(adapter, server)| (server.server_id(), format!("{}: ", adapter.short_name))) + .filter(|(_, server)| server.capabilities().completion_provider.is_some()) + .map(|(adapter, server)| (server.server_id(), adapter.short_name)) .collect::>() }); - let get_server_name = move |lookup_server_id: lsp::LanguageServerId| -> Option { - language_servers - .iter() - .flatten() - .find_map(|(server_id, server_name)| { - if *server_id == lookup_server_id { - Some(server_name.clone()) - } else { - None - } - }) - }; + let needs_server_name = language_servers + .as_ref() + .map_or(false, |servers| servers.len() > 1); + + let get_server_name = + move |lookup_server_id: lsp::LanguageServerId| -> Option<&'static str> { + language_servers + .iter() + .flatten() + .find_map(|(server_id, server_name)| { + if *server_id == lookup_server_id { + Some(*server_name) + } else { + None + } + }) + }; + + let widest_completion_ix = self + .matches + .iter() + .enumerate() + .max_by_key(|(_, mat)| { + let completion = &self.completions[mat.candidate_id]; + let mut len = completion.label.text.chars().count(); + + if let Some(server_name) = get_server_name(completion.server_id) { + len += server_name.chars().count(); + } + + len + }) + .map(|(ix, _)| ix); let completions = self.completions.clone(); let matches = self.matches.clone(); @@ -917,14 +939,66 @@ impl CompletionsMenu { if let Some(server_name) = get_server_name(completion.server_id) { Flex::row() - .with_child(Text::new(server_name, style.text.clone())) .with_child(completion_label) + .with_children((|| { + if !needs_server_name { + return None; + } + + let text_style = TextStyle { + color: style.autocomplete.server_name_color, + font_size: style.text.font_size + * style.autocomplete.server_name_size_percent, + ..style.text.clone() + }; + + let label = Text::new(server_name, text_style) + .aligned() + .constrained() + .dynamically(move |constraint, _, _| { + gpui::SizeConstraint { + min: constraint.min, + max: vec2f( + constraint.max.x(), + constraint.min.y(), + ), + } + }); + + if Some(item_ix) == widest_completion_ix { + Some( + label + .contained() + .with_style( + style + .autocomplete + .server_name_container, + ) + .into_any(), + ) + } else { + Some(label.flex_float().into_any()) + } + })()) .into_any() } else { completion_label.into_any() } .contained() .with_style(item_style) + .constrained() + .dynamically( + move |constraint, _, _| { + if Some(item_ix) == widest_completion_ix { + constraint + } else { + gpui::SizeConstraint { + min: constraint.min, + max: constraint.min, + } + } + }, + ) }, ) .with_cursor_style(CursorStyle::PointingHand) @@ -941,19 +1015,7 @@ impl CompletionsMenu { } }, ) - .with_width_from_item( - self.matches - .iter() - .enumerate() - .max_by_key(|(_, mat)| { - self.completions[mat.candidate_id] - .label - .text - .chars() - .count() - }) - .map(|(ix, _)| ix), - ) + .with_width_from_item(widest_completion_ix) .contained() .with_style(container_style) .into_any() diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 4766f636f37bbef734e364eaff95e0fb63e152ac..d692660738e3c446ab5416fdb723bfc07f3737a3 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -775,6 +775,9 @@ pub struct AutocompleteStyle { pub selected_item: ContainerStyle, pub hovered_item: ContainerStyle, pub match_highlight: HighlightStyle, + pub server_name_container: ContainerStyle, + pub server_name_color: Color, + pub server_name_size_percent: f32, } #[derive(Clone, Copy, Default, Deserialize, JsonSchema)] diff --git a/styles/src/style_tree/editor.ts b/styles/src/style_tree/editor.ts index 9ad008f38d4dd928af5b49c46b148df575b1c6a3..0b99b6fba6e15dc3afd127375e5a58b702ad8e40 100644 --- a/styles/src/style_tree/editor.ts +++ b/styles/src/style_tree/editor.ts @@ -205,6 +205,9 @@ export default function editor(): any { match_highlight: foreground(theme.middle, "accent", "active"), background: background(theme.middle, "active"), }, + server_name_container: { padding: { left: 40 } }, + server_name_color: text(theme.middle, "sans", "disabled", {}).color, + server_name_size_percent: 0.75, }, diagnostic_header: { background: background(theme.middle), From 0e6c91818f33aa69da759e4195ef899616a7c720 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 29 Aug 2023 15:37:51 -0400 Subject: [PATCH 18/27] Woooooops, don't notify the language server until initialized --- crates/project/src/project.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 63b6786d8c51ec1046f91a2a25464ee0d1f5626a..800e0ae01cebdb89cdf65118fd1b2a0a95a48eef 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2901,6 +2901,8 @@ impl Project { }) .detach(); + let language_server = language_server.initialize(initialization_options).await?; + language_server .notify::( lsp::DidChangeConfigurationParams { @@ -2909,9 +2911,7 @@ impl Project { ) .ok(); - Ok(Some( - language_server.initialize(initialization_options).await?, - )) + Ok(Some(language_server)) } fn insert_newly_running_language_server( From e3a0252b04e6db8ae327dc55df4e1a667e07c549 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 29 Aug 2023 20:42:13 -0400 Subject: [PATCH 19/27] Make multi-server completion requests not serial --- crates/project/src/project.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 800e0ae01cebdb89cdf65118fd1b2a0a95a48eef..c672a37cadbd2f7618f48d08fc3e1cde4cdf1534 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4438,21 +4438,27 @@ impl Project { let position = position.to_point_utf16(buffer.read(cx)); let server_ids: Vec<_> = self .language_servers_for_buffer(buffer.read(cx), cx) + .filter(|(_, server)| server.capabilities().completion_provider.is_some()) .map(|(_, server)| server.server_id()) .collect(); let buffer = buffer.clone(); cx.spawn(|this, mut cx| async move { - let mut completions = Vec::new(); - - for server_id in server_ids { - let new_completions = this - .update(&mut cx, |this, cx| { - this.request_lsp(buffer.clone(), server_id, GetCompletions { position }, cx) - }) - .await; + let mut tasks = Vec::with_capacity(server_ids.len()); + this.update(&mut cx, |this, cx| { + for server_id in server_ids { + tasks.push(this.request_lsp( + buffer.clone(), + server_id, + GetCompletions { position }, + cx, + )); + } + }); - if let Ok(new_completions) = new_completions { + let mut completions = Vec::new(); + for task in tasks { + if let Ok(new_completions) = task.await { completions.extend_from_slice(&new_completions); } } From 7e5735c8f1ecef5336b8f68ce6ad08b9a6880b8d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 30 Aug 2023 18:41:41 +0300 Subject: [PATCH 20/27] Reap overly long LSP requests with a 2m timeout Co-authored-by: Julia Risley --- crates/lsp/src/lsp.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index f39d97aeb56ae2ceaa919a59d6874a31e9328351..6a9e48b481574d185464bfbe9d8dd96b6afee837 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -4,7 +4,7 @@ pub use lsp_types::*; use anyhow::{anyhow, Context, Result}; use collections::HashMap; -use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite, FutureExt}; use gpui::{executor, AsyncAppContext, Task}; use parking_lot::Mutex; use postage::{barrier, prelude::Stream}; @@ -26,12 +26,14 @@ use std::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, Weak, }, + time::{Duration, Instant}, }; use std::{path::Path, process::Stdio}; use util::{ResultExt, TryFutureExt}; const JSON_RPC_VERSION: &str = "2.0"; const CONTENT_LEN_HEADER: &str = "Content-Length: "; +const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2); type NotificationHandler = Box, &str, AsyncAppContext)>; type ResponseHandler = Box)>; @@ -697,7 +699,7 @@ impl LanguageServer { outbound_tx: &channel::Sender, executor: &Arc, params: T::Params, - ) -> impl 'static + Future> + ) -> impl 'static + Future> where T::Result: 'static + Send, { @@ -738,10 +740,25 @@ impl LanguageServer { .try_send(message) .context("failed to write to language server's stdin"); + let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse(); + let started = Instant::now(); async move { handle_response?; send?; - rx.await? + + let method = T::METHOD; + futures::select! { + response = rx.fuse() => { + let elapsed = started.elapsed(); + log::trace!("Took {elapsed:?} to recieve response to {method:?} id {id}"); + response? + } + + _ = timeout => { + log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}"); + anyhow::bail!("LSP request timeout"); + } + } } } From 529adb95a1a2b3f97ea35b563ff1aa67546624d5 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 30 Aug 2023 21:14:39 -0400 Subject: [PATCH 21/27] Scope Tailwind in JS/TS to within string In some situations outside JSX elements Tailwind will never respond to a completion request, holding up the tsserver completions. Only submit the request to Tailwind when we wouldn't get tsserver completions anyway and don't submit to Tailwind when we know we won't get Tailwind completions Co-Authored-By: Kirill Bulatov --- crates/language/src/language.rs | 31 ++++++++++++++++++- crates/project/src/project.rs | 13 +++++++- .../zed/src/languages/javascript/config.toml | 2 ++ crates/zed/src/languages/tsx/config.toml | 2 ++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index a2d02ecd96b3059c5c2820d696e684f67cfba8ae..bb83beeeeae37938f7203b3792e56d3661559f70 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -345,6 +345,8 @@ pub struct LanguageConfig { #[serde(default)] pub block_comment: Option<(Arc, Arc)>, #[serde(default)] + pub scope_opt_in_language_servers: Vec, + #[serde(default)] pub overrides: HashMap, #[serde(default)] pub word_characters: HashSet, @@ -377,6 +379,8 @@ pub struct LanguageConfigOverride { pub disabled_bracket_ixs: Vec, #[serde(default)] pub word_characters: Override>, + #[serde(default)] + pub opt_into_language_servers: Vec, } #[derive(Clone, Deserialize, Debug)] @@ -415,6 +419,7 @@ impl Default for LanguageConfig { autoclose_before: Default::default(), line_comment: Default::default(), block_comment: Default::default(), + scope_opt_in_language_servers: Default::default(), overrides: Default::default(), collapsed_placeholder: Default::default(), word_characters: Default::default(), @@ -1352,13 +1357,23 @@ impl Language { Ok(self) } - pub fn with_override_query(mut self, source: &str) -> Result { + pub fn with_override_query(mut self, source: &str) -> anyhow::Result { let query = Query::new(self.grammar_mut().ts_language, source)?; let mut override_configs_by_id = HashMap::default(); for (ix, name) in query.capture_names().iter().enumerate() { if !name.starts_with('_') { let value = self.config.overrides.remove(name).unwrap_or_default(); + for server_name in &value.opt_into_language_servers { + if !self + .config + .scope_opt_in_language_servers + .contains(server_name) + { + util::debug_panic!("Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server"); + } + } + override_configs_by_id.insert(ix as u32, (name.clone(), value)); } } @@ -1597,6 +1612,20 @@ impl LanguageScope { c.is_whitespace() || self.language.config.autoclose_before.contains(c) } + pub fn language_allowed(&self, name: &LanguageServerName) -> bool { + let config = &self.language.config; + let opt_in_servers = &config.scope_opt_in_language_servers; + if opt_in_servers.iter().any(|o| *o == *name.0) { + if let Some(over) = self.config_override() { + over.opt_into_language_servers.iter().any(|o| *o == *name.0) + } else { + false + } + } else { + true + } + } + fn config_override(&self) -> Option<&LanguageConfigOverride> { let id = self.override_id?; let grammar = self.language.grammar.as_ref()?; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c672a37cadbd2f7618f48d08fc3e1cde4cdf1534..597deacd1a1aae6392b593bea3d158c0635cef13 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4429,16 +4429,27 @@ impl Project { self.request_primary_lsp(buffer.clone(), GetHover { position }, cx) } - pub fn completions( + pub fn completions( &self, buffer: &ModelHandle, position: T, cx: &mut ModelContext, ) -> Task>> { + let snapshot = buffer.read(cx).snapshot(); + let offset = position.to_offset(&snapshot); let position = position.to_point_utf16(buffer.read(cx)); + + let scope = snapshot.language_scope_at(offset); + let server_ids: Vec<_> = self .language_servers_for_buffer(buffer.read(cx), cx) .filter(|(_, server)| server.capabilities().completion_provider.is_some()) + .filter(|(adapter, _)| { + scope + .as_ref() + .map(|scope| scope.language_allowed(&adapter.name)) + .unwrap_or(true) + }) .map(|(_, server)| server.server_id()) .collect(); diff --git a/crates/zed/src/languages/javascript/config.toml b/crates/zed/src/languages/javascript/config.toml index 7f6c6931e4a25f6e500c1d1872e10c617d9d9e06..6f7ce49d3da40bc709d422abdad3260b8ad42da5 100644 --- a/crates/zed/src/languages/javascript/config.toml +++ b/crates/zed/src/languages/javascript/config.toml @@ -13,6 +13,7 @@ brackets = [ { start = "`", end = "`", close = true, newline = false, not_in = ["comment", "string"] }, { start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] }, ] +scope_opt_in_language_servers = ["tailwindcss-language-server"] [overrides.element] line_comment = { remove = true } @@ -20,3 +21,4 @@ block_comment = ["{/* ", " */}"] [overrides.string] word_characters = ["-"] +opt_into_language_servers = ["tailwindcss-language-server"] diff --git a/crates/zed/src/languages/tsx/config.toml b/crates/zed/src/languages/tsx/config.toml index 2f676f67107dad1e69e403288d1d3ef43fe7bfb7..3bdc638f731902dda01f74b39eef3f669bc749c6 100644 --- a/crates/zed/src/languages/tsx/config.toml +++ b/crates/zed/src/languages/tsx/config.toml @@ -12,6 +12,7 @@ brackets = [ { start = "`", end = "`", close = true, newline = false, not_in = ["string"] }, { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] }, ] +scope_opt_in_language_servers = ["tailwindcss-language-server"] [overrides.element] line_comment = { remove = true } @@ -19,3 +20,4 @@ block_comment = ["{/* ", " */}"] [overrides.string] word_characters = ["-"] +opt_into_language_servers = ["tailwindcss-language-server"] From 9e12df43d0c2b1d4fd19bc251bfbb72e6ce99fb2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 11:43:18 +0300 Subject: [PATCH 22/27] Post-rebase fixes --- Cargo.lock | 482 ++++++++++++++------------- crates/zed/src/languages/tailwind.rs | 1 - 2 files changed, 243 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f4bf4506e45f73abe793cd61005481c8cfd951cc..a185542c63d0226108eca3c22e769bfa10f37770 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,11 +36,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ - "gimli 0.27.3", + "gimli 0.28.0", ] [[package]] @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] @@ -146,7 +146,7 @@ source = "git+https://github.com/zed-industries/alacritty?rev=33306142195b354ef3 dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -157,7 +157,7 @@ dependencies = [ "alacritty_config", "alacritty_config_derive", "base64 0.13.1", - "bitflags 2.3.3", + "bitflags 2.4.0", "home", "libc", "log", @@ -250,9 +250,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "anstyle-parse" @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys", @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" @@ -343,7 +343,7 @@ dependencies = [ "futures-core", "futures-io", "once_cell", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", ] @@ -357,7 +357,7 @@ dependencies = [ "futures-core", "futures-io", "memchr", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", ] [[package]] @@ -417,15 +417,15 @@ dependencies = [ "polling", "rustix 0.37.23", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", ] [[package]] name = "async-lock" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener", ] @@ -488,7 +488,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -511,7 +511,7 @@ dependencies = [ "log", "memchr", "once_cell", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "pin-utils", "slab", "wasm-bindgen-futures", @@ -525,7 +525,7 @@ checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", ] [[package]] @@ -536,7 +536,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -573,13 +573,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -592,7 +592,7 @@ dependencies = [ "futures-io", "futures-util", "log", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tungstenite 0.16.0", ] @@ -687,12 +687,12 @@ dependencies = [ "http", "http-body", "hyper", - "itoa 1.0.9", + "itoa", "matchit", "memchr", "mime", "percent-encoding", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "serde", "serde_json", "serde_urlencoded", @@ -733,7 +733,7 @@ dependencies = [ "futures-util", "http", "mime", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "serde", "serde_json", "tokio", @@ -745,16 +745,16 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ - "addr2line 0.20.0", + "addr2line 0.21.0", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide 0.7.1", - "object 0.31.1", + "object 0.32.0", "rustc-demangle", ] @@ -837,7 +837,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.28", + "syn 2.0.29", "which", ] @@ -864,9 +864,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" dependencies = [ "serde", ] @@ -1002,7 +1002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", - "regex-automata 0.3.4", + "regex-automata 0.3.6", "serde", ] @@ -1163,11 +1163,12 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -1225,7 +1226,7 @@ dependencies = [ "tempfile", "text", "thiserror", - "time 0.3.24", + "time 0.3.27", "tiny_http", "url", "util", @@ -1293,9 +1294,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.19" +version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" dependencies = [ "clap_builder", "clap_derive 4.3.12", @@ -1304,9 +1305,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" dependencies = [ "anstream", "anstyle", @@ -1336,7 +1337,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1398,7 +1399,7 @@ dependencies = [ "tempfile", "text", "thiserror", - "time 0.3.24", + "time 0.3.27", "tiny_http", "url", "util", @@ -1505,7 +1506,7 @@ dependencies = [ "sqlx", "text", "theme", - "time 0.3.24", + "time 0.3.27", "tokio", "tokio-tungstenite", "toml 0.5.11", @@ -2047,7 +2048,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.4.9", "winapi 0.3.9", ] @@ -2068,9 +2069,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.0" +version = "5.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6943ae99c34386c84a470c499d3414f66502a41340aa895406e0d2e4a207b91d" +checksum = "edd72493923899c6f10c641bdbdeddc7183d6396641d99c1a0d1597f37f92e28" dependencies = [ "cfg-if 1.0.0", "hashbrown 0.14.0", @@ -2128,9 +2129,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" dependencies = [ "serde", ] @@ -2318,9 +2319,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" [[package]] name = "editor" @@ -2383,9 +2384,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if 1.0.0", ] @@ -2433,9 +2434,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da96524cc884f6558f1769b6c46686af2fe8e8b4cd253bd5a3cdba8181b8e070" +checksum = "fc978899517288e3ebbd1a3bfc1d9537dbb87eeab149e53ea490e63bcdff561a" dependencies = [ "serde", ] @@ -2606,13 +2607,13 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "windows-sys", ] @@ -2624,9 +2625,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide 0.7.1", @@ -2768,7 +2769,7 @@ dependencies = [ "sum_tree", "tempfile", "text", - "time 0.3.24", + "time 0.3.27", "util", ] @@ -2906,7 +2907,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "waker-fn", ] @@ -2918,7 +2919,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -2947,7 +2948,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "pin-utils", "slab", "tokio-io", @@ -3025,9 +3026,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "git" @@ -3070,11 +3071,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick 1.0.4", "bstr", "fnv", "log", @@ -3161,7 +3162,7 @@ dependencies = [ "sqlez", "sum_tree", "taffy", - "time 0.3.24", + "time 0.3.27", "tiny-skia", "usvg", "util", @@ -3187,9 +3188,9 @@ checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes 1.4.0", "fnv", @@ -3383,7 +3384,7 @@ checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes 1.4.0", "fnv", - "itoa 1.0.9", + "itoa", ] [[package]] @@ -3394,7 +3395,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes 1.4.0", "http", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", ] [[package]] @@ -3411,9 +3412,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "human_bytes" @@ -3442,9 +3443,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.9", - "pin-project-lite 0.2.10", - "socket2", + "itoa", + "pin-project-lite 0.2.12", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -3458,7 +3459,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", "tokio-io-timeout", ] @@ -3676,7 +3677,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.4", + "rustix 0.38.8", "windows-sys", ] @@ -3716,12 +3717,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.9" @@ -4148,9 +4143,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ "serde", "value-bag", @@ -4280,9 +4275,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" [[package]] name = "memfd" @@ -4638,9 +4633,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -4796,9 +4791,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" dependencies = [ "memchr", ] @@ -4840,9 +4835,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.55" +version = "0.10.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ "bitflags 1.3.2", "cfg-if 1.0.0", @@ -4861,7 +4856,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -4872,9 +4867,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.90" +version = "0.9.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" dependencies = [ "cc", "libc", @@ -5009,7 +5004,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -5101,12 +5096,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 1.9.3", + "indexmap 2.0.0", ] [[package]] @@ -5134,22 +5129,22 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" [[package]] name = "pin-project" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -5160,9 +5155,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -5214,7 +5209,7 @@ dependencies = [ "line-wrap", "quick-xml", "serde", - "time 0.3.24", + "time 0.3.27", ] [[package]] @@ -5279,7 +5274,7 @@ dependencies = [ "concurrent-queue", "libc", "log", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "windows-sys", ] @@ -5329,7 +5324,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -5670,9 +5665,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -5905,13 +5900,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick 1.0.4", "memchr", - "regex-automata 0.3.4", + "regex-automata 0.3.6", "regex-syntax 0.7.4", ] @@ -5926,11 +5921,11 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick 1.0.4", "memchr", "regex-syntax 0.7.4", ] @@ -5979,9 +5974,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "20b9b67e2ca7dd9e9f9285b759de30ff538aab981abaaf7bc9bd90b84a0126c3" dependencies = [ "base64 0.21.2", "bytes 1.4.0", @@ -6000,7 +5995,7 @@ dependencies = [ "native-tls", "once_cell", "percent-encoding", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "serde", "serde_json", "serde_urlencoded", @@ -6220,7 +6215,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.28", + "syn 2.0.29", "walkdir", ] @@ -6237,13 +6232,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.31.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ "arrayvec 0.7.4", "borsh", - "byteorder", "bytes 1.4.0", "num-traits", "rand 0.8.5", @@ -6291,7 +6285,7 @@ dependencies = [ "bitflags 1.3.2", "errno 0.2.8", "io-lifetimes 0.5.3", - "itoa 1.0.9", + "itoa", "libc", "linux-raw-sys 0.0.42", "once_cell", @@ -6314,11 +6308,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno 0.3.2", "libc", "linux-raw-sys 0.4.5", @@ -6520,7 +6514,7 @@ dependencies = [ "serde_json", "sqlx", "thiserror", - "time 0.3.24", + "time 0.3.27", "tracing", "url", "uuid 1.4.1", @@ -6548,7 +6542,7 @@ dependencies = [ "rust_decimal", "sea-query-derive", "serde_json", - "time 0.3.24", + "time 0.3.27", "uuid 1.4.1", ] @@ -6563,7 +6557,7 @@ dependencies = [ "sea-query", "serde_json", "sqlx", - "time 0.3.24", + "time 0.3.27", "uuid 1.4.1", ] @@ -6695,7 +6689,7 @@ dependencies = [ "smol", "tempdir", "theme", - "tiktoken-rs 0.5.0", + "tiktoken-rs 0.5.1", "tree-sitter", "tree-sitter-cpp", "tree-sitter-elixir", @@ -6743,22 +6737,22 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" [[package]] name = "serde" -version = "1.0.180" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.180" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -6783,24 +6777,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "indexmap 2.0.0", - "itoa 1.0.9", + "itoa", "ryu", "serde", ] [[package]] name = "serde_json_lenient" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d7b9ce5b0a63c6269b9623ed828b39259545a6ec0d8a35d6135ad6af6232add" +checksum = "29591aaa3a13f5ad0f2dd1a8a21bcddab11eaae7c3522b20ade2e85e9df52206" dependencies = [ - "indexmap 1.9.3", - "itoa 0.4.8", + "indexmap 2.0.0", + "itoa", "ryu", "serde", ] @@ -6813,7 +6807,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -6832,7 +6826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.9", + "itoa", "ryu", "serde", ] @@ -7044,9 +7038,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -7128,6 +7122,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" @@ -7227,7 +7231,7 @@ dependencies = [ "hkdf", "hmac 0.12.1", "indexmap 1.9.3", - "itoa 1.0.9", + "itoa", "libc", "libsqlite3-sys", "log", @@ -7250,7 +7254,7 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", - "time 0.3.24", + "time 0.3.27", "tokio-stream", "url", "uuid 1.4.1", @@ -7365,7 +7369,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326" dependencies = [ - "itoa 1.0.9", + "itoa", "ryu", "sval", ] @@ -7376,7 +7380,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d" dependencies = [ - "itoa 1.0.9", + "itoa", "ryu", "sval", ] @@ -7441,9 +7445,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -7538,14 +7542,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if 1.0.0", "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.38.4", + "rustix 0.38.8", "windows-sys", ] @@ -7691,22 +7695,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -7753,9 +7757,9 @@ dependencies = [ [[package]] name = "tiktoken-rs" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a99d843674a3468b4a9200a565bbe909a0152f95e82a52feae71e6bf2d4b49d" +checksum = "2bf14cb08d8fda6e484c75ec2bfb6bcef48347d47abcd011fa9d56ee995a3da0" dependencies = [ "anyhow", "base64 0.21.2", @@ -7779,12 +7783,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.24" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b" +checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" dependencies = [ "deranged", - "itoa 1.0.9", + "itoa", "serde", "time-core", "time-macros", @@ -7798,9 +7802,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" dependencies = [ "time-core", ] @@ -7849,20 +7853,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", "backtrace", "bytes 1.4.0", "libc", "mio 0.8.8", "num_cpus", "parking_lot 0.12.1", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "signal-hook-registry", - "socket2", + "socket2 0.5.3", "tokio-macros", "windows-sys", ] @@ -7884,7 +7887,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", ] @@ -7896,7 +7899,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -7927,7 +7930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", ] @@ -7953,7 +7956,7 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", ] @@ -7967,7 +7970,7 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tokio", "tracing", ] @@ -8056,7 +8059,7 @@ dependencies = [ "futures-util", "indexmap 1.9.3", "pin-project", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "rand 0.8.5", "slab", "tokio", @@ -8079,7 +8082,7 @@ dependencies = [ "http", "http-body", "http-range-header", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tower", "tower-layer", "tower-service", @@ -8105,7 +8108,7 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.10", + "pin-project-lite 0.2.12", "tracing-attributes", "tracing-core", ] @@ -8118,7 +8121,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -8203,9 +8206,9 @@ dependencies = [ [[package]] name = "tree-sitter-c" -version = "0.20.4" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1bb73a4101c88775e4fefcd0543ee25e192034484a5bd45cb99eefb997dca9" +checksum = "30b03bdf218020057abee831581a74bff8c298323d6c6cd1a70556430ded9f4b" dependencies = [ "cc", "tree-sitter", @@ -8352,9 +8355,9 @@ dependencies = [ [[package]] name = "tree-sitter-python" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47ebd9cac632764b2f4389b08517bf2ef895431dd163eb562e3d2062cc23a14" +checksum = "e6c93b1b1fbd0d399db3445f51fd3058e43d0b4dcff62ddbdb46e66550978aa5" dependencies = [ "cc", "tree-sitter", @@ -8381,9 +8384,9 @@ dependencies = [ [[package]] name = "tree-sitter-rust" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "797842733e252dc11ae5d403a18060bf337b822fc2ae5ddfaa6ff4d9cc20bda6" +checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594" dependencies = [ "cc", "tree-sitter", @@ -8504,9 +8507,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] @@ -8912,7 +8915,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", "wasm-bindgen-shared", ] @@ -8946,7 +8949,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -9183,9 +9186,9 @@ dependencies = [ [[package]] name = "wast" -version = "62.0.1" +version = "63.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ae06f09dbe377b889fbd620ff8fa21e1d49d1d9d364983c0cdbf9870cb9f1f" +checksum = "2560471f60a48b77fccefaf40796fda61c97ce1e790b59dfcec9dc3995c9f63a" dependencies = [ "leb128", "memchr", @@ -9195,11 +9198,11 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842e15861d203fb4a96d314b0751cdeaf0f6f8b35e8d81d2953af2af5e44e637" +checksum = "3bdc306c2c4c2f2bf2ba69e083731d0d2a77437fc6a350a19db139636e7e416c" dependencies = [ - "wast 62.0.1", + "wast 63.0.0", ] [[package]] @@ -9401,7 +9404,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -9410,7 +9413,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -9430,17 +9433,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -9451,9 +9454,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -9463,9 +9466,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -9475,9 +9478,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -9487,9 +9490,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -9499,9 +9502,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -9511,9 +9514,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -9523,26 +9526,27 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.2" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi 0.3.9", + "cfg-if 1.0.0", + "windows-sys", ] [[package]] @@ -9662,7 +9666,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.3.19", + "clap 4.3.24", "schemars", "serde_json", "theme", @@ -9845,7 +9849,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index 1b7c271d10584983244c2f14800bffee241c67eb..12a0a4e3b8f711f8fe05d7358030911466926f5f 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -66,7 +66,6 @@ impl LspAdapter for TailwindLspAdapter { let server_path = container_dir.join(SERVER_PATH); if fs::metadata(&server_path).await.is_err() { - dbg!(&container_dir, version.as_str()); self.node .npm_install_packages( &container_dir, From fff385a585f4e83db0bc96f403141f09ebdaf5f4 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 12:16:54 +0300 Subject: [PATCH 23/27] Fix project tests --- crates/editor/src/editor_tests.rs | 8 +++++++- crates/project/src/project_tests.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index a3688215db84f432982cad444b8e93f91797d423..d44b8728fd60433a0ab2ddabbb8c1cbc7756f76a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7712,7 +7712,13 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: ) .with_override_query("(jsx_self_closing_element) @element") .unwrap(), - Default::default(), + lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, cx, ) .await; diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 397223c4bb520e47ed6bb99da84d52340a7f5e45..b6adb371e10d0cc5129df4e043a918e9325a329c 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -2272,7 +2272,18 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { }, Some(tree_sitter_typescript::language_typescript()), ); - let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await; + let mut fake_language_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + })) + .await; let fs = FakeFs::new(cx.background()); fs.insert_tree( @@ -2358,7 +2369,18 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { }, Some(tree_sitter_typescript::language_typescript()), ); - let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await; + let mut fake_language_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + capabilities: lsp::ServerCapabilities { + completion_provider: Some(lsp::CompletionOptions { + trigger_characters: Some(vec![":".to_string()]), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + })) + .await; let fs = FakeFs::new(cx.background()); fs.insert_tree( From 292af55ebc7ac98d26d1d3f2e8d365151b473db2 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 13:41:20 +0300 Subject: [PATCH 24/27] Ensure all client LSP queries are forwarded via collab --- crates/project/src/project.rs | 106 +++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index edada5c6307024db5566619c411e9d4212a1e537..091e1986f65b56892a4107508df2361768cb8aa7 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -156,6 +156,11 @@ struct DelayedDebounced { cancel_channel: Option>, } +enum LanguageServerToQuery { + Primary, + Other(LanguageServerId), +} + impl DelayedDebounced { fn new() -> DelayedDebounced { DelayedDebounced { @@ -4199,7 +4204,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer.clone(), GetDefinition { position }, cx) + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetDefinition { position }, + cx, + ) } pub fn type_definition( @@ -4209,7 +4219,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer.clone(), GetTypeDefinition { position }, cx) + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetTypeDefinition { position }, + cx, + ) } pub fn references( @@ -4219,7 +4234,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer.clone(), GetReferences { position }, cx) + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetReferences { position }, + cx, + ) } pub fn document_highlights( @@ -4229,7 +4249,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer.clone(), GetDocumentHighlights { position }, cx) + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetDocumentHighlights { position }, + cx, + ) } pub fn symbols(&self, query: &str, cx: &mut ModelContext) -> Task>> { @@ -4457,7 +4482,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer.clone(), GetHover { position }, cx) + self.request_lsp( + buffer.clone(), + LanguageServerToQuery::Primary, + GetHover { position }, + cx, + ) } pub fn completions( @@ -4491,7 +4521,7 @@ impl Project { for server_id in server_ids { tasks.push(this.request_lsp( buffer.clone(), - server_id, + LanguageServerToQuery::Other(server_id), GetCompletions { position }, cx, )); @@ -4628,7 +4658,12 @@ impl Project { ) -> Task>> { let buffer = buffer_handle.read(cx); let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end); - self.request_primary_lsp(buffer_handle.clone(), GetCodeActions { range }, cx) + self.request_lsp( + buffer_handle.clone(), + LanguageServerToQuery::Primary, + GetCodeActions { range }, + cx, + ) } pub fn apply_code_action( @@ -4984,7 +5019,12 @@ impl Project { cx: &mut ModelContext, ) -> Task>>> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp(buffer, PrepareRename { position }, cx) + self.request_lsp( + buffer, + LanguageServerToQuery::Primary, + PrepareRename { position }, + cx, + ) } pub fn perform_rename( @@ -4996,8 +5036,9 @@ impl Project { cx: &mut ModelContext, ) -> Task> { let position = position.to_point_utf16(buffer.read(cx)); - self.request_primary_lsp( + self.request_lsp( buffer, + LanguageServerToQuery::Primary, PerformRename { position, new_name, @@ -5023,8 +5064,9 @@ impl Project { .tab_size, ) }); - self.request_primary_lsp( + self.request_lsp( buffer.clone(), + LanguageServerToQuery::Primary, OnTypeFormatting { position, trigger, @@ -5050,7 +5092,12 @@ impl Project { let lsp_request = InlayHints { range }; if self.is_local() { - let lsp_request_task = self.request_primary_lsp(buffer_handle.clone(), lsp_request, cx); + let lsp_request_task = self.request_lsp( + buffer_handle.clone(), + LanguageServerToQuery::Primary, + lsp_request, + cx, + ); cx.spawn(|_, mut cx| async move { buffer_handle .update(&mut cx, |buffer, _| { @@ -5483,28 +5530,10 @@ impl Project { .await; } - fn request_primary_lsp( - &self, - buffer_handle: ModelHandle, - request: R, - cx: &mut ModelContext, - ) -> Task> - where - ::Result: Send, - { - let buffer = buffer_handle.read(cx); - let server_id = match self.primary_language_server_for_buffer(buffer, cx) { - Some((_, server)) => server.server_id(), - None => return Task::ready(Ok(Default::default())), - }; - - self.request_lsp(buffer_handle, server_id, request, cx) - } - fn request_lsp( &self, buffer_handle: ModelHandle, - server_id: LanguageServerId, + server: LanguageServerToQuery, request: R, cx: &mut ModelContext, ) -> Task> @@ -5513,11 +5542,18 @@ impl Project { { let buffer = buffer_handle.read(cx); if self.is_local() { + let language_server = match server { + LanguageServerToQuery::Primary => { + match self.primary_language_server_for_buffer(buffer, cx) { + Some((_, server)) => Some(Arc::clone(server)), + None => return Task::ready(Ok(Default::default())), + } + } + LanguageServerToQuery::Other(id) => self + .language_server_for_buffer(buffer, id, cx) + .map(|(_, server)| Arc::clone(server)), + }; let file = File::from_dyn(buffer.file()).and_then(File::as_local); - let language_server = self - .language_server_for_buffer(buffer, server_id, cx) - .map(|(_, server)| server.clone()); - if let (Some(file), Some(language_server)) = (file, language_server) { let lsp_params = request.to_lsp(&file.abs_path(cx), buffer, &language_server, cx); return cx.spawn(|this, cx| async move { @@ -7212,7 +7248,7 @@ impl Project { let buffer_version = buffer_handle.read_with(&cx, |buffer, _| buffer.version()); let response = this .update(&mut cx, |this, cx| { - this.request_primary_lsp(buffer_handle, request, cx) + this.request_lsp(buffer_handle, LanguageServerToQuery::Primary, request, cx) }) .await?; this.update(&mut cx, |this, cx| { From 5bc5831032a84b3897330264c3e5fddf1e770358 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 14:31:43 +0300 Subject: [PATCH 25/27] Fix wrong assertion in the test --- crates/editor/src/editor_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d44b8728fd60433a0ab2ddabbb8c1cbc7756f76a..ad97639d0ba6158326c2d3f2331c4f32efc3d56a 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -7780,7 +7780,7 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { assert_eq!( menu.matches.iter().map(|m| &m.string).collect::>(), - &["bg-blue", "bg-yellow"] + &["bg-yellow"] ); } else { panic!("expected completion menu to be open"); From e682db7101edd69e64ae4cc133e74f7de935098d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 15:22:13 +0300 Subject: [PATCH 26/27] Route completion requests through remote protocol, if needed --- crates/project/src/project.rs | 127 +++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 091e1986f65b56892a4107508df2361768cb8aa7..5cd13b8be8aca39d73741849d10376e08baf21c9 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4496,47 +4496,52 @@ impl Project { position: T, cx: &mut ModelContext, ) -> Task>> { - let snapshot = buffer.read(cx).snapshot(); - let offset = position.to_offset(&snapshot); let position = position.to_point_utf16(buffer.read(cx)); + if self.is_local() { + let snapshot = buffer.read(cx).snapshot(); + let offset = position.to_offset(&snapshot); + let scope = snapshot.language_scope_at(offset); + + let server_ids: Vec<_> = self + .language_servers_for_buffer(buffer.read(cx), cx) + .filter(|(_, server)| server.capabilities().completion_provider.is_some()) + .filter(|(adapter, _)| { + scope + .as_ref() + .map(|scope| scope.language_allowed(&adapter.name)) + .unwrap_or(true) + }) + .map(|(_, server)| server.server_id()) + .collect(); - let scope = snapshot.language_scope_at(offset); - - let server_ids: Vec<_> = self - .language_servers_for_buffer(buffer.read(cx), cx) - .filter(|(_, server)| server.capabilities().completion_provider.is_some()) - .filter(|(adapter, _)| { - scope - .as_ref() - .map(|scope| scope.language_allowed(&adapter.name)) - .unwrap_or(true) - }) - .map(|(_, server)| server.server_id()) - .collect(); - - let buffer = buffer.clone(); - cx.spawn(|this, mut cx| async move { - let mut tasks = Vec::with_capacity(server_ids.len()); - this.update(&mut cx, |this, cx| { - for server_id in server_ids { - tasks.push(this.request_lsp( - buffer.clone(), - LanguageServerToQuery::Other(server_id), - GetCompletions { position }, - cx, - )); - } - }); + let buffer = buffer.clone(); + cx.spawn(|this, mut cx| async move { + let mut tasks = Vec::with_capacity(server_ids.len()); + this.update(&mut cx, |this, cx| { + for server_id in server_ids { + tasks.push(this.request_lsp( + buffer.clone(), + LanguageServerToQuery::Other(server_id), + GetCompletions { position }, + cx, + )); + } + }); - let mut completions = Vec::new(); - for task in tasks { - if let Ok(new_completions) = task.await { - completions.extend_from_slice(&new_completions); + let mut completions = Vec::new(); + for task in tasks { + if let Ok(new_completions) = task.await { + completions.extend_from_slice(&new_completions); + } } - } - Ok(completions) - }) + Ok(completions) + }) + } else if let Some(project_id) = self.remote_id() { + self.send_lsp_proto_request(buffer.clone(), project_id, GetCompletions { position }, cx) + } else { + Task::ready(Ok(Default::default())) + } } pub fn apply_additional_edits_for_completion( @@ -5587,32 +5592,40 @@ impl Project { }); } } else if let Some(project_id) = self.remote_id() { - let rpc = self.client.clone(); - let message = request.to_proto(project_id, buffer); - return cx.spawn_weak(|this, cx| async move { - // Ensure the project is still alive by the time the task - // is scheduled. - this.upgrade(&cx) - .ok_or_else(|| anyhow!("project dropped"))?; - - let response = rpc.request(message).await?; - - let this = this - .upgrade(&cx) - .ok_or_else(|| anyhow!("project dropped"))?; - if this.read_with(&cx, |this, _| this.is_read_only()) { - Err(anyhow!("disconnected before completing request")) - } else { - request - .response_from_proto(response, this, buffer_handle, cx) - .await - } - }); + return self.send_lsp_proto_request(buffer_handle, project_id, request, cx); } Task::ready(Ok(Default::default())) } + fn send_lsp_proto_request( + &self, + buffer: ModelHandle, + project_id: u64, + request: R, + cx: &mut ModelContext<'_, Project>, + ) -> Task::Response>> { + let rpc = self.client.clone(); + let message = request.to_proto(project_id, buffer.read(cx)); + cx.spawn_weak(|this, cx| async move { + // Ensure the project is still alive by the time the task + // is scheduled. + this.upgrade(&cx) + .ok_or_else(|| anyhow!("project dropped"))?; + let response = rpc.request(message).await?; + let this = this + .upgrade(&cx) + .ok_or_else(|| anyhow!("project dropped"))?; + if this.read_with(&cx, |this, _| this.is_read_only()) { + Err(anyhow!("disconnected before completing request")) + } else { + request + .response_from_proto(response, this, buffer, cx) + .await + } + }) + } + fn sort_candidates_and_open_buffers( mut matching_paths_rx: Receiver, cx: &mut ModelContext, From 5731ef51cd8f11aaa17606a0b36de5c4edb8e432 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 31 Aug 2023 15:32:24 +0300 Subject: [PATCH 27/27] Fix plugin LSP adapter intefrace --- crates/zed/src/languages/language_plugin.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/zed/src/languages/language_plugin.rs b/crates/zed/src/languages/language_plugin.rs index b0719363929232aa47721624a63a9b8bb4aacb5d..b2405d8bb83e58aa9c4e33ca6749b397ddc38191 100644 --- a/crates/zed/src/languages/language_plugin.rs +++ b/crates/zed/src/languages/language_plugin.rs @@ -70,6 +70,10 @@ impl LspAdapter for PluginLspAdapter { LanguageServerName(name.into()) } + fn short_name(&self) -> &'static str { + "PluginLspAdapter" + } + async fn fetch_latest_server_version( &self, _: &dyn LspAdapterDelegate,