Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
efcad22a2c | ||
![]() |
eeb3fd1cc9 | ||
![]() |
66776382b9 | ||
![]() |
aee54c96e3 | ||
![]() |
d290b1c83f | ||
![]() |
f6b953c884 |
9 changed files with 322 additions and 118 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -1358,7 +1358,6 @@ dependencies = [
|
||||||
"smol",
|
"smol",
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -8522,7 +8521,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.83.0"
|
version = "0.83.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
|
@ -32,7 +32,10 @@ use std::{
|
||||||
env, future, mem,
|
env, future, mem,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use workspace::{
|
use workspace::{
|
||||||
|
@ -3638,6 +3641,141 @@ async fn test_collaborating_with_diagnostics(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test(iterations = 10)]
|
||||||
|
async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
|
||||||
|
deterministic: Arc<Deterministic>,
|
||||||
|
cx_a: &mut TestAppContext,
|
||||||
|
cx_b: &mut TestAppContext,
|
||||||
|
) {
|
||||||
|
deterministic.forbid_parking();
|
||||||
|
let mut server = TestServer::start(&deterministic).await;
|
||||||
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
server
|
||||||
|
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Set up a fake language server.
|
||||||
|
let mut language = Language::new(
|
||||||
|
LanguageConfig {
|
||||||
|
name: "Rust".into(),
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Some(tree_sitter_rust::language()),
|
||||||
|
);
|
||||||
|
let mut fake_language_servers = language
|
||||||
|
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||||
|
disk_based_diagnostics_progress_token: Some("the-disk-based-token".into()),
|
||||||
|
disk_based_diagnostics_sources: vec!["the-disk-based-diagnostics-source".into()],
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
client_a.language_registry.add(Arc::new(language));
|
||||||
|
|
||||||
|
let file_names = &["one.rs", "two.rs", "three.rs", "four.rs", "five.rs"];
|
||||||
|
client_a
|
||||||
|
.fs
|
||||||
|
.insert_tree(
|
||||||
|
"/test",
|
||||||
|
json!({
|
||||||
|
"one.rs": "const ONE: usize = 1;",
|
||||||
|
"two.rs": "const TWO: usize = 2;",
|
||||||
|
"three.rs": "const THREE: usize = 3;",
|
||||||
|
"four.rs": "const FOUR: usize = 3;",
|
||||||
|
"five.rs": "const FIVE: usize = 3;",
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let (project_a, worktree_id) = client_a.build_local_project("/test", cx_a).await;
|
||||||
|
|
||||||
|
// Share a project as client A
|
||||||
|
let active_call_a = cx_a.read(ActiveCall::global);
|
||||||
|
let project_id = active_call_a
|
||||||
|
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Join the project as client B and open all three files.
|
||||||
|
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||||
|
let guest_buffers = futures::future::try_join_all(file_names.iter().map(|file_name| {
|
||||||
|
project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, file_name), cx))
|
||||||
|
}))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Simulate a language server reporting errors for a file.
|
||||||
|
let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||||
|
fake_language_server
|
||||||
|
.request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
|
||||||
|
token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
|
||||||
|
token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
|
||||||
|
value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
|
||||||
|
lsp::WorkDoneProgressBegin {
|
||||||
|
title: "Progress Began".into(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
for file_name in file_names {
|
||||||
|
fake_language_server.notify::<lsp::notification::PublishDiagnostics>(
|
||||||
|
lsp::PublishDiagnosticsParams {
|
||||||
|
uri: lsp::Url::from_file_path(Path::new("/test").join(file_name)).unwrap(),
|
||||||
|
version: None,
|
||||||
|
diagnostics: vec![lsp::Diagnostic {
|
||||||
|
severity: Some(lsp::DiagnosticSeverity::WARNING),
|
||||||
|
source: Some("the-disk-based-diagnostics-source".into()),
|
||||||
|
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)),
|
||||||
|
message: "message one".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fake_language_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
|
||||||
|
token: lsp::NumberOrString::String("the-disk-based-token".to_string()),
|
||||||
|
value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
|
||||||
|
lsp::WorkDoneProgressEnd { message: None },
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
|
||||||
|
// When the "disk base diagnostics finished" message is received, the buffers'
|
||||||
|
// diagnostics are expected to be present.
|
||||||
|
let disk_based_diagnostics_finished = Arc::new(AtomicBool::new(false));
|
||||||
|
project_b.update(cx_b, {
|
||||||
|
let project_b = project_b.clone();
|
||||||
|
let disk_based_diagnostics_finished = disk_based_diagnostics_finished.clone();
|
||||||
|
move |_, cx| {
|
||||||
|
cx.subscribe(&project_b, move |_, _, event, cx| {
|
||||||
|
if let project::Event::DiskBasedDiagnosticsFinished { .. } = event {
|
||||||
|
disk_based_diagnostics_finished.store(true, SeqCst);
|
||||||
|
for buffer in &guest_buffers {
|
||||||
|
assert_eq!(
|
||||||
|
buffer
|
||||||
|
.read(cx)
|
||||||
|
.snapshot()
|
||||||
|
.diagnostics_in_range::<_, usize>(0..5, false)
|
||||||
|
.count(),
|
||||||
|
1,
|
||||||
|
"expected a diagnostic for buffer {:?}",
|
||||||
|
buffer.read(cx).file().unwrap().path(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
deterministic.run_until_parked();
|
||||||
|
assert!(disk_based_diagnostics_finished.load(SeqCst));
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
async fn test_collaborating_with_completion(
|
async fn test_collaborating_with_completion(
|
||||||
deterministic: Arc<Deterministic>,
|
deterministic: Arc<Deterministic>,
|
||||||
|
|
|
@ -47,4 +47,3 @@ lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
rpc = { path = "../rpc", features = ["test-support"] }
|
rpc = { path = "../rpc", features = ["test-support"] }
|
||||||
settings = { path = "../settings", features = ["test-support"] }
|
settings = { path = "../settings", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
workspace = { path = "../workspace", features = ["test-support"] }
|
|
||||||
|
|
|
@ -378,13 +378,6 @@ impl Copilot {
|
||||||
cx.clone(),
|
cx.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let server = server.initialize(Default::default()).await?;
|
|
||||||
let status = server
|
|
||||||
.request::<request::CheckStatus>(request::CheckStatusParams {
|
|
||||||
local_checks_only: false,
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
server
|
server
|
||||||
.on_notification::<LogMessage, _>(|params, _cx| {
|
.on_notification::<LogMessage, _>(|params, _cx| {
|
||||||
match params.level {
|
match params.level {
|
||||||
|
@ -405,6 +398,14 @@ impl Copilot {
|
||||||
)
|
)
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
let server = server.initialize(Default::default()).await?;
|
||||||
|
|
||||||
|
let status = server
|
||||||
|
.request::<request::CheckStatus>(request::CheckStatusParams {
|
||||||
|
local_checks_only: false,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
server
|
server
|
||||||
.request::<request::SetEditorInfo>(request::SetEditorInfoParams {
|
.request::<request::SetEditorInfo>(request::SetEditorInfoParams {
|
||||||
editor_info: request::EditorInfo {
|
editor_info: request::EditorInfo {
|
||||||
|
|
|
@ -143,8 +143,8 @@ pub enum LogMessage {}
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct LogMessageParams {
|
pub struct LogMessageParams {
|
||||||
pub message: String,
|
|
||||||
pub level: u8,
|
pub level: u8,
|
||||||
|
pub message: String,
|
||||||
pub metadata_str: String,
|
pub metadata_str: String,
|
||||||
pub extra: Vec<String>,
|
pub extra: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ pub trait Item {
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
worktrees: Vec<WorktreeHandle>,
|
worktrees: Vec<WorktreeHandle>,
|
||||||
active_entry: Option<ProjectEntryId>,
|
active_entry: Option<ProjectEntryId>,
|
||||||
buffer_changes_tx: mpsc::UnboundedSender<BufferMessage>,
|
buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
language_servers: HashMap<LanguageServerId, LanguageServerState>,
|
language_servers: HashMap<LanguageServerId, LanguageServerState>,
|
||||||
language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
|
language_server_ids: HashMap<(WorktreeId, LanguageServerName), LanguageServerId>,
|
||||||
|
@ -137,11 +137,16 @@ struct LspBufferSnapshot {
|
||||||
snapshot: TextBufferSnapshot,
|
snapshot: TextBufferSnapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BufferMessage {
|
/// Message ordered with respect to buffer operations
|
||||||
|
enum BufferOrderedMessage {
|
||||||
Operation {
|
Operation {
|
||||||
buffer_id: u64,
|
buffer_id: u64,
|
||||||
operation: proto::Operation,
|
operation: proto::Operation,
|
||||||
},
|
},
|
||||||
|
LanguageServerUpdate {
|
||||||
|
language_server_id: LanguageServerId,
|
||||||
|
message: proto::update_language_server::Variant,
|
||||||
|
},
|
||||||
Resync,
|
Resync,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,11 +446,11 @@ impl Project {
|
||||||
) -> ModelHandle<Self> {
|
) -> ModelHandle<Self> {
|
||||||
cx.add_model(|cx: &mut ModelContext<Self>| {
|
cx.add_model(|cx: &mut ModelContext<Self>| {
|
||||||
let (tx, rx) = mpsc::unbounded();
|
let (tx, rx) = mpsc::unbounded();
|
||||||
cx.spawn_weak(|this, cx| Self::send_buffer_messages(this, rx, cx))
|
cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
|
||||||
.detach();
|
.detach();
|
||||||
Self {
|
Self {
|
||||||
worktrees: Default::default(),
|
worktrees: Default::default(),
|
||||||
buffer_changes_tx: tx,
|
buffer_ordered_messages_tx: tx,
|
||||||
collaborators: Default::default(),
|
collaborators: Default::default(),
|
||||||
opened_buffers: Default::default(),
|
opened_buffers: Default::default(),
|
||||||
shared_buffers: Default::default(),
|
shared_buffers: Default::default(),
|
||||||
|
@ -509,11 +514,11 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = mpsc::unbounded();
|
let (tx, rx) = mpsc::unbounded();
|
||||||
cx.spawn_weak(|this, cx| Self::send_buffer_messages(this, rx, cx))
|
cx.spawn_weak(|this, cx| Self::send_buffer_ordered_messages(this, rx, cx))
|
||||||
.detach();
|
.detach();
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
worktrees: Vec::new(),
|
worktrees: Vec::new(),
|
||||||
buffer_changes_tx: tx,
|
buffer_ordered_messages_tx: tx,
|
||||||
loading_buffers_by_path: Default::default(),
|
loading_buffers_by_path: Default::default(),
|
||||||
opened_buffer: watch::channel(),
|
opened_buffer: watch::channel(),
|
||||||
shared_buffers: Default::default(),
|
shared_buffers: Default::default(),
|
||||||
|
@ -1166,8 +1171,8 @@ impl Project {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.buffer_changes_tx
|
self.buffer_ordered_messages_tx
|
||||||
.unbounded_send(BufferMessage::Resync)
|
.unbounded_send(BufferOrderedMessage::Resync)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1782,23 +1787,49 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_buffer_messages(
|
async fn send_buffer_ordered_messages(
|
||||||
this: WeakModelHandle<Self>,
|
this: WeakModelHandle<Self>,
|
||||||
rx: UnboundedReceiver<BufferMessage>,
|
rx: UnboundedReceiver<BufferOrderedMessage>,
|
||||||
mut cx: AsyncAppContext,
|
mut cx: AsyncAppContext,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
const MAX_BATCH_SIZE: usize = 128;
|
const MAX_BATCH_SIZE: usize = 128;
|
||||||
|
|
||||||
let mut needs_resync_with_host = false;
|
|
||||||
let mut operations_by_buffer_id = HashMap::default();
|
let mut operations_by_buffer_id = HashMap::default();
|
||||||
|
async fn flush_operations(
|
||||||
|
this: &ModelHandle<Project>,
|
||||||
|
operations_by_buffer_id: &mut HashMap<u64, Vec<proto::Operation>>,
|
||||||
|
needs_resync_with_host: &mut bool,
|
||||||
|
is_local: bool,
|
||||||
|
cx: &AsyncAppContext,
|
||||||
|
) {
|
||||||
|
for (buffer_id, operations) in operations_by_buffer_id.drain() {
|
||||||
|
let request = this.read_with(cx, |this, _| {
|
||||||
|
let project_id = this.remote_id()?;
|
||||||
|
Some(this.client.request(proto::UpdateBuffer {
|
||||||
|
buffer_id,
|
||||||
|
project_id,
|
||||||
|
operations,
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
if let Some(request) = request {
|
||||||
|
if request.await.is_err() && !is_local {
|
||||||
|
*needs_resync_with_host = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut needs_resync_with_host = false;
|
||||||
let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
|
let mut changes = rx.ready_chunks(MAX_BATCH_SIZE);
|
||||||
|
|
||||||
while let Some(changes) = changes.next().await {
|
while let Some(changes) = changes.next().await {
|
||||||
let this = this.upgrade(&mut cx)?;
|
let this = this.upgrade(&mut cx)?;
|
||||||
let is_local = this.read_with(&cx, |this, _| this.is_local());
|
let is_local = this.read_with(&cx, |this, _| this.is_local());
|
||||||
|
|
||||||
for change in changes {
|
for change in changes {
|
||||||
match change {
|
match change {
|
||||||
BufferMessage::Operation {
|
BufferOrderedMessage::Operation {
|
||||||
buffer_id,
|
buffer_id,
|
||||||
operation,
|
operation,
|
||||||
} => {
|
} => {
|
||||||
|
@ -1811,7 +1842,8 @@ impl Project {
|
||||||
.or_insert(Vec::new())
|
.or_insert(Vec::new())
|
||||||
.push(operation);
|
.push(operation);
|
||||||
}
|
}
|
||||||
BufferMessage::Resync => {
|
|
||||||
|
BufferOrderedMessage::Resync => {
|
||||||
operations_by_buffer_id.clear();
|
operations_by_buffer_id.clear();
|
||||||
if this
|
if this
|
||||||
.update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))
|
.update(&mut cx, |this, cx| this.synchronize_remote_buffers(cx))
|
||||||
|
@ -1821,25 +1853,43 @@ impl Project {
|
||||||
needs_resync_with_host = false;
|
needs_resync_with_host = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BufferOrderedMessage::LanguageServerUpdate {
|
||||||
|
language_server_id,
|
||||||
|
message,
|
||||||
|
} => {
|
||||||
|
flush_operations(
|
||||||
|
&this,
|
||||||
|
&mut operations_by_buffer_id,
|
||||||
|
&mut needs_resync_with_host,
|
||||||
|
is_local,
|
||||||
|
&cx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
this.read_with(&cx, |this, _| {
|
||||||
|
if let Some(project_id) = this.remote_id() {
|
||||||
|
this.client
|
||||||
|
.send(proto::UpdateLanguageServer {
|
||||||
|
project_id,
|
||||||
|
language_server_id: language_server_id.0 as u64,
|
||||||
|
variant: Some(message),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (buffer_id, operations) in operations_by_buffer_id.drain() {
|
flush_operations(
|
||||||
let request = this.read_with(&cx, |this, _| {
|
&this,
|
||||||
let project_id = this.remote_id()?;
|
&mut operations_by_buffer_id,
|
||||||
Some(this.client.request(proto::UpdateBuffer {
|
&mut needs_resync_with_host,
|
||||||
buffer_id,
|
is_local,
|
||||||
project_id,
|
&cx,
|
||||||
operations,
|
)
|
||||||
}))
|
.await;
|
||||||
});
|
|
||||||
if let Some(request) = request {
|
|
||||||
if request.await.is_err() && !is_local {
|
|
||||||
needs_resync_with_host = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -1853,8 +1903,8 @@ impl Project {
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
match event {
|
match event {
|
||||||
BufferEvent::Operation(operation) => {
|
BufferEvent::Operation(operation) => {
|
||||||
self.buffer_changes_tx
|
self.buffer_ordered_messages_tx
|
||||||
.unbounded_send(BufferMessage::Operation {
|
.unbounded_send(BufferOrderedMessage::Operation {
|
||||||
buffer_id: buffer.read(cx).remote_id(),
|
buffer_id: buffer.read(cx).remote_id(),
|
||||||
operation: language::proto::serialize_operation(operation),
|
operation: language::proto::serialize_operation(operation),
|
||||||
})
|
})
|
||||||
|
@ -1962,14 +2012,19 @@ impl Project {
|
||||||
let task = cx.spawn_weak(|this, mut cx| async move {
|
let task = cx.spawn_weak(|this, mut cx| async move {
|
||||||
cx.background().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
|
cx.background().timer(DISK_BASED_DIAGNOSTICS_DEBOUNCE).await;
|
||||||
if let Some(this) = this.upgrade(&cx) {
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
this.update(&mut cx, |this, cx | {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.disk_based_diagnostics_finished(language_server_id, cx);
|
this.disk_based_diagnostics_finished(
|
||||||
this.broadcast_language_server_update(
|
|
||||||
language_server_id,
|
language_server_id,
|
||||||
proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
|
cx,
|
||||||
proto::LspDiskBasedDiagnosticsUpdated {},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
this.buffer_ordered_messages_tx
|
||||||
|
.unbounded_send(
|
||||||
|
BufferOrderedMessage::LanguageServerUpdate {
|
||||||
|
language_server_id,
|
||||||
|
message:proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(Default::default())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2607,7 +2662,7 @@ impl Project {
|
||||||
fn on_lsp_progress(
|
fn on_lsp_progress(
|
||||||
&mut self,
|
&mut self,
|
||||||
progress: lsp::ProgressParams,
|
progress: lsp::ProgressParams,
|
||||||
server_id: LanguageServerId,
|
language_server_id: LanguageServerId,
|
||||||
disk_based_diagnostics_progress_token: Option<String>,
|
disk_based_diagnostics_progress_token: Option<String>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -2620,7 +2675,7 @@ impl Project {
|
||||||
};
|
};
|
||||||
let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
|
let lsp::ProgressParamsValue::WorkDone(progress) = progress.value;
|
||||||
let language_server_status =
|
let language_server_status =
|
||||||
if let Some(status) = self.language_server_statuses.get_mut(&server_id) {
|
if let Some(status) = self.language_server_statuses.get_mut(&language_server_id) {
|
||||||
status
|
status
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
@ -2640,16 +2695,16 @@ impl Project {
|
||||||
lsp::WorkDoneProgress::Begin(report) => {
|
lsp::WorkDoneProgress::Begin(report) => {
|
||||||
if is_disk_based_diagnostics_progress {
|
if is_disk_based_diagnostics_progress {
|
||||||
language_server_status.has_pending_diagnostic_updates = true;
|
language_server_status.has_pending_diagnostic_updates = true;
|
||||||
self.disk_based_diagnostics_started(server_id, cx);
|
self.disk_based_diagnostics_started(language_server_id, cx);
|
||||||
self.broadcast_language_server_update(
|
self.buffer_ordered_messages_tx
|
||||||
server_id,
|
.unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
|
||||||
proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(
|
language_server_id,
|
||||||
proto::LspDiskBasedDiagnosticsUpdating {},
|
message: proto::update_language_server::Variant::DiskBasedDiagnosticsUpdating(Default::default())
|
||||||
),
|
})
|
||||||
);
|
.ok();
|
||||||
} else {
|
} else {
|
||||||
self.on_lsp_work_start(
|
self.on_lsp_work_start(
|
||||||
server_id,
|
language_server_id,
|
||||||
token.clone(),
|
token.clone(),
|
||||||
LanguageServerProgress {
|
LanguageServerProgress {
|
||||||
message: report.message.clone(),
|
message: report.message.clone(),
|
||||||
|
@ -2658,20 +2713,24 @@ impl Project {
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
self.broadcast_language_server_update(
|
self.buffer_ordered_messages_tx
|
||||||
server_id,
|
.unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
|
||||||
proto::update_language_server::Variant::WorkStart(proto::LspWorkStart {
|
language_server_id,
|
||||||
|
message: proto::update_language_server::Variant::WorkStart(
|
||||||
|
proto::LspWorkStart {
|
||||||
token,
|
token,
|
||||||
message: report.message,
|
message: report.message,
|
||||||
percentage: report.percentage.map(|p| p as u32),
|
percentage: report.percentage.map(|p| p as u32),
|
||||||
}),
|
},
|
||||||
);
|
),
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lsp::WorkDoneProgress::Report(report) => {
|
lsp::WorkDoneProgress::Report(report) => {
|
||||||
if !is_disk_based_diagnostics_progress {
|
if !is_disk_based_diagnostics_progress {
|
||||||
self.on_lsp_work_progress(
|
self.on_lsp_work_progress(
|
||||||
server_id,
|
language_server_id,
|
||||||
token.clone(),
|
token.clone(),
|
||||||
LanguageServerProgress {
|
LanguageServerProgress {
|
||||||
message: report.message.clone(),
|
message: report.message.clone(),
|
||||||
|
@ -2680,16 +2739,18 @@ impl Project {
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
self.broadcast_language_server_update(
|
self.buffer_ordered_messages_tx
|
||||||
server_id,
|
.unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
|
||||||
proto::update_language_server::Variant::WorkProgress(
|
language_server_id,
|
||||||
|
message: proto::update_language_server::Variant::WorkProgress(
|
||||||
proto::LspWorkProgress {
|
proto::LspWorkProgress {
|
||||||
token,
|
token,
|
||||||
message: report.message,
|
message: report.message,
|
||||||
percentage: report.percentage.map(|p| p as u32),
|
percentage: report.percentage.map(|p| p as u32),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lsp::WorkDoneProgress::End(_) => {
|
lsp::WorkDoneProgress::End(_) => {
|
||||||
|
@ -2697,21 +2758,26 @@ impl Project {
|
||||||
|
|
||||||
if is_disk_based_diagnostics_progress {
|
if is_disk_based_diagnostics_progress {
|
||||||
language_server_status.has_pending_diagnostic_updates = false;
|
language_server_status.has_pending_diagnostic_updates = false;
|
||||||
self.disk_based_diagnostics_finished(server_id, cx);
|
self.disk_based_diagnostics_finished(language_server_id, cx);
|
||||||
self.broadcast_language_server_update(
|
self.buffer_ordered_messages_tx
|
||||||
server_id,
|
.unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
|
||||||
|
language_server_id,
|
||||||
|
message:
|
||||||
proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
|
proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated(
|
||||||
proto::LspDiskBasedDiagnosticsUpdated {},
|
Default::default(),
|
||||||
),
|
),
|
||||||
);
|
})
|
||||||
|
.ok();
|
||||||
} else {
|
} else {
|
||||||
self.on_lsp_work_end(server_id, token.clone(), cx);
|
self.on_lsp_work_end(language_server_id, token.clone(), cx);
|
||||||
self.broadcast_language_server_update(
|
self.buffer_ordered_messages_tx
|
||||||
server_id,
|
.unbounded_send(BufferOrderedMessage::LanguageServerUpdate {
|
||||||
proto::update_language_server::Variant::WorkEnd(proto::LspWorkEnd {
|
language_server_id,
|
||||||
token,
|
message: proto::update_language_server::Variant::WorkEnd(
|
||||||
}),
|
proto::LspWorkEnd { token },
|
||||||
);
|
),
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2820,22 +2886,6 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn broadcast_language_server_update(
|
|
||||||
&self,
|
|
||||||
language_server_id: LanguageServerId,
|
|
||||||
event: proto::update_language_server::Variant,
|
|
||||||
) {
|
|
||||||
if let Some(project_id) = self.remote_id() {
|
|
||||||
self.client
|
|
||||||
.send(proto::UpdateLanguageServer {
|
|
||||||
project_id,
|
|
||||||
language_server_id: language_server_id.0 as u64,
|
|
||||||
variant: Some(event),
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn language_server_statuses(
|
pub fn language_server_statuses(
|
||||||
&self,
|
&self,
|
||||||
) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
|
) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
|
||||||
|
@ -4864,8 +4914,8 @@ impl Project {
|
||||||
if is_host {
|
if is_host {
|
||||||
this.opened_buffers
|
this.opened_buffers
|
||||||
.retain(|_, buffer| !matches!(buffer, OpenBuffer::Operations(_)));
|
.retain(|_, buffer| !matches!(buffer, OpenBuffer::Operations(_)));
|
||||||
this.buffer_changes_tx
|
this.buffer_ordered_messages_tx
|
||||||
.unbounded_send(BufferMessage::Resync)
|
.unbounded_send(BufferOrderedMessage::Resync)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -785,6 +785,10 @@ impl Pane {
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
let pane_handle = workspace.active_pane().clone();
|
let pane_handle = workspace.active_pane().clone();
|
||||||
let pane = pane_handle.read(cx);
|
let pane = pane_handle.read(cx);
|
||||||
|
|
||||||
|
if pane.items.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let active_item_id = pane.items[pane.active_item_index].id();
|
let active_item_id = pane.items[pane.active_item_index].id();
|
||||||
|
|
||||||
let task = Self::close_item_by_id(workspace, pane_handle, active_item_id, cx);
|
let task = Self::close_item_by_id(workspace, pane_handle, active_item_id, cx);
|
||||||
|
@ -2098,6 +2102,19 @@ mod tests {
|
||||||
use gpui::{executor::Deterministic, TestAppContext};
|
use gpui::{executor::Deterministic, TestAppContext};
|
||||||
use project::FakeFs;
|
use project::FakeFs;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_remove_active_empty(cx: &mut TestAppContext) {
|
||||||
|
Settings::test_async(cx);
|
||||||
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
|
let project = Project::test(fs, None, cx).await;
|
||||||
|
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
assert!(Pane::close_active_item(workspace, &CloseActiveItem, cx).is_none())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
|
async fn test_add_item_with_new_item(cx: &mut TestAppContext) {
|
||||||
cx.foreground().forbid_parking();
|
cx.foreground().forbid_parking();
|
||||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.83.0"
|
version = "0.83.2"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
dev
|
stable
|
Loading…
Add table
Add a link
Reference in a new issue