Skip to content

Commit

Permalink
Debug output grouping (#86)
Browse files Browse the repository at this point in the history
* Remove output editor

* Implement output grouping

* Remove OutputGroup when we found the end position

* Fix make gutter smaller

* Render placeholder

* Show group end on the same level as group start

* Add tests

* Add support for collapsed grouped output

* Fix crease placeholder is not showing up

* Don't trim output multiple times

* Update tests

* Fix clippy
  • Loading branch information
RemcoSmitsDev authored Jan 14, 2025
1 parent a7e26bb commit 8ecd547
Show file tree
Hide file tree
Showing 5 changed files with 580 additions and 217 deletions.
141 changes: 133 additions & 8 deletions crates/debugger_ui/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,31 @@ use crate::{
stack_frame_list::{StackFrameList, StackFrameListEvent},
variable_list::VariableList,
};
use dap::client::DebugAdapterClientId;
use editor::{CompletionProvider, Editor, EditorElement, EditorStyle};
use dap::{client::DebugAdapterClientId, OutputEvent, OutputEventGroup};
use editor::{
display_map::{Crease, CreaseId},
Anchor, CompletionProvider, Editor, EditorElement, EditorStyle, FoldPlaceholder,
};
use fuzzy::StringMatchCandidate;
use gpui::{Model, Render, Subscription, Task, TextStyle, View, ViewContext, WeakView};
use language::{Buffer, CodeLabel, LanguageServerId, ToOffsetUtf16};
use menu::Confirm;
use project::{dap_store::DapStore, Completion};
use settings::Settings;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
use theme::ThemeSettings;
use ui::prelude::*;
use ui::{prelude::*, ButtonLike, Disclosure, ElevationIndex};

pub struct OutputGroup {
pub start: Anchor,
pub collapsed: bool,
pub end: Option<Anchor>,
pub crease_ids: Vec<CreaseId>,
pub placeholder: SharedString,
}

pub struct Console {
groups: Vec<OutputGroup>,
console: View<Editor>,
query_bar: View<Editor>,
dap_store: Model<DapStore>,
Expand All @@ -36,7 +48,13 @@ impl Console {
let mut editor = Editor::multi_line(cx);
editor.move_to_end(&editor::actions::MoveToEnd, cx);
editor.set_read_only(true);
editor.set_show_gutter(false, cx);
editor.set_show_gutter(true, cx);
editor.set_show_runnables(false, cx);
editor.set_show_code_actions(false, cx);
editor.set_show_line_numbers(false, cx);
editor.set_show_git_diff_gutter(false, cx);
editor.set_autoindent(false);
editor.set_input_enabled(false);
editor.set_use_autoclose(false);
editor.set_show_wrap_guides(false, cx);
editor.set_show_indent_guides(false, cx);
Expand Down Expand Up @@ -67,6 +85,7 @@ impl Console {
variable_list,
_subscriptions,
client_id: *client_id,
groups: Vec::default(),
stack_frame_list: stack_frame_list.clone(),
}
}
Expand All @@ -93,15 +112,109 @@ impl Console {
}
}

pub fn add_message(&mut self, message: &str, cx: &mut ViewContext<Self>) {
pub fn add_message(&mut self, event: OutputEvent, cx: &mut ViewContext<Self>) {
self.console.update(cx, |console, cx| {
let output = event.output.trim_end().to_string();

let snapshot = console.buffer().read(cx).snapshot(cx);

let start = snapshot.anchor_before(snapshot.max_point());

let mut indent_size = self
.groups
.iter()
.filter(|group| group.end.is_none())
.count();
if Some(OutputEventGroup::End) == event.group {
indent_size = indent_size.saturating_sub(1);
}

let indent = if indent_size > 0 {
" ".repeat(indent_size)
} else {
"".to_string()
};

console.set_read_only(false);
console.move_to_end(&editor::actions::MoveToEnd, cx);
console.insert(format!("{}\n", message.trim_end()).as_str(), cx);
console.insert(format!("{}{}\n", indent, output).as_str(), cx);
console.set_read_only(true);

let end = snapshot.anchor_before(snapshot.max_point());

match event.group {
Some(OutputEventGroup::Start) => {
self.groups.push(OutputGroup {
start,
end: None,
collapsed: false,
placeholder: output.clone().into(),
crease_ids: console.insert_creases(
vec![Self::create_crease(output.into(), start, end)],
cx,
),
});
}
Some(OutputEventGroup::StartCollapsed) => {
self.groups.push(OutputGroup {
start,
end: None,
collapsed: true,
placeholder: output.clone().into(),
crease_ids: console.insert_creases(
vec![Self::create_crease(output.into(), start, end)],
cx,
),
});
}
Some(OutputEventGroup::End) => {
if let Some(index) = self.groups.iter().rposition(|group| group.end.is_none()) {
let group = self.groups.remove(index);

console.remove_creases(group.crease_ids.clone(), cx);

let creases =
vec![Self::create_crease(group.placeholder, group.start, end)];
console.insert_creases(creases.clone(), cx);

if group.collapsed {
console.fold_creases(creases, false, cx);
}
}
}
None => {}
}

cx.notify();
});
}

fn create_crease(placeholder: SharedString, start: Anchor, end: Anchor) -> Crease<Anchor> {
Crease::inline(
start..end,
FoldPlaceholder {
render: Arc::new({
let placeholder = placeholder.clone();
move |_id, _range, _cx| {
ButtonLike::new("output-group-placeholder")
.style(ButtonStyle::Transparent)
.layer(ElevationIndex::ElevatedSurface)
.child(Label::new(placeholder.clone()).single_line())
.into_any_element()
}
}),
..Default::default()
},
move |row, is_folded, fold, _cx| {
Disclosure::new(("output-group", row.0 as u64), !is_folded)
.toggle_state(is_folded)
.on_click(move |_event, cx| fold(!is_folded, cx))
.into_any_element()
},
move |_id, _range, _cx| gpui::Empty.into_any_element(),
)
}

pub fn evaluate(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
let expression = self.query_bar.update(cx, |editor, cx| {
let expression = editor.text(cx);
Expand All @@ -125,7 +238,19 @@ impl Console {
let response = evaluate_task.await?;

this.update(&mut cx, |console, cx| {
console.add_message(&response.result, cx);
console.add_message(
OutputEvent {
category: None,
output: response.result,
group: None,
variables_reference: Some(response.variables_reference),
source: None,
line: None,
column: None,
data: None,
},
cx,
);

console.variable_list.update(cx, |variable_list, cx| {
variable_list.invalidate(cx);
Expand Down
57 changes: 6 additions & 51 deletions crates/debugger_ui/src/debugger_panel_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ enum ThreadItem {
Console,
LoadedSource,
Modules,
Output,
Variables,
}

Expand All @@ -48,7 +47,6 @@ impl ThreadItem {
ThreadItem::Console => proto::DebuggerThreadItem::Console,
ThreadItem::LoadedSource => proto::DebuggerThreadItem::LoadedSource,
ThreadItem::Modules => proto::DebuggerThreadItem::Modules,
ThreadItem::Output => proto::DebuggerThreadItem::Output,
ThreadItem::Variables => proto::DebuggerThreadItem::Variables,
}
}
Expand All @@ -58,7 +56,6 @@ impl ThreadItem {
proto::DebuggerThreadItem::Console => ThreadItem::Console,
proto::DebuggerThreadItem::LoadedSource => ThreadItem::LoadedSource,
proto::DebuggerThreadItem::Modules => ThreadItem::Modules,
proto::DebuggerThreadItem::Output => ThreadItem::Output,
proto::DebuggerThreadItem::Variables => ThreadItem::Variables,
}
}
Expand All @@ -72,7 +69,6 @@ pub struct DebugPanelItem {
session_name: SharedString,
dap_store: Model<DapStore>,
session_id: DebugSessionId,
output_editor: View<Editor>,
show_console_indicator: bool,
module_list: View<ModuleList>,
active_thread_item: ThreadItem,
Expand Down Expand Up @@ -179,20 +175,6 @@ impl DebugPanelItem {
),
];

let output_editor = cx.new_view(|cx| {
let mut editor = Editor::multi_line(cx);
editor.set_placeholder_text("Debug adapter and script output", cx);
editor.set_read_only(true);
editor.set_show_inline_completions(Some(false), cx);
editor.set_searchable(false);
editor.set_auto_replace_emoji_shortcode(false);
editor.set_show_indent_guides(false, cx);
editor.set_autoindent(false);
editor.set_show_gutter(false, cx);
editor.set_show_line_numbers(false, cx);
editor
});

Self {
console,
thread_id,
Expand All @@ -202,7 +184,6 @@ impl DebugPanelItem {
module_list,
thread_state,
focus_handle,
output_editor,
variable_list,
_subscriptions,
remote_id: None,
Expand Down Expand Up @@ -347,6 +328,7 @@ impl DebugPanelItem {
return;
}

// skip telemetry output as it pollutes the users output view
let output_category = event
.category
.as_ref()
Expand All @@ -357,25 +339,11 @@ impl DebugPanelItem {
return;
}

match output_category {
OutputEventCategory::Console => {
self.console.update(cx, |console, cx| {
console.add_message(&event.output, cx);
});

if !matches!(self.active_thread_item, ThreadItem::Console) {
self.show_console_indicator = true;
}
}
_ => {
self.output_editor.update(cx, |editor, cx| {
editor.set_read_only(false);
editor.move_to_end(&editor::actions::MoveToEnd, cx);
editor.insert(format!("{}\n", &event.output.trim_end()).as_str(), cx);
editor.set_read_only(true);
});
}
}
self.console.update(cx, |console, cx| {
console.add_message(event.clone(), cx);
});
self.show_console_indicator = true;
cx.notify();
}

fn handle_module_event(
Expand Down Expand Up @@ -515,11 +483,6 @@ impl DebugPanelItem {
&self.stack_frame_list
}

#[cfg(any(test, feature = "test-support"))]
pub fn output_editor(&self) -> &View<Editor> {
&self.output_editor
}

#[cfg(any(test, feature = "test-support"))]
pub fn console(&self) -> &View<Console> {
&self.console
Expand Down Expand Up @@ -1046,11 +1009,6 @@ impl Render for DebugPanelItem {
&SharedString::from("Console"),
ThreadItem::Console,
cx,
))
.child(self.render_entry_button(
&SharedString::from("Output"),
ThreadItem::Output,
cx,
)),
)
.when(*active_thread_item == ThreadItem::Variables, |this| {
Expand All @@ -1062,9 +1020,6 @@ impl Render for DebugPanelItem {
.when(*active_thread_item == ThreadItem::LoadedSource, |this| {
this.size_full().child(self.loaded_source_list.clone())
})
.when(*active_thread_item == ThreadItem::Output, |this| {
this.child(self.output_editor.clone())
})
.when(*active_thread_item == ThreadItem::Console, |this| {
this.child(self.console.clone())
}),
Expand Down
Loading

0 comments on commit 8ecd547

Please sign in to comment.