mirror of
https://github.com/zed-industries/zed.git
synced 2026-10-21 05:31:11 +00:00
Read remote shell config when creating a terminal shell (#61451)
# Objective - Makes shell for remote configurable. - fixes #35226 ## Solution - Adds rpc so client can retrieve terminal settings from remote server - Vibe coded with codex Sol, I did my best to review it and make sure it's the minimal changes. - Added terminal dependency to remote_server, rather than a larger change (e.g. adding something to project to expose terminal shell setting to remote_server) ## Testing - Tested and works Windows client / linux remote server ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable --- Release Notes: - Added support for terminal.shell in remote sessions --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
7604aa3f19
commit
c87632ef44
8 changed files with 113 additions and 8 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -3218,7 +3218,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tokio-rustls",
|
||||
"yawc",
|
||||
]
|
||||
|
||||
|
|
@ -15307,6 +15307,7 @@ dependencies = [
|
|||
"task",
|
||||
"telemetry",
|
||||
"tempfile",
|
||||
"terminal",
|
||||
"theme",
|
||||
"theme_settings",
|
||||
"thiserror 2.0.17",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use gpui::{App, AppContext as _, Context, Entity, Task, WeakEntity};
|
||||
|
||||
|
|
@ -6,6 +6,7 @@ use futures::{FutureExt, future::Shared};
|
|||
use itertools::Itertools as _;
|
||||
use language::LanguageName;
|
||||
use remote::{Interactive, RemoteClient};
|
||||
use rpc::proto;
|
||||
use settings::{Settings, SettingsLocation};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
|
@ -353,6 +354,16 @@ impl Project {
|
|||
} else {
|
||||
self.remote_client.clone()
|
||||
};
|
||||
let remote_shell_request = remote_client.as_ref().map(|remote_client| {
|
||||
remote_client
|
||||
.read(cx)
|
||||
.proto_client()
|
||||
.request(proto::GetTerminalShell {
|
||||
project_id: proto::REMOTE_SERVER_PROJECT_ID,
|
||||
worktree_id: settings_location
|
||||
.map(|settings_location| settings_location.worktree_id.to_proto()),
|
||||
})
|
||||
});
|
||||
let shell = match &remote_client {
|
||||
Some(remote_client) => remote_client
|
||||
.read(cx)
|
||||
|
|
@ -373,7 +384,24 @@ impl Project {
|
|||
|
||||
let lang_registry = self.languages.clone();
|
||||
cx.spawn(async move |project, cx| {
|
||||
let shell_kind = ShellKind::new(&shell, path_style.is_windows());
|
||||
let remote_shell = if let Some(remote_shell_request) = remote_shell_request {
|
||||
let response = remote_shell_request
|
||||
.await
|
||||
.context("failed to get terminal shell settings from remote server")?;
|
||||
let shell = response
|
||||
.shell
|
||||
.context("remote server returned no terminal shell")?;
|
||||
let shell = task::shell_from_proto(shell)
|
||||
.context("remote server returned an invalid terminal shell")?;
|
||||
log::debug!(
|
||||
"create_terminal_shell_internal: using remote terminal shell setting: {shell:?}"
|
||||
);
|
||||
Some(shell)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let shell_program = remote_shell.as_ref().map(Shell::program).unwrap_or(shell);
|
||||
let shell_kind = ShellKind::new(&shell_program, path_style.is_windows());
|
||||
let mut env = env_task.await.unwrap_or_default();
|
||||
env.extend(settings.env);
|
||||
|
||||
|
|
@ -401,7 +429,15 @@ impl Project {
|
|||
let (shell, env) = {
|
||||
match remote_client {
|
||||
Some(remote_client) => {
|
||||
create_remote_shell(None, env, path, remote_client, cx)?
|
||||
let empty_args = Vec::new();
|
||||
let spawn_command = match remote_shell.as_ref() {
|
||||
Some(Shell::System) | None => None,
|
||||
Some(Shell::Program(program)) => Some((program, &empty_args)),
|
||||
Some(Shell::WithArguments { program, args, .. }) => {
|
||||
Some((program, args))
|
||||
}
|
||||
};
|
||||
create_remote_shell(spawn_command, env, path, remote_client, cx)?
|
||||
}
|
||||
None => (settings.shell, env),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,3 +58,12 @@ message GetDirectoryEnvironment {
|
|||
message DirectoryEnvironment {
|
||||
map<string, string> environment = 1;
|
||||
}
|
||||
|
||||
message GetTerminalShell {
|
||||
uint64 project_id = 1;
|
||||
optional uint64 worktree_id = 2;
|
||||
}
|
||||
|
||||
message GetTerminalShellResponse {
|
||||
Shell shell = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -522,7 +522,9 @@ message Envelope {
|
|||
LanguageServerShowDocumentRequest language_server_show_document_request = 490;
|
||||
ExecuteLspCommand execute_lsp_command = 491;
|
||||
ExecuteLspCommandResponse execute_lsp_command_response = 492;
|
||||
RefreshDocumentHighlights refresh_document_highlights = 493; // current max
|
||||
RefreshDocumentHighlights refresh_document_highlights = 493;
|
||||
GetTerminalShell get_terminal_shell = 494;
|
||||
GetTerminalShellResponse get_terminal_shell_response = 495; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ messages!(
|
|||
(GetReferencesResponse, Background),
|
||||
(GetSignatureHelp, Background),
|
||||
(GetSignatureHelpResponse, Background),
|
||||
(GetTerminalShell, Background),
|
||||
(GetTerminalShellResponse, Background),
|
||||
(GetTypeDefinition, Background),
|
||||
(GetTypeDefinitionResponse, Background),
|
||||
(GetImplementation, Background),
|
||||
|
|
@ -650,6 +652,7 @@ request_messages!(
|
|||
(GetTreeDiff, GetTreeDiffResponse),
|
||||
(GitClone, GitCloneResponse),
|
||||
(GetDirectoryEnvironment, DirectoryEnvironment),
|
||||
(GetTerminalShell, GetTerminalShellResponse),
|
||||
(GetProcesses, GetProcessesResponse),
|
||||
(GetAgentServerCommand, AgentServerCommand),
|
||||
(GetContextServerCommand, ContextServerCommand),
|
||||
|
|
@ -853,6 +856,7 @@ entity_messages!(
|
|||
SetIndexText,
|
||||
ToggleLspLogs,
|
||||
GetDirectoryEnvironment,
|
||||
GetTerminalShell,
|
||||
|
||||
Push,
|
||||
Fetch,
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ smol.workspace = true
|
|||
sysinfo.workspace = true
|
||||
task.workspace = true
|
||||
telemetry.workspace = true
|
||||
terminal.workspace = true
|
||||
util.workspace = true
|
||||
watch.workspace = true
|
||||
worktree.workspace = true
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ use rpc::{
|
|||
};
|
||||
use smol::process::Child;
|
||||
|
||||
use settings::initial_server_settings_content;
|
||||
use settings::{Settings as _, SettingsLocation, initial_server_settings_content};
|
||||
use std::{
|
||||
num::NonZeroU64,
|
||||
path::{Path, PathBuf},
|
||||
|
|
@ -48,6 +48,7 @@ use std::{
|
|||
time::Instant,
|
||||
};
|
||||
use sysinfo::{ProcessRefreshKind, RefreshKind, System, UpdateKind};
|
||||
use terminal::terminal_settings::TerminalSettings;
|
||||
use util::{ResultExt, paths::PathStyle, rel_path::RelPath};
|
||||
use worktree::Worktree;
|
||||
|
||||
|
|
@ -306,6 +307,7 @@ impl HeadlessProject {
|
|||
session.add_entity_request_handler(Self::handle_find_search_candidates);
|
||||
session.add_entity_request_handler(Self::handle_open_server_settings);
|
||||
session.add_entity_request_handler(Self::handle_get_directory_environment);
|
||||
session.add_entity_request_handler(Self::handle_get_terminal_shell);
|
||||
session.add_entity_message_handler(Self::handle_toggle_lsp_logs);
|
||||
session.add_entity_request_handler(Self::handle_open_image_by_path);
|
||||
session.add_entity_request_handler(Self::handle_trust_worktrees);
|
||||
|
|
@ -1400,6 +1402,26 @@ impl HeadlessProject {
|
|||
.collect();
|
||||
Ok(proto::DirectoryEnvironment { environment })
|
||||
}
|
||||
|
||||
async fn handle_get_terminal_shell(
|
||||
_this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GetTerminalShell>,
|
||||
cx: AsyncApp,
|
||||
) -> Result<proto::GetTerminalShellResponse> {
|
||||
let worktree_id = envelope.payload.worktree_id.map(WorktreeId::from_proto);
|
||||
let shell = cx.update(|cx| {
|
||||
let settings_location = worktree_id.map(|worktree_id| SettingsLocation {
|
||||
worktree_id,
|
||||
path: RelPath::empty(),
|
||||
});
|
||||
TerminalSettings::get(settings_location, cx).shell.clone()
|
||||
});
|
||||
log::debug!("handle_get_terminal_shell: resolved remote terminal shell setting: {shell:?}");
|
||||
|
||||
Ok(proto::GetTerminalShellResponse {
|
||||
shell: Some(task::shell_to_proto(shell)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt_to_proto(
|
||||
|
|
|
|||
|
|
@ -870,7 +870,10 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
|||
|
||||
cx.update_global(|settings_store: &mut SettingsStore, cx| {
|
||||
settings_store.set_user_settings(
|
||||
r#"{"languages":{"Rust":{"language_servers":["from-local-settings"]}}}"#,
|
||||
r#"{
|
||||
"languages": {"Rust": {"language_servers": ["from-local-settings"]}},
|
||||
"terminal": {"shell": {"program": "client-shell"}}
|
||||
}"#,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
|
@ -891,7 +894,10 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
|||
server_cx
|
||||
.update_global(|settings_store: &mut SettingsStore, cx| {
|
||||
settings_store.set_server_settings(
|
||||
r#"{"languages":{"Rust":{"language_servers":["from-server-settings"]}}}"#,
|
||||
r#"{
|
||||
"languages": {"Rust": {"language_servers": ["from-server-settings"]}},
|
||||
"terminal": {"shell": {"program": "remote-shell"}}
|
||||
}"#,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
|
@ -909,6 +915,30 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
|||
)
|
||||
});
|
||||
|
||||
let remote_client = project.read_with(cx, |project, _| project.remote_client());
|
||||
let remote_shell = if let Some(remote_client) = remote_client {
|
||||
remote_client
|
||||
.read_with(cx, |remote_client, _| {
|
||||
remote_client
|
||||
.proto_client()
|
||||
.request(proto::GetTerminalShell {
|
||||
project_id: proto::REMOTE_SERVER_PROJECT_ID,
|
||||
worktree_id: None,
|
||||
})
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|response| response.shell)
|
||||
.and_then(|shell| task::shell_from_proto(shell).ok())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
assert_eq!(
|
||||
remote_shell,
|
||||
Some(task::Shell::Program("remote-shell".to_string())),
|
||||
"Server terminal shell settings should take precedence over user settings"
|
||||
);
|
||||
|
||||
fs.insert_tree(
|
||||
"/code/project1/.zed",
|
||||
json!({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue