From ab180855de57fe3aec9a61fa68bb149ccfcffa34 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 28 Apr 2025 21:53:57 -0600 Subject: [PATCH] Debug console tweaks (#29586) Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: Anthony Eid Co-authored-by: Cole Miller --- .../src/session/running/console.rs | 67 +++--- crates/project/src/debugger/dap_command.rs | 4 +- crates/project/src/debugger/dap_store.rs | 16 +- crates/project/src/debugger/session.rs | 197 +++++++++--------- 4 files changed, 135 insertions(+), 149 deletions(-) diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index 2fb43fcb9a983531cdccb32df484adab323530cd..c960b9763cfd465592ccf552fd5ebea30861c180 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -45,7 +45,7 @@ impl Console { let mut editor = Editor::multi_line(window, cx); editor.move_to_end(&editor::actions::MoveToEnd, window, cx); editor.set_read_only(true); - editor.set_show_gutter(true, cx); + editor.set_show_gutter(false, cx); editor.set_show_runnables(false, cx); editor.set_show_breakpoints(false, cx); editor.set_show_code_actions(false, cx); @@ -57,6 +57,8 @@ impl Console { editor.set_show_wrap_guides(false, cx); editor.set_show_indent_guides(false, cx); editor.set_show_edit_predictions(Some(false), window, cx); + editor.set_use_modal_editing(false); + editor.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx); editor }); let focus_handle = cx.focus_handle(); @@ -146,6 +148,23 @@ impl Console { expression }); + self.add_messages( + [OutputEvent { + category: None, + output: format!("> {expression}"), + group: None, + variables_reference: None, + source: None, + line: None, + column: None, + data: None, + location_reference: None, + }] + .iter(), + window, + cx, + ); + self.session.update(cx, |session, cx| { session .evaluate( @@ -160,6 +179,10 @@ impl Console { } fn render_console(&self, cx: &Context) -> impl IntoElement { + EditorElement::new(&self.console, self.editor_style(cx)) + } + + fn editor_style(&self, cx: &Context) -> EditorStyle { let settings = ThemeSettings::get_global(cx); let text_style = TextStyle { color: if self.console.read(cx).read_only(cx) { @@ -174,44 +197,16 @@ impl Console { line_height: relative(settings.buffer_line_height.value()), ..Default::default() }; - - EditorElement::new( - &self.console, - EditorStyle { - background: cx.theme().colors().editor_background, - local_player: cx.theme().players().local(), - text: text_style, - ..Default::default() - }, - ) + EditorStyle { + background: cx.theme().colors().editor_background, + local_player: cx.theme().players().local(), + text: text_style, + ..Default::default() + } } fn render_query_bar(&self, cx: &Context) -> impl IntoElement { - let settings = ThemeSettings::get_global(cx); - let text_style = TextStyle { - color: if self.console.read(cx).read_only(cx) { - cx.theme().colors().text_disabled - } else { - cx.theme().colors().text - }, - font_family: settings.ui_font.family.clone(), - font_features: settings.ui_font.features.clone(), - font_fallbacks: settings.ui_font.fallbacks.clone(), - font_size: TextSize::Editor.rems(cx).into(), - font_weight: settings.ui_font.weight, - line_height: relative(1.3), - ..Default::default() - }; - - EditorElement::new( - &self.query_bar, - EditorStyle { - background: cx.theme().colors().editor_background, - local_player: cx.theme().players().local(), - text: text_style, - ..Default::default() - }, - ) + EditorElement::new(&self.query_bar, self.editor_style(cx)) } } diff --git a/crates/project/src/debugger/dap_command.rs b/crates/project/src/debugger/dap_command.rs index 4eac9f0c285f375df2820de219b1bf73993e84e2..b6133ddeb084650a8dcac57cea646cbe47ccc70b 100644 --- a/crates/project/src/debugger/dap_command.rs +++ b/crates/project/src/debugger/dap_command.rs @@ -14,7 +14,7 @@ use rpc::proto; use serde_json::Value; use util::ResultExt; -pub(crate) trait LocalDapCommand: 'static + Send + Sync + std::fmt::Debug { +pub trait LocalDapCommand: 'static + Send + Sync + std::fmt::Debug { type Response: 'static + Send + std::fmt::Debug; type DapRequest: 'static + Send + dap::requests::Request; @@ -30,7 +30,7 @@ pub(crate) trait LocalDapCommand: 'static + Send + Sync + std::fmt::Debug { ) -> Result; } -pub(crate) trait DapCommand: LocalDapCommand { +pub trait DapCommand: LocalDapCommand { type ProtoRequest: 'static + Send; type ProtoResponse: 'static + Send; const CACHEABLE: bool = false; diff --git a/crates/project/src/debugger/dap_store.rs b/crates/project/src/debugger/dap_store.rs index b19445e85d6f77fe6639134a85f499ad72de12d2..807e0a868985c30719cb2525804b406a490e64e5 100644 --- a/crates/project/src/debugger/dap_store.rs +++ b/crates/project/src/debugger/dap_store.rs @@ -1,5 +1,6 @@ use super::{ breakpoint_store::BreakpointStore, + dap_command::EvaluateCommand, locators, session::{self, Session, SessionStateEvent}, }; @@ -897,19 +898,18 @@ impl DapStore { .clone() .unwrap_or_else(|| snapshot.text_for_range(range.clone()).collect()); - let Ok(eval_task) = session.update(cx, |session, cx| { - session.evaluate( + let Ok(eval_task) = session.update(cx, |session, _| { + session.mode.request_dap(EvaluateCommand { expression, - Some(EvaluateArgumentsContext::Variables), - Some(stack_frame_id), - None, - cx, - ) + frame_id: Some(stack_frame_id), + source: None, + context: Some(EvaluateArgumentsContext::Variables), + }) }) else { continue; }; - if let Some(response) = eval_task.await { + if let Some(response) = eval_task.await.log_err() { inlay_hints.push(InlayHint { position: snapshot.anchor_after(range.end), label: InlayHintLabel::String(format!(": {}", response.result)), diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index cd6345e6f02f6f7ad80ac3c267c7d2a1fcfaf2b0..3e457a8a6d66ae4e8ede93549c847b94aa5459e7 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -20,9 +20,7 @@ use dap::{ client::{DebugAdapterClient, SessionId}, messages::{Events, Message}, }; -use dap::{ - EvaluateResponse, ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory, -}; +use dap::{ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory}; use futures::channel::oneshot; use futures::{FutureExt, future::Shared}; use gpui::{ @@ -115,7 +113,7 @@ impl From for Thread { } } -enum Mode { +pub enum Mode { Building, Running(LocalMode), } @@ -127,6 +125,7 @@ pub struct LocalMode { pub(crate) breakpoint_store: Entity, tmp_breakpoint: Option, worktree: WeakEntity, + executor: BackgroundExecutor, } fn client_source(abs_path: &Path) -> dap::Source { @@ -179,6 +178,7 @@ impl LocalMode { worktree, tmp_breakpoint: None, binary, + executor: cx.background_executor().clone(), }) } @@ -190,14 +190,11 @@ impl LocalMode { let tasks: Vec<_> = paths .into_iter() .map(|path| { - self.request( - dap_command::SetBreakpoints { - source: client_source(path), - source_modified: None, - breakpoints: vec![], - }, - cx.background_executor().clone(), - ) + self.request(dap_command::SetBreakpoints { + source: client_source(path), + source_modified: None, + breakpoints: vec![], + }) }) .collect(); @@ -229,14 +226,11 @@ impl LocalMode { .map(Into::into) .collect(); - let task = self.request( - dap_command::SetBreakpoints { - source: client_source(&abs_path), - source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)), - breakpoints, - }, - cx.background_executor().clone(), - ); + let task = self.request(dap_command::SetBreakpoints { + source: client_source(&abs_path), + source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)), + breakpoints, + }); cx.background_spawn(async move { match task.await { @@ -250,7 +244,6 @@ impl LocalMode { &self, filters: Vec, supports_filter_options: bool, - cx: &App, ) -> Task>> { let arg = if supports_filter_options { SetExceptionBreakpoints::WithOptions { @@ -268,7 +261,7 @@ impl LocalMode { filters: filters.into_iter().map(|filter| filter.filter).collect(), } }; - self.request(arg, cx.background_executor().clone()) + self.request(arg) } fn send_source_breakpoints( @@ -293,14 +286,11 @@ impl LocalMode { }; breakpoint_tasks.push( - self.request( - dap_command::SetBreakpoints { - source: client_source(&path), - source_modified: Some(false), - breakpoints, - }, - cx.background_executor().clone(), - ) + self.request(dap_command::SetBreakpoints { + source: client_source(&path), + source_modified: Some(false), + breakpoints, + }) .map(|result| result.map_err(|e| (path, e))), ); } @@ -331,18 +321,12 @@ impl LocalMode { // Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522 let launch = match raw.request { - dap::StartDebuggingRequestArgumentsRequest::Launch => self.request( - Launch { - raw: raw.configuration, - }, - cx.background_executor().clone(), - ), - dap::StartDebuggingRequestArgumentsRequest::Attach => self.request( - Attach { - raw: raw.configuration, - }, - cx.background_executor().clone(), - ), + dap::StartDebuggingRequestArgumentsRequest::Launch => self.request(Launch { + raw: raw.configuration, + }), + dap::StartDebuggingRequestArgumentsRequest::Attach => self.request(Attach { + raw: raw.configuration, + }), }; let configuration_done_supported = ConfigurationDone::is_supported(capabilities); @@ -396,17 +380,11 @@ impl LocalMode { } })?; - cx.update(|cx| { - this.send_exception_breakpoints( - exception_filters, - supports_exception_filters, - cx, - ) - })? - .await - .ok(); + this.send_exception_breakpoints(exception_filters, supports_exception_filters) + .await + .ok(); let ret = if configuration_done_supported { - this.request(ConfigurationDone {}, cx.background_executor().clone()) + this.request(ConfigurationDone {}) } else { Task::ready(Ok(())) } @@ -421,11 +399,7 @@ impl LocalMode { }) } - fn request( - &self, - request: R, - executor: BackgroundExecutor, - ) -> Task> + fn request(&self, request: R) -> Task> where ::Response: 'static, ::Arguments: 'static + Send, @@ -434,32 +408,22 @@ impl LocalMode { let request_clone = request.clone(); let connection = self.client.clone(); - let request_task = executor.spawn(async move { + self.executor.spawn(async move { let args = request_clone.to_dap(); - connection.request::(args).await - }); - - executor.spawn(async move { - let response = request.response_from_dap(request_task.await?); - response + let response = connection.request::(args).await?; + request.response_from_dap(response) }) } } impl Mode { - fn request_dap( - &self, - request: R, - cx: &mut Context, - ) -> Task> + pub(super) fn request_dap(&self, request: R) -> Task> where ::Response: 'static, ::Arguments: 'static + Send, { match self { - Mode::Running(debug_adapter_client) => { - debug_adapter_client.request(request, cx.background_executor().clone()) - } + Mode::Running(debug_adapter_client) => debug_adapter_client.request(request), Mode::Building => Task::ready(Err(anyhow!( "no adapter running to send request: {:?}", request @@ -539,7 +503,7 @@ type IsEnabled = bool; pub struct OutputToken(pub usize); /// Represents a current state of a single debug adapter and provides ways to mutate it. pub struct Session { - mode: Mode, + pub mode: Mode, definition: DebugTaskDefinition, pub(super) capabilities: Capabilities, id: SessionId, @@ -871,7 +835,7 @@ impl Session { let request = Initialize { adapter_id }; match &self.mode { Mode::Running(local_mode) => { - let capabilities = local_mode.request(request, cx.background_executor().clone()); + let capabilities = local_mode.request(request); cx.spawn(async move |this, cx| { let capabilities = capabilities.await?; @@ -1196,6 +1160,17 @@ impl Session { } } + pub async fn request2( + &self, + request: T, + ) -> Result { + if !T::is_supported(&self.capabilities) { + anyhow::bail!("DAP request {:?} is not supported", request); + } + + self.mode.request_dap(request).await + } + fn request_inner( capabilities: &Capabilities, mode: &Mode, @@ -1223,7 +1198,7 @@ impl Session { }); } - let request = mode.request_dap(request, cx); + let request = mode.request_dap(request); cx.spawn(async move |this, cx| { let result = request.await; this.update(cx, |this, cx| process_result(this, result, cx)) @@ -1377,7 +1352,7 @@ impl Session { .supports_exception_filter_options .unwrap_or_default(); local - .send_exception_breakpoints(exception_filters, supports_exception_filters, cx) + .send_exception_breakpoints(exception_filters, supports_exception_filters) .detach_and_log_err(cx); } else { debug_assert!(false, "Not implemented"); @@ -1878,35 +1853,51 @@ impl Session { frame_id: Option, source: Option, cx: &mut Context, - ) -> Task> { - self.request( - EvaluateCommand { - expression, - context, - frame_id, - source, - }, - |this, response, cx| { - let response = response.log_err()?; - this.output_token.0 += 1; - this.output.push_back(dap::OutputEvent { - category: None, - output: response.result.clone(), - group: None, - variables_reference: Some(response.variables_reference), - source: None, - line: None, - column: None, - data: None, - location_reference: None, - }); - + ) -> Task<()> { + let request = self.mode.request_dap(EvaluateCommand { + expression, + context, + frame_id, + source, + }); + cx.spawn(async move |this, cx| { + let response = request.await; + this.update(cx, |this, cx| { + match response { + Ok(response) => { + this.output_token.0 += 1; + this.output.push_back(dap::OutputEvent { + category: None, + output: format!("< {}", &response.result), + group: None, + variables_reference: Some(response.variables_reference), + source: None, + line: None, + column: None, + data: None, + location_reference: None, + }); + } + Err(e) => { + this.output_token.0 += 1; + this.output.push_back(dap::OutputEvent { + category: None, + output: format!("{}", e), + group: None, + variables_reference: None, + source: None, + line: None, + column: None, + data: None, + location_reference: None, + }); + } + }; this.invalidate_command_type::(); cx.notify(); - Some(response) - }, - cx, - ) + }) + .ok(); + }) } pub fn location(