use ssh lsp store (#17655)

Release Notes:

- ssh remoting: Added support for booting langauge servers (in limited
circumstances)

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
Conrad Irwin 2024-09-10 15:51:01 -04:00 committed by GitHub
parent 130f19d8f9
commit 36eb1c15ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 1553 additions and 671 deletions

View file

@ -4,14 +4,13 @@ use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext, Task};
use language::LanguageRegistry;
use project::{
buffer_store::BufferStore, project_settings::SettingsObserver, search::SearchQuery,
worktree_store::WorktreeStore, LspStore, ProjectPath, WorktreeId, WorktreeSettings,
worktree_store::WorktreeStore, LspStore, ProjectPath, WorktreeId,
};
use remote::SshSession;
use rpc::{
proto::{self, AnyProtoClient, SSH_PEER_ID, SSH_PROJECT_ID},
TypedEnvelope,
};
use settings::Settings as _;
use smol::stream::StreamExt;
use std::{
path::{Path, PathBuf},
@ -33,15 +32,17 @@ impl HeadlessProject {
pub fn init(cx: &mut AppContext) {
settings::init(cx);
language::init(cx);
WorktreeSettings::register(cx);
project::Project::init_settings(cx);
}
pub fn new(session: Arc<SshSession>, fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
// TODO: we should load the env correctly (as we do in login_shell_env_loaded when stdout is not a pty). Can we re-use the ProjectEnvironment for that?
let languages = Arc::new(LanguageRegistry::new(
Task::ready(()),
cx.background_executor().clone(),
));
let mut languages =
LanguageRegistry::new(Task::ready(()), cx.background_executor().clone());
languages
.set_language_server_download_dir(PathBuf::from("/Users/conrad/what-could-go-wrong"));
let languages = Arc::new(languages);
let worktree_store = cx.new_model(|_| WorktreeStore::new(true, fs.clone()));
let buffer_store = cx.new_model(|cx| {
@ -57,18 +58,17 @@ impl HeadlessProject {
});
let environment = project::ProjectEnvironment::new(&worktree_store, None, cx);
let lsp_store = cx.new_model(|cx| {
LspStore::new(
let mut lsp_store = LspStore::new_local(
buffer_store.clone(),
worktree_store.clone(),
Some(environment),
environment,
languages,
None,
fs.clone(),
Some(session.clone().into()),
None,
Some(0),
cx,
)
);
lsp_store.shared(SSH_PROJECT_ID, session.clone().into(), cx);
lsp_store
});
let client: AnyProtoClient = session.clone().into();
@ -88,9 +88,12 @@ impl HeadlessProject {
client.add_model_request_handler(BufferStore::handle_update_buffer);
client.add_model_message_handler(BufferStore::handle_close_buffer);
client.add_model_request_handler(LspStore::handle_create_language_server);
BufferStore::init(&client);
WorktreeStore::init(&client);
SettingsObserver::init(&client);
LspStore::init(&client);
HeadlessProject {
session: client,

View file

@ -6,7 +6,7 @@ use gpui::{Context, Model, TestAppContext};
use http_client::FakeHttpClient;
use language::{
language_settings::{all_language_settings, AllLanguageSettings},
Buffer, LanguageRegistry,
Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry,
};
use node_runtime::FakeNodeRuntime;
use project::{
@ -202,15 +202,29 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
server_cx.read(|cx| {
assert_eq!(
AllLanguageSettings::get_global(cx)
.language(Some("Rust"))
.language(Some(&"Rust".into()))
.language_servers,
["custom-rust-analyzer".into()]
)
});
fs.insert_tree("/code/project1/.zed", json!({
"settings.json": r#"{"languages":{"Rust":{"language_servers":["override-rust-analyzer"]}}}"#
})).await;
fs.insert_tree(
"/code/project1/.zed",
json!({
"settings.json": r#"
{
"languages": {"Rust":{"language_servers":["override-rust-analyzer"]}},
"lsp": {
"override-rust-analyzer": {
"binary": {
"path": "~/.cargo/bin/rust-analyzer"
}
}
}
}"#
}),
)
.await;
let worktree_id = project
.update(cx, |project, cx| {
@ -247,7 +261,7 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
}),
cx
)
.language(Some("Rust"))
.language(Some(&"Rust".into()))
.language_servers,
["override-rust-analyzer".into()]
)
@ -257,13 +271,107 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
let file = buffer.read(cx).file();
assert_eq!(
all_language_settings(file, cx)
.language(Some("Rust"))
.language(Some(&"Rust".into()))
.language_servers,
["override-rust-analyzer".into()]
)
});
}
#[gpui::test]
async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
let (project, headless, fs) = init_test(cx, server_cx).await;
fs.insert_tree(
"/code/project1/.zed",
json!({
"settings.json": r#"
{
"languages": {"Rust":{"language_servers":["rust-analyzer"]}},
"lsp": {
"rust-analyzer": {
"binary": {
"path": "~/.cargo/bin/rust-analyzer"
}
}
}
}"#
}),
)
.await;
cx.update_model(&project, |project, _| {
project.languages().register_test_language(LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".into()],
..Default::default()
},
..Default::default()
});
project.languages().register_fake_lsp_adapter(
"Rust",
FakeLspAdapter {
name: "rust-analyzer",
..Default::default()
},
)
});
cx.run_until_parked();
let worktree_id = project
.update(cx, |project, cx| {
project.find_or_create_worktree("/code/project1", true, cx)
})
.await
.unwrap()
.0
.read_with(cx, |worktree, _| worktree.id());
// Wait for the settings to synchronize
cx.run_until_parked();
let buffer = project
.update(cx, |project, cx| {
project.open_buffer((worktree_id, Path::new("src/lib.rs")), cx)
})
.await
.unwrap();
cx.run_until_parked();
cx.read(|cx| {
let file = buffer.read(cx).file();
assert_eq!(
all_language_settings(file, cx)
.language(Some(&"Rust".into()))
.language_servers,
["rust-analyzer".into()]
)
});
let buffer_id = cx.read(|cx| {
let buffer = buffer.read(cx);
assert_eq!(buffer.language().unwrap().name(), "Rust".into());
buffer.remote_id()
});
server_cx.read(|cx| {
let buffer = headless
.read(cx)
.buffer_store
.read(cx)
.get(buffer_id)
.unwrap();
assert_eq!(buffer.read(cx).language().unwrap().name(), "Rust".into());
});
server_cx.read(|cx| {
let lsp_store = headless.read(cx).lsp_store.read(cx);
assert_eq!(lsp_store.as_local().unwrap().language_servers.len(), 1);
});
}
fn init_logger() {
if std::env::var("RUST_LOG").is_ok() {
env_logger::try_init().ok();