parent
7814dd0301
commit
8cc6df573c
8 changed files with 268 additions and 148 deletions
|
@ -54,7 +54,7 @@ use language_model::{
|
||||||
use language_model::{LanguageModelImage, LanguageModelToolUse};
|
use language_model::{LanguageModelImage, LanguageModelToolUse};
|
||||||
use multi_buffer::MultiBufferRow;
|
use multi_buffer::MultiBufferRow;
|
||||||
use picker::{Picker, PickerDelegate};
|
use picker::{Picker, PickerDelegate};
|
||||||
use project::lsp_store::ProjectLspAdapterDelegate;
|
use project::lsp_store::LocalLspAdapterDelegate;
|
||||||
use project::{Project, Worktree};
|
use project::{Project, Worktree};
|
||||||
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
use search::{buffer_search::DivRegistrar, BufferSearchBar};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -5384,18 +5384,16 @@ fn make_lsp_adapter_delegate(
|
||||||
let worktree = project
|
let worktree = project
|
||||||
.worktrees(cx)
|
.worktrees(cx)
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| anyhow!("no worktrees when constructing ProjectLspAdapterDelegate"))?;
|
.ok_or_else(|| anyhow!("no worktrees when constructing LocalLspAdapterDelegate"))?;
|
||||||
let fs = if project.is_local() {
|
|
||||||
Some(project.fs().clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let http_client = project.client().http_client().clone();
|
let http_client = project.client().http_client().clone();
|
||||||
project.lsp_store().update(cx, |lsp_store, cx| {
|
project.lsp_store().update(cx, |lsp_store, cx| {
|
||||||
Ok(
|
Ok(LocalLspAdapterDelegate::new(
|
||||||
ProjectLspAdapterDelegate::new(lsp_store, &worktree, http_client, fs, None, cx)
|
lsp_store,
|
||||||
as Arc<dyn LspAdapterDelegate>,
|
&worktree,
|
||||||
)
|
http_client,
|
||||||
|
project.fs().clone(),
|
||||||
|
cx,
|
||||||
|
) as Arc<dyn LspAdapterDelegate>)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl LspAdapter for RustLspAdapter {
|
||||||
{
|
{
|
||||||
Ok(()) => (Some(path), Some(env), None),
|
Ok(()) => (Some(path), Some(env), None),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {:?}", path, err);
|
log::error!("failed to run rust-analyzer after detecting it in PATH: binary: {:?}: {}", path, err);
|
||||||
(None, None, None)
|
(None, None, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2305,8 +2305,7 @@ impl LspStore {
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.worktree_for_id(*worktree_id, cx)?;
|
.worktree_for_id(*worktree_id, cx)?;
|
||||||
let state = this.as_local()?.language_servers.get(server_id)?;
|
let state = this.as_local()?.language_servers.get(server_id)?;
|
||||||
let delegate =
|
let delegate = LocalLspAdapterDelegate::for_local(this, &worktree, cx);
|
||||||
ProjectLspAdapterDelegate::for_local(this, &worktree, cx);
|
|
||||||
match state {
|
match state {
|
||||||
LanguageServerState::Starting(_) => None,
|
LanguageServerState::Starting(_) => None,
|
||||||
LanguageServerState::Running {
|
LanguageServerState::Running {
|
||||||
|
@ -4368,7 +4367,7 @@ impl LspStore {
|
||||||
let response = this
|
let response = this
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
||||||
let delegate = ProjectLspAdapterDelegate::for_local(this, &worktree, cx);
|
let delegate = LocalLspAdapterDelegate::for_local(this, &worktree, cx);
|
||||||
anyhow::Ok(
|
anyhow::Ok(
|
||||||
cx.spawn(|_, _| async move { delegate.which(command.as_os_str()).await }),
|
cx.spawn(|_, _| async move { delegate.which(command.as_os_str()).await }),
|
||||||
)
|
)
|
||||||
|
@ -4389,7 +4388,7 @@ impl LspStore {
|
||||||
let response = this
|
let response = this
|
||||||
.update(&mut cx, |this, cx| {
|
.update(&mut cx, |this, cx| {
|
||||||
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
||||||
let delegate = ProjectLspAdapterDelegate::for_local(this, &worktree, cx);
|
let delegate = LocalLspAdapterDelegate::for_local(this, &worktree, cx);
|
||||||
anyhow::Ok(cx.spawn(|_, _| async move { delegate.shell_env().await }))
|
anyhow::Ok(cx.spawn(|_, _| async move { delegate.shell_env().await }))
|
||||||
})??
|
})??
|
||||||
.await;
|
.await;
|
||||||
|
@ -4398,6 +4397,52 @@ impl LspStore {
|
||||||
env: response.into_iter().collect(),
|
env: response.into_iter().collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub async fn handle_try_exec(
|
||||||
|
this: Model<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::TryExec>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) -> Result<proto::Ack> {
|
||||||
|
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||||
|
let binary = envelope
|
||||||
|
.payload
|
||||||
|
.binary
|
||||||
|
.ok_or_else(|| anyhow!("missing binary"))?;
|
||||||
|
let binary = LanguageServerBinary {
|
||||||
|
path: PathBuf::from(binary.path),
|
||||||
|
env: None,
|
||||||
|
arguments: binary.arguments.into_iter().map(Into::into).collect(),
|
||||||
|
};
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
||||||
|
let delegate = LocalLspAdapterDelegate::for_local(this, &worktree, cx);
|
||||||
|
anyhow::Ok(cx.spawn(|_, _| async move { delegate.try_exec(binary).await }))
|
||||||
|
})??
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(proto::Ack {})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_read_text_file(
|
||||||
|
this: Model<Self>,
|
||||||
|
envelope: TypedEnvelope<proto::ReadTextFile>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) -> Result<proto::ReadTextFileResponse> {
|
||||||
|
let path = envelope
|
||||||
|
.payload
|
||||||
|
.path
|
||||||
|
.ok_or_else(|| anyhow!("missing path"))?;
|
||||||
|
let worktree_id = WorktreeId::from_proto(path.worktree_id);
|
||||||
|
let path = PathBuf::from(path.path);
|
||||||
|
let response = this
|
||||||
|
.update(&mut cx, |this, cx| {
|
||||||
|
let worktree = this.worktree_for_id(worktree_id, cx)?;
|
||||||
|
let delegate = LocalLspAdapterDelegate::for_local(this, &worktree, cx);
|
||||||
|
anyhow::Ok(cx.spawn(|_, _| async move { delegate.read_text_file(path).await }))
|
||||||
|
})??
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(proto::ReadTextFileResponse { text: response })
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_apply_additional_edits_for_completion(
|
async fn handle_apply_additional_edits_for_completion(
|
||||||
this: Model<Self>,
|
this: Model<Self>,
|
||||||
|
@ -4535,9 +4580,12 @@ impl LspStore {
|
||||||
) {
|
) {
|
||||||
let ssh = self.as_ssh().unwrap();
|
let ssh = self.as_ssh().unwrap();
|
||||||
|
|
||||||
let delegate =
|
let delegate = Arc::new(SshLspAdapterDelegate {
|
||||||
ProjectLspAdapterDelegate::for_ssh(self, worktree, ssh.upstream_client.clone(), cx)
|
lsp_store: cx.handle().downgrade(),
|
||||||
as Arc<dyn LspAdapterDelegate>;
|
worktree: worktree.read(cx).snapshot(),
|
||||||
|
upstream_client: ssh.upstream_client.clone(),
|
||||||
|
language_registry: self.languages.clone(),
|
||||||
|
}) as Arc<dyn LspAdapterDelegate>;
|
||||||
|
|
||||||
// TODO: We should use `adapter` here instead of reaching through the `CachedLspAdapter`.
|
// TODO: We should use `adapter` here instead of reaching through the `CachedLspAdapter`.
|
||||||
let lsp_adapter = adapter.adapter.clone();
|
let lsp_adapter = adapter.adapter.clone();
|
||||||
|
@ -4645,7 +4693,7 @@ impl LspStore {
|
||||||
let local = self.as_local().unwrap();
|
let local = self.as_local().unwrap();
|
||||||
|
|
||||||
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
|
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
|
||||||
let lsp_adapter_delegate = ProjectLspAdapterDelegate::for_local(self, worktree_handle, cx);
|
let lsp_adapter_delegate = LocalLspAdapterDelegate::for_local(self, worktree_handle, cx);
|
||||||
let project_environment = local.environment.update(cx, |environment, cx| {
|
let project_environment = local.environment.update(cx, |environment, cx| {
|
||||||
environment.get_environment(Some(worktree_id), Some(worktree_path.clone()), cx)
|
environment.get_environment(Some(worktree_id), Some(worktree_path.clone()), cx)
|
||||||
});
|
});
|
||||||
|
@ -6938,18 +6986,32 @@ impl LspAdapter for SshLspAdapter {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn language_server_settings<'a, 'b: 'a>(
|
||||||
|
delegate: &'a dyn LspAdapterDelegate,
|
||||||
|
language: &str,
|
||||||
|
cx: &'b AppContext,
|
||||||
|
) -> Option<&'a LspSettings> {
|
||||||
|
ProjectSettings::get(
|
||||||
|
Some(SettingsLocation {
|
||||||
|
worktree_id: delegate.worktree_id(),
|
||||||
|
path: delegate.worktree_root_path(),
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.lsp
|
||||||
|
.get(language)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ProjectLspAdapterDelegate {
|
pub struct LocalLspAdapterDelegate {
|
||||||
lsp_store: WeakModel<LspStore>,
|
lsp_store: WeakModel<LspStore>,
|
||||||
worktree: worktree::Snapshot,
|
worktree: worktree::Snapshot,
|
||||||
fs: Option<Arc<dyn Fs>>,
|
fs: Arc<dyn Fs>,
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
load_shell_env_task: Shared<Task<Option<HashMap<String, String>>>>,
|
load_shell_env_task: Shared<Task<Option<HashMap<String, String>>>>,
|
||||||
upstream_client: Option<AnyProtoClient>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProjectLspAdapterDelegate {
|
impl LocalLspAdapterDelegate {
|
||||||
fn for_local(
|
fn for_local(
|
||||||
lsp_store: &LspStore,
|
lsp_store: &LspStore,
|
||||||
worktree: &Model<Worktree>,
|
worktree: &Model<Worktree>,
|
||||||
|
@ -6957,45 +7019,37 @@ impl ProjectLspAdapterDelegate {
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let local = lsp_store
|
let local = lsp_store
|
||||||
.as_local()
|
.as_local()
|
||||||
.expect("ProjectLspAdapterDelegate cannot be constructed on a remote");
|
.expect("LocalLspAdapterDelegate cannot be constructed on a remote");
|
||||||
|
|
||||||
let http_client = local
|
let http_client = local
|
||||||
.http_client
|
.http_client
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| Arc::new(BlockedHttpClient));
|
.unwrap_or_else(|| Arc::new(BlockedHttpClient));
|
||||||
|
|
||||||
Self::new(
|
Self::new(lsp_store, worktree, http_client, local.fs.clone(), cx)
|
||||||
lsp_store,
|
|
||||||
worktree,
|
|
||||||
http_client,
|
|
||||||
Some(local.fs.clone()),
|
|
||||||
None,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn for_ssh(
|
// fn for_ssh(
|
||||||
lsp_store: &LspStore,
|
// lsp_store: &LspStore,
|
||||||
worktree: &Model<Worktree>,
|
// worktree: &Model<Worktree>,
|
||||||
upstream_client: AnyProtoClient,
|
// upstream_client: AnyProtoClient,
|
||||||
cx: &mut ModelContext<LspStore>,
|
// cx: &mut ModelContext<LspStore>,
|
||||||
) -> Arc<Self> {
|
// ) -> Arc<Self> {
|
||||||
Self::new(
|
// Self::new(
|
||||||
lsp_store,
|
// lsp_store,
|
||||||
worktree,
|
// worktree,
|
||||||
Arc::new(BlockedHttpClient),
|
// Arc::new(BlockedHttpClient),
|
||||||
None,
|
// None,
|
||||||
Some(upstream_client),
|
// Some(upstream_client),
|
||||||
cx,
|
// cx,
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
lsp_store: &LspStore,
|
lsp_store: &LspStore,
|
||||||
worktree: &Model<Worktree>,
|
worktree: &Model<Worktree>,
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
fs: Option<Arc<dyn Fs>>,
|
fs: Arc<dyn Fs>,
|
||||||
upstream_client: Option<AnyProtoClient>,
|
|
||||||
cx: &mut ModelContext<LspStore>,
|
cx: &mut ModelContext<LspStore>,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let worktree_id = worktree.read(cx).id();
|
let worktree_id = worktree.read(cx).id();
|
||||||
|
@ -7015,52 +7069,14 @@ impl ProjectLspAdapterDelegate {
|
||||||
worktree: worktree.read(cx).snapshot(),
|
worktree: worktree.read(cx).snapshot(),
|
||||||
fs,
|
fs,
|
||||||
http_client,
|
http_client,
|
||||||
upstream_client,
|
|
||||||
language_registry: lsp_store.languages.clone(),
|
language_registry: lsp_store.languages.clone(),
|
||||||
load_shell_env_task,
|
load_shell_env_task,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BlockedHttpClient;
|
|
||||||
|
|
||||||
impl HttpClient for BlockedHttpClient {
|
|
||||||
fn send(
|
|
||||||
&self,
|
|
||||||
_req: Request<AsyncBody>,
|
|
||||||
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
|
|
||||||
Box::pin(async {
|
|
||||||
Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::PermissionDenied,
|
|
||||||
"ssh host blocked http connection",
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proxy(&self) -> Option<&Uri> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn language_server_settings<'a, 'b: 'a>(
|
|
||||||
delegate: &'a dyn LspAdapterDelegate,
|
|
||||||
language: &str,
|
|
||||||
cx: &'b AppContext,
|
|
||||||
) -> Option<&'a LspSettings> {
|
|
||||||
ProjectSettings::get(
|
|
||||||
Some(SettingsLocation {
|
|
||||||
worktree_id: delegate.worktree_id(),
|
|
||||||
path: delegate.worktree_root_path(),
|
|
||||||
}),
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.lsp
|
|
||||||
.get(language)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
impl LspAdapterDelegate for LocalLspAdapterDelegate {
|
||||||
fn show_notification(&self, message: &str, cx: &mut AppContext) {
|
fn show_notification(&self, message: &str, cx: &mut AppContext) {
|
||||||
self.lsp_store
|
self.lsp_store
|
||||||
.update(cx, |_, cx| {
|
.update(cx, |_, cx| {
|
||||||
|
@ -7082,42 +7098,12 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shell_env(&self) -> HashMap<String, String> {
|
async fn shell_env(&self) -> HashMap<String, String> {
|
||||||
if let Some(upstream_client) = &self.upstream_client {
|
|
||||||
use rpc::proto::SSH_PROJECT_ID;
|
|
||||||
|
|
||||||
return upstream_client
|
|
||||||
.request(proto::ShellEnv {
|
|
||||||
project_id: SSH_PROJECT_ID,
|
|
||||||
worktree_id: self.worktree_id().to_proto(),
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map(|response| response.env.into_iter().collect())
|
|
||||||
.unwrap_or_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = self.load_shell_env_task.clone();
|
let task = self.load_shell_env_task.clone();
|
||||||
task.await.unwrap_or_default()
|
task.await.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
||||||
if let Some(upstream_client) = &self.upstream_client {
|
|
||||||
use rpc::proto::SSH_PROJECT_ID;
|
|
||||||
|
|
||||||
return upstream_client
|
|
||||||
.request(proto::WhichCommand {
|
|
||||||
project_id: SSH_PROJECT_ID,
|
|
||||||
worktree_id: self.worktree_id().to_proto(),
|
|
||||||
command: command.to_string_lossy().to_string(),
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.log_err()
|
|
||||||
.and_then(|response| response.path)
|
|
||||||
.map(PathBuf::from);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fs.as_ref()?;
|
|
||||||
|
|
||||||
let worktree_abs_path = self.worktree.abs_path();
|
let worktree_abs_path = self.worktree.abs_path();
|
||||||
let shell_path = self.shell_env().await.get("PATH").cloned();
|
let shell_path = self.shell_env().await.get("PATH").cloned();
|
||||||
which::which_in(command, shell_path.as_ref(), worktree_abs_path).ok()
|
which::which_in(command, shell_path.as_ref(), worktree_abs_path).ok()
|
||||||
|
@ -7125,8 +7111,6 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
||||||
self.fs.as_ref()?;
|
|
||||||
|
|
||||||
// todo(windows) Getting the shell env variables in a current directory on Windows is more complicated than other platforms
|
// todo(windows) Getting the shell env variables in a current directory on Windows is more complicated than other platforms
|
||||||
// there isn't a 'default shell' necessarily. The closest would be the default profile on the windows terminal
|
// there isn't a 'default shell' necessarily. The closest would be the default profile on the windows terminal
|
||||||
// SEE: https://learn.microsoft.com/en-us/windows/terminal/customize-settings/startup
|
// SEE: https://learn.microsoft.com/en-us/windows/terminal/customize-settings/startup
|
||||||
|
@ -7134,10 +7118,6 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn try_exec(&self, command: LanguageServerBinary) -> Result<()> {
|
async fn try_exec(&self, command: LanguageServerBinary) -> Result<()> {
|
||||||
if self.fs.is_none() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let working_dir = self.worktree_root_path();
|
let working_dir = self.worktree_root_path();
|
||||||
let output = smol::process::Command::new(&command.path)
|
let output = smol::process::Command::new(&command.path)
|
||||||
.args(command.arguments)
|
.args(command.arguments)
|
||||||
|
@ -7170,13 +7150,128 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||||
if self.worktree.entry_for_path(&path).is_none() {
|
if self.worktree.entry_for_path(&path).is_none() {
|
||||||
return Err(anyhow!("no such path {path:?}"));
|
return Err(anyhow!("no such path {path:?}"));
|
||||||
};
|
};
|
||||||
if let Some(fs) = &self.fs {
|
self.fs.load(&path).await
|
||||||
let content = fs.load(&path).await?;
|
|
||||||
Ok(content)
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!("cannot open {path:?} on ssh host (yet!)"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BlockedHttpClient;
|
||||||
|
|
||||||
|
impl HttpClient for BlockedHttpClient {
|
||||||
|
fn send(
|
||||||
|
&self,
|
||||||
|
_req: Request<AsyncBody>,
|
||||||
|
) -> BoxFuture<'static, Result<Response<AsyncBody>, Error>> {
|
||||||
|
Box::pin(async {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::PermissionDenied,
|
||||||
|
"ssh host blocked http connection",
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn proxy(&self) -> Option<&Uri> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SshLspAdapterDelegate {
|
||||||
|
lsp_store: WeakModel<LspStore>,
|
||||||
|
worktree: worktree::Snapshot,
|
||||||
|
upstream_client: AnyProtoClient,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LspAdapterDelegate for SshLspAdapterDelegate {
|
||||||
|
fn show_notification(&self, message: &str, cx: &mut AppContext) {
|
||||||
|
self.lsp_store
|
||||||
|
.update(cx, |_, cx| {
|
||||||
|
cx.emit(LspStoreEvent::Notification(message.to_owned()))
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn http_client(&self) -> Arc<dyn HttpClient> {
|
||||||
|
Arc::new(BlockedHttpClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worktree_id(&self) -> WorktreeId {
|
||||||
|
self.worktree.id()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn worktree_root_path(&self) -> &Path {
|
||||||
|
self.worktree.abs_path().as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shell_env(&self) -> HashMap<String, String> {
|
||||||
|
use rpc::proto::SSH_PROJECT_ID;
|
||||||
|
|
||||||
|
self.upstream_client
|
||||||
|
.request(proto::ShellEnv {
|
||||||
|
project_id: SSH_PROJECT_ID,
|
||||||
|
worktree_id: self.worktree_id().to_proto(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|response| response.env.into_iter().collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn which(&self, command: &OsStr) -> Option<PathBuf> {
|
||||||
|
use rpc::proto::SSH_PROJECT_ID;
|
||||||
|
|
||||||
|
self.upstream_client
|
||||||
|
.request(proto::WhichCommand {
|
||||||
|
project_id: SSH_PROJECT_ID,
|
||||||
|
worktree_id: self.worktree_id().to_proto(),
|
||||||
|
command: command.to_string_lossy().to_string(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
.and_then(|response| response.path)
|
||||||
|
.map(PathBuf::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_exec(&self, command: LanguageServerBinary) -> Result<()> {
|
||||||
|
self.upstream_client
|
||||||
|
.request(proto::TryExec {
|
||||||
|
project_id: rpc::proto::SSH_PROJECT_ID,
|
||||||
|
worktree_id: self.worktree.id().to_proto(),
|
||||||
|
binary: Some(proto::LanguageServerCommand {
|
||||||
|
path: command.path.to_string_lossy().to_string(),
|
||||||
|
arguments: command
|
||||||
|
.arguments
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.to_string_lossy().to_string())
|
||||||
|
.collect(),
|
||||||
|
env: command.env.unwrap_or_default().into_iter().collect(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_status(
|
||||||
|
&self,
|
||||||
|
server_name: LanguageServerName,
|
||||||
|
status: language::LanguageServerBinaryStatus,
|
||||||
|
) {
|
||||||
|
self.language_registry
|
||||||
|
.update_lsp_status(server_name, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_text_file(&self, path: PathBuf) -> Result<String> {
|
||||||
|
self.upstream_client
|
||||||
|
.request(proto::ReadTextFile {
|
||||||
|
project_id: rpc::proto::SSH_PROJECT_ID,
|
||||||
|
path: Some(proto::ProjectPath {
|
||||||
|
worktree_id: self.worktree.id().to_proto(),
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|r| r.text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn populate_labels_for_symbols(
|
async fn populate_labels_for_symbols(
|
||||||
|
|
|
@ -289,7 +289,11 @@ message Envelope {
|
||||||
WhichCommandResponse which_command_response = 249;
|
WhichCommandResponse which_command_response = 249;
|
||||||
|
|
||||||
ShellEnv shell_env = 250;
|
ShellEnv shell_env = 250;
|
||||||
ShellEnvResponse shell_env_response = 251; // current max
|
ShellEnvResponse shell_env_response = 251;
|
||||||
|
|
||||||
|
TryExec try_exec = 252;
|
||||||
|
ReadTextFile read_text_file = 253;
|
||||||
|
ReadTextFileResponse read_text_file_response = 254; // current max
|
||||||
}
|
}
|
||||||
|
|
||||||
reserved 158 to 161;
|
reserved 158 to 161;
|
||||||
|
@ -2551,13 +2555,21 @@ message ShellEnvResponse {
|
||||||
map<string, string> env = 1;
|
map<string, string> env = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// message RestartLanguageServer {
|
message ReadTextFile {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
ProjectPath path = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
message ReadTextFileResponse {
|
||||||
// message DestroyLanguageServer {
|
string text = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// }
|
message TryExec {
|
||||||
|
uint64 project_id = 1;
|
||||||
|
uint64 worktree_id = 2;
|
||||||
|
LanguageServerCommand binary = 3;
|
||||||
|
}
|
||||||
|
|
||||||
// message LspWorkspaceConfiguration {
|
message TryExecResponse {
|
||||||
|
string text = 1;
|
||||||
// }
|
}
|
||||||
|
|
|
@ -370,6 +370,9 @@ messages!(
|
||||||
(WhichCommandResponse, Foreground),
|
(WhichCommandResponse, Foreground),
|
||||||
(ShellEnv, Foreground),
|
(ShellEnv, Foreground),
|
||||||
(ShellEnvResponse, Foreground),
|
(ShellEnvResponse, Foreground),
|
||||||
|
(TryExec, Foreground),
|
||||||
|
(ReadTextFile, Foreground),
|
||||||
|
(ReadTextFileResponse, Foreground)
|
||||||
);
|
);
|
||||||
|
|
||||||
request_messages!(
|
request_messages!(
|
||||||
|
@ -495,7 +498,9 @@ request_messages!(
|
||||||
(AddWorktree, AddWorktreeResponse),
|
(AddWorktree, AddWorktreeResponse),
|
||||||
(CreateLanguageServer, Ack),
|
(CreateLanguageServer, Ack),
|
||||||
(WhichCommand, WhichCommandResponse),
|
(WhichCommand, WhichCommandResponse),
|
||||||
(ShellEnv, ShellEnvResponse)
|
(ShellEnv, ShellEnvResponse),
|
||||||
|
(ReadTextFile, ReadTextFileResponse),
|
||||||
|
(TryExec, Ack),
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
|
@ -571,7 +576,9 @@ entity_messages!(
|
||||||
UpdateUserSettings,
|
UpdateUserSettings,
|
||||||
CreateLanguageServer,
|
CreateLanguageServer,
|
||||||
WhichCommand,
|
WhichCommand,
|
||||||
ShellEnv
|
ShellEnv,
|
||||||
|
TryExec,
|
||||||
|
ReadTextFile
|
||||||
);
|
);
|
||||||
|
|
||||||
entity_messages!(
|
entity_messages!(
|
||||||
|
|
|
@ -15,7 +15,7 @@ use gpui::{AppContext, AsyncAppContext, Model, SemanticVersion};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rpc::{
|
use rpc::{
|
||||||
proto::{self, build_typed_envelope, Envelope, EnvelopedMessage, PeerId, RequestMessage},
|
proto::{self, build_typed_envelope, Envelope, EnvelopedMessage, PeerId, RequestMessage},
|
||||||
EntityMessageSubscriber, ProtoClient, ProtoMessageHandlerSet,
|
EntityMessageSubscriber, ProtoClient, ProtoMessageHandlerSet, RpcError,
|
||||||
};
|
};
|
||||||
use smol::{
|
use smol::{
|
||||||
fs,
|
fs,
|
||||||
|
@ -157,8 +157,9 @@ impl SshSession {
|
||||||
|
|
||||||
let mut remote_server_child = socket
|
let mut remote_server_child = socket
|
||||||
.ssh_command(format!(
|
.ssh_command(format!(
|
||||||
"RUST_LOG={} {:?} run",
|
"RUST_LOG={} RUST_BACKTRACE={} {:?} run",
|
||||||
std::env::var("RUST_LOG").unwrap_or_default(),
|
std::env::var("RUST_LOG").unwrap_or_default(),
|
||||||
|
std::env::var("RUST_BACKTRACE").unwrap_or_default(),
|
||||||
remote_binary_path,
|
remote_binary_path,
|
||||||
))
|
))
|
||||||
.spawn()
|
.spawn()
|
||||||
|
@ -349,7 +350,7 @@ impl SshSession {
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"error handling message. type:{type_name}, error:{error:?}",
|
"error handling message. type:{type_name}, error:{error}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,7 +372,7 @@ impl SshSession {
|
||||||
payload: T,
|
payload: T,
|
||||||
) -> impl 'static + Future<Output = Result<T::Response>> {
|
) -> impl 'static + Future<Output = Result<T::Response>> {
|
||||||
log::debug!("ssh request start. name:{}", T::NAME);
|
log::debug!("ssh request start. name:{}", T::NAME);
|
||||||
let response = self.request_dynamic(payload.into_envelope(0, None, None), "");
|
let response = self.request_dynamic(payload.into_envelope(0, None, None), T::NAME);
|
||||||
async move {
|
async move {
|
||||||
let response = response.await?;
|
let response = response.await?;
|
||||||
log::debug!("ssh request finish. name:{}", T::NAME);
|
log::debug!("ssh request finish. name:{}", T::NAME);
|
||||||
|
@ -388,7 +389,7 @@ impl SshSession {
|
||||||
pub fn request_dynamic(
|
pub fn request_dynamic(
|
||||||
&self,
|
&self,
|
||||||
mut envelope: proto::Envelope,
|
mut envelope: proto::Envelope,
|
||||||
_request_type: &'static str,
|
type_name: &'static str,
|
||||||
) -> impl 'static + Future<Output = Result<proto::Envelope>> {
|
) -> impl 'static + Future<Output = Result<proto::Envelope>> {
|
||||||
envelope.id = self.next_message_id.fetch_add(1, SeqCst);
|
envelope.id = self.next_message_id.fetch_add(1, SeqCst);
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
@ -396,7 +397,13 @@ impl SshSession {
|
||||||
response_channels_lock.insert(MessageId(envelope.id), tx);
|
response_channels_lock.insert(MessageId(envelope.id), tx);
|
||||||
drop(response_channels_lock);
|
drop(response_channels_lock);
|
||||||
self.outgoing_tx.unbounded_send(envelope).ok();
|
self.outgoing_tx.unbounded_send(envelope).ok();
|
||||||
async move { Ok(rx.await.context("connection lost")?.0) }
|
async move {
|
||||||
|
let response = rx.await.context("connection lost")?.0;
|
||||||
|
if let Some(proto::envelope::Payload::Error(error)) = &response.payload {
|
||||||
|
return Err(RpcError::from_proto(error, type_name));
|
||||||
|
}
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_dynamic(&self, mut envelope: proto::Envelope) -> Result<()> {
|
pub fn send_dynamic(&self, mut envelope: proto::Envelope) -> Result<()> {
|
||||||
|
|
|
@ -107,6 +107,8 @@ impl HeadlessProject {
|
||||||
client.add_model_request_handler(LspStore::handle_create_language_server);
|
client.add_model_request_handler(LspStore::handle_create_language_server);
|
||||||
client.add_model_request_handler(LspStore::handle_which_command);
|
client.add_model_request_handler(LspStore::handle_which_command);
|
||||||
client.add_model_request_handler(LspStore::handle_shell_env);
|
client.add_model_request_handler(LspStore::handle_shell_env);
|
||||||
|
client.add_model_request_handler(LspStore::handle_try_exec);
|
||||||
|
client.add_model_request_handler(LspStore::handle_read_text_file);
|
||||||
|
|
||||||
BufferStore::init(&client);
|
BufferStore::init(&client);
|
||||||
WorktreeStore::init(&client);
|
WorktreeStore::init(&client);
|
||||||
|
|
|
@ -24,7 +24,6 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn main() {
|
fn main() {
|
||||||
env::set_var("RUST_BACKTRACE", "1");
|
|
||||||
env_logger::builder()
|
env_logger::builder()
|
||||||
.format(|buf, record| {
|
.format(|buf, record| {
|
||||||
serde_json::to_writer(&mut *buf, &LogRecord::new(record))?;
|
serde_json::to_writer(&mut *buf, &LogRecord::new(record))?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue