debugger: More tidy up for SSH (#28993)
Split `locator` out of DebugTaskDefinition to make it clearer when location needs to happen. Release Notes: - N/A --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Anthony <anthony@zed.dev> Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
parent
d13cd007a2
commit
9d35f0389d
57 changed files with 1146 additions and 884 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -4013,6 +4013,7 @@ dependencies = [
|
|||
"node_runtime",
|
||||
"parking_lot",
|
||||
"paths",
|
||||
"proto",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -4897,7 +4898,6 @@ dependencies = [
|
|||
"client",
|
||||
"collections",
|
||||
"context_server",
|
||||
"dap",
|
||||
"dirs 5.0.1",
|
||||
"env_logger 0.11.8",
|
||||
"extension",
|
||||
|
@ -11743,6 +11743,7 @@ dependencies = [
|
|||
"client",
|
||||
"clock",
|
||||
"dap",
|
||||
"dap_adapters",
|
||||
"env_logger 0.11.8",
|
||||
"extension",
|
||||
"extension_host",
|
||||
|
@ -14214,11 +14215,11 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"collections",
|
||||
"dap-types",
|
||||
"futures 0.3.31",
|
||||
"gpui",
|
||||
"hex",
|
||||
"parking_lot",
|
||||
"proto",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::tests::TestServer;
|
||||
use call::ActiveCall;
|
||||
use collections::{HashMap, HashSet};
|
||||
use dap::DapRegistry;
|
||||
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
||||
use futures::StreamExt as _;
|
||||
|
@ -86,7 +86,6 @@ async fn test_sharing_an_ssh_remote_project(
|
|||
http_client: remote_http_client,
|
||||
node_runtime: node,
|
||||
languages,
|
||||
debug_adapters: Arc::new(DapRegistry::fake()),
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
|
@ -254,7 +253,6 @@ async fn test_ssh_collaboration_git_branches(
|
|||
http_client: remote_http_client,
|
||||
node_runtime: node,
|
||||
languages,
|
||||
debug_adapters: Arc::new(DapRegistry::fake()),
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
|
@ -460,7 +458,6 @@ async fn test_ssh_collaboration_formatting_with_prettier(
|
|||
http_client: remote_http_client,
|
||||
node_runtime: NodeRuntime::unavailable(),
|
||||
languages,
|
||||
debug_adapters: Arc::new(DapRegistry::fake()),
|
||||
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||
},
|
||||
cx,
|
||||
|
|
|
@ -14,7 +14,7 @@ use client::{
|
|||
use clock::FakeSystemClock;
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use collections::{HashMap, HashSet};
|
||||
use dap::DapRegistry;
|
||||
|
||||
use fs::FakeFs;
|
||||
use futures::{StreamExt as _, channel::oneshot};
|
||||
use git::GitHostingProviderRegistry;
|
||||
|
@ -275,14 +275,12 @@ impl TestServer {
|
|||
let user_store = cx.new(|cx| UserStore::new(client.clone(), cx));
|
||||
let workspace_store = cx.new(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
let language_registry = Arc::new(LanguageRegistry::test(cx.executor()));
|
||||
let debug_adapters = Arc::new(DapRegistry::default());
|
||||
let session = cx.new(|cx| AppSession::new(Session::test(), cx));
|
||||
let app_state = Arc::new(workspace::AppState {
|
||||
client: client.clone(),
|
||||
user_store: user_store.clone(),
|
||||
workspace_store,
|
||||
languages: language_registry,
|
||||
debug_adapters,
|
||||
fs: fs.clone(),
|
||||
build_window_options: |_, _| Default::default(),
|
||||
node_runtime: NodeRuntime::unavailable(),
|
||||
|
@ -798,7 +796,6 @@ impl TestClient {
|
|||
self.app_state.node_runtime.clone(),
|
||||
self.app_state.user_store.clone(),
|
||||
self.app_state.languages.clone(),
|
||||
self.app_state.debug_adapters.clone(),
|
||||
self.app_state.fs.clone(),
|
||||
None,
|
||||
cx,
|
||||
|
|
|
@ -39,6 +39,7 @@ log.workspace = true
|
|||
node_runtime.workspace = true
|
||||
parking_lot.workspace = true
|
||||
paths.workspace = true
|
||||
proto.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
|
@ -3,7 +3,8 @@ use anyhow::{Context as _, Result, anyhow};
|
|||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use dap_types::StartDebuggingRequestArguments;
|
||||
use collections::HashMap;
|
||||
use dap_types::{StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest};
|
||||
use futures::io::BufReader;
|
||||
use gpui::{AsyncApp, SharedString};
|
||||
pub use http_client::{HttpClient, github::latest_github_release};
|
||||
|
@ -13,16 +14,10 @@ use serde::{Deserialize, Serialize};
|
|||
use settings::WorktreeId;
|
||||
use smol::{self, fs::File, lock::Mutex};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{HashMap, HashSet},
|
||||
ffi::{OsStr, OsString},
|
||||
fmt::Debug,
|
||||
net::Ipv4Addr,
|
||||
ops::Deref,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
borrow::Borrow, collections::HashSet, ffi::OsStr, fmt::Debug, net::Ipv4Addr, ops::Deref,
|
||||
path::PathBuf, sync::Arc,
|
||||
};
|
||||
use task::DebugTaskDefinition;
|
||||
use task::{DebugTaskDefinition, TcpArgumentsTemplate};
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -93,17 +88,91 @@ pub struct TcpArguments {
|
|||
pub port: u16,
|
||||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
||||
impl TcpArguments {
|
||||
pub fn from_proto(proto: proto::TcpHost) -> anyhow::Result<Self> {
|
||||
let host = TcpArgumentsTemplate::from_proto(proto)?;
|
||||
Ok(TcpArguments {
|
||||
host: host.host.ok_or_else(|| anyhow!("missing host"))?,
|
||||
port: host.port.ok_or_else(|| anyhow!("missing port"))?,
|
||||
timeout: host.timeout,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::TcpHost {
|
||||
TcpArgumentsTemplate {
|
||||
host: Some(self.host),
|
||||
port: Some(self.port),
|
||||
timeout: self.timeout,
|
||||
}
|
||||
.to_proto()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebugAdapterBinary {
|
||||
pub adapter_name: DebugAdapterName,
|
||||
pub command: String,
|
||||
pub arguments: Option<Vec<OsString>>,
|
||||
pub envs: Option<HashMap<String, String>>,
|
||||
pub arguments: Vec<String>,
|
||||
pub envs: HashMap<String, String>,
|
||||
pub cwd: Option<PathBuf>,
|
||||
pub connection: Option<TcpArguments>,
|
||||
pub request_args: StartDebuggingRequestArguments,
|
||||
}
|
||||
|
||||
impl DebugAdapterBinary {
|
||||
pub fn from_proto(binary: proto::DebugAdapterBinary) -> anyhow::Result<Self> {
|
||||
let request = match binary.launch_type() {
|
||||
proto::debug_adapter_binary::LaunchType::Launch => {
|
||||
StartDebuggingRequestArgumentsRequest::Launch
|
||||
}
|
||||
proto::debug_adapter_binary::LaunchType::Attach => {
|
||||
StartDebuggingRequestArgumentsRequest::Attach
|
||||
}
|
||||
};
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
command: binary.command,
|
||||
arguments: binary.arguments,
|
||||
envs: binary.envs.into_iter().collect(),
|
||||
connection: binary
|
||||
.connection
|
||||
.map(TcpArguments::from_proto)
|
||||
.transpose()?,
|
||||
request_args: StartDebuggingRequestArguments {
|
||||
configuration: serde_json::from_str(&binary.configuration)?,
|
||||
request,
|
||||
},
|
||||
cwd: binary.cwd.map(|cwd| cwd.into()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::DebugAdapterBinary {
|
||||
proto::DebugAdapterBinary {
|
||||
command: self.command.clone(),
|
||||
arguments: self.arguments.clone(),
|
||||
envs: self
|
||||
.envs
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
cwd: self
|
||||
.cwd
|
||||
.as_ref()
|
||||
.map(|cwd| cwd.to_string_lossy().to_string()),
|
||||
connection: self.connection.as_ref().map(|c| c.to_proto()),
|
||||
launch_type: match self.request_args.request {
|
||||
StartDebuggingRequestArgumentsRequest::Launch => {
|
||||
proto::debug_adapter_binary::LaunchType::Launch.into()
|
||||
}
|
||||
StartDebuggingRequestArgumentsRequest::Attach => {
|
||||
proto::debug_adapter_binary::LaunchType::Attach.into()
|
||||
}
|
||||
},
|
||||
configuration: self.request_args.configuration.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AdapterVersion {
|
||||
pub tag_name: String,
|
||||
|
@ -318,22 +387,22 @@ impl FakeAdapter {
|
|||
|
||||
fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
|
||||
use serde_json::json;
|
||||
use task::DebugRequestType;
|
||||
use task::DebugRequest;
|
||||
|
||||
let value = json!({
|
||||
"request": match config.request {
|
||||
DebugRequestType::Launch(_) => "launch",
|
||||
DebugRequestType::Attach(_) => "attach",
|
||||
DebugRequest::Launch(_) => "launch",
|
||||
DebugRequest::Attach(_) => "attach",
|
||||
},
|
||||
"process_id": if let DebugRequestType::Attach(attach_config) = &config.request {
|
||||
"process_id": if let DebugRequest::Attach(attach_config) = &config.request {
|
||||
attach_config.process_id
|
||||
} else {
|
||||
None
|
||||
},
|
||||
});
|
||||
let request = match config.request {
|
||||
DebugRequestType::Launch(_) => dap_types::StartDebuggingRequestArgumentsRequest::Launch,
|
||||
DebugRequestType::Attach(_) => dap_types::StartDebuggingRequestArgumentsRequest::Attach,
|
||||
DebugRequest::Launch(_) => dap_types::StartDebuggingRequestArgumentsRequest::Launch,
|
||||
DebugRequest::Attach(_) => dap_types::StartDebuggingRequestArgumentsRequest::Attach,
|
||||
};
|
||||
StartDebuggingRequestArguments {
|
||||
configuration: value,
|
||||
|
@ -357,11 +426,10 @@ impl DebugAdapter for FakeAdapter {
|
|||
_: &mut AsyncApp,
|
||||
) -> Result<DebugAdapterBinary> {
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: Self::ADAPTER_NAME.into(),
|
||||
command: "command".into(),
|
||||
arguments: None,
|
||||
arguments: vec![],
|
||||
connection: None,
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
cwd: None,
|
||||
request_args: self.request_args(config),
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
adapters::{DebugAdapterBinary, DebugAdapterName},
|
||||
adapters::DebugAdapterBinary,
|
||||
transport::{IoKind, LogKind, TransportDelegate},
|
||||
};
|
||||
use anyhow::{Result, anyhow};
|
||||
|
@ -88,7 +88,6 @@ impl DebugAdapterClient {
|
|||
) -> Result<Self> {
|
||||
let binary = match self.transport_delegate.transport() {
|
||||
crate::transport::Transport::Tcp(tcp_transport) => DebugAdapterBinary {
|
||||
adapter_name: binary.adapter_name,
|
||||
command: binary.command,
|
||||
arguments: binary.arguments,
|
||||
envs: binary.envs,
|
||||
|
@ -219,9 +218,6 @@ impl DebugAdapterClient {
|
|||
self.id
|
||||
}
|
||||
|
||||
pub fn name(&self) -> DebugAdapterName {
|
||||
self.binary.adapter_name.clone()
|
||||
}
|
||||
pub fn binary(&self) -> &DebugAdapterBinary {
|
||||
&self.binary
|
||||
}
|
||||
|
@ -322,7 +318,6 @@ mod tests {
|
|||
let client = DebugAdapterClient::start(
|
||||
crate::client::SessionId(1),
|
||||
DebugAdapterBinary {
|
||||
adapter_name: "adapter".into(),
|
||||
command: "command".into(),
|
||||
arguments: Default::default(),
|
||||
envs: Default::default(),
|
||||
|
@ -393,7 +388,6 @@ mod tests {
|
|||
let client = DebugAdapterClient::start(
|
||||
crate::client::SessionId(1),
|
||||
DebugAdapterBinary {
|
||||
adapter_name: "adapter".into(),
|
||||
command: "command".into(),
|
||||
arguments: Default::default(),
|
||||
envs: Default::default(),
|
||||
|
@ -447,7 +441,6 @@ mod tests {
|
|||
let client = DebugAdapterClient::start(
|
||||
crate::client::SessionId(1),
|
||||
DebugAdapterBinary {
|
||||
adapter_name: "test-adapter".into(),
|
||||
command: "command".into(),
|
||||
arguments: Default::default(),
|
||||
envs: Default::default(),
|
||||
|
|
|
@ -7,7 +7,7 @@ pub mod transport;
|
|||
|
||||
pub use dap_types::*;
|
||||
pub use registry::DapRegistry;
|
||||
pub use task::DebugRequestType;
|
||||
pub use task::DebugRequest;
|
||||
|
||||
pub type ScopeId = u64;
|
||||
pub type VariableReference = u64;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use gpui::{App, Global};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::adapters::{DebugAdapter, DebugAdapterName};
|
||||
|
@ -11,8 +12,20 @@ struct DapRegistryState {
|
|||
#[derive(Clone, Default)]
|
||||
/// Stores available debug adapters.
|
||||
pub struct DapRegistry(Arc<RwLock<DapRegistryState>>);
|
||||
impl Global for DapRegistry {}
|
||||
|
||||
impl DapRegistry {
|
||||
pub fn global(cx: &mut App) -> &mut Self {
|
||||
let ret = cx.default_global::<Self>();
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if ret.adapter(crate::FakeAdapter::ADAPTER_NAME).is_none() {
|
||||
ret.add_adapter(Arc::new(crate::FakeAdapter::new()));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn add_adapter(&self, adapter: Arc<dyn DebugAdapter>) {
|
||||
let name = adapter.name();
|
||||
let _previous_value = self.0.write().adapters.insert(name, adapter);
|
||||
|
@ -21,19 +34,12 @@ impl DapRegistry {
|
|||
"Attempted to insert a new debug adapter when one is already registered"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn adapter(&self, name: &str) -> Option<Arc<dyn DebugAdapter>> {
|
||||
self.0.read().adapters.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn enumerate_adapters(&self) -> Vec<DebugAdapterName> {
|
||||
self.0.read().adapters.keys().cloned().collect()
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn fake() -> Self {
|
||||
use crate::FakeAdapter;
|
||||
|
||||
let register = Self::default();
|
||||
register.add_adapter(Arc::new(FakeAdapter::new()));
|
||||
register
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use std::{
|
|||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use task::TCPHost;
|
||||
use task::TcpArgumentsTemplate;
|
||||
use util::ResultExt as _;
|
||||
|
||||
use crate::{adapters::DebugAdapterBinary, debugger_settings::DebuggerSettings};
|
||||
|
@ -74,16 +74,14 @@ pub enum Transport {
|
|||
}
|
||||
|
||||
impl Transport {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
async fn start(_: &DebugAdapterBinary, cx: AsyncApp) -> Result<(TransportPipe, Self)> {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
return FakeTransport::start(cx)
|
||||
.await
|
||||
.map(|(transports, fake)| (transports, Self::Fake(fake)));
|
||||
}
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
async fn start(binary: &DebugAdapterBinary, cx: AsyncApp) -> Result<(TransportPipe, Self)> {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if cfg!(any(test, feature = "test-support")) {
|
||||
return FakeTransport::start(cx)
|
||||
.await
|
||||
.map(|(transports, fake)| (transports, Self::Fake(fake)));
|
||||
}
|
||||
|
||||
if binary.connection.is_some() {
|
||||
TcpTransport::start(binary, cx)
|
||||
.await
|
||||
|
@ -520,18 +518,21 @@ pub struct TcpTransport {
|
|||
|
||||
impl TcpTransport {
|
||||
/// Get an open port to use with the tcp client when not supplied by debug config
|
||||
pub async fn port(host: &TCPHost) -> Result<u16> {
|
||||
pub async fn port(host: &TcpArgumentsTemplate) -> Result<u16> {
|
||||
if let Some(port) = host.port {
|
||||
Ok(port)
|
||||
} else {
|
||||
Ok(TcpListener::bind(SocketAddrV4::new(host.host(), 0))
|
||||
.await?
|
||||
.local_addr()?
|
||||
.port())
|
||||
Self::unused_port(host.host()).await
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code, reason = "This is used in non test builds of Zed")]
|
||||
pub async fn unused_port(host: Ipv4Addr) -> Result<u16> {
|
||||
Ok(TcpListener::bind(SocketAddrV4::new(host, 0))
|
||||
.await?
|
||||
.local_addr()?
|
||||
.port())
|
||||
}
|
||||
|
||||
async fn start(binary: &DebugAdapterBinary, cx: AsyncApp) -> Result<(TransportPipe, Self)> {
|
||||
let Some(connection_args) = binary.connection.as_ref() else {
|
||||
return Err(anyhow!("No connection arguments provided"));
|
||||
|
@ -546,13 +547,8 @@ impl TcpTransport {
|
|||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
if let Some(args) = &binary.arguments {
|
||||
command.args(args);
|
||||
}
|
||||
|
||||
if let Some(envs) = &binary.envs {
|
||||
command.envs(envs);
|
||||
}
|
||||
command.args(&binary.arguments);
|
||||
command.envs(&binary.envs);
|
||||
|
||||
command
|
||||
.stdin(Stdio::null())
|
||||
|
@ -635,13 +631,8 @@ impl StdioTransport {
|
|||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
if let Some(args) = &binary.arguments {
|
||||
command.args(args);
|
||||
}
|
||||
|
||||
if let Some(envs) = &binary.envs {
|
||||
command.envs(envs);
|
||||
}
|
||||
command.args(&binary.arguments);
|
||||
command.envs(&binary.envs);
|
||||
|
||||
command
|
||||
.stdin(Stdio::piped())
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::{path::PathBuf, sync::OnceLock};
|
||||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use async_trait::async_trait;
|
||||
use dap::adapters::latest_github_release;
|
||||
use gpui::AsyncApp;
|
||||
use task::{DebugRequestType, DebugTaskDefinition};
|
||||
use task::{DebugRequest, DebugTaskDefinition};
|
||||
|
||||
use crate::*;
|
||||
|
||||
|
@ -19,8 +19,8 @@ impl CodeLldbDebugAdapter {
|
|||
fn request_args(&self, config: &DebugTaskDefinition) -> dap::StartDebuggingRequestArguments {
|
||||
let mut configuration = json!({
|
||||
"request": match config.request {
|
||||
DebugRequestType::Launch(_) => "launch",
|
||||
DebugRequestType::Attach(_) => "attach",
|
||||
DebugRequest::Launch(_) => "launch",
|
||||
DebugRequest::Attach(_) => "attach",
|
||||
},
|
||||
});
|
||||
let map = configuration.as_object_mut().unwrap();
|
||||
|
@ -28,10 +28,10 @@ impl CodeLldbDebugAdapter {
|
|||
map.insert("name".into(), Value::String(config.label.clone()));
|
||||
let request = config.request.to_dap();
|
||||
match &config.request {
|
||||
DebugRequestType::Attach(attach) => {
|
||||
DebugRequest::Attach(attach) => {
|
||||
map.insert("pid".into(), attach.process_id.into());
|
||||
}
|
||||
DebugRequestType::Launch(launch) => {
|
||||
DebugRequest::Launch(launch) => {
|
||||
map.insert("program".into(), launch.program.clone().into());
|
||||
|
||||
if !launch.args.is_empty() {
|
||||
|
@ -140,16 +140,13 @@ impl DebugAdapter for CodeLldbDebugAdapter {
|
|||
.ok_or_else(|| anyhow!("Adapter path is expected to be valid UTF-8"))?;
|
||||
Ok(DebugAdapterBinary {
|
||||
command,
|
||||
cwd: Some(adapter_dir),
|
||||
arguments: Some(vec![
|
||||
cwd: None,
|
||||
arguments: vec![
|
||||
"--settings".into(),
|
||||
json!({"sourceLanguages": ["cpp", "rust"]})
|
||||
.to_string()
|
||||
.into(),
|
||||
]),
|
||||
json!({"sourceLanguages": ["cpp", "rust"]}).to_string(),
|
||||
],
|
||||
request_args: self.request_args(config),
|
||||
adapter_name: "test".into(),
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
connection: None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use anyhow::{Result, anyhow};
|
|||
use async_trait::async_trait;
|
||||
use codelldb::CodeLldbDebugAdapter;
|
||||
use dap::{
|
||||
DapRegistry, DebugRequestType,
|
||||
DapRegistry, DebugRequest,
|
||||
adapters::{
|
||||
self, AdapterVersion, DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName,
|
||||
GithubRepo,
|
||||
|
@ -19,23 +19,26 @@ use dap::{
|
|||
};
|
||||
use gdb::GdbDebugAdapter;
|
||||
use go::GoDebugAdapter;
|
||||
use gpui::{App, BorrowAppContext};
|
||||
use javascript::JsDebugAdapter;
|
||||
use php::PhpDebugAdapter;
|
||||
use python::PythonDebugAdapter;
|
||||
use serde_json::{Value, json};
|
||||
use task::TCPHost;
|
||||
use task::TcpArgumentsTemplate;
|
||||
|
||||
pub fn init(registry: Arc<DapRegistry>) {
|
||||
registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
|
||||
registry.add_adapter(Arc::from(PythonDebugAdapter));
|
||||
registry.add_adapter(Arc::from(PhpDebugAdapter));
|
||||
registry.add_adapter(Arc::from(JsDebugAdapter));
|
||||
registry.add_adapter(Arc::from(GoDebugAdapter));
|
||||
registry.add_adapter(Arc::from(GdbDebugAdapter));
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.update_default_global(|registry: &mut DapRegistry, _cx| {
|
||||
registry.add_adapter(Arc::from(CodeLldbDebugAdapter::default()));
|
||||
registry.add_adapter(Arc::from(PythonDebugAdapter));
|
||||
registry.add_adapter(Arc::from(PhpDebugAdapter));
|
||||
registry.add_adapter(Arc::from(JsDebugAdapter));
|
||||
registry.add_adapter(Arc::from(GoDebugAdapter));
|
||||
registry.add_adapter(Arc::from(GdbDebugAdapter));
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn configure_tcp_connection(
|
||||
tcp_connection: TCPHost,
|
||||
tcp_connection: TcpArgumentsTemplate,
|
||||
) -> Result<(Ipv4Addr, u16, Option<u64>)> {
|
||||
let host = tcp_connection.host();
|
||||
let timeout = tcp_connection.timeout;
|
||||
|
@ -53,7 +56,7 @@ trait ToDap {
|
|||
fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest;
|
||||
}
|
||||
|
||||
impl ToDap for DebugRequestType {
|
||||
impl ToDap for DebugRequest {
|
||||
fn to_dap(&self) -> dap::StartDebuggingRequestArgumentsRequest {
|
||||
match self {
|
||||
Self::Launch(_) => dap::StartDebuggingRequestArgumentsRequest::Launch,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::ffi::OsStr;
|
||||
use std::{collections::HashMap, ffi::OsStr};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use async_trait::async_trait;
|
||||
use dap::StartDebuggingRequestArguments;
|
||||
use gpui::AsyncApp;
|
||||
use task::{DebugRequestType, DebugTaskDefinition};
|
||||
use task::{DebugRequest, DebugTaskDefinition};
|
||||
|
||||
use crate::*;
|
||||
|
||||
|
@ -17,18 +17,18 @@ impl GdbDebugAdapter {
|
|||
fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
|
||||
let mut args = json!({
|
||||
"request": match config.request {
|
||||
DebugRequestType::Launch(_) => "launch",
|
||||
DebugRequestType::Attach(_) => "attach",
|
||||
DebugRequest::Launch(_) => "launch",
|
||||
DebugRequest::Attach(_) => "attach",
|
||||
},
|
||||
});
|
||||
|
||||
let map = args.as_object_mut().unwrap();
|
||||
match &config.request {
|
||||
DebugRequestType::Attach(attach) => {
|
||||
DebugRequest::Attach(attach) => {
|
||||
map.insert("pid".into(), attach.process_id.into());
|
||||
}
|
||||
|
||||
DebugRequestType::Launch(launch) => {
|
||||
DebugRequest::Launch(launch) => {
|
||||
map.insert("program".into(), launch.program.clone().into());
|
||||
|
||||
if !launch.args.is_empty() {
|
||||
|
@ -82,10 +82,9 @@ impl DebugAdapter for GdbDebugAdapter {
|
|||
let gdb_path = user_setting_path.unwrap_or(gdb_path?);
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: Self::ADAPTER_NAME.into(),
|
||||
command: gdb_path,
|
||||
arguments: Some(vec!["-i=dap".into()]),
|
||||
envs: None,
|
||||
arguments: vec!["-i=dap".into()],
|
||||
envs: HashMap::default(),
|
||||
cwd: None,
|
||||
connection: None,
|
||||
request_args: self.request_args(config),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use dap::StartDebuggingRequestArguments;
|
||||
use gpui::AsyncApp;
|
||||
use std::{ffi::OsStr, path::PathBuf};
|
||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
use crate::*;
|
||||
|
@ -12,12 +12,12 @@ impl GoDebugAdapter {
|
|||
const ADAPTER_NAME: &'static str = "Delve";
|
||||
fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
|
||||
let mut args = match &config.request {
|
||||
dap::DebugRequestType::Attach(attach_config) => {
|
||||
dap::DebugRequest::Attach(attach_config) => {
|
||||
json!({
|
||||
"processId": attach_config.process_id,
|
||||
})
|
||||
}
|
||||
dap::DebugRequestType::Launch(launch_config) => json!({
|
||||
dap::DebugRequest::Launch(launch_config) => json!({
|
||||
"program": launch_config.program,
|
||||
"cwd": launch_config.cwd,
|
||||
"args": launch_config.args
|
||||
|
@ -92,15 +92,14 @@ impl DebugAdapter for GoDebugAdapter {
|
|||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: self.name(),
|
||||
command: delve_path,
|
||||
arguments: Some(vec![
|
||||
arguments: vec![
|
||||
"dap".into(),
|
||||
"--listen".into(),
|
||||
format!("{}:{}", host, port).into(),
|
||||
]),
|
||||
format!("{}:{}", host, port),
|
||||
],
|
||||
cwd: None,
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
connection: Some(adapters::TcpArguments {
|
||||
host,
|
||||
port,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use adapters::latest_github_release;
|
||||
use dap::StartDebuggingRequestArguments;
|
||||
use gpui::AsyncApp;
|
||||
use std::path::PathBuf;
|
||||
use task::{DebugRequestType, DebugTaskDefinition};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use task::{DebugRequest, DebugTaskDefinition};
|
||||
|
||||
use crate::*;
|
||||
|
||||
|
@ -18,16 +18,16 @@ impl JsDebugAdapter {
|
|||
let mut args = json!({
|
||||
"type": "pwa-node",
|
||||
"request": match config.request {
|
||||
DebugRequestType::Launch(_) => "launch",
|
||||
DebugRequestType::Attach(_) => "attach",
|
||||
DebugRequest::Launch(_) => "launch",
|
||||
DebugRequest::Attach(_) => "attach",
|
||||
},
|
||||
});
|
||||
let map = args.as_object_mut().unwrap();
|
||||
match &config.request {
|
||||
DebugRequestType::Attach(attach) => {
|
||||
DebugRequest::Attach(attach) => {
|
||||
map.insert("processId".into(), attach.process_id.into());
|
||||
}
|
||||
DebugRequestType::Launch(launch) => {
|
||||
DebugRequest::Launch(launch) => {
|
||||
map.insert("program".into(), launch.program.clone().into());
|
||||
|
||||
if !launch.args.is_empty() {
|
||||
|
@ -106,20 +106,22 @@ impl DebugAdapter for JsDebugAdapter {
|
|||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: self.name(),
|
||||
command: delegate
|
||||
.node_runtime()
|
||||
.binary_path()
|
||||
.await?
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
arguments: Some(vec![
|
||||
adapter_path.join(Self::ADAPTER_PATH).into(),
|
||||
port.to_string().into(),
|
||||
host.to_string().into(),
|
||||
]),
|
||||
arguments: vec![
|
||||
adapter_path
|
||||
.join(Self::ADAPTER_PATH)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
port.to_string(),
|
||||
host.to_string(),
|
||||
],
|
||||
cwd: None,
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
connection: Some(adapters::TcpArguments {
|
||||
host,
|
||||
port,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use adapters::latest_github_release;
|
||||
use dap::adapters::TcpArguments;
|
||||
use gpui::AsyncApp;
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
use crate::*;
|
||||
|
@ -19,20 +19,18 @@ impl PhpDebugAdapter {
|
|||
config: &DebugTaskDefinition,
|
||||
) -> Result<dap::StartDebuggingRequestArguments> {
|
||||
match &config.request {
|
||||
dap::DebugRequestType::Attach(_) => {
|
||||
dap::DebugRequest::Attach(_) => {
|
||||
anyhow::bail!("php adapter does not support attaching")
|
||||
}
|
||||
dap::DebugRequestType::Launch(launch_config) => {
|
||||
Ok(dap::StartDebuggingRequestArguments {
|
||||
configuration: json!({
|
||||
"program": launch_config.program,
|
||||
"cwd": launch_config.cwd,
|
||||
"args": launch_config.args,
|
||||
"stopOnEntry": config.stop_on_entry.unwrap_or_default(),
|
||||
}),
|
||||
request: config.request.to_dap(),
|
||||
})
|
||||
}
|
||||
dap::DebugRequest::Launch(launch_config) => Ok(dap::StartDebuggingRequestArguments {
|
||||
configuration: json!({
|
||||
"program": launch_config.program,
|
||||
"cwd": launch_config.cwd,
|
||||
"args": launch_config.args,
|
||||
"stopOnEntry": config.stop_on_entry.unwrap_or_default(),
|
||||
}),
|
||||
request: config.request.to_dap(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,24 +92,26 @@ impl DebugAdapter for PhpDebugAdapter {
|
|||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: self.name(),
|
||||
command: delegate
|
||||
.node_runtime()
|
||||
.binary_path()
|
||||
.await?
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
arguments: Some(vec![
|
||||
adapter_path.join(Self::ADAPTER_PATH).into(),
|
||||
format!("--server={}", port).into(),
|
||||
]),
|
||||
arguments: vec![
|
||||
adapter_path
|
||||
.join(Self::ADAPTER_PATH)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
format!("--server={}", port),
|
||||
],
|
||||
connection: Some(TcpArguments {
|
||||
port,
|
||||
host,
|
||||
timeout,
|
||||
}),
|
||||
cwd: None,
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
request_args: self.request_args(config)?,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::*;
|
||||
use dap::{DebugRequestType, StartDebuggingRequestArguments};
|
||||
use dap::{DebugRequest, StartDebuggingRequestArguments};
|
||||
use gpui::AsyncApp;
|
||||
use std::{ffi::OsStr, path::PathBuf};
|
||||
use std::{collections::HashMap, ffi::OsStr, path::PathBuf};
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -16,18 +16,18 @@ impl PythonDebugAdapter {
|
|||
fn request_args(&self, config: &DebugTaskDefinition) -> StartDebuggingRequestArguments {
|
||||
let mut args = json!({
|
||||
"request": match config.request {
|
||||
DebugRequestType::Launch(_) => "launch",
|
||||
DebugRequestType::Attach(_) => "attach",
|
||||
DebugRequest::Launch(_) => "launch",
|
||||
DebugRequest::Attach(_) => "attach",
|
||||
},
|
||||
"subProcess": true,
|
||||
"redirectOutput": true,
|
||||
});
|
||||
let map = args.as_object_mut().unwrap();
|
||||
match &config.request {
|
||||
DebugRequestType::Attach(attach) => {
|
||||
DebugRequest::Attach(attach) => {
|
||||
map.insert("processId".into(), attach.process_id.into());
|
||||
}
|
||||
DebugRequestType::Launch(launch) => {
|
||||
DebugRequest::Launch(launch) => {
|
||||
map.insert("program".into(), launch.program.clone().into());
|
||||
map.insert("args".into(), launch.args.clone().into());
|
||||
|
||||
|
@ -141,20 +141,22 @@ impl DebugAdapter for PythonDebugAdapter {
|
|||
};
|
||||
|
||||
Ok(DebugAdapterBinary {
|
||||
adapter_name: self.name(),
|
||||
command: python_path.ok_or(anyhow!("failed to find binary path for python"))?,
|
||||
arguments: Some(vec![
|
||||
debugpy_dir.join(Self::ADAPTER_PATH).into(),
|
||||
format!("--port={}", port).into(),
|
||||
format!("--host={}", host).into(),
|
||||
]),
|
||||
arguments: vec![
|
||||
debugpy_dir
|
||||
.join(Self::ADAPTER_PATH)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
format!("--port={}", port),
|
||||
format!("--host={}", host),
|
||||
],
|
||||
connection: Some(adapters::TcpArguments {
|
||||
host,
|
||||
port,
|
||||
timeout,
|
||||
}),
|
||||
cwd: None,
|
||||
envs: None,
|
||||
envs: HashMap::default(),
|
||||
request_args: self.request_args(config),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -566,11 +566,13 @@ impl DapLogView {
|
|||
.dap_store()
|
||||
.read(cx)
|
||||
.sessions()
|
||||
.filter_map(|client| {
|
||||
let client = client.read(cx).adapter_client()?;
|
||||
.filter_map(|session| {
|
||||
let session = session.read(cx);
|
||||
session.adapter_name();
|
||||
let client = session.adapter_client()?;
|
||||
Some(DapMenuItem {
|
||||
client_id: client.id(),
|
||||
client_name: client.name().0.as_ref().into(),
|
||||
client_name: session.adapter_name().to_string(),
|
||||
has_adapter_logs: client.has_adapter_logs(),
|
||||
selected_entry: self.current_view.map_or(LogKind::Adapter, |(_, kind)| kind),
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use dap::DebugRequestType;
|
||||
use dap::DebugRequest;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::Subscription;
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, Focusable, Render};
|
||||
|
@ -216,10 +216,10 @@ impl PickerDelegate for AttachModalDelegate {
|
|||
};
|
||||
|
||||
match &mut self.debug_config.request {
|
||||
DebugRequestType::Attach(config) => {
|
||||
DebugRequest::Attach(config) => {
|
||||
config.process_id = Some(candidate.pid);
|
||||
}
|
||||
DebugRequestType::Launch(_) => {
|
||||
DebugRequest::Launch(_) => {
|
||||
debug_panic!("Debugger attach modal used on launch debug config");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use dap::DebugRequestType;
|
||||
use dap::{DapRegistry, DebugRequest};
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use gpui::{
|
||||
App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Render, TextStyle,
|
||||
|
@ -13,7 +13,7 @@ use gpui::{
|
|||
};
|
||||
use project::Project;
|
||||
use settings::Settings;
|
||||
use task::{DebugTaskDefinition, LaunchConfig};
|
||||
use task::{DebugTaskDefinition, DebugTaskTemplate, LaunchRequest};
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
|
||||
|
@ -37,9 +37,9 @@ pub(super) struct NewSessionModal {
|
|||
last_selected_profile_name: Option<SharedString>,
|
||||
}
|
||||
|
||||
fn suggested_label(request: &DebugRequestType, debugger: &str) -> String {
|
||||
fn suggested_label(request: &DebugRequest, debugger: &str) -> String {
|
||||
match request {
|
||||
DebugRequestType::Launch(config) => {
|
||||
DebugRequest::Launch(config) => {
|
||||
let last_path_component = Path::new(&config.program)
|
||||
.file_name()
|
||||
.map(|name| name.to_string_lossy())
|
||||
|
@ -47,7 +47,7 @@ fn suggested_label(request: &DebugRequestType, debugger: &str) -> String {
|
|||
|
||||
format!("{} ({debugger})", last_path_component)
|
||||
}
|
||||
DebugRequestType::Attach(config) => format!(
|
||||
DebugRequest::Attach(config) => format!(
|
||||
"pid: {} ({debugger})",
|
||||
config.process_id.unwrap_or(u32::MAX)
|
||||
),
|
||||
|
@ -71,7 +71,7 @@ impl NewSessionModal {
|
|||
.and_then(|def| def.stop_on_entry);
|
||||
|
||||
let launch_config = match past_debug_definition.map(|def| def.request) {
|
||||
Some(DebugRequestType::Launch(launch_config)) => Some(launch_config),
|
||||
Some(DebugRequest::Launch(launch_config)) => Some(launch_config),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
@ -96,7 +96,6 @@ impl NewSessionModal {
|
|||
request,
|
||||
initialize_args: self.initialize_args.clone(),
|
||||
tcp_connection: None,
|
||||
locator: None,
|
||||
stop_on_entry: match self.stop_on_entry {
|
||||
ToggleState::Selected => Some(true),
|
||||
_ => None,
|
||||
|
@ -131,20 +130,16 @@ impl NewSessionModal {
|
|||
let project = workspace.update(cx, |workspace, _| workspace.project().clone())?;
|
||||
|
||||
let task = project.update(cx, |this, cx| {
|
||||
if let Some(debug_config) =
|
||||
config
|
||||
.clone()
|
||||
.to_zed_format()
|
||||
.ok()
|
||||
.and_then(|task_template| {
|
||||
task_template
|
||||
.resolve_task("debug_task", &task_context)
|
||||
.and_then(|resolved_task| {
|
||||
resolved_task.resolved_debug_adapter_config()
|
||||
})
|
||||
})
|
||||
let template = DebugTaskTemplate {
|
||||
locator: None,
|
||||
definition: config.clone(),
|
||||
};
|
||||
if let Some(debug_config) = template
|
||||
.to_zed_format()
|
||||
.resolve_task("debug_task", &task_context)
|
||||
.and_then(|resolved_task| resolved_task.resolved_debug_adapter_config())
|
||||
{
|
||||
this.start_debug_session(debug_config, cx)
|
||||
this.start_debug_session(debug_config.definition, cx)
|
||||
} else {
|
||||
this.start_debug_session(config, cx)
|
||||
}
|
||||
|
@ -214,12 +209,7 @@ impl NewSessionModal {
|
|||
};
|
||||
|
||||
let available_adapters = workspace
|
||||
.update(cx, |this, cx| {
|
||||
this.project()
|
||||
.read(cx)
|
||||
.debug_adapters()
|
||||
.enumerate_adapters()
|
||||
})
|
||||
.update(cx, |_, cx| DapRegistry::global(cx).enumerate_adapters())
|
||||
.ok()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -251,14 +241,14 @@ impl NewSessionModal {
|
|||
this.debugger = Some(task.adapter.clone().into());
|
||||
this.initialize_args = task.initialize_args.clone();
|
||||
match &task.request {
|
||||
DebugRequestType::Launch(launch_config) => {
|
||||
DebugRequest::Launch(launch_config) => {
|
||||
this.mode = NewSessionMode::launch(
|
||||
Some(launch_config.clone()),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
DebugRequestType::Attach(_) => {
|
||||
DebugRequest::Attach(_) => {
|
||||
let Ok(project) = this
|
||||
.workspace
|
||||
.read_with(cx, |this, _| this.project().clone())
|
||||
|
@ -285,7 +275,7 @@ impl NewSessionModal {
|
|||
}
|
||||
};
|
||||
|
||||
let available_adapters: Vec<DebugTaskDefinition> = workspace
|
||||
let available_adapters: Vec<DebugTaskTemplate> = workspace
|
||||
.update(cx, |this, cx| {
|
||||
this.project()
|
||||
.read(cx)
|
||||
|
@ -303,9 +293,9 @@ impl NewSessionModal {
|
|||
|
||||
for debug_definition in available_adapters {
|
||||
menu = menu.entry(
|
||||
debug_definition.label.clone(),
|
||||
debug_definition.definition.label.clone(),
|
||||
None,
|
||||
setter_for_name(debug_definition),
|
||||
setter_for_name(debug_definition.definition),
|
||||
);
|
||||
}
|
||||
menu
|
||||
|
@ -322,7 +312,7 @@ struct LaunchMode {
|
|||
|
||||
impl LaunchMode {
|
||||
fn new(
|
||||
past_launch_config: Option<LaunchConfig>,
|
||||
past_launch_config: Option<LaunchRequest>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<Self> {
|
||||
|
@ -348,9 +338,9 @@ impl LaunchMode {
|
|||
cx.new(|_| Self { program, cwd })
|
||||
}
|
||||
|
||||
fn debug_task(&self, cx: &App) -> task::LaunchConfig {
|
||||
fn debug_task(&self, cx: &App) -> task::LaunchRequest {
|
||||
let path = self.cwd.read(cx).text(cx);
|
||||
task::LaunchConfig {
|
||||
task::LaunchRequest {
|
||||
program: self.program.read(cx).text(cx),
|
||||
cwd: path.is_empty().not().then(|| PathBuf::from(path)),
|
||||
args: Default::default(),
|
||||
|
@ -373,10 +363,9 @@ impl AttachMode {
|
|||
) -> Entity<Self> {
|
||||
let debug_definition = DebugTaskDefinition {
|
||||
label: "Attach New Session Setup".into(),
|
||||
request: dap::DebugRequestType::Attach(task::AttachConfig { process_id: None }),
|
||||
request: dap::DebugRequest::Attach(task::AttachRequest { process_id: None }),
|
||||
tcp_connection: None,
|
||||
adapter: debugger.clone().unwrap_or_default().into(),
|
||||
locator: None,
|
||||
initialize_args: None,
|
||||
stop_on_entry: Some(false),
|
||||
};
|
||||
|
@ -391,8 +380,8 @@ impl AttachMode {
|
|||
attach_picker,
|
||||
})
|
||||
}
|
||||
fn debug_task(&self) -> task::AttachConfig {
|
||||
task::AttachConfig { process_id: None }
|
||||
fn debug_task(&self) -> task::AttachRequest {
|
||||
task::AttachRequest { process_id: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,7 +395,7 @@ enum NewSessionMode {
|
|||
}
|
||||
|
||||
impl NewSessionMode {
|
||||
fn debug_task(&self, cx: &App) -> DebugRequestType {
|
||||
fn debug_task(&self, cx: &App) -> DebugRequest {
|
||||
match self {
|
||||
NewSessionMode::Launch(entity) => entity.read(cx).debug_task(cx).into(),
|
||||
NewSessionMode::Attach(entity) => entity.read(cx).debug_task().into(),
|
||||
|
@ -488,7 +477,7 @@ impl NewSessionMode {
|
|||
Self::Attach(AttachMode::new(debugger, project, window, cx))
|
||||
}
|
||||
fn launch(
|
||||
past_launch_config: Option<LaunchConfig>,
|
||||
past_launch_config: Option<LaunchRequest>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<NewSessionModal>,
|
||||
) -> Self {
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext};
|
|||
use menu::Confirm;
|
||||
use project::{FakeFs, Project};
|
||||
use serde_json::json;
|
||||
use task::{AttachConfig, DebugTaskDefinition, TCPHost};
|
||||
use task::{AttachRequest, DebugTaskDefinition, TcpArgumentsTemplate};
|
||||
use tests::{init_test, init_test_workspace};
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -31,13 +31,12 @@ async fn test_direct_attach_to_process(executor: BackgroundExecutor, cx: &mut Te
|
|||
cx,
|
||||
DebugTaskDefinition {
|
||||
adapter: "fake-adapter".to_string(),
|
||||
request: dap::DebugRequestType::Attach(AttachConfig {
|
||||
request: dap::DebugRequest::Attach(AttachRequest {
|
||||
process_id: Some(10),
|
||||
}),
|
||||
label: "label".to_string(),
|
||||
initialize_args: None,
|
||||
tcp_connection: None,
|
||||
locator: None,
|
||||
stop_on_entry: None,
|
||||
},
|
||||
|client| {
|
||||
|
@ -105,11 +104,10 @@ async fn test_show_attach_modal_and_select_process(
|
|||
project.clone(),
|
||||
DebugTaskDefinition {
|
||||
adapter: FakeAdapter::ADAPTER_NAME.into(),
|
||||
request: dap::DebugRequestType::Attach(AttachConfig::default()),
|
||||
request: dap::DebugRequest::Attach(AttachRequest::default()),
|
||||
label: "attach example".into(),
|
||||
initialize_args: None,
|
||||
tcp_connection: Some(TCPHost::default()),
|
||||
locator: None,
|
||||
tcp_connection: Some(TcpArgumentsTemplate::default()),
|
||||
stop_on_entry: None,
|
||||
},
|
||||
vec![
|
||||
|
|
|
@ -5111,44 +5111,21 @@ impl Editor {
|
|||
CodeActionsItem::Task(task_source_kind, resolved_task) => {
|
||||
match resolved_task.task_type() {
|
||||
task::TaskType::Script => workspace.update(cx, |workspace, cx| {
|
||||
workspace::tasks::schedule_resolved_task(
|
||||
workspace,
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
resolved_task,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
||||
Some(Task::ready(Ok(())))
|
||||
}),
|
||||
task::TaskType::Debug(debug_args) => {
|
||||
if debug_args.locator.is_some() {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace::tasks::schedule_resolved_task(
|
||||
workspace,
|
||||
task_source_kind,
|
||||
resolved_task,
|
||||
false,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
return Some(Task::ready(Ok(())));
|
||||
}
|
||||
|
||||
if let Some(project) = self.project.as_ref() {
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.start_debug_session(
|
||||
resolved_task.resolved_debug_adapter_config().unwrap(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
Some(Task::ready(Ok(())))
|
||||
} else {
|
||||
Some(Task::ready(Ok(())))
|
||||
}
|
||||
task::TaskType::Debug(_) => {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.schedule_debug_task(resolved_task, window, cx);
|
||||
});
|
||||
Some(Task::ready(Ok(())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6845,12 +6822,12 @@ impl Editor {
|
|||
resolved.reveal = reveal_strategy;
|
||||
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace::tasks::schedule_resolved_task(
|
||||
workspace,
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
resolved_task,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
|
|
|
@ -15,7 +15,6 @@ clap.workspace = true
|
|||
client.workspace = true
|
||||
collections.workspace = true
|
||||
context_server.workspace = true
|
||||
dap.workspace = true
|
||||
dirs = "5.0"
|
||||
env_logger.workspace = true
|
||||
extension.workspace = true
|
||||
|
|
|
@ -3,7 +3,6 @@ use agent::{ThreadEvent, ThreadStore};
|
|||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
use client::proto::LspWorkProgress;
|
||||
use dap::DapRegistry;
|
||||
use futures::channel::mpsc;
|
||||
use futures::{FutureExt, StreamExt as _, select_biased};
|
||||
use gpui::{App, AppContext as _, AsyncApp, Entity, Task};
|
||||
|
@ -243,7 +242,6 @@ impl Example {
|
|||
app_state.node_runtime.clone(),
|
||||
app_state.user_store.clone(),
|
||||
app_state.languages.clone(),
|
||||
Arc::new(DapRegistry::default()),
|
||||
app_state.fs.clone(),
|
||||
None,
|
||||
cx,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
pub mod breakpoint_store;
|
||||
pub mod dap_command;
|
||||
pub mod dap_store;
|
||||
mod locator_store;
|
||||
pub mod locators;
|
||||
pub mod session;
|
||||
|
||||
#[cfg(any(feature = "test-support", test))]
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use super::{
|
||||
breakpoint_store::BreakpointStore,
|
||||
locator_store::LocatorStore,
|
||||
locators::DapLocator,
|
||||
session::{self, Session, SessionStateEvent},
|
||||
};
|
||||
use crate::{ProjectEnvironment, debugger};
|
||||
use crate::{
|
||||
ProjectEnvironment, debugger, project_settings::ProjectSettings, worktree_store::WorktreeStore,
|
||||
};
|
||||
use anyhow::{Result, anyhow};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use dap::{
|
||||
Capabilities, CompletionItem, CompletionsArguments, ErrorResponse, EvaluateArguments,
|
||||
EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments, Source,
|
||||
StartDebuggingRequestArguments,
|
||||
Capabilities, CompletionItem, CompletionsArguments, DapRegistry, ErrorResponse,
|
||||
EvaluateArguments, EvaluateArgumentsContext, EvaluateResponse, RunInTerminalRequestArguments,
|
||||
Source, StartDebuggingRequestArguments,
|
||||
adapters::{DapStatus, DebugAdapterBinary, DebugAdapterName},
|
||||
client::SessionId,
|
||||
messages::Message,
|
||||
|
@ -22,8 +24,7 @@ use futures::{
|
|||
future::{Shared, join_all},
|
||||
};
|
||||
use gpui::{
|
||||
App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString,
|
||||
Task, WeakEntity,
|
||||
App, AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity,
|
||||
};
|
||||
use http_client::HttpClient;
|
||||
use language::{BinaryStatus, LanguageRegistry, LanguageToolchainStore};
|
||||
|
@ -35,17 +36,16 @@ use rpc::{
|
|||
proto::{self},
|
||||
};
|
||||
use serde_json::Value;
|
||||
use settings::WorktreeId;
|
||||
use settings::{Settings, WorktreeId};
|
||||
use smol::{lock::Mutex, stream::StreamExt};
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{BTreeMap, HashSet},
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, atomic::Ordering::SeqCst},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::VecDeque, sync::atomic::AtomicU32};
|
||||
use task::DebugTaskDefinition;
|
||||
use task::{DebugTaskDefinition, DebugTaskTemplate};
|
||||
use util::ResultExt as _;
|
||||
use worktree::Worktree;
|
||||
|
||||
|
@ -71,45 +71,26 @@ pub enum DapStoreEvent {
|
|||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum DapStoreMode {
|
||||
Local(LocalDapStore), // ssh host and collab host
|
||||
Remote(RemoteDapStore), // collab guest
|
||||
enum DapStoreMode {
|
||||
Local(LocalDapStore),
|
||||
Ssh(SshDapStore),
|
||||
Collab,
|
||||
}
|
||||
|
||||
pub struct LocalDapStore {
|
||||
fs: Arc<dyn Fs>,
|
||||
node_runtime: NodeRuntime,
|
||||
next_session_id: AtomicU32,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
environment: Entity<ProjectEnvironment>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
locator_store: Arc<LocatorStore>,
|
||||
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
||||
_start_debugging_task: Task<()>,
|
||||
locators: HashMap<String, Arc<dyn DapLocator>>,
|
||||
}
|
||||
|
||||
impl LocalDapStore {
|
||||
fn next_session_id(&self) -> SessionId {
|
||||
SessionId(self.next_session_id.fetch_add(1, SeqCst))
|
||||
}
|
||||
pub(crate) fn locate_binary(
|
||||
&self,
|
||||
mut definition: DebugTaskDefinition,
|
||||
executor: BackgroundExecutor,
|
||||
) -> Task<DebugTaskDefinition> {
|
||||
let locator_store = self.locator_store.clone();
|
||||
executor.spawn(async move {
|
||||
let _ = locator_store.resolve_debug_config(&mut definition).await;
|
||||
definition
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RemoteDapStore {
|
||||
pub struct SshDapStore {
|
||||
upstream_client: AnyProtoClient,
|
||||
upstream_project_id: u64,
|
||||
event_queue: Option<VecDeque<DapStoreEvent>>,
|
||||
}
|
||||
|
||||
pub struct DapStore {
|
||||
|
@ -117,25 +98,17 @@ pub struct DapStore {
|
|||
downstream_client: Option<(AnyProtoClient, u64)>,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
sessions: BTreeMap<SessionId, Entity<Session>>,
|
||||
next_session_id: u32,
|
||||
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
||||
_start_debugging_task: Task<()>,
|
||||
}
|
||||
|
||||
impl EventEmitter<DapStoreEvent> for DapStore {}
|
||||
|
||||
impl DapStore {
|
||||
pub fn init(_client: &AnyProtoClient) {
|
||||
// todo(debugger): Reenable these after we finish handle_dap_command refactor
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<NextCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<StepInCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<StepOutCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<StepBackCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<ContinueCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<PauseCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<DisconnectCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<TerminateThreadsCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<TerminateCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<RestartCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<VariablesCommand>);
|
||||
// client.add_entity_request_handler(Self::handle_dap_command::<RestartStackFrameCommand>);
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_request_handler(Self::handle_run_debug_locator);
|
||||
client.add_entity_request_handler(Self::handle_get_debug_adapter_binary);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
|
@ -146,15 +119,62 @@ impl DapStore {
|
|||
language_registry: Arc<LanguageRegistry>,
|
||||
environment: Entity<ProjectEnvironment>,
|
||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
cx.on_app_quit(Self::shutdown_sessions).detach();
|
||||
|
||||
let locators = HashMap::from_iter([(
|
||||
"cargo".to_string(),
|
||||
Arc::new(super::locators::cargo::CargoLocator {}) as _,
|
||||
)]);
|
||||
|
||||
let mode = DapStoreMode::Local(LocalDapStore {
|
||||
fs,
|
||||
environment,
|
||||
http_client,
|
||||
node_runtime,
|
||||
toolchain_store,
|
||||
worktree_store,
|
||||
language_registry,
|
||||
locators,
|
||||
});
|
||||
|
||||
Self::new(mode, breakpoint_store, cx)
|
||||
}
|
||||
|
||||
pub fn new_ssh(
|
||||
project_id: u64,
|
||||
upstream_client: AnyProtoClient,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let mode = DapStoreMode::Ssh(SshDapStore {
|
||||
upstream_client,
|
||||
upstream_project_id: project_id,
|
||||
});
|
||||
|
||||
Self::new(mode, breakpoint_store, cx)
|
||||
}
|
||||
|
||||
pub fn new_collab(
|
||||
_project_id: u64,
|
||||
_upstream_client: AnyProtoClient,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
Self::new(DapStoreMode::Collab, breakpoint_store, cx)
|
||||
}
|
||||
|
||||
fn new(
|
||||
mode: DapStoreMode,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let (start_debugging_tx, mut message_rx) =
|
||||
futures::channel::mpsc::unbounded::<(SessionId, Message)>();
|
||||
|
||||
let _start_debugging_task = cx.spawn(async move |this, cx| {
|
||||
let task = cx.spawn(async move |this, cx| {
|
||||
while let Some((session_id, message)) = message_rx.next().await {
|
||||
match message {
|
||||
Message::Request(request) => {
|
||||
|
@ -174,94 +194,135 @@ impl DapStore {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
mode: DapStoreMode::Local(LocalDapStore {
|
||||
fs,
|
||||
environment,
|
||||
http_client,
|
||||
node_runtime,
|
||||
toolchain_store,
|
||||
language_registry,
|
||||
start_debugging_tx,
|
||||
_start_debugging_task,
|
||||
locator_store: Arc::from(LocatorStore::new()),
|
||||
next_session_id: Default::default(),
|
||||
}),
|
||||
mode,
|
||||
_start_debugging_task: task,
|
||||
start_debugging_tx,
|
||||
next_session_id: 0,
|
||||
downstream_client: None,
|
||||
breakpoint_store,
|
||||
sessions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_remote(
|
||||
project_id: u64,
|
||||
upstream_client: AnyProtoClient,
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
) -> Self {
|
||||
Self {
|
||||
mode: DapStoreMode::Remote(RemoteDapStore {
|
||||
upstream_client,
|
||||
upstream_project_id: project_id,
|
||||
event_queue: Some(VecDeque::default()),
|
||||
}),
|
||||
downstream_client: None,
|
||||
breakpoint_store,
|
||||
sessions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_remote(&self) -> Option<&RemoteDapStore> {
|
||||
pub fn get_debug_adapter_binary(
|
||||
&mut self,
|
||||
definition: DebugTaskDefinition,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<DebugAdapterBinary>> {
|
||||
match &self.mode {
|
||||
DapStoreMode::Remote(remote_dap_store) => Some(remote_dap_store),
|
||||
_ => None,
|
||||
DapStoreMode::Local(local) => {
|
||||
let Some(worktree) = local.worktree_store.read(cx).visible_worktrees(cx).next()
|
||||
else {
|
||||
return Task::ready(Err(anyhow!("Failed to find a worktree")));
|
||||
};
|
||||
let Some(adapter) = DapRegistry::global(cx).adapter(&definition.adapter) else {
|
||||
return Task::ready(Err(anyhow!("Failed to find a debug adapter")));
|
||||
};
|
||||
|
||||
let user_installed_path = ProjectSettings::get_global(cx)
|
||||
.dap
|
||||
.get(&adapter.name())
|
||||
.and_then(|s| s.binary.as_ref().map(PathBuf::from));
|
||||
|
||||
let delegate = self.delegate(&worktree, cx);
|
||||
let cwd: Arc<Path> = definition
|
||||
.cwd()
|
||||
.unwrap_or(worktree.read(cx).abs_path().as_ref())
|
||||
.into();
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let mut binary = adapter
|
||||
.get_binary(&delegate, &definition, user_installed_path, cx)
|
||||
.await?;
|
||||
|
||||
let env = this
|
||||
.update(cx, |this, cx| {
|
||||
this.as_local()
|
||||
.unwrap()
|
||||
.environment
|
||||
.update(cx, |environment, cx| {
|
||||
environment.get_directory_environment(cwd, cx)
|
||||
})
|
||||
})?
|
||||
.await;
|
||||
|
||||
if let Some(mut env) = env {
|
||||
env.extend(std::mem::take(&mut binary.envs));
|
||||
binary.envs = env;
|
||||
}
|
||||
|
||||
Ok(binary)
|
||||
})
|
||||
}
|
||||
DapStoreMode::Ssh(ssh) => {
|
||||
let request = ssh.upstream_client.request(proto::GetDebugAdapterBinary {
|
||||
project_id: ssh.upstream_project_id,
|
||||
task: Some(definition.to_proto()),
|
||||
});
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let response = request.await?;
|
||||
DebugAdapterBinary::from_proto(response)
|
||||
})
|
||||
}
|
||||
DapStoreMode::Collab => {
|
||||
Task::ready(Err(anyhow!("Debugging is not yet supported via collab")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remote_event_queue(&mut self) -> Option<VecDeque<DapStoreEvent>> {
|
||||
if let DapStoreMode::Remote(remote) = &mut self.mode {
|
||||
remote.event_queue.take()
|
||||
} else {
|
||||
None
|
||||
pub fn run_debug_locator(
|
||||
&mut self,
|
||||
template: DebugTaskTemplate,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<DebugTaskDefinition>> {
|
||||
let Some(locator_name) = template.locator else {
|
||||
return Task::ready(Ok(template.definition));
|
||||
};
|
||||
|
||||
match &self.mode {
|
||||
DapStoreMode::Local(local) => {
|
||||
if let Some(locator) = local.locators.get(&locator_name).cloned() {
|
||||
cx.background_spawn(
|
||||
async move { locator.run_locator(template.definition).await },
|
||||
)
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("Couldn't find locator {}", locator_name)))
|
||||
}
|
||||
}
|
||||
DapStoreMode::Ssh(ssh) => {
|
||||
let request = ssh.upstream_client.request(proto::RunDebugLocator {
|
||||
project_id: ssh.upstream_project_id,
|
||||
locator: locator_name,
|
||||
task: Some(template.definition.to_proto()),
|
||||
});
|
||||
cx.background_spawn(async move {
|
||||
let response = request.await?;
|
||||
DebugTaskDefinition::from_proto(response)
|
||||
})
|
||||
}
|
||||
DapStoreMode::Collab => {
|
||||
Task::ready(Err(anyhow!("Debugging is not yet supported via collab")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_local(&self) -> Option<&LocalDapStore> {
|
||||
fn as_local(&self) -> Option<&LocalDapStore> {
|
||||
match &self.mode {
|
||||
DapStoreMode::Local(local_dap_store) => Some(local_dap_store),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_local_mut(&mut self) -> Option<&mut LocalDapStore> {
|
||||
match &mut self.mode {
|
||||
DapStoreMode::Local(local_dap_store) => Some(local_dap_store),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upstream_client(&self) -> Option<(AnyProtoClient, u64)> {
|
||||
match &self.mode {
|
||||
DapStoreMode::Remote(RemoteDapStore {
|
||||
upstream_client,
|
||||
upstream_project_id,
|
||||
..
|
||||
}) => Some((upstream_client.clone(), *upstream_project_id)),
|
||||
|
||||
DapStoreMode::Local(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downstream_client(&self) -> Option<&(AnyProtoClient, u64)> {
|
||||
self.downstream_client.as_ref()
|
||||
}
|
||||
|
||||
pub fn add_remote_client(
|
||||
&mut self,
|
||||
session_id: SessionId,
|
||||
ignore: Option<bool>,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let DapStoreMode::Remote(remote) = &self.mode {
|
||||
if let DapStoreMode::Ssh(remote) = &self.mode {
|
||||
self.sessions.insert(
|
||||
session_id,
|
||||
cx.new(|_| {
|
||||
|
@ -328,7 +389,7 @@ impl DapStore {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delegate(&self, worktree: &Entity<Worktree>, cx: &mut App) -> DapAdapterDelegate {
|
||||
fn delegate(&self, worktree: &Entity<Worktree>, cx: &mut App) -> DapAdapterDelegate {
|
||||
let Some(local_store) = self.as_local() else {
|
||||
unimplemented!("Starting session on remote side");
|
||||
};
|
||||
|
@ -354,11 +415,7 @@ impl DapStore {
|
|||
parent_session: Option<Entity<Session>>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> (SessionId, Task<Result<Entity<Session>>>) {
|
||||
let Some(local_store) = self.as_local() else {
|
||||
unimplemented!("Starting session on remote side");
|
||||
};
|
||||
|
||||
let session_id = local_store.next_session_id();
|
||||
let session_id = SessionId(util::post_inc(&mut self.next_session_id));
|
||||
|
||||
if let Some(session) = &parent_session {
|
||||
session.update(cx, |session, _| {
|
||||
|
@ -368,7 +425,7 @@ impl DapStore {
|
|||
|
||||
let (initialized_tx, initialized_rx) = oneshot::channel();
|
||||
|
||||
let start_debugging_tx = local_store.start_debugging_tx.clone();
|
||||
let start_debugging_tx = self.start_debugging_tx.clone();
|
||||
|
||||
let task = cx.spawn(async move |this, cx| {
|
||||
let start_client_task = this.update(cx, |this, cx| {
|
||||
|
@ -682,10 +739,6 @@ impl DapStore {
|
|||
session_id: SessionId,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let Some(_) = self.as_local_mut() else {
|
||||
return Task::ready(Err(anyhow!("Cannot shutdown session on remote side")));
|
||||
};
|
||||
|
||||
let Some(session) = self.sessions.remove(&session_id) else {
|
||||
return Task::ready(Err(anyhow!("Could not find session: {:?}", session_id)));
|
||||
};
|
||||
|
@ -748,6 +801,45 @@ impl DapStore {
|
|||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
async fn handle_run_debug_locator(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::RunDebugLocator>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::DebugTaskDefinition> {
|
||||
let template = DebugTaskTemplate {
|
||||
locator: Some(envelope.payload.locator),
|
||||
definition: DebugTaskDefinition::from_proto(
|
||||
envelope
|
||||
.payload
|
||||
.task
|
||||
.ok_or_else(|| anyhow!("missing definition"))?,
|
||||
)?,
|
||||
};
|
||||
let definition = this
|
||||
.update(&mut cx, |this, cx| this.run_debug_locator(template, cx))?
|
||||
.await?;
|
||||
Ok(definition.to_proto())
|
||||
}
|
||||
|
||||
async fn handle_get_debug_adapter_binary(
|
||||
this: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::GetDebugAdapterBinary>,
|
||||
mut cx: AsyncApp,
|
||||
) -> Result<proto::DebugAdapterBinary> {
|
||||
let definition = DebugTaskDefinition::from_proto(
|
||||
envelope
|
||||
.payload
|
||||
.task
|
||||
.ok_or_else(|| anyhow!("missing definition"))?,
|
||||
)?;
|
||||
let binary = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.get_debug_adapter_binary(definition, cx)
|
||||
})?
|
||||
.await?;
|
||||
Ok(binary.to_proto())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_new_session(
|
||||
|
|
|
@ -3,10 +3,10 @@ use cargo::CargoLocator;
|
|||
use collections::HashMap;
|
||||
use gpui::SharedString;
|
||||
use locators::DapLocator;
|
||||
use task::DebugTaskDefinition;
|
||||
use task::{DebugTaskDefinition, DebugTaskTemplate};
|
||||
|
||||
mod cargo;
|
||||
mod locators;
|
||||
pub mod locators;
|
||||
|
||||
pub(super) struct LocatorStore {
|
||||
locators: HashMap<SharedString, Box<dyn DapLocator>>,
|
||||
|
@ -14,24 +14,19 @@ pub(super) struct LocatorStore {
|
|||
|
||||
impl LocatorStore {
|
||||
pub(super) fn new() -> Self {
|
||||
let locators = HashMap::from_iter([(
|
||||
SharedString::new("cargo"),
|
||||
Box::new(CargoLocator {}) as Box<dyn DapLocator>,
|
||||
)]);
|
||||
Self { locators }
|
||||
}
|
||||
|
||||
pub(super) async fn resolve_debug_config(
|
||||
&self,
|
||||
debug_config: &mut DebugTaskDefinition,
|
||||
) -> Result<()> {
|
||||
let Some(locator_name) = &debug_config.locator else {
|
||||
log::debug!("Attempted to resolve debug config without a locator field");
|
||||
return Ok(());
|
||||
template: DebugTaskTemplate,
|
||||
) -> Result<DebugTaskDefinition> {
|
||||
let Some(locator_name) = &template.locator else {
|
||||
return Ok(template.definition);
|
||||
};
|
||||
|
||||
if let Some(locator) = self.locators.get(locator_name as &str) {
|
||||
locator.run_locator(debug_config).await
|
||||
locator.run_locator(template.definition).await
|
||||
} else {
|
||||
Err(anyhow!("Couldn't find locator {}", locator_name))
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ use anyhow::Result;
|
|||
use async_trait::async_trait;
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
pub(crate) mod cargo;
|
||||
|
||||
#[async_trait]
|
||||
pub(super) trait DapLocator: Send + Sync {
|
||||
async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()>;
|
||||
async fn run_locator(&self, debug_config: DebugTaskDefinition) -> Result<DebugTaskDefinition>;
|
||||
}
|
|
@ -8,7 +8,7 @@ use smol::{
|
|||
};
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
pub(super) struct CargoLocator;
|
||||
pub(crate) struct CargoLocator;
|
||||
|
||||
async fn find_best_executable(executables: &[String], test_name: &str) -> Option<String> {
|
||||
if executables.len() == 1 {
|
||||
|
@ -37,9 +37,12 @@ async fn find_best_executable(executables: &[String], test_name: &str) -> Option
|
|||
}
|
||||
#[async_trait]
|
||||
impl DapLocator for CargoLocator {
|
||||
async fn run_locator(&self, debug_config: &mut DebugTaskDefinition) -> Result<()> {
|
||||
async fn run_locator(
|
||||
&self,
|
||||
mut debug_config: DebugTaskDefinition,
|
||||
) -> Result<DebugTaskDefinition> {
|
||||
let Some(launch_config) = (match &mut debug_config.request {
|
||||
task::DebugRequestType::Launch(launch_config) => Some(launch_config),
|
||||
task::DebugRequest::Launch(launch_config) => Some(launch_config),
|
||||
_ => None,
|
||||
}) else {
|
||||
return Err(anyhow!("Couldn't get launch config in locator"));
|
||||
|
@ -119,6 +122,6 @@ impl DapLocator for CargoLocator {
|
|||
if let Some(test_name) = test_name {
|
||||
launch_config.args.push(test_name);
|
||||
}
|
||||
Ok(())
|
||||
Ok(debug_config)
|
||||
}
|
||||
}
|
|
@ -396,7 +396,7 @@ impl LocalMode {
|
|||
}
|
||||
|
||||
fn request_initialization(&self, cx: &App) -> Task<Result<Capabilities>> {
|
||||
let adapter_id = self.binary.adapter_name.to_string();
|
||||
let adapter_id = self.definition.adapter.clone();
|
||||
|
||||
self.request(Initialize { adapter_id }, cx.background_executor().clone())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use dap::{DebugRequestType, client::DebugAdapterClient};
|
||||
use dap::{DebugRequest, client::DebugAdapterClient};
|
||||
use gpui::{App, AppContext, Entity, Subscription, Task};
|
||||
use task::DebugTaskDefinition;
|
||||
|
||||
|
@ -53,11 +53,10 @@ pub fn start_debug_session<T: Fn(&Arc<DebugAdapterClient>) + 'static>(
|
|||
cx,
|
||||
DebugTaskDefinition {
|
||||
adapter: "fake-adapter".to_string(),
|
||||
request: DebugRequestType::Launch(Default::default()),
|
||||
request: DebugRequest::Launch(Default::default()),
|
||||
label: "test".to_string(),
|
||||
initialize_args: None,
|
||||
tcp_connection: None,
|
||||
locator: None,
|
||||
stop_on_entry: None,
|
||||
},
|
||||
configure,
|
||||
|
|
|
@ -39,7 +39,10 @@ use client::{
|
|||
};
|
||||
use clock::ReplicaId;
|
||||
|
||||
use dap::{DapRegistry, client::DebugAdapterClient};
|
||||
use dap::{
|
||||
adapters::{DebugAdapterBinary, TcpArguments},
|
||||
client::DebugAdapterClient,
|
||||
};
|
||||
|
||||
use collections::{BTreeSet, HashMap, HashSet};
|
||||
use debounced_delay::DebouncedDelay;
|
||||
|
@ -94,6 +97,7 @@ use snippet::Snippet;
|
|||
use snippet_provider::SnippetProvider;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
net::Ipv4Addr,
|
||||
ops::Range,
|
||||
path::{Component, Path, PathBuf},
|
||||
pin::pin,
|
||||
|
@ -103,7 +107,7 @@ use std::{
|
|||
};
|
||||
|
||||
use task_store::TaskStore;
|
||||
use terminals::Terminals;
|
||||
use terminals::{SshCommand, Terminals, wrap_for_ssh};
|
||||
use text::{Anchor, BufferId};
|
||||
use toolchain_store::EmptyToolchainStore;
|
||||
use util::{
|
||||
|
@ -165,7 +169,6 @@ pub struct Project {
|
|||
active_entry: Option<ProjectEntryId>,
|
||||
buffer_ordered_messages_tx: mpsc::UnboundedSender<BufferOrderedMessage>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
debug_adapters: Arc<DapRegistry>,
|
||||
dap_store: Entity<DapStore>,
|
||||
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
|
@ -834,7 +837,6 @@ impl Project {
|
|||
node: NodeRuntime,
|
||||
user_store: Entity<UserStore>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
debug_adapters: Arc<DapRegistry>,
|
||||
fs: Arc<dyn Fs>,
|
||||
env: Option<HashMap<String, String>>,
|
||||
cx: &mut App,
|
||||
|
@ -873,6 +875,7 @@ impl Project {
|
|||
languages.clone(),
|
||||
environment.clone(),
|
||||
toolchain_store.read(cx).as_language_toolchain_store(),
|
||||
worktree_store.clone(),
|
||||
breakpoint_store.clone(),
|
||||
cx,
|
||||
)
|
||||
|
@ -955,7 +958,6 @@ impl Project {
|
|||
active_entry: None,
|
||||
snippets,
|
||||
languages,
|
||||
debug_adapters,
|
||||
client,
|
||||
task_store,
|
||||
user_store,
|
||||
|
@ -1065,13 +1067,14 @@ impl Project {
|
|||
cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach();
|
||||
|
||||
let breakpoint_store =
|
||||
cx.new(|_| BreakpointStore::remote(SSH_PROJECT_ID, client.clone().into()));
|
||||
cx.new(|_| BreakpointStore::remote(SSH_PROJECT_ID, ssh_proto.clone()));
|
||||
|
||||
let dap_store = cx.new(|_| {
|
||||
DapStore::new_remote(
|
||||
let dap_store = cx.new(|cx| {
|
||||
DapStore::new_ssh(
|
||||
SSH_PROJECT_ID,
|
||||
client.clone().into(),
|
||||
ssh_proto.clone(),
|
||||
breakpoint_store.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -1113,7 +1116,6 @@ impl Project {
|
|||
active_entry: None,
|
||||
snippets,
|
||||
languages,
|
||||
debug_adapters: Arc::new(DapRegistry::default()),
|
||||
client,
|
||||
task_store,
|
||||
user_store,
|
||||
|
@ -1251,8 +1253,13 @@ impl Project {
|
|||
|
||||
let breakpoint_store =
|
||||
cx.new(|_| BreakpointStore::remote(remote_id, client.clone().into()))?;
|
||||
let dap_store = cx.new(|_cx| {
|
||||
DapStore::new_remote(remote_id, client.clone().into(), breakpoint_store.clone())
|
||||
let dap_store = cx.new(|cx| {
|
||||
DapStore::new_collab(
|
||||
remote_id,
|
||||
client.clone().into(),
|
||||
breakpoint_store.clone(),
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
|
||||
let lsp_store = cx.new(|cx| {
|
||||
|
@ -1337,7 +1344,6 @@ impl Project {
|
|||
collaborators: Default::default(),
|
||||
join_project_response_message_id: response.message_id,
|
||||
languages,
|
||||
debug_adapters: Arc::new(DapRegistry::default()),
|
||||
user_store: user_store.clone(),
|
||||
task_store,
|
||||
snippets,
|
||||
|
@ -1459,49 +1465,68 @@ impl Project {
|
|||
|
||||
pub fn start_debug_session(
|
||||
&mut self,
|
||||
config: DebugTaskDefinition,
|
||||
definition: DebugTaskDefinition,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Entity<Session>>> {
|
||||
let Some(worktree) = self.worktrees(cx).find(|tree| tree.read(cx).is_visible()) else {
|
||||
return Task::ready(Err(anyhow!("Failed to find a worktree")));
|
||||
};
|
||||
|
||||
let Some(adapter) = self.debug_adapters.adapter(&config.adapter) else {
|
||||
return Task::ready(Err(anyhow!("Failed to find a debug adapter")));
|
||||
};
|
||||
|
||||
let user_installed_path = ProjectSettings::get_global(cx)
|
||||
.dap
|
||||
.get(&adapter.name())
|
||||
.and_then(|s| s.binary.as_ref().map(PathBuf::from));
|
||||
let ssh_client = self.ssh_client().clone();
|
||||
|
||||
let result = cx.spawn(async move |this, cx| {
|
||||
let delegate = this.update(cx, |project, cx| {
|
||||
project
|
||||
.dap_store
|
||||
.update(cx, |dap_store, cx| dap_store.delegate(&worktree, cx))
|
||||
})?;
|
||||
|
||||
let task = this.update(cx, |project, cx| {
|
||||
project.dap_store.read(cx).as_local().and_then(|local| {
|
||||
config.locator.is_some().then(|| {
|
||||
local.locate_binary(config.clone(), cx.background_executor().clone())
|
||||
let mut binary = this
|
||||
.update(cx, |this, cx| {
|
||||
this.dap_store.update(cx, |dap_store, cx| {
|
||||
dap_store.get_debug_adapter_binary(definition.clone(), cx)
|
||||
})
|
||||
})
|
||||
})?;
|
||||
let config = if let Some(task) = task {
|
||||
task.await
|
||||
} else {
|
||||
config
|
||||
};
|
||||
let binary = adapter
|
||||
.get_binary(&delegate, &config, user_installed_path, cx)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
if let Some(ssh_client) = ssh_client {
|
||||
let mut ssh_command = ssh_client.update(cx, |ssh, _| {
|
||||
anyhow::Ok(SshCommand {
|
||||
arguments: ssh
|
||||
.ssh_args()
|
||||
.ok_or_else(|| anyhow!("SSH arguments not found"))?,
|
||||
})
|
||||
})??;
|
||||
|
||||
let mut connection = None;
|
||||
if let Some(c) = binary.connection {
|
||||
let local_bind_addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||
let port = dap::transport::TcpTransport::unused_port(local_bind_addr).await?;
|
||||
|
||||
ssh_command.add_port_forwarding(port, c.host.to_string(), c.port);
|
||||
connection = Some(TcpArguments {
|
||||
port: c.port,
|
||||
host: local_bind_addr,
|
||||
timeout: c.timeout,
|
||||
})
|
||||
}
|
||||
|
||||
let (program, args) = wrap_for_ssh(
|
||||
&ssh_command,
|
||||
Some((&binary.command, &binary.arguments)),
|
||||
binary.cwd.as_deref(),
|
||||
binary.envs,
|
||||
None,
|
||||
);
|
||||
|
||||
binary = DebugAdapterBinary {
|
||||
command: program,
|
||||
arguments: args,
|
||||
envs: HashMap::default(),
|
||||
cwd: None,
|
||||
connection,
|
||||
request_args: binary.request_args,
|
||||
}
|
||||
};
|
||||
|
||||
let ret = this
|
||||
.update(cx, |project, cx| {
|
||||
project.dap_store.update(cx, |dap_store, cx| {
|
||||
dap_store.new_session(binary, config, worktree.downgrade(), None, cx)
|
||||
dap_store.new_session(binary, definition, worktree.downgrade(), None, cx)
|
||||
})
|
||||
})?
|
||||
.1
|
||||
|
@ -1520,7 +1545,6 @@ impl Project {
|
|||
|
||||
let fs = Arc::new(RealFs::new(None, cx.background_executor().clone()));
|
||||
let languages = LanguageRegistry::test(cx.background_executor().clone());
|
||||
let debug_adapters = DapRegistry::default().into();
|
||||
let clock = Arc::new(FakeSystemClock::new());
|
||||
let http_client = http_client::FakeHttpClient::with_404_response();
|
||||
let client = cx
|
||||
|
@ -1534,7 +1558,6 @@ impl Project {
|
|||
node_runtime::NodeRuntime::unavailable(),
|
||||
user_store,
|
||||
Arc::new(languages),
|
||||
debug_adapters,
|
||||
fs,
|
||||
None,
|
||||
cx,
|
||||
|
@ -1565,7 +1588,6 @@ impl Project {
|
|||
use clock::FakeSystemClock;
|
||||
|
||||
let languages = LanguageRegistry::test(cx.executor());
|
||||
let debug_adapters = DapRegistry::fake();
|
||||
let clock = Arc::new(FakeSystemClock::new());
|
||||
let http_client = http_client::FakeHttpClient::with_404_response();
|
||||
let client = cx.update(|cx| client::Client::new(clock, http_client.clone(), cx));
|
||||
|
@ -1576,7 +1598,6 @@ impl Project {
|
|||
node_runtime::NodeRuntime::unavailable(),
|
||||
user_store,
|
||||
Arc::new(languages),
|
||||
Arc::new(debug_adapters),
|
||||
fs,
|
||||
None,
|
||||
cx,
|
||||
|
@ -1620,10 +1641,6 @@ impl Project {
|
|||
&self.languages
|
||||
}
|
||||
|
||||
pub fn debug_adapters(&self) -> &Arc<DapRegistry> {
|
||||
&self.debug_adapters
|
||||
}
|
||||
|
||||
pub fn client(&self) -> Arc<Client> {
|
||||
self.client.clone()
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use language::{
|
|||
use lsp::{LanguageServerId, LanguageServerName};
|
||||
use settings::{InvalidSettingsError, TaskKind, parse_json_with_comments};
|
||||
use task::{
|
||||
DebugTaskDefinition, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
|
||||
DebugTaskTemplate, ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates,
|
||||
TaskVariables, VariableName,
|
||||
};
|
||||
use text::{BufferId, Point, ToPoint};
|
||||
|
@ -435,9 +435,9 @@ impl Inventory {
|
|||
.into_iter()
|
||||
.filter_map(|raw_template| match &task_kind {
|
||||
TaskKind::Script => serde_json::from_value::<TaskTemplate>(raw_template).log_err(),
|
||||
TaskKind::Debug => serde_json::from_value::<DebugTaskDefinition>(raw_template)
|
||||
TaskKind::Debug => serde_json::from_value::<DebugTaskTemplate>(raw_template)
|
||||
.log_err()
|
||||
.and_then(|content| content.to_zed_format().log_err()),
|
||||
.map(|content| content.to_zed_format()),
|
||||
});
|
||||
|
||||
let parsed_templates = &mut self.templates_from_settings;
|
||||
|
|
|
@ -9,20 +9,16 @@ use smol::channel::bounded;
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
env::{self},
|
||||
iter,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use task::{Shell, ShellBuilder, SpawnInTerminal};
|
||||
use task::{DEFAULT_REMOTE_SHELL, Shell, ShellBuilder, SpawnInTerminal};
|
||||
use terminal::{
|
||||
TaskState, TaskStatus, Terminal, TerminalBuilder,
|
||||
terminal_settings::{self, TerminalSettings, VenvSettings},
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
// #[cfg(target_os = "macos")]
|
||||
// use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
pub struct Terminals {
|
||||
pub(crate) local_handles: Vec<WeakEntity<terminal::Terminal>>,
|
||||
}
|
||||
|
@ -48,7 +44,15 @@ pub enum TerminalKind {
|
|||
/// SshCommand describes how to connect to a remote server
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SshCommand {
|
||||
arguments: Vec<String>,
|
||||
pub arguments: Vec<String>,
|
||||
}
|
||||
|
||||
impl SshCommand {
|
||||
pub fn add_port_forwarding(&mut self, local_port: u16, host: String, remote_port: u16) {
|
||||
self.arguments.push("-L".to_string());
|
||||
self.arguments
|
||||
.push(format!("{}:{}:{}", local_port, host, remote_port));
|
||||
}
|
||||
}
|
||||
|
||||
impl Project {
|
||||
|
@ -551,7 +555,7 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
fn wrap_for_ssh(
|
||||
pub fn wrap_for_ssh(
|
||||
ssh_command: &SshCommand,
|
||||
command: Option<(&String, &Vec<String>)>,
|
||||
path: Option<&Path>,
|
||||
|
@ -559,9 +563,14 @@ fn wrap_for_ssh(
|
|||
venv_directory: Option<&Path>,
|
||||
) -> (String, Vec<String>) {
|
||||
let to_run = if let Some((command, args)) = command {
|
||||
let command = Cow::Borrowed(command.as_str());
|
||||
// DEFAULT_REMOTE_SHELL is '"${SHELL:-sh}"' so must not be escaped
|
||||
let command: Option<Cow<str>> = if command == DEFAULT_REMOTE_SHELL {
|
||||
Some(command.into())
|
||||
} else {
|
||||
shlex::try_quote(command).ok()
|
||||
};
|
||||
let args = args.iter().filter_map(|arg| shlex::try_quote(arg).ok());
|
||||
iter::once(command).chain(args).join(" ")
|
||||
command.into_iter().chain(args).join(" ")
|
||||
} else {
|
||||
"exec ${SHELL:-sh} -l".to_string()
|
||||
};
|
||||
|
|
|
@ -508,7 +508,6 @@ enum DapStackPresentationHint {
|
|||
Subtle = 2;
|
||||
StackUnknown = 3;
|
||||
}
|
||||
|
||||
message DapModule {
|
||||
DapModuleId id = 1;
|
||||
string name = 2;
|
||||
|
@ -522,9 +521,62 @@ message DapModule {
|
|||
optional string address_range = 10;
|
||||
}
|
||||
|
||||
message DebugTaskDefinition {
|
||||
string adapter = 1;
|
||||
string label = 2;
|
||||
oneof request {
|
||||
DebugLaunchRequest debug_launch_request = 3;
|
||||
DebugAttachRequest debug_attach_request = 4;
|
||||
}
|
||||
optional string initialize_args = 5;
|
||||
optional TcpHost tcp_connection = 6;
|
||||
optional bool stop_on_entry = 7;
|
||||
}
|
||||
|
||||
message TcpHost {
|
||||
optional uint32 port = 1;
|
||||
optional string host = 2;
|
||||
optional uint64 timeout = 3;
|
||||
}
|
||||
|
||||
message DebugLaunchRequest {
|
||||
string program = 1;
|
||||
optional string cwd = 2;
|
||||
repeated string args = 3;
|
||||
}
|
||||
|
||||
message DebugAttachRequest {
|
||||
uint32 process_id = 1;
|
||||
}
|
||||
|
||||
message DapModuleId {
|
||||
oneof id {
|
||||
uint32 number = 1;
|
||||
string string = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message GetDebugAdapterBinary {
|
||||
uint64 project_id = 1;
|
||||
DebugTaskDefinition task = 2;
|
||||
}
|
||||
|
||||
message DebugAdapterBinary {
|
||||
string command = 1;
|
||||
repeated string arguments = 2;
|
||||
map<string, string> envs = 3;
|
||||
optional string cwd = 4;
|
||||
optional TcpHost connection = 5;
|
||||
string configuration = 7;
|
||||
LaunchType launch_type = 8;
|
||||
enum LaunchType {
|
||||
Attach = 0;
|
||||
Launch = 1;
|
||||
}
|
||||
}
|
||||
|
||||
message RunDebugLocator {
|
||||
uint64 project_id = 1;
|
||||
string locator = 2;
|
||||
DebugTaskDefinition task = 3;
|
||||
}
|
||||
|
|
|
@ -380,7 +380,12 @@ message Envelope {
|
|||
StopLanguageServers stop_language_servers = 336;
|
||||
|
||||
LspExtRunnables lsp_ext_runnables = 337;
|
||||
LspExtRunnablesResponse lsp_ext_runnables_response = 338; // current max
|
||||
LspExtRunnablesResponse lsp_ext_runnables_response = 338;
|
||||
|
||||
GetDebugAdapterBinary get_debug_adapter_binary = 339;
|
||||
DebugAdapterBinary debug_adapter_binary = 340;
|
||||
RunDebugLocator run_debug_locator = 341;
|
||||
DebugTaskDefinition debug_task_definition = 342; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
|
|
@ -302,6 +302,10 @@ messages!(
|
|||
(GitDiff, Background),
|
||||
(GitDiffResponse, Background),
|
||||
(GitInit, Background),
|
||||
(GetDebugAdapterBinary, Background),
|
||||
(DebugAdapterBinary, Background),
|
||||
(RunDebugLocator, Background),
|
||||
(DebugTaskDefinition, Background),
|
||||
);
|
||||
|
||||
request_messages!(
|
||||
|
@ -460,6 +464,8 @@ request_messages!(
|
|||
(GitDiff, GitDiffResponse),
|
||||
(GitInit, Ack),
|
||||
(ToggleBreakpoint, Ack),
|
||||
(GetDebugAdapterBinary, DebugAdapterBinary),
|
||||
(RunDebugLocator, DebugTaskDefinition),
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
@ -579,6 +585,8 @@ entity_messages!(
|
|||
GitInit,
|
||||
BreakpointsForFile,
|
||||
ToggleBreakpoint,
|
||||
RunDebugLocator,
|
||||
GetDebugAdapterBinary,
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
|
|
@ -570,7 +570,6 @@ pub async fn open_ssh_project(
|
|||
app_state.node_runtime.clone(),
|
||||
app_state.user_store.clone(),
|
||||
app_state.languages.clone(),
|
||||
app_state.debug_adapters.clone(),
|
||||
app_state.fs.clone(),
|
||||
None,
|
||||
cx,
|
||||
|
|
|
@ -29,7 +29,7 @@ backtrace = "0.3"
|
|||
chrono.workspace = true
|
||||
clap.workspace = true
|
||||
client.workspace = true
|
||||
dap.workspace = true
|
||||
dap_adapters.workspace = true
|
||||
env_logger.workspace = true
|
||||
extension.workspace = true
|
||||
extension_host.workspace = true
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ::proto::{FromProto, ToProto};
|
||||
use anyhow::{Result, anyhow};
|
||||
use dap::DapRegistry;
|
||||
|
||||
use extension::ExtensionHostProxy;
|
||||
use extension_host::headless_host::HeadlessExtensionStore;
|
||||
use fs::Fs;
|
||||
|
@ -41,6 +41,7 @@ pub struct HeadlessProject {
|
|||
pub buffer_store: Entity<BufferStore>,
|
||||
pub lsp_store: Entity<LspStore>,
|
||||
pub task_store: Entity<TaskStore>,
|
||||
pub dap_store: Entity<DapStore>,
|
||||
pub settings_observer: Entity<SettingsObserver>,
|
||||
pub next_entry_id: Arc<AtomicUsize>,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
|
@ -54,7 +55,6 @@ pub struct HeadlessAppState {
|
|||
pub http_client: Arc<dyn HttpClient>,
|
||||
pub node_runtime: NodeRuntime,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
pub debug_adapters: Arc<DapRegistry>,
|
||||
pub extension_host_proxy: Arc<ExtensionHostProxy>,
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,6 @@ impl HeadlessProject {
|
|||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
debug_adapters: _debug_adapters,
|
||||
extension_host_proxy: proxy,
|
||||
}: HeadlessAppState,
|
||||
cx: &mut Context<Self>,
|
||||
|
@ -114,6 +113,7 @@ impl HeadlessProject {
|
|||
languages.clone(),
|
||||
environment.clone(),
|
||||
toolchain_store.read(cx).as_language_toolchain_store(),
|
||||
worktree_store.clone(),
|
||||
breakpoint_store.clone(),
|
||||
cx,
|
||||
)
|
||||
|
@ -258,6 +258,7 @@ impl HeadlessProject {
|
|||
buffer_store,
|
||||
lsp_store,
|
||||
task_store,
|
||||
dap_store,
|
||||
next_entry_id: Default::default(),
|
||||
languages,
|
||||
extensions,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use crate::headless_project::HeadlessProject;
|
||||
use client::{Client, UserStore};
|
||||
use clock::FakeSystemClock;
|
||||
use dap::DapRegistry;
|
||||
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{AppContext as _, Entity, SemanticVersion, TestAppContext};
|
||||
|
@ -1566,7 +1566,6 @@ pub async fn init_test(
|
|||
let http_client = Arc::new(BlockedHttpClient);
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
let languages = Arc::new(LanguageRegistry::new(cx.executor()));
|
||||
let debug_adapters = DapRegistry::default().into();
|
||||
let proxy = Arc::new(ExtensionHostProxy::new());
|
||||
server_cx.update(HeadlessProject::init);
|
||||
let headless = server_cx.new(|cx| {
|
||||
|
@ -1579,7 +1578,6 @@ pub async fn init_test(
|
|||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
debug_adapters,
|
||||
extension_host_proxy: proxy,
|
||||
},
|
||||
cx,
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::headless_project::HeadlessAppState;
|
|||
use anyhow::{Context as _, Result, anyhow};
|
||||
use chrono::Utc;
|
||||
use client::{ProxySettings, telemetry};
|
||||
use dap::DapRegistry;
|
||||
|
||||
use extension::ExtensionHostProxy;
|
||||
use fs::{Fs, RealFs};
|
||||
use futures::channel::mpsc;
|
||||
|
@ -441,6 +441,7 @@ pub fn execute_run(
|
|||
|
||||
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
|
||||
git_hosting_providers::init(cx);
|
||||
dap_adapters::init(cx);
|
||||
|
||||
extension::init(cx);
|
||||
let extension_host_proxy = ExtensionHostProxy::global(cx);
|
||||
|
@ -472,7 +473,6 @@ pub fn execute_run(
|
|||
let mut languages = LanguageRegistry::new(cx.background_executor().clone());
|
||||
languages.set_language_server_download_dir(paths::languages_dir().clone());
|
||||
let languages = Arc::new(languages);
|
||||
let debug_adapters = DapRegistry::default().into();
|
||||
|
||||
HeadlessProject::new(
|
||||
HeadlessAppState {
|
||||
|
@ -481,7 +481,6 @@ pub fn execute_run(
|
|||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
debug_adapters,
|
||||
extension_host_proxy,
|
||||
},
|
||||
cx,
|
||||
|
|
|
@ -17,11 +17,11 @@ workspace = true
|
|||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
collections.workspace = true
|
||||
dap-types.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
hex.workspace = true
|
||||
parking_lot.workspace = true
|
||||
proto.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
use dap_types::StartDebuggingRequestArguments;
|
||||
use anyhow::Result;
|
||||
use schemars::{JsonSchema, r#gen::SchemaSettings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use util::ResultExt;
|
||||
use std::{net::Ipv4Addr, path::Path};
|
||||
|
||||
use crate::{TaskTemplate, TaskTemplates, TaskType, task_template::DebugArgs};
|
||||
|
||||
impl Default for DebugConnectionType {
|
||||
fn default() -> Self {
|
||||
DebugConnectionType::TCP(TCPHost::default())
|
||||
}
|
||||
}
|
||||
use crate::{TaskTemplate, TaskType, task_template::DebugArgs};
|
||||
|
||||
/// Represents the host information of the debug adapter
|
||||
#[derive(Default, Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||
pub struct TCPHost {
|
||||
pub struct TcpArgumentsTemplate {
|
||||
/// The port that the debug adapter is listening on
|
||||
///
|
||||
/// Default: We will try to find an open port
|
||||
|
@ -30,23 +23,39 @@ pub struct TCPHost {
|
|||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
||||
impl TCPHost {
|
||||
impl TcpArgumentsTemplate {
|
||||
/// Get the host or fallback to the default host
|
||||
pub fn host(&self) -> Ipv4Addr {
|
||||
self.host.unwrap_or_else(|| Ipv4Addr::new(127, 0, 0, 1))
|
||||
}
|
||||
|
||||
pub fn from_proto(proto: proto::TcpHost) -> Result<Self> {
|
||||
Ok(Self {
|
||||
port: proto.port.map(|p| p.try_into()).transpose()?,
|
||||
host: proto.host.map(|h| h.parse()).transpose()?,
|
||||
timeout: proto.timeout,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::TcpHost {
|
||||
proto::TcpHost {
|
||||
port: self.port.map(|p| p.into()),
|
||||
host: self.host.map(|h| h.to_string()),
|
||||
timeout: self.timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the attach request information of the debug adapter
|
||||
#[derive(Default, Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||
pub struct AttachConfig {
|
||||
pub struct AttachRequest {
|
||||
/// The processId to attach to, if left empty we will show a process picker
|
||||
pub process_id: Option<u32>,
|
||||
}
|
||||
|
||||
/// Represents the launch request information of the debug adapter
|
||||
#[derive(Deserialize, Serialize, Default, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||
pub struct LaunchConfig {
|
||||
pub struct LaunchRequest {
|
||||
/// The program that you trying to debug
|
||||
pub program: String,
|
||||
/// The current working directory of your project
|
||||
|
@ -59,47 +68,26 @@ pub struct LaunchConfig {
|
|||
/// Represents the type that will determine which request to call on the debug adapter
|
||||
#[derive(Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase", untagged)]
|
||||
pub enum DebugRequestType {
|
||||
pub enum DebugRequest {
|
||||
/// Call the `launch` request on the debug adapter
|
||||
Launch(LaunchConfig),
|
||||
Launch(LaunchRequest),
|
||||
/// Call the `attach` request on the debug adapter
|
||||
Attach(AttachConfig),
|
||||
Attach(AttachRequest),
|
||||
}
|
||||
|
||||
impl From<LaunchConfig> for DebugRequestType {
|
||||
fn from(launch_config: LaunchConfig) -> Self {
|
||||
DebugRequestType::Launch(launch_config)
|
||||
impl From<LaunchRequest> for DebugRequest {
|
||||
fn from(launch_config: LaunchRequest) -> Self {
|
||||
DebugRequest::Launch(launch_config)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AttachConfig> for DebugRequestType {
|
||||
fn from(attach_config: AttachConfig) -> Self {
|
||||
DebugRequestType::Attach(attach_config)
|
||||
}
|
||||
}
|
||||
/// Represents a request for starting the debugger.
|
||||
/// Contrary to `DebugRequestType`, `DebugRequestDisposition` is not Serializable.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum DebugRequestDisposition {
|
||||
/// Debug session configured by the user.
|
||||
UserConfigured(DebugRequestType),
|
||||
/// Debug session configured by the debug adapter
|
||||
ReverseRequest(StartDebuggingRequestArguments),
|
||||
}
|
||||
|
||||
impl DebugRequestDisposition {
|
||||
/// Get the current working directory from request if it's a launch request and exits
|
||||
pub fn cwd(&self) -> Option<PathBuf> {
|
||||
match self {
|
||||
Self::UserConfigured(DebugRequestType::Launch(launch_config)) => {
|
||||
launch_config.cwd.clone()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
impl From<AttachRequest> for DebugRequest {
|
||||
fn from(attach_config: AttachRequest) -> Self {
|
||||
DebugRequest::Attach(attach_config)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TaskTemplate> for DebugTaskDefinition {
|
||||
impl TryFrom<TaskTemplate> for DebugTaskTemplate {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: TaskTemplate) -> Result<Self, Self::Error> {
|
||||
|
@ -108,40 +96,40 @@ impl TryFrom<TaskTemplate> for DebugTaskDefinition {
|
|||
};
|
||||
|
||||
let request = match debug_args.request {
|
||||
crate::DebugArgsRequest::Launch => DebugRequestType::Launch(LaunchConfig {
|
||||
crate::DebugArgsRequest::Launch => DebugRequest::Launch(LaunchRequest {
|
||||
program: value.command,
|
||||
cwd: value.cwd.map(PathBuf::from),
|
||||
args: value.args,
|
||||
}),
|
||||
crate::DebugArgsRequest::Attach(attach_config) => {
|
||||
DebugRequestType::Attach(attach_config)
|
||||
}
|
||||
crate::DebugArgsRequest::Attach(attach_config) => DebugRequest::Attach(attach_config),
|
||||
};
|
||||
|
||||
Ok(DebugTaskDefinition {
|
||||
adapter: debug_args.adapter,
|
||||
request,
|
||||
label: value.label,
|
||||
initialize_args: debug_args.initialize_args,
|
||||
tcp_connection: debug_args.tcp_connection,
|
||||
Ok(DebugTaskTemplate {
|
||||
locator: debug_args.locator,
|
||||
stop_on_entry: debug_args.stop_on_entry,
|
||||
definition: DebugTaskDefinition {
|
||||
adapter: debug_args.adapter,
|
||||
request,
|
||||
label: value.label,
|
||||
initialize_args: debug_args.initialize_args,
|
||||
tcp_connection: debug_args.tcp_connection,
|
||||
stop_on_entry: debug_args.stop_on_entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugTaskDefinition {
|
||||
impl DebugTaskTemplate {
|
||||
/// Translate from debug definition to a task template
|
||||
pub fn to_zed_format(self) -> anyhow::Result<TaskTemplate> {
|
||||
let (command, cwd, request) = match self.request {
|
||||
DebugRequestType::Launch(launch_config) => (
|
||||
pub fn to_zed_format(self) -> TaskTemplate {
|
||||
let (command, cwd, request) = match self.definition.request {
|
||||
DebugRequest::Launch(launch_config) => (
|
||||
launch_config.program,
|
||||
launch_config
|
||||
.cwd
|
||||
.map(|cwd| cwd.to_string_lossy().to_string()),
|
||||
crate::task_template::DebugArgsRequest::Launch,
|
||||
),
|
||||
DebugRequestType::Attach(attach_config) => (
|
||||
DebugRequest::Attach(attach_config) => (
|
||||
"".to_owned(),
|
||||
None,
|
||||
crate::task_template::DebugArgsRequest::Attach(attach_config),
|
||||
|
@ -149,34 +137,33 @@ impl DebugTaskDefinition {
|
|||
};
|
||||
|
||||
let task_type = TaskType::Debug(DebugArgs {
|
||||
adapter: self.adapter,
|
||||
adapter: self.definition.adapter,
|
||||
request,
|
||||
initialize_args: self.initialize_args,
|
||||
initialize_args: self.definition.initialize_args,
|
||||
locator: self.locator,
|
||||
tcp_connection: self.tcp_connection,
|
||||
stop_on_entry: self.stop_on_entry,
|
||||
tcp_connection: self.definition.tcp_connection,
|
||||
stop_on_entry: self.definition.stop_on_entry,
|
||||
});
|
||||
|
||||
let label = self.label.clone();
|
||||
let label = self.definition.label.clone();
|
||||
|
||||
Ok(TaskTemplate {
|
||||
TaskTemplate {
|
||||
label,
|
||||
command,
|
||||
args: vec![],
|
||||
task_type,
|
||||
cwd,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Represents the type of the debugger adapter connection
|
||||
|
||||
#[derive(Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase", tag = "connection")]
|
||||
pub enum DebugConnectionType {
|
||||
/// Connect to the debug adapter via TCP
|
||||
TCP(TCPHost),
|
||||
/// Connect to the debug adapter via STDIO
|
||||
STDIO,
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct DebugTaskTemplate {
|
||||
pub locator: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub definition: DebugTaskDefinition,
|
||||
}
|
||||
|
||||
/// This struct represent a user created debug task
|
||||
|
@ -187,7 +174,7 @@ pub struct DebugTaskDefinition {
|
|||
pub adapter: String,
|
||||
/// The type of request that should be called on the debug adapter
|
||||
#[serde(flatten)]
|
||||
pub request: DebugRequestType,
|
||||
pub request: DebugRequest,
|
||||
/// Name of the debug task
|
||||
pub label: String,
|
||||
/// Additional initialization arguments to be sent on DAP initialization
|
||||
|
@ -197,18 +184,83 @@ pub struct DebugTaskDefinition {
|
|||
/// If provided, this will be used to connect to the debug adapter instead of
|
||||
/// spawning a new process. This is useful for connecting to a debug adapter
|
||||
/// that is already running or is started by another process.
|
||||
pub tcp_connection: Option<TCPHost>,
|
||||
/// Locator to use
|
||||
/// -- cargo
|
||||
pub locator: Option<String>,
|
||||
pub tcp_connection: Option<TcpArgumentsTemplate>,
|
||||
/// Whether to tell the debug adapter to stop on entry
|
||||
pub stop_on_entry: Option<bool>,
|
||||
}
|
||||
|
||||
impl DebugTaskDefinition {
|
||||
pub fn cwd(&self) -> Option<&Path> {
|
||||
if let DebugRequest::Launch(config) = &self.request {
|
||||
config.cwd.as_deref()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn to_proto(&self) -> proto::DebugTaskDefinition {
|
||||
proto::DebugTaskDefinition {
|
||||
adapter: self.adapter.clone(),
|
||||
request: Some(match &self.request {
|
||||
DebugRequest::Launch(config) => {
|
||||
proto::debug_task_definition::Request::DebugLaunchRequest(
|
||||
proto::DebugLaunchRequest {
|
||||
program: config.program.clone(),
|
||||
cwd: config.cwd.as_ref().map(|c| c.to_string_lossy().to_string()),
|
||||
args: config.args.clone(),
|
||||
},
|
||||
)
|
||||
}
|
||||
DebugRequest::Attach(attach_request) => {
|
||||
proto::debug_task_definition::Request::DebugAttachRequest(
|
||||
proto::DebugAttachRequest {
|
||||
process_id: attach_request.process_id.unwrap_or_default(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
label: self.label.clone(),
|
||||
initialize_args: self.initialize_args.as_ref().map(|v| v.to_string()),
|
||||
tcp_connection: self.tcp_connection.as_ref().map(|t| t.to_proto()),
|
||||
stop_on_entry: self.stop_on_entry,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_proto(proto: proto::DebugTaskDefinition) -> Result<Self> {
|
||||
let request = proto
|
||||
.request
|
||||
.ok_or_else(|| anyhow::anyhow!("request is required"))?;
|
||||
Ok(Self {
|
||||
label: proto.label,
|
||||
initialize_args: proto.initialize_args.map(|v| v.into()),
|
||||
tcp_connection: proto
|
||||
.tcp_connection
|
||||
.map(TcpArgumentsTemplate::from_proto)
|
||||
.transpose()?,
|
||||
stop_on_entry: proto.stop_on_entry,
|
||||
adapter: proto.adapter.clone(),
|
||||
request: match request {
|
||||
proto::debug_task_definition::Request::DebugAttachRequest(config) => {
|
||||
DebugRequest::Attach(AttachRequest {
|
||||
process_id: Some(config.process_id),
|
||||
})
|
||||
}
|
||||
|
||||
proto::debug_task_definition::Request::DebugLaunchRequest(config) => {
|
||||
DebugRequest::Launch(LaunchRequest {
|
||||
program: config.program,
|
||||
cwd: config.cwd.map(|cwd| cwd.into()),
|
||||
args: config.args,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A group of Debug Tasks defined in a JSON file.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(transparent)]
|
||||
pub struct DebugTaskFile(pub Vec<DebugTaskDefinition>);
|
||||
pub struct DebugTaskFile(pub Vec<DebugTaskTemplate>);
|
||||
|
||||
impl DebugTaskFile {
|
||||
/// Generates JSON schema of Tasks JSON template format.
|
||||
|
@ -222,31 +274,17 @@ impl DebugTaskFile {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<DebugTaskFile> for TaskTemplates {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: DebugTaskFile) -> Result<Self, Self::Error> {
|
||||
let templates = value
|
||||
.0
|
||||
.into_iter()
|
||||
.filter_map(|debug_definition| debug_definition.to_zed_format().log_err())
|
||||
.collect();
|
||||
|
||||
Ok(Self(templates))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{DebugRequestType, LaunchConfig};
|
||||
use crate::{DebugRequest, LaunchRequest};
|
||||
|
||||
#[test]
|
||||
fn test_can_deserialize_non_attach_task() {
|
||||
let deserialized: DebugRequestType =
|
||||
let deserialized: DebugRequest =
|
||||
serde_json::from_str(r#"{"program": "cafebabe"}"#).unwrap();
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
DebugRequestType::Launch(LaunchConfig {
|
||||
DebugRequest::Launch(LaunchRequest {
|
||||
program: "cafebabe".to_owned(),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//! Baseline interface of Tasks in Zed: all tasks in Zed are intended to use those for implementing their own logic.
|
||||
#![deny(missing_docs)]
|
||||
|
||||
mod debug_format;
|
||||
mod serde_helpers;
|
||||
|
@ -16,8 +15,8 @@ use std::path::PathBuf;
|
|||
use std::str::FromStr;
|
||||
|
||||
pub use debug_format::{
|
||||
AttachConfig, DebugConnectionType, DebugRequestDisposition, DebugRequestType,
|
||||
DebugTaskDefinition, DebugTaskFile, LaunchConfig, TCPHost,
|
||||
AttachRequest, DebugRequest, DebugTaskDefinition, DebugTaskFile, DebugTaskTemplate,
|
||||
LaunchRequest, TcpArgumentsTemplate,
|
||||
};
|
||||
pub use task_template::{
|
||||
DebugArgs, DebugArgsRequest, HideStrategy, RevealStrategy, TaskModal, TaskTemplate,
|
||||
|
@ -104,7 +103,7 @@ impl ResolvedTask {
|
|||
}
|
||||
|
||||
/// Get the configuration for the debug adapter that should be used for this task.
|
||||
pub fn resolved_debug_adapter_config(&self) -> Option<DebugTaskDefinition> {
|
||||
pub fn resolved_debug_adapter_config(&self) -> Option<DebugTaskTemplate> {
|
||||
match self.original_task.task_type.clone() {
|
||||
TaskType::Debug(debug_args) if self.resolved.is_some() => {
|
||||
let resolved = self
|
||||
|
@ -127,25 +126,27 @@ impl ResolvedTask {
|
|||
})
|
||||
.collect();
|
||||
|
||||
Some(DebugTaskDefinition {
|
||||
label: resolved.label.clone(),
|
||||
adapter: debug_args.adapter.clone(),
|
||||
request: match debug_args.request {
|
||||
crate::task_template::DebugArgsRequest::Launch => {
|
||||
DebugRequestType::Launch(LaunchConfig {
|
||||
program: resolved.command.clone(),
|
||||
cwd: resolved.cwd.clone(),
|
||||
args,
|
||||
})
|
||||
}
|
||||
crate::task_template::DebugArgsRequest::Attach(attach_config) => {
|
||||
DebugRequestType::Attach(attach_config)
|
||||
}
|
||||
},
|
||||
initialize_args: debug_args.initialize_args,
|
||||
tcp_connection: debug_args.tcp_connection,
|
||||
Some(DebugTaskTemplate {
|
||||
locator: debug_args.locator.clone(),
|
||||
stop_on_entry: debug_args.stop_on_entry,
|
||||
definition: DebugTaskDefinition {
|
||||
label: resolved.label.clone(),
|
||||
adapter: debug_args.adapter.clone(),
|
||||
request: match debug_args.request {
|
||||
crate::task_template::DebugArgsRequest::Launch => {
|
||||
DebugRequest::Launch(LaunchRequest {
|
||||
program: resolved.command.clone(),
|
||||
cwd: resolved.cwd.clone(),
|
||||
args,
|
||||
})
|
||||
}
|
||||
crate::task_template::DebugArgsRequest::Attach(attach_config) => {
|
||||
DebugRequest::Attach(attach_config)
|
||||
}
|
||||
},
|
||||
initialize_args: debug_args.initialize_args,
|
||||
tcp_connection: debug_args.tcp_connection,
|
||||
stop_on_entry: debug_args.stop_on_entry,
|
||||
},
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
|
@ -366,6 +367,8 @@ pub struct ShellBuilder {
|
|||
args: Vec<String>,
|
||||
}
|
||||
|
||||
pub static DEFAULT_REMOTE_SHELL: &str = "\"${SHELL:-sh}\"";
|
||||
|
||||
impl ShellBuilder {
|
||||
/// Create a new ShellBuilder as configured.
|
||||
pub fn new(is_local: bool, shell: &Shell) -> Self {
|
||||
|
@ -374,7 +377,7 @@ impl ShellBuilder {
|
|||
if is_local {
|
||||
(Self::system_shell(), Vec::new())
|
||||
} else {
|
||||
("\"${SHELL:-sh}\"".to_string(), Vec::new())
|
||||
(DEFAULT_REMOTE_SHELL.to_string(), Vec::new())
|
||||
}
|
||||
}
|
||||
Shell::Program(shell) => (shell.clone(), Vec::new()),
|
||||
|
|
|
@ -7,8 +7,9 @@ use std::path::PathBuf;
|
|||
use util::serde::default_true;
|
||||
use util::{ResultExt, truncate_and_remove_front};
|
||||
|
||||
use crate::debug_format::TcpArgumentsTemplate;
|
||||
use crate::{
|
||||
AttachConfig, ResolvedTask, RevealTarget, Shell, SpawnInTerminal, TCPHost, TaskContext, TaskId,
|
||||
AttachRequest, ResolvedTask, RevealTarget, Shell, SpawnInTerminal, TaskContext, TaskId,
|
||||
VariableName, ZED_VARIABLE_NAME_PREFIX,
|
||||
serde_helpers::{non_empty_string_vec, non_empty_string_vec_json_schema},
|
||||
};
|
||||
|
@ -83,7 +84,7 @@ pub enum DebugArgsRequest {
|
|||
/// launch (program, cwd) are stored in TaskTemplate as (command, cwd)
|
||||
Launch,
|
||||
/// Attach
|
||||
Attach(AttachConfig),
|
||||
Attach(AttachRequest),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Eq, PartialEq, Clone, Debug)]
|
||||
|
@ -94,7 +95,7 @@ pub struct DebugArgs {
|
|||
/// Adapter choice
|
||||
pub adapter: String,
|
||||
/// TCP connection to make with debug adapter
|
||||
pub tcp_connection: Option<TCPHost>,
|
||||
pub tcp_connection: Option<TcpArgumentsTemplate>,
|
||||
/// Args to send to debug adapter
|
||||
pub initialize_args: Option<serde_json::value::Value>,
|
||||
/// the locator to use
|
||||
|
|
|
@ -11,7 +11,7 @@ use itertools::Itertools;
|
|||
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
|
||||
use project::{TaskSourceKind, task_store::TaskStore};
|
||||
use task::{
|
||||
DebugRequestType, DebugTaskDefinition, ResolvedTask, RevealTarget, TaskContext, TaskModal,
|
||||
DebugRequest, DebugTaskDefinition, ResolvedTask, RevealTarget, TaskContext, TaskModal,
|
||||
TaskTemplate, TaskType,
|
||||
};
|
||||
use ui::{
|
||||
|
@ -21,7 +21,7 @@ use ui::{
|
|||
};
|
||||
|
||||
use util::{ResultExt, truncate_and_trailoff};
|
||||
use workspace::{ModalView, Workspace, tasks::schedule_resolved_task};
|
||||
use workspace::{ModalView, Workspace};
|
||||
pub use zed_actions::{Rerun, Spawn};
|
||||
|
||||
/// A modal used to spawn new tasks.
|
||||
|
@ -334,7 +334,7 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
fn confirm(
|
||||
&mut self,
|
||||
omit_history_entry: bool,
|
||||
_: &mut Window,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<picker::Picker<Self>>,
|
||||
) {
|
||||
let current_match_index = self.selected_index();
|
||||
|
@ -360,17 +360,14 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
}
|
||||
|
||||
match task.task_type() {
|
||||
TaskType::Debug(config) if config.locator.is_none() => {
|
||||
let Some(config): Option<DebugTaskDefinition> =
|
||||
task.resolved_debug_adapter_config()
|
||||
else {
|
||||
TaskType::Debug(_) => {
|
||||
let Some(config) = task.resolved_debug_adapter_config() else {
|
||||
return;
|
||||
};
|
||||
let config = config.definition;
|
||||
|
||||
match &config.request {
|
||||
DebugRequestType::Attach(attach_config)
|
||||
if attach_config.process_id.is_none() =>
|
||||
{
|
||||
DebugRequest::Attach(attach_config) if attach_config.process_id.is_none() => {
|
||||
cx.emit(ShowAttachModal {
|
||||
debug_config: config.clone(),
|
||||
});
|
||||
|
@ -379,24 +376,20 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
_ => {
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
project
|
||||
.start_debug_session(config, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
workspace.schedule_debug_task(task, window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
TaskType::Script => {
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
schedule_resolved_task(
|
||||
workspace,
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
task,
|
||||
omit_history_entry,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})
|
||||
|
@ -566,7 +559,7 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
fn confirm_input(
|
||||
&mut self,
|
||||
omit_history_entry: bool,
|
||||
_: &mut Window,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) {
|
||||
let Some((task_source_kind, mut task)) = self.spawn_oneshot() else {
|
||||
|
@ -584,36 +577,17 @@ impl PickerDelegate for TasksModalDelegate {
|
|||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
match task.task_type() {
|
||||
TaskType::Script => schedule_resolved_task(
|
||||
workspace,
|
||||
TaskType::Script => workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
task,
|
||||
omit_history_entry,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
// todo(debugger): Should create a schedule_resolved_debug_task function
|
||||
// This would allow users to access to debug history and other issues
|
||||
TaskType::Debug(debug_args) => {
|
||||
let Some(debug_config) = task.resolved_debug_adapter_config() else {
|
||||
// todo(debugger) log an error, this should never happen
|
||||
return;
|
||||
};
|
||||
|
||||
if debug_args.locator.is_some() {
|
||||
schedule_resolved_task(
|
||||
workspace,
|
||||
task_source_kind,
|
||||
task,
|
||||
omit_history_entry,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
project
|
||||
.start_debug_session(debug_config, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
TaskType::Debug(_) => {
|
||||
workspace.schedule_debug_task(task, window, cx);
|
||||
}
|
||||
};
|
||||
})
|
||||
|
|
|
@ -8,8 +8,7 @@ use project::{Location, TaskContexts, TaskSourceKind, Worktree};
|
|||
use task::{
|
||||
RevealTarget, TaskContext, TaskId, TaskModal, TaskTemplate, TaskVariables, VariableName,
|
||||
};
|
||||
use workspace::tasks::schedule_task;
|
||||
use workspace::{Workspace, tasks::schedule_resolved_task};
|
||||
use workspace::Workspace;
|
||||
|
||||
mod modal;
|
||||
|
||||
|
@ -50,15 +49,15 @@ pub fn init(cx: &mut App) {
|
|||
let task_contexts = task_contexts.await;
|
||||
let default_context = TaskContext::default();
|
||||
workspace
|
||||
.update_in(cx, |workspace, _, cx| {
|
||||
schedule_task(
|
||||
workspace,
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.schedule_task(
|
||||
task_source_kind,
|
||||
&original_task,
|
||||
task_contexts
|
||||
.active_context()
|
||||
.unwrap_or(&default_context),
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
@ -75,11 +74,11 @@ pub fn init(cx: &mut App) {
|
|||
}
|
||||
}
|
||||
|
||||
schedule_resolved_task(
|
||||
workspace,
|
||||
workspace.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
last_scheduled_task,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
@ -217,7 +216,7 @@ where
|
|||
})?;
|
||||
|
||||
let did_spawn = workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
let default_context = TaskContext::default();
|
||||
let active_context = task_contexts.active_context().unwrap_or(&default_context);
|
||||
|
||||
|
@ -228,12 +227,12 @@ where
|
|||
target_task.reveal_target = target_override;
|
||||
}
|
||||
}
|
||||
schedule_task(
|
||||
workspace,
|
||||
workspace.schedule_task(
|
||||
task_source_kind.clone(),
|
||||
target_task,
|
||||
active_context,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
true
|
||||
|
|
|
@ -46,13 +46,14 @@ use smol::channel::{Receiver, Sender};
|
|||
use task::{HideStrategy, Shell, TaskId};
|
||||
use terminal_settings::{AlternateScroll, CursorShape, TerminalSettings};
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use util::{paths::home_dir, truncate_and_trailoff};
|
||||
use util::{ResultExt, paths::home_dir, truncate_and_trailoff};
|
||||
|
||||
use std::{
|
||||
cmp::{self, min},
|
||||
fmt::Display,
|
||||
ops::{Deref, Index, RangeInclusive},
|
||||
path::PathBuf,
|
||||
process::ExitStatus,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Duration,
|
||||
};
|
||||
|
@ -109,7 +110,6 @@ pub enum Event {
|
|||
SelectionsChanged,
|
||||
NewNavigationTarget(Option<MaybeNavigationTarget>),
|
||||
Open(MaybeNavigationTarget),
|
||||
TaskLocatorReady { task_id: TaskId, success: bool },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -351,7 +351,7 @@ impl TerminalBuilder {
|
|||
max_scroll_history_lines: Option<usize>,
|
||||
is_ssh_terminal: bool,
|
||||
window: AnyWindowHandle,
|
||||
completion_tx: Sender<()>,
|
||||
completion_tx: Sender<Option<ExitStatus>>,
|
||||
debug_terminal: bool,
|
||||
cx: &App,
|
||||
) -> Result<TerminalBuilder> {
|
||||
|
@ -639,7 +639,7 @@ pub enum SelectionPhase {
|
|||
|
||||
pub struct Terminal {
|
||||
pty_tx: Notifier,
|
||||
completion_tx: Sender<()>,
|
||||
completion_tx: Sender<Option<ExitStatus>>,
|
||||
term: Arc<FairMutex<Term<ZedListener>>>,
|
||||
term_config: Config,
|
||||
events: VecDeque<InternalEvent>,
|
||||
|
@ -670,7 +670,7 @@ pub struct TaskState {
|
|||
pub label: String,
|
||||
pub command_label: String,
|
||||
pub status: TaskStatus,
|
||||
pub completion_rx: Receiver<()>,
|
||||
pub completion_rx: Receiver<Option<ExitStatus>>,
|
||||
pub hide: HideStrategy,
|
||||
pub show_summary: bool,
|
||||
pub show_command: bool,
|
||||
|
@ -1859,20 +1859,30 @@ impl Terminal {
|
|||
self.debug_terminal
|
||||
}
|
||||
|
||||
pub fn wait_for_completed_task(&self, cx: &App) -> Task<()> {
|
||||
pub fn wait_for_completed_task(&self, cx: &App) -> Task<Option<ExitStatus>> {
|
||||
if let Some(task) = self.task() {
|
||||
if task.status == TaskStatus::Running {
|
||||
let completion_receiver = task.completion_rx.clone();
|
||||
return cx.spawn(async move |_| {
|
||||
let _ = completion_receiver.recv().await;
|
||||
});
|
||||
return cx
|
||||
.spawn(async move |_| completion_receiver.recv().await.log_err().flatten());
|
||||
}
|
||||
}
|
||||
Task::ready(())
|
||||
Task::ready(None)
|
||||
}
|
||||
|
||||
fn register_task_finished(&mut self, error_code: Option<i32>, cx: &mut Context<Terminal>) {
|
||||
self.completion_tx.try_send(()).ok();
|
||||
let e: Option<ExitStatus> = error_code.map(|code| {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
return std::os::unix::process::ExitStatusExt::from_raw(code);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
return std::os::windows::process::ExitStatusExt::from_raw(code as u32);
|
||||
}
|
||||
});
|
||||
|
||||
self.completion_tx.try_send(e).ok();
|
||||
let task = match &mut self.task {
|
||||
Some(task) => task,
|
||||
None => {
|
||||
|
@ -1911,11 +1921,6 @@ impl Terminal {
|
|||
unsafe { append_text_to_term(&mut self.term.lock(), &lines_to_show) };
|
||||
}
|
||||
|
||||
cx.emit(Event::TaskLocatorReady {
|
||||
task_id: task.id.clone(),
|
||||
success: finished_successfully,
|
||||
});
|
||||
|
||||
match task.hide {
|
||||
HideStrategy::Never => {}
|
||||
HideStrategy::Always => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cmp, ops::ControlFlow, path::PathBuf, sync::Arc, time::Duration};
|
||||
use std::{cmp, ops::ControlFlow, path::PathBuf, process::ExitStatus, sync::Arc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
TerminalView, default_working_directory,
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||
use breadcrumbs::Breadcrumbs;
|
||||
use collections::HashMap;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use futures::future::join_all;
|
||||
use futures::{channel::oneshot, future::join_all};
|
||||
use gpui::{
|
||||
Action, AnyView, App, AsyncApp, AsyncWindowContext, Context, Corner, Entity, EventEmitter,
|
||||
ExternalPaths, FocusHandle, Focusable, IntoElement, ParentElement, Pixels, Render, Styled,
|
||||
|
@ -279,17 +279,9 @@ impl TerminalPanel {
|
|||
};
|
||||
|
||||
if let Some(workspace) = workspace.upgrade() {
|
||||
terminal_panel
|
||||
.update_in(&mut cx, |_, window, cx| {
|
||||
cx.subscribe_in(&workspace, window, |terminal_panel, _, e, window, cx| {
|
||||
if let workspace::Event::SpawnTask {
|
||||
action: spawn_in_terminal,
|
||||
} = e
|
||||
{
|
||||
terminal_panel.spawn_task(spawn_in_terminal, window, cx);
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
workspace
|
||||
.update(&mut cx, |workspace, _| {
|
||||
workspace.set_terminal_provider(TerminalProvider(terminal_panel.clone()))
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
@ -486,12 +478,17 @@ impl TerminalPanel {
|
|||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn spawn_task(&mut self, task: &SpawnInTerminal, window: &mut Window, cx: &mut Context<Self>) {
|
||||
fn spawn_task(
|
||||
&mut self,
|
||||
task: &SpawnInTerminal,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<Entity<Terminal>>> {
|
||||
let Ok(is_local) = self
|
||||
.workspace
|
||||
.update(cx, |workspace, cx| workspace.project().read(cx).is_local())
|
||||
else {
|
||||
return;
|
||||
return Task::ready(Err(anyhow!("Project is not local")));
|
||||
};
|
||||
|
||||
let builder = ShellBuilder::new(is_local, &task.shell);
|
||||
|
@ -506,58 +503,53 @@ impl TerminalPanel {
|
|||
};
|
||||
|
||||
if task.allow_concurrent_runs && task.use_new_terminal {
|
||||
self.spawn_in_new_terminal(task, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
return;
|
||||
return self.spawn_in_new_terminal(task, window, cx);
|
||||
}
|
||||
|
||||
let mut terminals_for_task = self.terminals_for_task(&task.full_label, cx);
|
||||
let Some(existing) = terminals_for_task.pop() else {
|
||||
self.spawn_in_new_terminal(task, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
return;
|
||||
return self.spawn_in_new_terminal(task, window, cx);
|
||||
};
|
||||
|
||||
let (existing_item_index, task_pane, existing_terminal) = existing;
|
||||
if task.allow_concurrent_runs {
|
||||
self.replace_terminal(
|
||||
return self.replace_terminal(
|
||||
task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.detach();
|
||||
return;
|
||||
);
|
||||
}
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
self.deferred_tasks.insert(
|
||||
task.id.clone(),
|
||||
cx.spawn_in(window, async move |terminal_panel, cx| {
|
||||
wait_for_terminals_tasks(terminals_for_task, cx).await;
|
||||
let task = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
|
||||
if task.use_new_terminal {
|
||||
terminal_panel
|
||||
.spawn_in_new_terminal(task, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
None
|
||||
terminal_panel.spawn_in_new_terminal(task, window, cx)
|
||||
} else {
|
||||
Some(terminal_panel.replace_terminal(
|
||||
terminal_panel.replace_terminal(
|
||||
task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
)
|
||||
}
|
||||
});
|
||||
if let Ok(Some(task)) = task {
|
||||
task.await;
|
||||
if let Ok(task) = task {
|
||||
tx.send(task.await).ok();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
cx.spawn(async move |_, _| rx.await?)
|
||||
}
|
||||
|
||||
pub fn spawn_in_new_terminal(
|
||||
|
@ -810,60 +802,47 @@ impl TerminalPanel {
|
|||
terminal_to_replace: Entity<TerminalView>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Option<()>> {
|
||||
) -> Task<Result<Entity<Terminal>>> {
|
||||
let reveal = spawn_task.reveal;
|
||||
let reveal_target = spawn_task.reveal_target;
|
||||
let window_handle = window.window_handle();
|
||||
let task_workspace = self.workspace.clone();
|
||||
cx.spawn_in(window, async move |terminal_panel, cx| {
|
||||
let project = terminal_panel
|
||||
.update(cx, |this, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, _| workspace.project().clone())
|
||||
.ok()
|
||||
})
|
||||
.ok()
|
||||
.flatten()?;
|
||||
let project = terminal_panel.update(cx, |this, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, _| workspace.project().clone())
|
||||
})??;
|
||||
let new_terminal = project
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Task(spawn_task), window_handle, cx)
|
||||
})
|
||||
.ok()?
|
||||
.await
|
||||
.log_err()?;
|
||||
terminal_to_replace
|
||||
.update_in(cx, |terminal_to_replace, window, cx| {
|
||||
terminal_to_replace.set_terminal(new_terminal, window, cx);
|
||||
})
|
||||
.ok()?;
|
||||
})?
|
||||
.await?;
|
||||
terminal_to_replace.update_in(cx, |terminal_to_replace, window, cx| {
|
||||
terminal_to_replace.set_terminal(new_terminal.clone(), window, cx);
|
||||
})?;
|
||||
|
||||
match reveal {
|
||||
RevealStrategy::Always => match reveal_target {
|
||||
RevealTarget::Center => {
|
||||
task_workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
.context("retrieving active terminal item in the workspace")
|
||||
.log_err()?
|
||||
.item_focus_handle(cx)
|
||||
.focus(window);
|
||||
Some(())
|
||||
})
|
||||
.ok()??;
|
||||
task_workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
.context("retrieving active terminal item in the workspace")?
|
||||
.item_focus_handle(cx)
|
||||
.focus(window);
|
||||
anyhow::Ok(())
|
||||
})??;
|
||||
}
|
||||
RevealTarget::Dock => {
|
||||
terminal_panel
|
||||
.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.activate_terminal_view(
|
||||
&task_pane,
|
||||
terminal_item_index,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok()?;
|
||||
terminal_panel.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.activate_terminal_view(
|
||||
&task_pane,
|
||||
terminal_item_index,
|
||||
true,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
task_workspace
|
||||
|
@ -877,24 +856,20 @@ impl TerminalPanel {
|
|||
},
|
||||
RevealStrategy::NoFocus => match reveal_target {
|
||||
RevealTarget::Center => {
|
||||
task_workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.active_pane().focus_handle(cx).focus(window);
|
||||
})
|
||||
.ok()?;
|
||||
task_workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.active_pane().focus_handle(cx).focus(window);
|
||||
})?;
|
||||
}
|
||||
RevealTarget::Dock => {
|
||||
terminal_panel
|
||||
.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.activate_terminal_view(
|
||||
&task_pane,
|
||||
terminal_item_index,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok()?;
|
||||
terminal_panel.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.activate_terminal_view(
|
||||
&task_pane,
|
||||
terminal_item_index,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
task_workspace
|
||||
|
@ -909,7 +884,7 @@ impl TerminalPanel {
|
|||
RevealStrategy::Never => {}
|
||||
}
|
||||
|
||||
Some(())
|
||||
Ok(new_terminal)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1158,7 +1133,7 @@ async fn wait_for_terminals_tasks(
|
|||
})
|
||||
.ok()
|
||||
});
|
||||
let _: Vec<()> = join_all(pending_tasks).await;
|
||||
let _: Vec<_> = join_all(pending_tasks).await;
|
||||
}
|
||||
|
||||
fn add_paths_to_terminal(
|
||||
|
@ -1475,6 +1450,34 @@ impl Panel for TerminalPanel {
|
|||
}
|
||||
}
|
||||
|
||||
struct TerminalProvider(Entity<TerminalPanel>);
|
||||
|
||||
impl workspace::TerminalProvider for TerminalProvider {
|
||||
fn spawn(
|
||||
&self,
|
||||
task: SpawnInTerminal,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<ExitStatus>> {
|
||||
let this = self.0.clone();
|
||||
window.spawn(cx, async move |cx| {
|
||||
let terminal = this
|
||||
.update_in(cx, |terminal_panel, window, cx| {
|
||||
terminal_panel.spawn_task(&task, window, cx)
|
||||
})?
|
||||
.await?;
|
||||
let Some(exit_code) = terminal
|
||||
.read_with(cx, |terminal, cx| terminal.wait_for_completed_task(cx))?
|
||||
.await
|
||||
else {
|
||||
return Err(anyhow!("Task cancelled"));
|
||||
};
|
||||
|
||||
Ok(exit_code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InlineAssistTabBarButton {
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
|
|
@ -982,15 +982,6 @@ fn subscribe_for_terminal_events(
|
|||
window.invalidate_character_coordinates();
|
||||
cx.emit(SearchEvent::ActiveMatchChanged)
|
||||
}
|
||||
Event::TaskLocatorReady { task_id, success } => {
|
||||
if *success {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.debug_task_ready(task_id, cx);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
vec![terminal_subscription, terminal_events_subscription]
|
||||
|
|
|
@ -1446,27 +1446,29 @@ impl ShellExec {
|
|||
let project = workspace.project().read(cx);
|
||||
let cwd = project.first_project_directory(cx);
|
||||
let shell = project.terminal_settings(&cwd, cx).shell.clone();
|
||||
cx.emit(workspace::Event::SpawnTask {
|
||||
action: Box::new(SpawnInTerminal {
|
||||
id: TaskId("vim".to_string()),
|
||||
full_label: command.clone(),
|
||||
label: command.clone(),
|
||||
command: command.clone(),
|
||||
args: Vec::new(),
|
||||
command_label: command.clone(),
|
||||
cwd,
|
||||
env: HashMap::default(),
|
||||
use_new_terminal: true,
|
||||
allow_concurrent_runs: true,
|
||||
reveal: RevealStrategy::NoFocus,
|
||||
reveal_target: RevealTarget::Dock,
|
||||
hide: HideStrategy::Never,
|
||||
shell,
|
||||
show_summary: false,
|
||||
show_command: false,
|
||||
show_rerun: false,
|
||||
}),
|
||||
});
|
||||
|
||||
let spawn_in_terminal = SpawnInTerminal {
|
||||
id: TaskId("vim".to_string()),
|
||||
full_label: command.clone(),
|
||||
label: command.clone(),
|
||||
command: command.clone(),
|
||||
args: Vec::new(),
|
||||
command_label: command.clone(),
|
||||
cwd,
|
||||
env: HashMap::default(),
|
||||
use_new_terminal: true,
|
||||
allow_concurrent_runs: true,
|
||||
reveal: RevealStrategy::NoFocus,
|
||||
reveal_target: RevealTarget::Dock,
|
||||
hide: HideStrategy::Never,
|
||||
shell,
|
||||
show_summary: false,
|
||||
show_command: false,
|
||||
show_rerun: false,
|
||||
};
|
||||
workspace
|
||||
.spawn_in_terminal(spawn_in_terminal, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
|
|
@ -35,7 +35,6 @@ client.workspace = true
|
|||
clock.workspace = true
|
||||
collections.workspace = true
|
||||
component.workspace = true
|
||||
dap.workspace = true
|
||||
db.workspace = true
|
||||
derive_more.workspace = true
|
||||
fs.workspace = true
|
||||
|
|
|
@ -1,75 +1,132 @@
|
|||
use gpui::Context;
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use gpui::{Context, Task};
|
||||
use project::TaskSourceKind;
|
||||
use remote::ConnectionState;
|
||||
use task::{ResolvedTask, TaskContext, TaskTemplate};
|
||||
use task::{ResolvedTask, SpawnInTerminal, TaskContext, TaskTemplate};
|
||||
use ui::Window;
|
||||
|
||||
use crate::Workspace;
|
||||
|
||||
pub fn schedule_task(
|
||||
workspace: &mut Workspace,
|
||||
task_source_kind: TaskSourceKind,
|
||||
task_to_resolve: &TaskTemplate,
|
||||
task_cx: &TaskContext,
|
||||
omit_history: bool,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
match workspace.project.read(cx).ssh_connection_state(cx) {
|
||||
None | Some(ConnectionState::Connected) => {}
|
||||
Some(
|
||||
ConnectionState::Connecting
|
||||
| ConnectionState::Disconnected
|
||||
| ConnectionState::HeartbeatMissed
|
||||
| ConnectionState::Reconnecting,
|
||||
) => {
|
||||
log::warn!("Cannot schedule tasks when disconnected from a remote host");
|
||||
impl Workspace {
|
||||
pub fn schedule_task(
|
||||
self: &mut Workspace,
|
||||
task_source_kind: TaskSourceKind,
|
||||
task_to_resolve: &TaskTemplate,
|
||||
task_cx: &TaskContext,
|
||||
omit_history: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
match self.project.read(cx).ssh_connection_state(cx) {
|
||||
None | Some(ConnectionState::Connected) => {}
|
||||
Some(
|
||||
ConnectionState::Connecting
|
||||
| ConnectionState::Disconnected
|
||||
| ConnectionState::HeartbeatMissed
|
||||
| ConnectionState::Reconnecting,
|
||||
) => {
|
||||
log::warn!("Cannot schedule tasks when disconnected from a remote host");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(spawn_in_terminal) =
|
||||
task_to_resolve.resolve_task(&task_source_kind.to_id_base(), task_cx)
|
||||
{
|
||||
self.schedule_resolved_task(
|
||||
task_source_kind,
|
||||
spawn_in_terminal,
|
||||
omit_history,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule_resolved_task(
|
||||
self: &mut Workspace,
|
||||
task_source_kind: TaskSourceKind,
|
||||
mut resolved_task: ResolvedTask,
|
||||
omit_history: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
if let Some(spawn_in_terminal) = resolved_task.resolved.take() {
|
||||
if !omit_history {
|
||||
resolved_task.resolved = Some(spawn_in_terminal.clone());
|
||||
self.project().update(cx, |project, cx| {
|
||||
if let Some(task_inventory) =
|
||||
project.task_store().read(cx).task_inventory().cloned()
|
||||
{
|
||||
task_inventory.update(cx, |inventory, _| {
|
||||
inventory.task_scheduled(task_source_kind, resolved_task);
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(terminal_provider) = self.terminal_provider.as_ref() {
|
||||
terminal_provider
|
||||
.spawn(spawn_in_terminal, window, cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule_debug_task(
|
||||
&mut self,
|
||||
task: ResolvedTask,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
let Some(debug_config) = task.resolved_debug_adapter_config() else {
|
||||
log::error!("Debug task has no debug adapter config");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(spawn_in_terminal) =
|
||||
task_to_resolve.resolve_task(&task_source_kind.to_id_base(), task_cx)
|
||||
{
|
||||
schedule_resolved_task(
|
||||
workspace,
|
||||
task_source_kind,
|
||||
spawn_in_terminal,
|
||||
omit_history,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
let project = self.project().clone();
|
||||
cx.spawn_in(window, async move |workspace, cx| {
|
||||
let config = if debug_config.locator.is_some() {
|
||||
let task = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.spawn_in_terminal(task.resolved.unwrap(), window, cx)
|
||||
})?;
|
||||
|
||||
pub fn schedule_resolved_task(
|
||||
workspace: &mut Workspace,
|
||||
task_source_kind: TaskSourceKind,
|
||||
mut resolved_task: ResolvedTask,
|
||||
omit_history: bool,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
let debug_config = resolved_task.resolved_debug_adapter_config();
|
||||
|
||||
if let Some(spawn_in_terminal) = resolved_task.resolved.take() {
|
||||
if let Some(debug_config) = debug_config {
|
||||
workspace
|
||||
.debug_task_queue
|
||||
.insert(resolved_task.id.clone(), debug_config);
|
||||
}
|
||||
|
||||
if !omit_history {
|
||||
resolved_task.resolved = Some(spawn_in_terminal.clone());
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
if let Some(task_inventory) =
|
||||
project.task_store().read(cx).task_inventory().cloned()
|
||||
{
|
||||
task_inventory.update(cx, |inventory, _| {
|
||||
inventory.task_scheduled(task_source_kind, resolved_task);
|
||||
})
|
||||
let exit_code = task.await?;
|
||||
if !exit_code.success() {
|
||||
return anyhow::Ok(());
|
||||
}
|
||||
});
|
||||
}
|
||||
let ret = project
|
||||
.update(cx, |project, cx| {
|
||||
project.dap_store().update(cx, |dap_store, cx| {
|
||||
dap_store.run_debug_locator(debug_config, cx)
|
||||
})
|
||||
})?
|
||||
.await?;
|
||||
ret
|
||||
} else {
|
||||
debug_config.definition
|
||||
};
|
||||
|
||||
cx.emit(crate::Event::SpawnTask {
|
||||
action: Box::new(spawn_in_terminal),
|
||||
});
|
||||
project
|
||||
.update(cx, |project, cx| project.start_debug_session(config, cx))?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
pub fn spawn_in_terminal(
|
||||
self: &mut Workspace,
|
||||
spawn_in_terminal: SpawnInTerminal,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) -> Task<Result<ExitStatus>> {
|
||||
if let Some(terminal_provider) = self.terminal_provider.as_ref() {
|
||||
terminal_provider.spawn(spawn_in_terminal, window, cx)
|
||||
} else {
|
||||
Task::ready(Err(anyhow!("No terminal provider")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ mod toast_layer;
|
|||
mod toolbar;
|
||||
mod workspace_settings;
|
||||
|
||||
use dap::DapRegistry;
|
||||
pub use toast_layer::{RunAction, ToastAction, ToastLayer, ToastView};
|
||||
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
|
@ -92,11 +91,12 @@ use std::{
|
|||
env,
|
||||
hash::{Hash, Hasher},
|
||||
path::{Path, PathBuf},
|
||||
process::ExitStatus,
|
||||
rc::Rc,
|
||||
sync::{Arc, LazyLock, Weak, atomic::AtomicUsize},
|
||||
time::Duration,
|
||||
};
|
||||
use task::{DebugTaskDefinition, SpawnInTerminal, TaskId};
|
||||
use task::SpawnInTerminal;
|
||||
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
||||
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
||||
pub use ui;
|
||||
|
@ -130,6 +130,15 @@ static ZED_WINDOW_POSITION: LazyLock<Option<Point<Pixels>>> = LazyLock::new(|| {
|
|||
.and_then(parse_pixel_position_env_var)
|
||||
});
|
||||
|
||||
pub trait TerminalProvider {
|
||||
fn spawn(
|
||||
&self,
|
||||
task: SpawnInTerminal,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<ExitStatus>>;
|
||||
}
|
||||
|
||||
actions!(
|
||||
workspace,
|
||||
[
|
||||
|
@ -626,7 +635,6 @@ pub fn register_serializable_item<I: SerializableItem>(cx: &mut App) {
|
|||
|
||||
pub struct AppState {
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
pub debug_adapters: Arc<DapRegistry>,
|
||||
pub client: Arc<Client>,
|
||||
pub user_store: Entity<UserStore>,
|
||||
pub workspace_store: Entity<WorkspaceStore>,
|
||||
|
@ -678,7 +686,6 @@ impl AppState {
|
|||
|
||||
let fs = fs::FakeFs::new(cx.background_executor().clone());
|
||||
let languages = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
|
||||
let debug_adapters = Arc::new(DapRegistry::fake());
|
||||
let clock = Arc::new(clock::FakeSystemClock::new());
|
||||
let http_client = http_client::FakeHttpClient::with_404_response();
|
||||
let client = Client::new(clock, http_client.clone(), cx);
|
||||
|
@ -694,7 +701,6 @@ impl AppState {
|
|||
client,
|
||||
fs,
|
||||
languages,
|
||||
debug_adapters,
|
||||
user_store,
|
||||
workspace_store,
|
||||
node_runtime: NodeRuntime::unavailable(),
|
||||
|
@ -772,9 +778,6 @@ pub enum Event {
|
|||
},
|
||||
ContactRequestedJoin(u64),
|
||||
WorkspaceCreated(WeakEntity<Workspace>),
|
||||
SpawnTask {
|
||||
action: Box<SpawnInTerminal>,
|
||||
},
|
||||
OpenBundledFile {
|
||||
text: Cow<'static, str>,
|
||||
title: &'static str,
|
||||
|
@ -856,11 +859,11 @@ pub struct Workspace {
|
|||
bounds_save_task_queued: Option<Task<()>>,
|
||||
on_prompt_for_new_path: Option<PromptForNewPath>,
|
||||
on_prompt_for_open_path: Option<PromptForOpenPath>,
|
||||
terminal_provider: Option<Box<dyn TerminalProvider>>,
|
||||
serializable_items_tx: UnboundedSender<Box<dyn SerializableItemHandle>>,
|
||||
serialized_ssh_project: Option<SerializedSshProject>,
|
||||
_items_serializer: Task<Result<()>>,
|
||||
session_id: Option<String>,
|
||||
debug_task_queue: HashMap<task::TaskId, DebugTaskDefinition>,
|
||||
}
|
||||
|
||||
impl EventEmitter<Event> for Workspace {}
|
||||
|
@ -1182,11 +1185,11 @@ impl Workspace {
|
|||
bounds_save_task_queued: None,
|
||||
on_prompt_for_new_path: None,
|
||||
on_prompt_for_open_path: None,
|
||||
terminal_provider: None,
|
||||
serializable_items_tx,
|
||||
_items_serializer,
|
||||
session_id: Some(session_id),
|
||||
serialized_ssh_project: None,
|
||||
debug_task_queue: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1207,7 +1210,6 @@ impl Workspace {
|
|||
app_state.node_runtime.clone(),
|
||||
app_state.user_store.clone(),
|
||||
app_state.languages.clone(),
|
||||
app_state.debug_adapters.clone(),
|
||||
app_state.fs.clone(),
|
||||
env,
|
||||
cx,
|
||||
|
@ -1699,6 +1701,10 @@ impl Workspace {
|
|||
self.on_prompt_for_open_path = Some(prompt)
|
||||
}
|
||||
|
||||
pub fn set_terminal_provider(&mut self, provider: impl TerminalProvider + 'static) {
|
||||
self.terminal_provider = Some(Box::new(provider));
|
||||
}
|
||||
|
||||
pub fn serialized_ssh_project(&self) -> Option<SerializedSshProject> {
|
||||
self.serialized_ssh_project.clone()
|
||||
}
|
||||
|
@ -5082,7 +5088,6 @@ impl Workspace {
|
|||
window.activate_window();
|
||||
let app_state = Arc::new(AppState {
|
||||
languages: project.read(cx).languages().clone(),
|
||||
debug_adapters: project.read(cx).debug_adapters().clone(),
|
||||
workspace_store,
|
||||
client,
|
||||
user_store,
|
||||
|
@ -5238,16 +5243,6 @@ impl Workspace {
|
|||
.update(cx, |_, window, _| window.activate_window())
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub fn debug_task_ready(&mut self, task_id: &TaskId, cx: &mut App) {
|
||||
if let Some(debug_config) = self.debug_task_queue.remove(task_id) {
|
||||
self.project.update(cx, |project, cx| {
|
||||
project
|
||||
.start_debug_session(debug_config, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn leader_border_for_pane(
|
||||
|
|
|
@ -42,7 +42,6 @@ command_palette.workspace = true
|
|||
command_palette_hooks.workspace = true
|
||||
component_preview.workspace = true
|
||||
copilot.workspace = true
|
||||
dap.workspace = true
|
||||
dap_adapters.workspace = true
|
||||
debugger_ui.workspace = true
|
||||
debugger_tools.workspace = true
|
||||
|
|
|
@ -10,7 +10,6 @@ use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
|||
use client::{Client, ProxySettings, UserStore, parse_zed_link};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use collections::HashMap;
|
||||
use dap::DapRegistry;
|
||||
use db::kvp::{GLOBAL_KEY_VALUE_STORE, KEY_VALUE_STORE};
|
||||
use editor::Editor;
|
||||
use extension::ExtensionHostProxy;
|
||||
|
@ -449,7 +448,6 @@ fn main() {
|
|||
|
||||
let app_state = Arc::new(AppState {
|
||||
languages: languages.clone(),
|
||||
debug_adapters: DapRegistry::default().into(),
|
||||
client: client.clone(),
|
||||
user_store: user_store.clone(),
|
||||
fs: fs.clone(),
|
||||
|
@ -461,7 +459,7 @@ fn main() {
|
|||
AppState::set_global(Arc::downgrade(&app_state), cx);
|
||||
|
||||
auto_update::init(client.http_client(), cx);
|
||||
dap_adapters::init(app_state.debug_adapters.clone());
|
||||
dap_adapters::init(cx);
|
||||
auto_update_ui::init(cx);
|
||||
reliability::init(
|
||||
client.http_client(),
|
||||
|
|
|
@ -14,7 +14,7 @@ use log;
|
|||
static ENV_FILTER: OnceLock<env_config::EnvFilter> = OnceLock::new();
|
||||
static SCOPE_MAP: RwLock<Option<ScopeMap>> = RwLock::new(None);
|
||||
|
||||
const LEVEL_ENABLED_MAX_DEFAULT: log::LevelFilter = log::LevelFilter::Info;
|
||||
pub const LEVEL_ENABLED_MAX_DEFAULT: log::LevelFilter = log::LevelFilter::Info;
|
||||
/// The maximum log level of verbosity that is enabled by default.
|
||||
/// All messages more verbose than this level will be discarded
|
||||
/// by default unless specially configured.
|
||||
|
@ -34,7 +34,7 @@ static mut LEVEL_ENABLED_MAX_STATIC: log::LevelFilter = LEVEL_ENABLED_MAX_DEFAUL
|
|||
/// `trace` logs will be discarded.
|
||||
/// Therefore, it should always be `>= LEVEL_ENABLED_MAX_STATIC`
|
||||
// PERF: this doesn't need to be an atomic, we don't actually care about race conditions here
|
||||
static LEVEL_ENABLED_MAX_CONFIG: AtomicU8 = AtomicU8::new(LEVEL_ENABLED_MAX_DEFAULT as u8);
|
||||
pub static LEVEL_ENABLED_MAX_CONFIG: AtomicU8 = AtomicU8::new(LEVEL_ENABLED_MAX_DEFAULT as u8);
|
||||
|
||||
pub fn init_env_filter(filter: env_config::EnvFilter) {
|
||||
if let Some(level_max) = filter.level_global {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue