Give the editor a handle to the project, not a weak handle to the workspace
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
624dbc1d0e
commit
6731d92f60
10 changed files with 112 additions and 112 deletions
|
@ -148,7 +148,7 @@ impl ProjectDiagnosticsEditor {
|
||||||
let mut editor = Editor::for_buffer(
|
let mut editor = Editor::for_buffer(
|
||||||
excerpts.clone(),
|
excerpts.clone(),
|
||||||
build_settings.clone(),
|
build_settings.clone(),
|
||||||
Some(workspace.clone()),
|
Some(project.clone()),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.set_vertical_scroll_margin(5, cx);
|
editor.set_vertical_scroll_margin(5, cx);
|
||||||
|
|
|
@ -12,6 +12,7 @@ test-support = [
|
||||||
"text/test-support",
|
"text/test-support",
|
||||||
"language/test-support",
|
"language/test-support",
|
||||||
"gpui/test-support",
|
"gpui/test-support",
|
||||||
|
"project/test-support",
|
||||||
"util/test-support",
|
"util/test-support",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ language = { path = "../language", features = ["test-support"] }
|
||||||
lsp = { path = "../lsp", features = ["test-support"] }
|
lsp = { path = "../lsp", features = ["test-support"] }
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
util = { path = "../util", features = ["test-support"] }
|
util = { path = "../util", features = ["test-support"] }
|
||||||
|
project = { path = "../project", features = ["test-support"] }
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
|
@ -40,6 +40,7 @@ pub use multi_buffer::{
|
||||||
};
|
};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
|
use project::Project;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
|
@ -415,7 +416,7 @@ pub struct Editor {
|
||||||
scroll_top_anchor: Option<Anchor>,
|
scroll_top_anchor: Option<Anchor>,
|
||||||
autoscroll_request: Option<Autoscroll>,
|
autoscroll_request: Option<Autoscroll>,
|
||||||
build_settings: BuildSettings,
|
build_settings: BuildSettings,
|
||||||
workspace: Option<WeakViewHandle<Workspace>>,
|
project: Option<ModelHandle<Project>>,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
show_local_cursors: bool,
|
show_local_cursors: bool,
|
||||||
blink_epoch: usize,
|
blink_epoch: usize,
|
||||||
|
@ -772,17 +773,17 @@ impl Editor {
|
||||||
pub fn for_buffer(
|
pub fn for_buffer(
|
||||||
buffer: ModelHandle<MultiBuffer>,
|
buffer: ModelHandle<MultiBuffer>,
|
||||||
build_settings: BuildSettings,
|
build_settings: BuildSettings,
|
||||||
workspace: Option<WeakViewHandle<Workspace>>,
|
project: Option<ModelHandle<Project>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(buffer, build_settings, workspace, cx)
|
Self::new(buffer, build_settings, project, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let mut clone = Self::new(
|
let mut clone = Self::new(
|
||||||
self.buffer.clone(),
|
self.buffer.clone(),
|
||||||
self.build_settings.clone(),
|
self.build_settings.clone(),
|
||||||
self.workspace.clone(),
|
self.project.clone(),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
clone.scroll_position = self.scroll_position;
|
clone.scroll_position = self.scroll_position;
|
||||||
|
@ -797,7 +798,7 @@ impl Editor {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
buffer: ModelHandle<MultiBuffer>,
|
buffer: ModelHandle<MultiBuffer>,
|
||||||
build_settings: BuildSettings,
|
build_settings: BuildSettings,
|
||||||
workspace: Option<WeakViewHandle<Workspace>>,
|
project: Option<ModelHandle<Project>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let settings = build_settings(cx);
|
let settings = build_settings(cx);
|
||||||
|
@ -832,7 +833,7 @@ impl Editor {
|
||||||
select_larger_syntax_node_stack: Vec::new(),
|
select_larger_syntax_node_stack: Vec::new(),
|
||||||
active_diagnostics: None,
|
active_diagnostics: None,
|
||||||
build_settings,
|
build_settings,
|
||||||
workspace,
|
project,
|
||||||
scroll_position: Vector2F::zero(),
|
scroll_position: Vector2F::zero(),
|
||||||
scroll_top_anchor: None,
|
scroll_top_anchor: None,
|
||||||
autoscroll_request: None,
|
autoscroll_request: None,
|
||||||
|
@ -1882,8 +1883,8 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
|
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
|
||||||
let project = if let Some(workspace) = self.workspace.as_ref().and_then(|w| w.upgrade(cx)) {
|
let project = if let Some(project) = self.project.clone() {
|
||||||
workspace.read(cx).project().clone()
|
project
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -2050,13 +2051,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
self.end_transaction(cx);
|
self.end_transaction(cx);
|
||||||
|
|
||||||
let project = self
|
let project = self.project.clone()?;
|
||||||
.workspace
|
|
||||||
.as_ref()?
|
|
||||||
.upgrade(cx)?
|
|
||||||
.read(cx)
|
|
||||||
.project()
|
|
||||||
.clone();
|
|
||||||
let apply_edits = project.update(cx, |project, cx| {
|
let apply_edits = project.update(cx, |project, cx| {
|
||||||
project.apply_additional_edits_for_completion(
|
project.apply_additional_edits_for_completion(
|
||||||
buffer_handle,
|
buffer_handle,
|
||||||
|
@ -2077,19 +2072,14 @@ impl Editor {
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let workspace = if let Some(workspace) = self.workspace.as_ref().and_then(|w| w.upgrade(cx))
|
let project = if let Some(project) = self.project.clone() {
|
||||||
{
|
project
|
||||||
workspace
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let (buffer, head) = self.buffer.read(cx).text_anchor_for_position(head, cx);
|
let (buffer, head) = self.buffer.read(cx).text_anchor_for_position(head, cx);
|
||||||
let actions = workspace
|
let actions = project.update(cx, |project, cx| project.code_actions(&buffer, head, cx));
|
||||||
.read(cx)
|
|
||||||
.project()
|
|
||||||
.clone()
|
|
||||||
.update(cx, |project, cx| project.code_actions(&buffer, head, cx));
|
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let actions = actions.await?;
|
let actions = actions.await?;
|
||||||
|
@ -2114,13 +2104,14 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_code_action(
|
fn confirm_code_action(
|
||||||
&mut self,
|
workspace: &mut Workspace,
|
||||||
ConfirmCodeAction(action_ix): &ConfirmCodeAction,
|
ConfirmCodeAction(action_ix): &ConfirmCodeAction,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
let workspace = self.workspace.as_ref()?.upgrade(cx)?;
|
let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
|
||||||
|
let actions_menu = if let ContextMenu::CodeActions(menu) =
|
||||||
let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
|
editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
|
||||||
|
{
|
||||||
menu
|
menu
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -2129,14 +2120,10 @@ impl Editor {
|
||||||
let action = actions_menu.actions.get(action_ix)?.clone();
|
let action = actions_menu.actions.get(action_ix)?.clone();
|
||||||
let buffer = actions_menu.buffer;
|
let buffer = actions_menu.buffer;
|
||||||
|
|
||||||
let apply_code_actions = workspace
|
let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
|
||||||
.read(cx)
|
project.apply_code_action(buffer, action, true, cx)
|
||||||
.project()
|
});
|
||||||
.clone()
|
Some(cx.spawn(|workspace, mut cx| async move {
|
||||||
.update(cx, |project, cx| {
|
|
||||||
project.apply_code_action(buffer, action, true, cx)
|
|
||||||
});
|
|
||||||
Some(cx.spawn(|_, mut cx| async move {
|
|
||||||
let project_transaction = apply_code_actions.await?;
|
let project_transaction = apply_code_actions.await?;
|
||||||
|
|
||||||
// TODO: replace this with opening a single tab that is a multibuffer
|
// TODO: replace this with opening a single tab that is a multibuffer
|
||||||
|
@ -5272,9 +5259,10 @@ fn styled_runs_for_completion_label<'a>(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use language::{FakeFile, LanguageConfig};
|
use language::LanguageConfig;
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
use std::{cell::RefCell, path::Path, rc::Rc, time::Instant};
|
use project::{FakeFs, ProjectPath};
|
||||||
|
use std::{cell::RefCell, rc::Rc, time::Instant};
|
||||||
use text::Point;
|
use text::Point;
|
||||||
use unindent::Unindent;
|
use unindent::Unindent;
|
||||||
use util::test::sample_text;
|
use util::test::sample_text;
|
||||||
|
@ -7562,7 +7550,7 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cx.background(),
|
&cx,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -7573,30 +7561,43 @@ mod tests {
|
||||||
"
|
"
|
||||||
.unindent();
|
.unindent();
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| {
|
let fs = Arc::new(FakeFs::new(cx.background().clone()));
|
||||||
Buffer::from_file(
|
fs.insert_file("/file", text).await.unwrap();
|
||||||
0,
|
|
||||||
text,
|
let project = Project::test(fs, &mut cx);
|
||||||
Box::new(FakeFile {
|
|
||||||
path: Arc::from(Path::new("/the/file")),
|
let (worktree, relative_path) = project
|
||||||
}),
|
.update(&mut cx, |project, cx| {
|
||||||
cx,
|
project.find_or_create_local_worktree("/file", false, cx)
|
||||||
)
|
})
|
||||||
.with_language_server(language_server, cx)
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let project_path = ProjectPath {
|
||||||
|
worktree_id: worktree.read_with(&cx, |worktree, _| worktree.id()),
|
||||||
|
path: relative_path.into(),
|
||||||
|
};
|
||||||
|
let buffer = project
|
||||||
|
.update(&mut cx, |project, cx| project.open_buffer(project_path, cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
buffer.update(&mut cx, |buffer, cx| {
|
||||||
|
buffer.set_language_server(Some(language_server), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
buffer.next_notification(&cx).await;
|
buffer.next_notification(&cx).await;
|
||||||
|
|
||||||
let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
|
let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
|
||||||
|
|
||||||
editor.update(&mut cx, |editor, cx| {
|
editor.update(&mut cx, |editor, cx| {
|
||||||
|
editor.project = Some(project);
|
||||||
editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx);
|
editor.select_ranges([Point::new(0, 3)..Point::new(0, 3)], None, cx);
|
||||||
editor.handle_input(&Input(".".to_string()), cx);
|
editor.handle_input(&Input(".".to_string()), cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
handle_completion_request(
|
handle_completion_request(
|
||||||
&mut fake,
|
&mut fake,
|
||||||
"/the/file",
|
"/file",
|
||||||
Point::new(0, 4),
|
Point::new(0, 4),
|
||||||
&[
|
&[
|
||||||
(Point::new(0, 4)..Point::new(0, 4), "first_completion"),
|
(Point::new(0, 4)..Point::new(0, 4), "first_completion"),
|
||||||
|
@ -7658,7 +7659,7 @@ mod tests {
|
||||||
|
|
||||||
handle_completion_request(
|
handle_completion_request(
|
||||||
&mut fake,
|
&mut fake,
|
||||||
"/the/file",
|
"/file",
|
||||||
Point::new(2, 7),
|
Point::new(2, 7),
|
||||||
&[
|
&[
|
||||||
(Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
|
(Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
|
||||||
|
@ -7677,7 +7678,7 @@ mod tests {
|
||||||
|
|
||||||
handle_completion_request(
|
handle_completion_request(
|
||||||
&mut fake,
|
&mut fake,
|
||||||
"/the/file",
|
"/file",
|
||||||
Point::new(2, 8),
|
Point::new(2, 8),
|
||||||
&[
|
&[
|
||||||
(Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
|
(Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl ItemHandle for BufferItemHandle {
|
||||||
let mut editor = Editor::for_buffer(
|
let mut editor = Editor::for_buffer(
|
||||||
buffer,
|
buffer,
|
||||||
crate::settings_builder(weak_buffer, workspace.settings()),
|
crate::settings_builder(weak_buffer, workspace.settings()),
|
||||||
Some(workspace.weak_handle()),
|
Some(workspace.project().clone()),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));
|
editor.nav_history = Some(ItemNavHistory::new(nav_history, &cx.handle()));
|
||||||
|
|
|
@ -8,7 +8,7 @@ mod tests;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use gpui::AppContext;
|
use gpui::{AppContext, TestAppContext};
|
||||||
use highlight_map::HighlightMap;
|
use highlight_map::HighlightMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -357,18 +357,15 @@ impl CompletionLabel {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl LanguageServerConfig {
|
impl LanguageServerConfig {
|
||||||
pub async fn fake(
|
pub async fn fake(cx: &TestAppContext) -> (Self, lsp::FakeLanguageServer) {
|
||||||
executor: Arc<gpui::executor::Background>,
|
Self::fake_with_capabilities(Default::default(), cx).await
|
||||||
) -> (Self, lsp::FakeLanguageServer) {
|
|
||||||
Self::fake_with_capabilities(Default::default(), executor).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fake_with_capabilities(
|
pub async fn fake_with_capabilities(
|
||||||
capabilites: lsp::ServerCapabilities,
|
capabilites: lsp::ServerCapabilities,
|
||||||
executor: Arc<gpui::executor::Background>,
|
cx: &TestAppContext,
|
||||||
) -> (Self, lsp::FakeLanguageServer) {
|
) -> (Self, lsp::FakeLanguageServer) {
|
||||||
let (server, fake) =
|
let (server, fake) = lsp::LanguageServer::fake_with_capabilities(capabilites, cx).await;
|
||||||
lsp::LanguageServer::fake_with_capabilities(capabilites, executor).await;
|
|
||||||
fake.started
|
fake.started
|
||||||
.store(false, std::sync::atomic::Ordering::SeqCst);
|
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
let started = fake.started.clone();
|
let started = fake.started.clone();
|
||||||
|
|
|
@ -557,7 +557,7 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppConte
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_diagnostics(mut cx: gpui::TestAppContext) {
|
async fn test_diagnostics(mut cx: gpui::TestAppContext) {
|
||||||
let (language_server, mut fake) = lsp::LanguageServer::fake(cx.background()).await;
|
let (language_server, mut fake) = lsp::LanguageServer::fake(&cx).await;
|
||||||
let mut rust_lang = rust_lang();
|
let mut rust_lang = rust_lang();
|
||||||
rust_lang.config.language_server = Some(LanguageServerConfig {
|
rust_lang.config.language_server = Some(LanguageServerConfig {
|
||||||
disk_based_diagnostic_sources: HashSet::from_iter(["disk".to_string()]),
|
disk_based_diagnostic_sources: HashSet::from_iter(["disk".to_string()]),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use futures::{io::BufWriter, AsyncRead, AsyncWrite};
|
use futures::{io::BufWriter, AsyncRead, AsyncWrite};
|
||||||
use gpui::{executor, Task};
|
use gpui::{executor, Task, TestAppContext};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use postage::{barrier, oneshot, prelude::Stream, sink::Sink, watch};
|
use postage::{barrier, oneshot, prelude::Stream, sink::Sink, watch};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -14,6 +14,7 @@ use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
future::Future,
|
future::Future,
|
||||||
io::Write,
|
io::Write,
|
||||||
|
rc::Rc,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
|
@ -472,6 +473,7 @@ pub struct FakeLanguageServer {
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
stdin: smol::io::BufReader<async_pipe::PipeReader>,
|
stdin: smol::io::BufReader<async_pipe::PipeReader>,
|
||||||
stdout: smol::io::BufWriter<async_pipe::PipeWriter>,
|
stdout: smol::io::BufWriter<async_pipe::PipeWriter>,
|
||||||
|
executor: Rc<executor::Foreground>,
|
||||||
pub started: Arc<std::sync::atomic::AtomicBool>,
|
pub started: Arc<std::sync::atomic::AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,13 +485,13 @@ pub struct RequestId<T> {
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl LanguageServer {
|
impl LanguageServer {
|
||||||
pub async fn fake(executor: Arc<executor::Background>) -> (Arc<Self>, FakeLanguageServer) {
|
pub async fn fake(cx: &TestAppContext) -> (Arc<Self>, FakeLanguageServer) {
|
||||||
Self::fake_with_capabilities(Default::default(), executor).await
|
Self::fake_with_capabilities(Default::default(), cx).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fake_with_capabilities(
|
pub async fn fake_with_capabilities(
|
||||||
capabilities: ServerCapabilities,
|
capabilities: ServerCapabilities,
|
||||||
executor: Arc<executor::Background>,
|
cx: &TestAppContext,
|
||||||
) -> (Arc<Self>, FakeLanguageServer) {
|
) -> (Arc<Self>, FakeLanguageServer) {
|
||||||
let stdin = async_pipe::pipe();
|
let stdin = async_pipe::pipe();
|
||||||
let stdout = async_pipe::pipe();
|
let stdout = async_pipe::pipe();
|
||||||
|
@ -497,10 +499,12 @@ impl LanguageServer {
|
||||||
stdin: smol::io::BufReader::new(stdin.1),
|
stdin: smol::io::BufReader::new(stdin.1),
|
||||||
stdout: smol::io::BufWriter::new(stdout.0),
|
stdout: smol::io::BufWriter::new(stdout.0),
|
||||||
buffer: Vec::new(),
|
buffer: Vec::new(),
|
||||||
|
executor: cx.foreground(),
|
||||||
started: Arc::new(std::sync::atomic::AtomicBool::new(true)),
|
started: Arc::new(std::sync::atomic::AtomicBool::new(true)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let server = Self::new_internal(stdin.0, stdout.1, Path::new("/"), executor).unwrap();
|
let server =
|
||||||
|
Self::new_internal(stdin.0, stdout.1, Path::new("/"), cx.background()).unwrap();
|
||||||
|
|
||||||
let (init_id, _) = fake.receive_request::<request::Initialize>().await;
|
let (init_id, _) = fake.receive_request::<request::Initialize>().await;
|
||||||
fake.respond(
|
fake.respond(
|
||||||
|
@ -549,11 +553,14 @@ impl FakeLanguageServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn receive_request<T: request::Request>(&mut self) -> (RequestId<T>, T::Params) {
|
pub async fn receive_request<T: request::Request>(&mut self) -> (RequestId<T>, T::Params) {
|
||||||
|
let executor = self.executor.clone();
|
||||||
|
executor.start_waiting();
|
||||||
loop {
|
loop {
|
||||||
self.receive().await;
|
self.receive().await;
|
||||||
if let Ok(request) = serde_json::from_slice::<Request<T::Params>>(&self.buffer) {
|
if let Ok(request) = serde_json::from_slice::<Request<T::Params>>(&self.buffer) {
|
||||||
assert_eq!(request.method, T::METHOD);
|
assert_eq!(request.method, T::METHOD);
|
||||||
assert_eq!(request.jsonrpc, JSON_RPC_VERSION);
|
assert_eq!(request.jsonrpc, JSON_RPC_VERSION);
|
||||||
|
executor.finish_waiting();
|
||||||
return (
|
return (
|
||||||
RequestId {
|
RequestId {
|
||||||
id: request.id,
|
id: request.id,
|
||||||
|
@ -704,7 +711,7 @@ mod tests {
|
||||||
async fn test_fake(cx: TestAppContext) {
|
async fn test_fake(cx: TestAppContext) {
|
||||||
SimpleLogger::init(log::LevelFilter::Info, Default::default()).unwrap();
|
SimpleLogger::init(log::LevelFilter::Info, Default::default()).unwrap();
|
||||||
|
|
||||||
let (server, mut fake) = LanguageServer::fake(cx.background()).await;
|
let (server, mut fake) = LanguageServer::fake(&cx).await;
|
||||||
|
|
||||||
let (message_tx, message_rx) = channel::unbounded();
|
let (message_tx, message_rx) = channel::unbounded();
|
||||||
let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
|
let (diagnostics_tx, diagnostics_rx) = channel::unbounded();
|
||||||
|
|
|
@ -7,7 +7,11 @@ edition = "2021"
|
||||||
path = "src/project.rs"
|
path = "src/project.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-support = ["language/test-support", "text/test-support"]
|
test-support = [
|
||||||
|
"client/test-support",
|
||||||
|
"language/test-support",
|
||||||
|
"text/test-support",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
|
|
|
@ -322,6 +322,15 @@ impl Project {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub fn test(fs: Arc<dyn Fs>, cx: &mut gpui::TestAppContext) -> ModelHandle<Project> {
|
||||||
|
let languages = Arc::new(LanguageRegistry::new());
|
||||||
|
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||||
|
let client = client::Client::new(http_client.clone());
|
||||||
|
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||||
|
cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
|
||||||
|
}
|
||||||
|
|
||||||
fn set_remote_id(&mut self, remote_id: Option<u64>, cx: &mut ModelContext<Self>) {
|
fn set_remote_id(&mut self, remote_id: Option<u64>, cx: &mut ModelContext<Self>) {
|
||||||
if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
|
if let ProjectClientState::Local { remote_id_tx, .. } = &mut self.client_state {
|
||||||
*remote_id_tx.borrow_mut() = remote_id;
|
*remote_id_tx.borrow_mut() = remote_id;
|
||||||
|
@ -1197,21 +1206,11 @@ impl Project {
|
||||||
|
|
||||||
if worktree.read(cx).as_local().is_some() {
|
if worktree.read(cx).as_local().is_some() {
|
||||||
let buffer_abs_path = buffer_abs_path.unwrap();
|
let buffer_abs_path = buffer_abs_path.unwrap();
|
||||||
let lang_name;
|
let lang_server = if let Some(server) = source_buffer.language_server().cloned() {
|
||||||
let lang_server;
|
server
|
||||||
if let Some(lang) = &language {
|
|
||||||
lang_name = lang.name().to_string();
|
|
||||||
if let Some(server) = self
|
|
||||||
.language_servers
|
|
||||||
.get(&(worktree.read(cx).id(), lang_name.clone()))
|
|
||||||
{
|
|
||||||
lang_server = server.clone();
|
|
||||||
} else {
|
|
||||||
return Task::ready(Err(anyhow!("buffer does not have a language server")));
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return Task::ready(Err(anyhow!("buffer does not have a language")));
|
return Task::ready(Err(anyhow!("buffer does not have a language server")));
|
||||||
}
|
};
|
||||||
|
|
||||||
cx.spawn(|_, cx| async move {
|
cx.spawn(|_, cx| async move {
|
||||||
let completions = lang_server
|
let completions = lang_server
|
||||||
|
@ -2793,7 +2792,7 @@ mod tests {
|
||||||
use client::test::FakeHttpClient;
|
use client::test::FakeHttpClient;
|
||||||
use fs::RealFs;
|
use fs::RealFs;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{test::subscribe, TestAppContext};
|
use gpui::test::subscribe;
|
||||||
use language::{
|
use language::{
|
||||||
tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageRegistry,
|
tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageRegistry,
|
||||||
LanguageServerConfig, Point,
|
LanguageServerConfig, Point,
|
||||||
|
@ -2830,7 +2829,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let project = build_project(Arc::new(RealFs), &mut cx);
|
let project = Project::test(Arc::new(RealFs), &mut cx);
|
||||||
|
|
||||||
let (tree, _) = project
|
let (tree, _) = project
|
||||||
.update(&mut cx, |project, cx| {
|
.update(&mut cx, |project, cx| {
|
||||||
|
@ -2870,8 +2869,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
|
async fn test_language_server_diagnostics(mut cx: gpui::TestAppContext) {
|
||||||
let (language_server_config, mut fake_server) =
|
let (language_server_config, mut fake_server) = LanguageServerConfig::fake(&cx).await;
|
||||||
LanguageServerConfig::fake(cx.background()).await;
|
|
||||||
let progress_token = language_server_config
|
let progress_token = language_server_config
|
||||||
.disk_based_diagnostics_progress_token
|
.disk_based_diagnostics_progress_token
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -3012,7 +3010,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let project = build_project(Arc::new(RealFs), &mut cx);
|
let project = Project::test(Arc::new(RealFs), &mut cx);
|
||||||
let (tree, _) = project
|
let (tree, _) = project
|
||||||
.update(&mut cx, |project, cx| {
|
.update(&mut cx, |project, cx| {
|
||||||
project.find_or_create_local_worktree(&dir.path(), false, cx)
|
project.find_or_create_local_worktree(&dir.path(), false, cx)
|
||||||
|
@ -3035,8 +3033,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_definition(mut cx: gpui::TestAppContext) {
|
async fn test_definition(mut cx: gpui::TestAppContext) {
|
||||||
let (language_server_config, mut fake_server) =
|
let (language_server_config, mut fake_server) = LanguageServerConfig::fake(&cx).await;
|
||||||
LanguageServerConfig::fake(cx.background()).await;
|
|
||||||
|
|
||||||
let mut languages = LanguageRegistry::new();
|
let mut languages = LanguageRegistry::new();
|
||||||
languages.add(Arc::new(Language::new(
|
languages.add(Arc::new(Language::new(
|
||||||
|
@ -3169,7 +3166,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = build_project(fs.clone(), &mut cx);
|
let project = Project::test(fs.clone(), &mut cx);
|
||||||
let worktree_id = project
|
let worktree_id = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree("/dir", false, cx)
|
p.find_or_create_local_worktree("/dir", false, cx)
|
||||||
|
@ -3207,7 +3204,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = build_project(fs.clone(), &mut cx);
|
let project = Project::test(fs.clone(), &mut cx);
|
||||||
let worktree_id = project
|
let worktree_id = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree("/dir/file1", false, cx)
|
p.find_or_create_local_worktree("/dir/file1", false, cx)
|
||||||
|
@ -3249,7 +3246,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let project = build_project(Arc::new(RealFs), &mut cx);
|
let project = Project::test(Arc::new(RealFs), &mut cx);
|
||||||
let rpc = project.read_with(&cx, |p, _| p.client.clone());
|
let rpc = project.read_with(&cx, |p, _| p.client.clone());
|
||||||
|
|
||||||
let (tree, _) = project
|
let (tree, _) = project
|
||||||
|
@ -3395,7 +3392,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = build_project(fs.clone(), &mut cx);
|
let project = Project::test(fs.clone(), &mut cx);
|
||||||
let worktree_id = project
|
let worktree_id = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree("/the-dir", false, cx)
|
p.find_or_create_local_worktree("/the-dir", false, cx)
|
||||||
|
@ -3445,7 +3442,7 @@ mod tests {
|
||||||
"file3": "ghi",
|
"file3": "ghi",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let project = build_project(Arc::new(RealFs), &mut cx);
|
let project = Project::test(Arc::new(RealFs), &mut cx);
|
||||||
let (worktree, _) = project
|
let (worktree, _) = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree(dir.path(), false, cx)
|
p.find_or_create_local_worktree(dir.path(), false, cx)
|
||||||
|
@ -3579,7 +3576,7 @@ mod tests {
|
||||||
let initial_contents = "aaa\nbbbbb\nc\n";
|
let initial_contents = "aaa\nbbbbb\nc\n";
|
||||||
let dir = temp_tree(json!({ "the-file": initial_contents }));
|
let dir = temp_tree(json!({ "the-file": initial_contents }));
|
||||||
|
|
||||||
let project = build_project(Arc::new(RealFs), &mut cx);
|
let project = Project::test(Arc::new(RealFs), &mut cx);
|
||||||
let (worktree, _) = project
|
let (worktree, _) = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree(dir.path(), false, cx)
|
p.find_or_create_local_worktree(dir.path(), false, cx)
|
||||||
|
@ -3690,7 +3687,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = build_project(fs.clone(), &mut cx);
|
let project = Project::test(fs.clone(), &mut cx);
|
||||||
let (worktree, _) = project
|
let (worktree, _) = project
|
||||||
.update(&mut cx, |p, cx| {
|
.update(&mut cx, |p, cx| {
|
||||||
p.find_or_create_local_worktree("/the-dir", false, cx)
|
p.find_or_create_local_worktree("/the-dir", false, cx)
|
||||||
|
@ -3930,12 +3927,4 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_project(fs: Arc<dyn Fs>, cx: &mut TestAppContext) -> ModelHandle<Project> {
|
|
||||||
let languages = Arc::new(LanguageRegistry::new());
|
|
||||||
let http_client = FakeHttpClient::with_404_response();
|
|
||||||
let client = client::Client::new(http_client.clone());
|
|
||||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
|
||||||
cx.update(|cx| Project::local(client, user_store, languages, fs, cx))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2090,7 +2090,7 @@ mod tests {
|
||||||
|
|
||||||
// Set up a fake language server.
|
// Set up a fake language server.
|
||||||
let (language_server_config, mut fake_language_server) =
|
let (language_server_config, mut fake_language_server) =
|
||||||
LanguageServerConfig::fake(cx_a.background()).await;
|
LanguageServerConfig::fake(&cx_a).await;
|
||||||
Arc::get_mut(&mut lang_registry)
|
Arc::get_mut(&mut lang_registry)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add(Arc::new(Language::new(
|
.add(Arc::new(Language::new(
|
||||||
|
@ -2322,7 +2322,7 @@ mod tests {
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
cx_a.background(),
|
&cx_a,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Arc::get_mut(&mut lang_registry)
|
Arc::get_mut(&mut lang_registry)
|
||||||
|
@ -2401,7 +2401,7 @@ mod tests {
|
||||||
Editor::for_buffer(
|
Editor::for_buffer(
|
||||||
cx.add_model(|cx| MultiBuffer::singleton(buffer_b.clone(), cx)),
|
cx.add_model(|cx| MultiBuffer::singleton(buffer_b.clone(), cx)),
|
||||||
Arc::new(|cx| EditorSettings::test(cx)),
|
Arc::new(|cx| EditorSettings::test(cx)),
|
||||||
None,
|
Some(project_b.clone()),
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -2537,7 +2537,7 @@ mod tests {
|
||||||
|
|
||||||
// Set up a fake language server.
|
// Set up a fake language server.
|
||||||
let (language_server_config, mut fake_language_server) =
|
let (language_server_config, mut fake_language_server) =
|
||||||
LanguageServerConfig::fake(cx_a.background()).await;
|
LanguageServerConfig::fake(&cx_a).await;
|
||||||
Arc::get_mut(&mut lang_registry)
|
Arc::get_mut(&mut lang_registry)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add(Arc::new(Language::new(
|
.add(Arc::new(Language::new(
|
||||||
|
@ -2656,7 +2656,7 @@ mod tests {
|
||||||
|
|
||||||
// Set up a fake language server.
|
// Set up a fake language server.
|
||||||
let (language_server_config, mut fake_language_server) =
|
let (language_server_config, mut fake_language_server) =
|
||||||
LanguageServerConfig::fake(cx_a.background()).await;
|
LanguageServerConfig::fake(&cx_a).await;
|
||||||
Arc::get_mut(&mut lang_registry)
|
Arc::get_mut(&mut lang_registry)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add(Arc::new(Language::new(
|
.add(Arc::new(Language::new(
|
||||||
|
@ -2811,7 +2811,7 @@ mod tests {
|
||||||
|
|
||||||
// Set up a fake language server.
|
// Set up a fake language server.
|
||||||
let (language_server_config, mut fake_language_server) =
|
let (language_server_config, mut fake_language_server) =
|
||||||
LanguageServerConfig::fake(cx_a.background()).await;
|
LanguageServerConfig::fake(&cx_a).await;
|
||||||
Arc::get_mut(&mut lang_registry)
|
Arc::get_mut(&mut lang_registry)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.add(Arc::new(Language::new(
|
.add(Arc::new(Language::new(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue