Compare commits
10 commits
main
...
cherry-pic
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7f2283749b | ||
![]() |
2d724520bc | ||
![]() |
473062aeef | ||
![]() |
612c9addff | ||
![]() |
19a60dbf9c | ||
![]() |
acba38dabd | ||
![]() |
c1b3111c15 | ||
![]() |
623388ad80 | ||
![]() |
eb89e9a572 | ||
![]() |
e306a55073 |
19 changed files with 534 additions and 253 deletions
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
|
@ -800,7 +800,8 @@ jobs:
|
||||||
|
|
||||||
- name: Upload Artifacts to release
|
- name: Upload Artifacts to release
|
||||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||||
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview
|
# Re-enable when we are ready to publish windows preview releases
|
||||||
|
if: false && ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||||
|
|
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -3043,6 +3043,7 @@ dependencies = [
|
||||||
"context_server",
|
"context_server",
|
||||||
"ctor",
|
"ctor",
|
||||||
"dap",
|
"dap",
|
||||||
|
"dap-types",
|
||||||
"dap_adapters",
|
"dap_adapters",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"debugger_ui",
|
"debugger_ui",
|
||||||
|
@ -19972,7 +19973,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.195.0"
|
version = "0.195.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activity_indicator",
|
"activity_indicator",
|
||||||
"agent",
|
"agent",
|
||||||
|
|
|
@ -94,6 +94,7 @@ context_server.workspace = true
|
||||||
ctor.workspace = true
|
ctor.workspace = true
|
||||||
dap = { workspace = true, features = ["test-support"] }
|
dap = { workspace = true, features = ["test-support"] }
|
||||||
dap_adapters = { workspace = true, features = ["test-support"] }
|
dap_adapters = { workspace = true, features = ["test-support"] }
|
||||||
|
dap-types.workspace = true
|
||||||
debugger_ui = { workspace = true, features = ["test-support"] }
|
debugger_ui = { workspace = true, features = ["test-support"] }
|
||||||
editor = { workspace = true, features = ["test-support"] }
|
editor = { workspace = true, features = ["test-support"] }
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::tests::TestServer;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use dap::{Capabilities, adapters::DebugTaskDefinition, transport::RequestHandling};
|
||||||
use debugger_ui::debugger_panel::DebugPanel;
|
use debugger_ui::debugger_panel::DebugPanel;
|
||||||
use extension::ExtensionHostProxy;
|
use extension::ExtensionHostProxy;
|
||||||
use fs::{FakeFs, Fs as _, RemoveOptions};
|
use fs::{FakeFs, Fs as _, RemoveOptions};
|
||||||
|
@ -22,6 +23,7 @@ use language::{
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use project::{
|
use project::{
|
||||||
ProjectPath,
|
ProjectPath,
|
||||||
|
debugger::session::ThreadId,
|
||||||
lsp_store::{FormatTrigger, LspFormatTarget},
|
lsp_store::{FormatTrigger, LspFormatTarget},
|
||||||
};
|
};
|
||||||
use remote::SshRemoteClient;
|
use remote::SshRemoteClient;
|
||||||
|
@ -29,7 +31,11 @@ use remote_server::{HeadlessAppState, HeadlessProject};
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use std::{path::Path, sync::Arc};
|
use std::{
|
||||||
|
path::Path,
|
||||||
|
sync::{Arc, atomic::AtomicUsize},
|
||||||
|
};
|
||||||
|
use task::TcpArgumentsTemplate;
|
||||||
use util::path;
|
use util::path;
|
||||||
|
|
||||||
#[gpui::test(iterations = 10)]
|
#[gpui::test(iterations = 10)]
|
||||||
|
@ -688,3 +694,162 @@ async fn test_remote_server_debugger(
|
||||||
|
|
||||||
shutdown_session.await.unwrap();
|
shutdown_session.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_slow_adapter_startup_retries(
|
||||||
|
cx_a: &mut TestAppContext,
|
||||||
|
server_cx: &mut TestAppContext,
|
||||||
|
executor: BackgroundExecutor,
|
||||||
|
) {
|
||||||
|
cx_a.update(|cx| {
|
||||||
|
release_channel::init(SemanticVersion::default(), cx);
|
||||||
|
command_palette_hooks::init(cx);
|
||||||
|
zlog::init_test();
|
||||||
|
dap_adapters::init(cx);
|
||||||
|
});
|
||||||
|
server_cx.update(|cx| {
|
||||||
|
release_channel::init(SemanticVersion::default(), cx);
|
||||||
|
dap_adapters::init(cx);
|
||||||
|
});
|
||||||
|
let (opts, server_ssh) = SshRemoteClient::fake_server(cx_a, server_cx);
|
||||||
|
let remote_fs = FakeFs::new(server_cx.executor());
|
||||||
|
remote_fs
|
||||||
|
.insert_tree(
|
||||||
|
path!("/code"),
|
||||||
|
json!({
|
||||||
|
"lib.rs": "fn one() -> usize { 1 }"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// User A connects to the remote project via SSH.
|
||||||
|
server_cx.update(HeadlessProject::init);
|
||||||
|
let remote_http_client = Arc::new(BlockedHttpClient);
|
||||||
|
let node = NodeRuntime::unavailable();
|
||||||
|
let languages = Arc::new(LanguageRegistry::new(server_cx.executor()));
|
||||||
|
let _headless_project = server_cx.new(|cx| {
|
||||||
|
client::init_settings(cx);
|
||||||
|
HeadlessProject::new(
|
||||||
|
HeadlessAppState {
|
||||||
|
session: server_ssh,
|
||||||
|
fs: remote_fs.clone(),
|
||||||
|
http_client: remote_http_client,
|
||||||
|
node_runtime: node,
|
||||||
|
languages,
|
||||||
|
extension_host_proxy: Arc::new(ExtensionHostProxy::new()),
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let client_ssh = SshRemoteClient::fake_client(opts, cx_a).await;
|
||||||
|
let mut server = TestServer::start(server_cx.executor()).await;
|
||||||
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
|
cx_a.update(|cx| {
|
||||||
|
debugger_ui::init(cx);
|
||||||
|
command_palette_hooks::init(cx);
|
||||||
|
});
|
||||||
|
let (project_a, _) = client_a
|
||||||
|
.build_ssh_project(path!("/code"), client_ssh.clone(), cx_a)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let (workspace, cx_a) = client_a.build_workspace(&project_a, cx_a);
|
||||||
|
|
||||||
|
let debugger_panel = workspace
|
||||||
|
.update_in(cx_a, |_workspace, window, cx| {
|
||||||
|
cx.spawn_in(window, DebugPanel::load)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
workspace.update_in(cx_a, |workspace, window, cx| {
|
||||||
|
workspace.add_panel(debugger_panel, window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
let debug_panel = workspace
|
||||||
|
.update(cx_a, |workspace, cx| workspace.panel::<DebugPanel>(cx))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let workspace_window = cx_a
|
||||||
|
.window_handle()
|
||||||
|
.downcast::<workspace::Workspace>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let count = Arc::new(AtomicUsize::new(0));
|
||||||
|
let session = debugger_ui::tests::start_debug_session_with(
|
||||||
|
&workspace_window,
|
||||||
|
cx_a,
|
||||||
|
DebugTaskDefinition {
|
||||||
|
adapter: "fake-adapter".into(),
|
||||||
|
label: "test".into(),
|
||||||
|
config: json!({
|
||||||
|
"request": "launch"
|
||||||
|
}),
|
||||||
|
tcp_connection: Some(TcpArgumentsTemplate {
|
||||||
|
port: None,
|
||||||
|
host: None,
|
||||||
|
timeout: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
move |client| {
|
||||||
|
let count = count.clone();
|
||||||
|
client.on_request_ext::<dap::requests::Initialize, _>(move |_seq, _request| {
|
||||||
|
if count.fetch_add(1, std::sync::atomic::Ordering::SeqCst) < 5 {
|
||||||
|
return RequestHandling::Exit;
|
||||||
|
}
|
||||||
|
RequestHandling::Respond(Ok(Capabilities::default()))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
|
||||||
|
let client = session.update(cx_a, |session, _| session.adapter_client().unwrap());
|
||||||
|
client
|
||||||
|
.fake_event(dap::messages::Events::Stopped(dap::StoppedEvent {
|
||||||
|
reason: dap::StoppedEventReason::Pause,
|
||||||
|
description: None,
|
||||||
|
thread_id: Some(1),
|
||||||
|
preserve_focus_hint: None,
|
||||||
|
text: None,
|
||||||
|
all_threads_stopped: None,
|
||||||
|
hit_breakpoint_ids: None,
|
||||||
|
}))
|
||||||
|
.await;
|
||||||
|
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
|
||||||
|
let active_session = debug_panel
|
||||||
|
.update(cx_a, |this, _| this.active_session())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let running_state = active_session.update(cx_a, |active_session, _| {
|
||||||
|
active_session.running_state().clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
client.id(),
|
||||||
|
running_state.read_with(cx_a, |running_state, _| running_state.session_id())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
ThreadId(1),
|
||||||
|
running_state.read_with(cx_a, |running_state, _| running_state
|
||||||
|
.selected_thread_id()
|
||||||
|
.unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
|
let shutdown_session = workspace.update(cx_a, |workspace, cx| {
|
||||||
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
project.dap_store().update(cx, |dap_store, cx| {
|
||||||
|
dap_store.shutdown_session(session.read(cx).session_id(), cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
client_ssh.update(cx_a, |a, _| {
|
||||||
|
a.shutdown_processes(Some(proto::ShutdownRemoteServer {}), executor)
|
||||||
|
});
|
||||||
|
|
||||||
|
shutdown_session.await.unwrap();
|
||||||
|
}
|
||||||
|
|
|
@ -442,10 +442,18 @@ impl DebugAdapter for FakeAdapter {
|
||||||
_: Option<Vec<String>>,
|
_: Option<Vec<String>>,
|
||||||
_: &mut AsyncApp,
|
_: &mut AsyncApp,
|
||||||
) -> Result<DebugAdapterBinary> {
|
) -> Result<DebugAdapterBinary> {
|
||||||
|
let connection = task_definition
|
||||||
|
.tcp_connection
|
||||||
|
.as_ref()
|
||||||
|
.map(|connection| TcpArguments {
|
||||||
|
host: connection.host(),
|
||||||
|
port: connection.port.unwrap_or(17),
|
||||||
|
timeout: connection.timeout,
|
||||||
|
});
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some("command".into()),
|
command: Some("command".into()),
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
connection: None,
|
connection,
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
cwd: None,
|
cwd: None,
|
||||||
request_args: StartDebuggingRequestArguments {
|
request_args: StartDebuggingRequestArguments {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
adapters::DebugAdapterBinary,
|
adapters::DebugAdapterBinary,
|
||||||
transport::{IoKind, LogKind, TransportDelegate},
|
transport::{IoKind, LogKind, TransportDelegate},
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::Result;
|
||||||
use dap_types::{
|
use dap_types::{
|
||||||
messages::{Message, Response},
|
messages::{Message, Response},
|
||||||
requests::Request,
|
requests::Request,
|
||||||
|
@ -110,9 +110,7 @@ impl DebugAdapterClient {
|
||||||
self.transport_delegate
|
self.transport_delegate
|
||||||
.pending_requests
|
.pending_requests
|
||||||
.lock()
|
.lock()
|
||||||
.as_mut()
|
.insert(sequence_id, callback_tx)?;
|
||||||
.context("client is closed")?
|
|
||||||
.insert(sequence_id, callback_tx);
|
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Client {} send `{}` request with sequence_id: {}",
|
"Client {} send `{}` request with sequence_id: {}",
|
||||||
|
@ -170,6 +168,7 @@ impl DebugAdapterClient {
|
||||||
pub fn kill(&self) {
|
pub fn kill(&self) {
|
||||||
log::debug!("Killing DAP process");
|
log::debug!("Killing DAP process");
|
||||||
self.transport_delegate.transport.lock().kill();
|
self.transport_delegate.transport.lock().kill();
|
||||||
|
self.transport_delegate.pending_requests.lock().shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_adapter_logs(&self) -> bool {
|
pub fn has_adapter_logs(&self) -> bool {
|
||||||
|
@ -184,11 +183,34 @@ impl DebugAdapterClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn on_request<R: dap_types::requests::Request, F>(&self, handler: F)
|
pub fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
|
||||||
where
|
where
|
||||||
F: 'static
|
F: 'static
|
||||||
+ Send
|
+ Send
|
||||||
+ FnMut(u64, R::Arguments) -> Result<R::Response, dap_types::ErrorResponse>,
|
+ FnMut(u64, R::Arguments) -> Result<R::Response, dap_types::ErrorResponse>,
|
||||||
|
{
|
||||||
|
use crate::transport::RequestHandling;
|
||||||
|
|
||||||
|
self.transport_delegate
|
||||||
|
.transport
|
||||||
|
.lock()
|
||||||
|
.as_fake()
|
||||||
|
.on_request::<R, _>(move |seq, request| {
|
||||||
|
RequestHandling::Respond(handler(seq, request))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub fn on_request_ext<R: dap_types::requests::Request, F>(&self, handler: F)
|
||||||
|
where
|
||||||
|
F: 'static
|
||||||
|
+ Send
|
||||||
|
+ FnMut(
|
||||||
|
u64,
|
||||||
|
R::Arguments,
|
||||||
|
) -> crate::transport::RequestHandling<
|
||||||
|
Result<R::Response, dap_types::ErrorResponse>,
|
||||||
|
>,
|
||||||
{
|
{
|
||||||
self.transport_delegate
|
self.transport_delegate
|
||||||
.transport
|
.transport
|
||||||
|
|
|
@ -49,6 +49,12 @@ pub enum IoKind {
|
||||||
StdErr,
|
StdErr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub enum RequestHandling<T> {
|
||||||
|
Respond(T),
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
type LogHandlers = Arc<Mutex<SmallVec<[(LogKind, IoHandler); 2]>>>;
|
type LogHandlers = Arc<Mutex<SmallVec<[(LogKind, IoHandler); 2]>>>;
|
||||||
|
|
||||||
pub trait Transport: Send + Sync {
|
pub trait Transport: Send + Sync {
|
||||||
|
@ -76,7 +82,11 @@ async fn start(
|
||||||
) -> Result<Box<dyn Transport>> {
|
) -> Result<Box<dyn Transport>> {
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
if cfg!(any(test, feature = "test-support")) {
|
if cfg!(any(test, feature = "test-support")) {
|
||||||
return Ok(Box::new(FakeTransport::start(cx).await?));
|
if let Some(connection) = binary.connection.clone() {
|
||||||
|
return Ok(Box::new(FakeTransport::start_tcp(connection, cx).await?));
|
||||||
|
} else {
|
||||||
|
return Ok(Box::new(FakeTransport::start_stdio(cx).await?));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if binary.connection.is_some() {
|
if binary.connection.is_some() {
|
||||||
|
@ -90,11 +100,57 @@ async fn start(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct PendingRequests {
|
||||||
|
inner: Option<HashMap<u64, oneshot::Sender<Result<Response>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PendingRequests {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Some(HashMap::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self, e: anyhow::Error) {
|
||||||
|
let Some(inner) = self.inner.as_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for (_, sender) in inner.drain() {
|
||||||
|
sender.send(Err(e.cloned())).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert(
|
||||||
|
&mut self,
|
||||||
|
sequence_id: u64,
|
||||||
|
callback_tx: oneshot::Sender<Result<Response>>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let Some(inner) = self.inner.as_mut() else {
|
||||||
|
bail!("client is closed")
|
||||||
|
};
|
||||||
|
inner.insert(sequence_id, callback_tx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove(
|
||||||
|
&mut self,
|
||||||
|
sequence_id: u64,
|
||||||
|
) -> anyhow::Result<Option<oneshot::Sender<Result<Response>>>> {
|
||||||
|
let Some(inner) = self.inner.as_mut() else {
|
||||||
|
bail!("client is closed");
|
||||||
|
};
|
||||||
|
Ok(inner.remove(&sequence_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn shutdown(&mut self) {
|
||||||
|
self.flush(anyhow!("transport shutdown"));
|
||||||
|
self.inner = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct TransportDelegate {
|
pub(crate) struct TransportDelegate {
|
||||||
log_handlers: LogHandlers,
|
log_handlers: LogHandlers,
|
||||||
// TODO this should really be some kind of associative channel
|
pub(crate) pending_requests: Arc<Mutex<PendingRequests>>,
|
||||||
pub(crate) pending_requests:
|
|
||||||
Arc<Mutex<Option<HashMap<u64, oneshot::Sender<Result<Response>>>>>>,
|
|
||||||
pub(crate) transport: Mutex<Box<dyn Transport>>,
|
pub(crate) transport: Mutex<Box<dyn Transport>>,
|
||||||
pub(crate) server_tx: smol::lock::Mutex<Option<Sender<Message>>>,
|
pub(crate) server_tx: smol::lock::Mutex<Option<Sender<Message>>>,
|
||||||
tasks: Mutex<Vec<Task<()>>>,
|
tasks: Mutex<Vec<Task<()>>>,
|
||||||
|
@ -108,7 +164,7 @@ impl TransportDelegate {
|
||||||
transport: Mutex::new(transport),
|
transport: Mutex::new(transport),
|
||||||
log_handlers,
|
log_handlers,
|
||||||
server_tx: Default::default(),
|
server_tx: Default::default(),
|
||||||
pending_requests: Arc::new(Mutex::new(Some(HashMap::default()))),
|
pending_requests: Arc::new(Mutex::new(PendingRequests::new())),
|
||||||
tasks: Default::default(),
|
tasks: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -151,24 +207,10 @@ impl TransportDelegate {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
pending_requests
|
pending_requests
|
||||||
.lock()
|
.lock()
|
||||||
.take()
|
.flush(anyhow!("debugger shutdown unexpectedly"));
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.for_each(|(_, request)| {
|
|
||||||
request
|
|
||||||
.send(Err(anyhow!("debugger shutdown unexpectedly")))
|
|
||||||
.ok();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
pending_requests
|
pending_requests.lock().flush(e);
|
||||||
.lock()
|
|
||||||
.take()
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.for_each(|(_, request)| {
|
|
||||||
request.send(Err(e.cloned())).ok();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -286,7 +328,7 @@ impl TransportDelegate {
|
||||||
async fn recv_from_server<Stdout>(
|
async fn recv_from_server<Stdout>(
|
||||||
server_stdout: Stdout,
|
server_stdout: Stdout,
|
||||||
mut message_handler: DapMessageHandler,
|
mut message_handler: DapMessageHandler,
|
||||||
pending_requests: Arc<Mutex<Option<HashMap<u64, oneshot::Sender<Result<Response>>>>>>,
|
pending_requests: Arc<Mutex<PendingRequests>>,
|
||||||
log_handlers: Option<LogHandlers>,
|
log_handlers: Option<LogHandlers>,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
|
@ -303,14 +345,10 @@ impl TransportDelegate {
|
||||||
ConnectionResult::Timeout => anyhow::bail!("Timed out when connecting to debugger"),
|
ConnectionResult::Timeout => anyhow::bail!("Timed out when connecting to debugger"),
|
||||||
ConnectionResult::ConnectionReset => {
|
ConnectionResult::ConnectionReset => {
|
||||||
log::info!("Debugger closed the connection");
|
log::info!("Debugger closed the connection");
|
||||||
break Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
ConnectionResult::Result(Ok(Message::Response(res))) => {
|
ConnectionResult::Result(Ok(Message::Response(res))) => {
|
||||||
let tx = pending_requests
|
let tx = pending_requests.lock().remove(res.request_seq)?;
|
||||||
.lock()
|
|
||||||
.as_mut()
|
|
||||||
.context("client is closed")?
|
|
||||||
.remove(&res.request_seq);
|
|
||||||
if let Some(tx) = tx {
|
if let Some(tx) = tx {
|
||||||
if let Err(e) = tx.send(Self::process_response(res)) {
|
if let Err(e) = tx.send(Self::process_response(res)) {
|
||||||
log::trace!("Did not send response `{:?}` for a cancelled", e);
|
log::trace!("Did not send response `{:?}` for a cancelled", e);
|
||||||
|
@ -704,8 +742,7 @@ impl Drop for StdioTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
type RequestHandler =
|
type RequestHandler = Box<dyn Send + FnMut(u64, serde_json::Value) -> RequestHandling<Response>>;
|
||||||
Box<dyn Send + FnMut(u64, serde_json::Value) -> dap_types::messages::Response>;
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
type ResponseHandler = Box<dyn Send + Fn(Response)>;
|
type ResponseHandler = Box<dyn Send + Fn(Response)>;
|
||||||
|
@ -716,23 +753,38 @@ pub struct FakeTransport {
|
||||||
request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,
|
request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,
|
||||||
// for reverse request responses
|
// for reverse request responses
|
||||||
response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,
|
response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,
|
||||||
|
|
||||||
stdin_writer: Option<PipeWriter>,
|
|
||||||
stdout_reader: Option<PipeReader>,
|
|
||||||
message_handler: Option<Task<Result<()>>>,
|
message_handler: Option<Task<Result<()>>>,
|
||||||
|
kind: FakeTransportKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
|
pub enum FakeTransportKind {
|
||||||
|
Stdio {
|
||||||
|
stdin_writer: Option<PipeWriter>,
|
||||||
|
stdout_reader: Option<PipeReader>,
|
||||||
|
},
|
||||||
|
Tcp {
|
||||||
|
connection: TcpArguments,
|
||||||
|
executor: BackgroundExecutor,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl FakeTransport {
|
impl FakeTransport {
|
||||||
pub fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
|
pub fn on_request<R: dap_types::requests::Request, F>(&self, mut handler: F)
|
||||||
where
|
where
|
||||||
F: 'static + Send + FnMut(u64, R::Arguments) -> Result<R::Response, ErrorResponse>,
|
F: 'static
|
||||||
|
+ Send
|
||||||
|
+ FnMut(u64, R::Arguments) -> RequestHandling<Result<R::Response, ErrorResponse>>,
|
||||||
{
|
{
|
||||||
self.request_handlers.lock().insert(
|
self.request_handlers.lock().insert(
|
||||||
R::COMMAND,
|
R::COMMAND,
|
||||||
Box::new(move |seq, args| {
|
Box::new(move |seq, args| {
|
||||||
let result = handler(seq, serde_json::from_value(args).unwrap());
|
let result = handler(seq, serde_json::from_value(args).unwrap());
|
||||||
let response = match result {
|
let RequestHandling::Respond(response) = result else {
|
||||||
|
return RequestHandling::Exit;
|
||||||
|
};
|
||||||
|
let response = match response {
|
||||||
Ok(response) => Response {
|
Ok(response) => Response {
|
||||||
seq: seq + 1,
|
seq: seq + 1,
|
||||||
request_seq: seq,
|
request_seq: seq,
|
||||||
|
@ -750,7 +802,7 @@ impl FakeTransport {
|
||||||
message: None,
|
message: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
response
|
RequestHandling::Respond(response)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -764,86 +816,75 @@ impl FakeTransport {
|
||||||
.insert(R::COMMAND, Box::new(handler));
|
.insert(R::COMMAND, Box::new(handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start(cx: &mut AsyncApp) -> Result<Self> {
|
async fn start_tcp(connection: TcpArguments, cx: &mut AsyncApp) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
request_handlers: Arc::new(Mutex::new(HashMap::default())),
|
||||||
|
response_handlers: Arc::new(Mutex::new(HashMap::default())),
|
||||||
|
message_handler: None,
|
||||||
|
kind: FakeTransportKind::Tcp {
|
||||||
|
connection,
|
||||||
|
executor: cx.background_executor().clone(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_messages(
|
||||||
|
request_handlers: Arc<Mutex<HashMap<&'static str, RequestHandler>>>,
|
||||||
|
response_handlers: Arc<Mutex<HashMap<&'static str, ResponseHandler>>>,
|
||||||
|
stdin_reader: PipeReader,
|
||||||
|
stdout_writer: PipeWriter,
|
||||||
|
) -> Result<()> {
|
||||||
use dap_types::requests::{Request, RunInTerminal, StartDebugging};
|
use dap_types::requests::{Request, RunInTerminal, StartDebugging};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
let (stdin_writer, stdin_reader) = async_pipe::pipe();
|
let mut reader = BufReader::new(stdin_reader);
|
||||||
let (stdout_writer, stdout_reader) = async_pipe::pipe();
|
|
||||||
|
|
||||||
let mut this = Self {
|
|
||||||
request_handlers: Arc::new(Mutex::new(HashMap::default())),
|
|
||||||
response_handlers: Arc::new(Mutex::new(HashMap::default())),
|
|
||||||
stdin_writer: Some(stdin_writer),
|
|
||||||
stdout_reader: Some(stdout_reader),
|
|
||||||
message_handler: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let request_handlers = this.request_handlers.clone();
|
|
||||||
let response_handlers = this.response_handlers.clone();
|
|
||||||
let stdout_writer = Arc::new(smol::lock::Mutex::new(stdout_writer));
|
let stdout_writer = Arc::new(smol::lock::Mutex::new(stdout_writer));
|
||||||
|
let mut buffer = String::new();
|
||||||
|
|
||||||
this.message_handler = Some(cx.background_spawn(async move {
|
loop {
|
||||||
let mut reader = BufReader::new(stdin_reader);
|
match TransportDelegate::receive_server_message(&mut reader, &mut buffer, None).await {
|
||||||
let mut buffer = String::new();
|
ConnectionResult::Timeout => {
|
||||||
|
anyhow::bail!("Timed out when connecting to debugger");
|
||||||
loop {
|
}
|
||||||
match TransportDelegate::receive_server_message(&mut reader, &mut buffer, None)
|
ConnectionResult::ConnectionReset => {
|
||||||
.await
|
log::info!("Debugger closed the connection");
|
||||||
{
|
break Ok(());
|
||||||
ConnectionResult::Timeout => {
|
}
|
||||||
anyhow::bail!("Timed out when connecting to debugger");
|
ConnectionResult::Result(Err(e)) => break Err(e),
|
||||||
}
|
ConnectionResult::Result(Ok(message)) => {
|
||||||
ConnectionResult::ConnectionReset => {
|
match message {
|
||||||
log::info!("Debugger closed the connection");
|
Message::Request(request) => {
|
||||||
break Ok(());
|
// redirect reverse requests to stdout writer/reader
|
||||||
}
|
if request.command == RunInTerminal::COMMAND
|
||||||
ConnectionResult::Result(Err(e)) => break Err(e),
|
|| request.command == StartDebugging::COMMAND
|
||||||
ConnectionResult::Result(Ok(message)) => {
|
{
|
||||||
match message {
|
|
||||||
Message::Request(request) => {
|
|
||||||
// redirect reverse requests to stdout writer/reader
|
|
||||||
if request.command == RunInTerminal::COMMAND
|
|
||||||
|| request.command == StartDebugging::COMMAND
|
|
||||||
{
|
|
||||||
let message =
|
|
||||||
serde_json::to_string(&Message::Request(request)).unwrap();
|
|
||||||
|
|
||||||
let mut writer = stdout_writer.lock().await;
|
|
||||||
writer
|
|
||||||
.write_all(
|
|
||||||
TransportDelegate::build_rpc_message(message)
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
writer.flush().await.unwrap();
|
|
||||||
} else {
|
|
||||||
let response = if let Some(handle) =
|
|
||||||
request_handlers.lock().get_mut(request.command.as_str())
|
|
||||||
{
|
|
||||||
handle(request.seq, request.arguments.unwrap_or(json!({})))
|
|
||||||
} else {
|
|
||||||
panic!("No request handler for {}", request.command);
|
|
||||||
};
|
|
||||||
let message =
|
|
||||||
serde_json::to_string(&Message::Response(response))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut writer = stdout_writer.lock().await;
|
|
||||||
writer
|
|
||||||
.write_all(
|
|
||||||
TransportDelegate::build_rpc_message(message)
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
writer.flush().await.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Event(event) => {
|
|
||||||
let message =
|
let message =
|
||||||
serde_json::to_string(&Message::Event(event)).unwrap();
|
serde_json::to_string(&Message::Request(request)).unwrap();
|
||||||
|
|
||||||
|
let mut writer = stdout_writer.lock().await;
|
||||||
|
writer
|
||||||
|
.write_all(
|
||||||
|
TransportDelegate::build_rpc_message(message).as_bytes(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
writer.flush().await.unwrap();
|
||||||
|
} else {
|
||||||
|
let response = if let Some(handle) =
|
||||||
|
request_handlers.lock().get_mut(request.command.as_str())
|
||||||
|
{
|
||||||
|
handle(request.seq, request.arguments.unwrap_or(json!({})))
|
||||||
|
} else {
|
||||||
|
panic!("No request handler for {}", request.command);
|
||||||
|
};
|
||||||
|
let response = match response {
|
||||||
|
RequestHandling::Respond(response) => response,
|
||||||
|
RequestHandling::Exit => {
|
||||||
|
break Err(anyhow!("exit in response to request"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let message =
|
||||||
|
serde_json::to_string(&Message::Response(response)).unwrap();
|
||||||
|
|
||||||
let mut writer = stdout_writer.lock().await;
|
let mut writer = stdout_writer.lock().await;
|
||||||
writer
|
writer
|
||||||
|
@ -854,20 +895,56 @@ impl FakeTransport {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writer.flush().await.unwrap();
|
writer.flush().await.unwrap();
|
||||||
}
|
}
|
||||||
Message::Response(response) => {
|
}
|
||||||
if let Some(handle) =
|
Message::Event(event) => {
|
||||||
response_handlers.lock().get(response.command.as_str())
|
let message = serde_json::to_string(&Message::Event(event)).unwrap();
|
||||||
{
|
|
||||||
handle(response);
|
let mut writer = stdout_writer.lock().await;
|
||||||
} else {
|
writer
|
||||||
log::error!("No response handler for {}", response.command);
|
.write_all(TransportDelegate::build_rpc_message(message).as_bytes())
|
||||||
}
|
.await
|
||||||
|
.unwrap();
|
||||||
|
writer.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
Message::Response(response) => {
|
||||||
|
if let Some(handle) =
|
||||||
|
response_handlers.lock().get(response.command.as_str())
|
||||||
|
{
|
||||||
|
handle(response);
|
||||||
|
} else {
|
||||||
|
log::error!("No response handler for {}", response.command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_stdio(cx: &mut AsyncApp) -> Result<Self> {
|
||||||
|
let (stdin_writer, stdin_reader) = async_pipe::pipe();
|
||||||
|
let (stdout_writer, stdout_reader) = async_pipe::pipe();
|
||||||
|
let kind = FakeTransportKind::Stdio {
|
||||||
|
stdin_writer: Some(stdin_writer),
|
||||||
|
stdout_reader: Some(stdout_reader),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut this = Self {
|
||||||
|
request_handlers: Arc::new(Mutex::new(HashMap::default())),
|
||||||
|
response_handlers: Arc::new(Mutex::new(HashMap::default())),
|
||||||
|
message_handler: None,
|
||||||
|
kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
let request_handlers = this.request_handlers.clone();
|
||||||
|
let response_handlers = this.response_handlers.clone();
|
||||||
|
|
||||||
|
this.message_handler = Some(cx.background_spawn(Self::handle_messages(
|
||||||
|
request_handlers,
|
||||||
|
response_handlers,
|
||||||
|
stdin_reader,
|
||||||
|
stdout_writer,
|
||||||
|
)));
|
||||||
|
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
@ -876,7 +953,10 @@ impl FakeTransport {
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl Transport for FakeTransport {
|
impl Transport for FakeTransport {
|
||||||
fn tcp_arguments(&self) -> Option<TcpArguments> {
|
fn tcp_arguments(&self) -> Option<TcpArguments> {
|
||||||
None
|
match &self.kind {
|
||||||
|
FakeTransportKind::Stdio { .. } => None,
|
||||||
|
FakeTransportKind::Tcp { connection, .. } => Some(connection.clone()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect(
|
fn connect(
|
||||||
|
@ -887,12 +967,33 @@ impl Transport for FakeTransport {
|
||||||
Box<dyn AsyncRead + Unpin + Send + 'static>,
|
Box<dyn AsyncRead + Unpin + Send + 'static>,
|
||||||
)>,
|
)>,
|
||||||
> {
|
> {
|
||||||
let result = util::maybe!({
|
let result = match &mut self.kind {
|
||||||
Ok((
|
FakeTransportKind::Stdio {
|
||||||
Box::new(self.stdin_writer.take().context("Cannot reconnect")?) as _,
|
stdin_writer,
|
||||||
Box::new(self.stdout_reader.take().context("Cannot reconnect")?) as _,
|
stdout_reader,
|
||||||
))
|
} => util::maybe!({
|
||||||
});
|
Ok((
|
||||||
|
Box::new(stdin_writer.take().context("Cannot reconnect")?) as _,
|
||||||
|
Box::new(stdout_reader.take().context("Cannot reconnect")?) as _,
|
||||||
|
))
|
||||||
|
}),
|
||||||
|
FakeTransportKind::Tcp { executor, .. } => {
|
||||||
|
let (stdin_writer, stdin_reader) = async_pipe::pipe();
|
||||||
|
let (stdout_writer, stdout_reader) = async_pipe::pipe();
|
||||||
|
|
||||||
|
let request_handlers = self.request_handlers.clone();
|
||||||
|
let response_handlers = self.response_handlers.clone();
|
||||||
|
|
||||||
|
self.message_handler = Some(executor.spawn(Self::handle_messages(
|
||||||
|
request_handlers,
|
||||||
|
response_handlers,
|
||||||
|
stdin_reader,
|
||||||
|
stdout_writer,
|
||||||
|
)));
|
||||||
|
|
||||||
|
Ok((Box::new(stdin_writer) as _, Box::new(stdout_reader) as _))
|
||||||
|
}
|
||||||
|
};
|
||||||
Task::ready(result)
|
Task::ready(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ impl DebugSession {
|
||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn running_state(&self) -> &Entity<RunningState> {
|
pub fn running_state(&self) -> &Entity<RunningState> {
|
||||||
&self.running_state
|
&self.running_state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1459,7 +1459,7 @@ impl RunningState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn selected_thread_id(&self) -> Option<ThreadId> {
|
pub fn selected_thread_id(&self) -> Option<ThreadId> {
|
||||||
self.thread_id
|
self.thread_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -482,9 +482,7 @@ pub enum SelectMode {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum EditorMode {
|
pub enum EditorMode {
|
||||||
SingleLine {
|
SingleLine,
|
||||||
auto_width: bool,
|
|
||||||
},
|
|
||||||
AutoHeight {
|
AutoHeight {
|
||||||
min_lines: usize,
|
min_lines: usize,
|
||||||
max_lines: Option<usize>,
|
max_lines: Option<usize>,
|
||||||
|
@ -1662,13 +1660,7 @@ impl Editor {
|
||||||
pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn single_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let buffer = cx.new(|cx| Buffer::local("", cx));
|
let buffer = cx.new(|cx| Buffer::local("", cx));
|
||||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
||||||
Self::new(
|
Self::new(EditorMode::SingleLine, buffer, None, window, cx)
|
||||||
EditorMode::SingleLine { auto_width: false },
|
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn multi_line(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
|
@ -1677,18 +1669,6 @@ impl Editor {
|
||||||
Self::new(EditorMode::full(), buffer, None, window, cx)
|
Self::new(EditorMode::full(), buffer, None, window, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn auto_width(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
|
||||||
let buffer = cx.new(|cx| Buffer::local("", cx));
|
|
||||||
let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
|
|
||||||
Self::new(
|
|
||||||
EditorMode::SingleLine { auto_width: true },
|
|
||||||
buffer,
|
|
||||||
None,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn auto_height(
|
pub fn auto_height(
|
||||||
min_lines: usize,
|
min_lines: usize,
|
||||||
max_lines: usize,
|
max_lines: usize,
|
||||||
|
|
|
@ -7777,46 +7777,13 @@ impl Element for EditorElement {
|
||||||
editor.set_style(self.style.clone(), window, cx);
|
editor.set_style(self.style.clone(), window, cx);
|
||||||
|
|
||||||
let layout_id = match editor.mode {
|
let layout_id = match editor.mode {
|
||||||
EditorMode::SingleLine { auto_width } => {
|
EditorMode::SingleLine => {
|
||||||
let rem_size = window.rem_size();
|
let rem_size = window.rem_size();
|
||||||
|
|
||||||
let height = self.style.text.line_height_in_pixels(rem_size);
|
let height = self.style.text.line_height_in_pixels(rem_size);
|
||||||
if auto_width {
|
let mut style = Style::default();
|
||||||
let editor_handle = cx.entity().clone();
|
style.size.height = height.into();
|
||||||
let style = self.style.clone();
|
style.size.width = relative(1.).into();
|
||||||
window.request_measured_layout(
|
window.request_layout(style, None, cx)
|
||||||
Style::default(),
|
|
||||||
move |_, _, window, cx| {
|
|
||||||
let editor_snapshot = editor_handle
|
|
||||||
.update(cx, |editor, cx| editor.snapshot(window, cx));
|
|
||||||
let line = Self::layout_lines(
|
|
||||||
DisplayRow(0)..DisplayRow(1),
|
|
||||||
&editor_snapshot,
|
|
||||||
&style,
|
|
||||||
px(f32::MAX),
|
|
||||||
|_| false, // Single lines never soft wrap
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
.pop()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let font_id =
|
|
||||||
window.text_system().resolve_font(&style.text.font());
|
|
||||||
let font_size =
|
|
||||||
style.text.font_size.to_pixels(window.rem_size());
|
|
||||||
let em_width =
|
|
||||||
window.text_system().em_width(font_id, font_size).unwrap();
|
|
||||||
|
|
||||||
size(line.width + em_width, height)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let mut style = Style::default();
|
|
||||||
style.size.height = height.into();
|
|
||||||
style.size.width = relative(1.).into();
|
|
||||||
window.request_layout(style, None, cx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EditorMode::AutoHeight {
|
EditorMode::AutoHeight {
|
||||||
min_lines,
|
min_lines,
|
||||||
|
@ -10388,7 +10355,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
for editor_mode_without_invisibles in [
|
for editor_mode_without_invisibles in [
|
||||||
EditorMode::SingleLine { auto_width: false },
|
EditorMode::SingleLine,
|
||||||
EditorMode::AutoHeight {
|
EditorMode::AutoHeight {
|
||||||
min_lines: 1,
|
min_lines: 1,
|
||||||
max_lines: Some(100),
|
max_lines: Some(100),
|
||||||
|
|
|
@ -166,46 +166,9 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = Self::fetch_models(client, llm_api_token, use_cloud).await?;
|
let response = Self::fetch_models(client, llm_api_token, use_cloud).await?;
|
||||||
cx.update(|cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.update(cx, |this, cx| {
|
this.update_models(response, cx);
|
||||||
let mut models = Vec::new();
|
})
|
||||||
|
|
||||||
for model in response.models {
|
|
||||||
models.push(Arc::new(model.clone()));
|
|
||||||
|
|
||||||
// Right now we represent thinking variants of models as separate models on the client,
|
|
||||||
// so we need to insert variants for any model that supports thinking.
|
|
||||||
if model.supports_thinking {
|
|
||||||
models.push(Arc::new(zed_llm_client::LanguageModel {
|
|
||||||
id: zed_llm_client::LanguageModelId(
|
|
||||||
format!("{}-thinking", model.id).into(),
|
|
||||||
),
|
|
||||||
display_name: format!("{} Thinking", model.display_name),
|
|
||||||
..model
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.default_model = models
|
|
||||||
.iter()
|
|
||||||
.find(|model| model.id == response.default_model)
|
|
||||||
.cloned();
|
|
||||||
this.default_fast_model = models
|
|
||||||
.iter()
|
|
||||||
.find(|model| model.id == response.default_fast_model)
|
|
||||||
.cloned();
|
|
||||||
this.recommended_models = response
|
|
||||||
.recommended_models
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| models.iter().find(|model| &model.id == id))
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
this.models = models;
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
})??;
|
|
||||||
|
|
||||||
anyhow::Ok(())
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.context("failed to fetch Zed models")
|
.context("failed to fetch Zed models")
|
||||||
|
@ -216,12 +179,15 @@ impl State {
|
||||||
}),
|
}),
|
||||||
_llm_token_subscription: cx.subscribe(
|
_llm_token_subscription: cx.subscribe(
|
||||||
&refresh_llm_token_listener,
|
&refresh_llm_token_listener,
|
||||||
|this, _listener, _event, cx| {
|
move |this, _listener, _event, cx| {
|
||||||
let client = this.client.clone();
|
let client = this.client.clone();
|
||||||
let llm_api_token = this.llm_api_token.clone();
|
let llm_api_token = this.llm_api_token.clone();
|
||||||
cx.spawn(async move |_this, _cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
llm_api_token.refresh(&client).await?;
|
llm_api_token.refresh(&client).await?;
|
||||||
anyhow::Ok(())
|
let response = Self::fetch_models(client, llm_api_token, use_cloud).await?;
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.update_models(response, cx);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
},
|
},
|
||||||
|
@ -264,6 +230,41 @@ impl State {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_models(&mut self, response: ListModelsResponse, cx: &mut Context<Self>) {
|
||||||
|
let mut models = Vec::new();
|
||||||
|
|
||||||
|
for model in response.models {
|
||||||
|
models.push(Arc::new(model.clone()));
|
||||||
|
|
||||||
|
// Right now we represent thinking variants of models as separate models on the client,
|
||||||
|
// so we need to insert variants for any model that supports thinking.
|
||||||
|
if model.supports_thinking {
|
||||||
|
models.push(Arc::new(zed_llm_client::LanguageModel {
|
||||||
|
id: zed_llm_client::LanguageModelId(format!("{}-thinking", model.id).into()),
|
||||||
|
display_name: format!("{} Thinking", model.display_name),
|
||||||
|
..model
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.default_model = models
|
||||||
|
.iter()
|
||||||
|
.find(|model| model.id == response.default_model)
|
||||||
|
.cloned();
|
||||||
|
self.default_fast_model = models
|
||||||
|
.iter()
|
||||||
|
.find(|model| model.id == response.default_fast_model)
|
||||||
|
.cloned();
|
||||||
|
self.recommended_models = response
|
||||||
|
.recommended_models
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| models.iter().find(|model| &model.id == id))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
self.models = models;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
async fn fetch_models(
|
async fn fetch_models(
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
llm_api_token: LlmApiToken,
|
llm_api_token: LlmApiToken,
|
||||||
|
|
|
@ -3362,8 +3362,14 @@ impl Project {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Vec<LocationLink>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.definitions(buffer, position, cx)
|
lsp_store.definitions(buffer, position, cx)
|
||||||
|
});
|
||||||
|
cx.spawn(async move |_, _| {
|
||||||
|
let result = task.await;
|
||||||
|
drop(guard);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3374,8 +3380,14 @@ impl Project {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Vec<LocationLink>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.declarations(buffer, position, cx)
|
lsp_store.declarations(buffer, position, cx)
|
||||||
|
});
|
||||||
|
cx.spawn(async move |_, _| {
|
||||||
|
let result = task.await;
|
||||||
|
drop(guard);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3386,8 +3398,14 @@ impl Project {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Vec<LocationLink>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.type_definitions(buffer, position, cx)
|
lsp_store.type_definitions(buffer, position, cx)
|
||||||
|
});
|
||||||
|
cx.spawn(async move |_, _| {
|
||||||
|
let result = task.await;
|
||||||
|
drop(guard);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3398,8 +3416,14 @@ impl Project {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<LocationLink>>> {
|
) -> Task<Result<Vec<LocationLink>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.implementations(buffer, position, cx)
|
lsp_store.implementations(buffer, position, cx)
|
||||||
|
});
|
||||||
|
cx.spawn(async move |_, _| {
|
||||||
|
let result = task.await;
|
||||||
|
drop(guard);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3410,8 +3434,14 @@ impl Project {
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Task<Result<Vec<Location>>> {
|
) -> Task<Result<Vec<Location>>> {
|
||||||
let position = position.to_point_utf16(buffer.read(cx));
|
let position = position.to_point_utf16(buffer.read(cx));
|
||||||
self.lsp_store.update(cx, |lsp_store, cx| {
|
let guard = self.retain_remotely_created_models(cx);
|
||||||
|
let task = self.lsp_store.update(cx, |lsp_store, cx| {
|
||||||
lsp_store.references(buffer, position, cx)
|
lsp_store.references(buffer, position, cx)
|
||||||
|
});
|
||||||
|
cx.spawn(async move |_, _| {
|
||||||
|
let result = task.await;
|
||||||
|
drop(guard);
|
||||||
|
result
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -611,7 +611,7 @@ impl RulesLibrary {
|
||||||
this.update_in(cx, |this, window, cx| match rule {
|
this.update_in(cx, |this, window, cx| match rule {
|
||||||
Ok(rule) => {
|
Ok(rule) => {
|
||||||
let title_editor = cx.new(|cx| {
|
let title_editor = cx.new(|cx| {
|
||||||
let mut editor = Editor::auto_width(window, cx);
|
let mut editor = Editor::single_line(window, cx);
|
||||||
editor.set_placeholder_text("Untitled", cx);
|
editor.set_placeholder_text("Untitled", cx);
|
||||||
editor.set_text(rule_metadata.title.unwrap_or_default(), window, cx);
|
editor.set_text(rule_metadata.title.unwrap_or_default(), window, cx);
|
||||||
if prompt_id.is_built_in() {
|
if prompt_id.is_built_in() {
|
||||||
|
|
|
@ -127,7 +127,7 @@ impl BatchedTextRun {
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
let pos = Point::new(
|
let pos = Point::new(
|
||||||
(origin.x + self.start_point.column as f32 * dimensions.cell_width).floor(),
|
origin.x + self.start_point.column as f32 * dimensions.cell_width,
|
||||||
origin.y + self.start_point.line as f32 * dimensions.line_height,
|
origin.y + self.start_point.line as f32 * dimensions.line_height,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,11 @@ fn scroll_editor(
|
||||||
// column position, or the right-most column in the current
|
// column position, or the right-most column in the current
|
||||||
// line, seeing as the cursor might be in a short line, in which
|
// line, seeing as the cursor might be in a short line, in which
|
||||||
// case we don't want to go past its last column.
|
// case we don't want to go past its last column.
|
||||||
let max_row_column = map.line_len(new_row);
|
let max_row_column = if new_row <= map.max_point().row() {
|
||||||
|
map.line_len(new_row)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let max_column = match min_column + visible_column_count as u32 {
|
let max_column = match min_column + visible_column_count as u32 {
|
||||||
max_column if max_column >= max_row_column => max_row_column,
|
max_column if max_column >= max_row_column => max_row_column,
|
||||||
max_column => max_column,
|
max_column => max_column,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
description = "The fast, collaborative code editor."
|
description = "The fast, collaborative code editor."
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
name = "zed"
|
name = "zed"
|
||||||
version = "0.195.0"
|
version = "0.195.1"
|
||||||
publish.workspace = true
|
publish.workspace = true
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0-or-later"
|
||||||
authors = ["Zed Team <hi@zed.dev>"]
|
authors = ["Zed Team <hi@zed.dev>"]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
dev
|
preview
|
|
@ -44,8 +44,6 @@ function CheckEnvironmentVariables {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$innoDir = "$env:ZED_WORKSPACE\inno"
|
|
||||||
|
|
||||||
function PrepareForBundle {
|
function PrepareForBundle {
|
||||||
if (Test-Path "$innoDir") {
|
if (Test-Path "$innoDir") {
|
||||||
Remove-Item -Path "$innoDir" -Recurse -Force
|
Remove-Item -Path "$innoDir" -Recurse -Force
|
||||||
|
@ -236,6 +234,8 @@ function BuildInstaller {
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseZedWorkspace
|
ParseZedWorkspace
|
||||||
|
$innoDir = "$env:ZED_WORKSPACE\inno"
|
||||||
|
|
||||||
CheckEnvironmentVariables
|
CheckEnvironmentVariables
|
||||||
PrepareForBundle
|
PrepareForBundle
|
||||||
BuildZedAndItsFriends
|
BuildZedAndItsFriends
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue