Skip to content

Commit

Permalink
Clean up breakpoint prompt UI & allow editting of log messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony-Eid committed Oct 3, 2024
1 parent 876870e commit d6d7ceb
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 23 deletions.
79 changes: 68 additions & 11 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ use language::{
};
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
use linked_editing_ranges::refresh_linked_ranges;
use project::dap_store::BreakpointEditAction;
use proposed_changes_editor::{ProposedChangesBuffer, ProposedChangesEditor};
use similar::{ChangeTag, TextDiff};
use task::{ResolvedTask, TaskTemplate, TaskVariables};
Expand Down Expand Up @@ -5439,6 +5440,7 @@ impl Editor {
BreakpointKind::Log(_) => ui::IconName::DebugLogBreakpoint,
};
let arc_kind = Arc::new(kind.clone());
let arc_kind2 = arc_kind.clone();

IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
.icon_size(IconSize::XSmall)
Expand All @@ -5447,7 +5449,12 @@ impl Editor {
.style(ButtonStyle::Transparent)
.on_click(cx.listener(move |editor, _e, cx| {
editor.focus(cx);
editor.toggle_breakpoint_at_anchor(position, (*arc_kind).clone(), cx);
editor.edit_breakpoint_at_anchor(
position,
(*arc_kind).clone(),
BreakpointEditAction::Toggle,
cx,
);
}))
.on_right_click(cx.listener(move |editor, event: &ClickEvent, cx| {
let source = editor
Expand All @@ -5461,31 +5468,47 @@ impl Editor {
let editor_weak = cx.view().downgrade();
let second_weak = editor_weak.clone();

let log_message = arc_kind2.log_message();

let second_entry_msg = if log_message.is_some() {
"Edit Log Breakpoint"
} else {
"Toggle Log Breakpoint"
};

let context_menu = ui::ContextMenu::build(cx, move |menu, _cx| {
let anchor = position;
menu.on_blur_subscription(Subscription::new(|| {}))
.context(focus_handle)
.entry("Toggle Breakpoint", None, move |cx| {
if let Some(editor) = editor_weak.upgrade() {
editor.update(cx, |this, cx| {
this.toggle_breakpoint_at_anchor(
this.edit_breakpoint_at_anchor(
anchor,
BreakpointKind::Standard,
BreakpointEditAction::Toggle,
cx,
);
})
}
})
.entry("Toggle Log Breakpoint", None, move |cx| {
.entry(second_entry_msg, None, move |cx| {
if let Some(editor) = second_weak.clone().upgrade() {
let log_message = log_message.clone();
editor.update(cx, |this, cx| {
let position = this.snapshot(cx).display_point_to_anchor(
DisplayPoint::new(row, 0),
Bias::Right,
);

let weak_editor = cx.view().downgrade();
let bp_prompt = cx.new_view(|cx| {
BreakpointPromptEditor::new(weak_editor, anchor, cx)
BreakpointPromptEditor::new(
weak_editor,
anchor,
log_message,
cx,
)
});

let height = bp_prompt.update(cx, |this, cx| {
Expand All @@ -5498,13 +5521,18 @@ impl Editor {
style: BlockStyle::Sticky,
position,
height,
render: Box::new(move |_cx| {
render: Box::new(move |cx| {
*cloned_prompt.read(cx).gutter_dimensions.lock() =
*cx.gutter_dimensions;
cloned_prompt.clone().into_any_element()
}),
disposition: BlockDisposition::Above,
priority: 0,
}];

let focus_handle = bp_prompt.focus_handle(cx);
cx.focus(&focus_handle);

let block_ids = this.insert_blocks(blocks, None, cx);
bp_prompt.update(cx, |prompt, _| {
prompt.add_block_ids(block_ids);
Expand Down Expand Up @@ -6401,17 +6429,25 @@ impl Editor {
Some((bp.active_position?, bp.kind))
});

let edit_action = BreakpointEditAction::Toggle;

if let Some((anchor, kind)) = found_bp {
self.toggle_breakpoint_at_anchor(anchor, kind, cx);
self.edit_breakpoint_at_anchor(anchor, kind, edit_action, cx);
} else {
self.toggle_breakpoint_at_anchor(breakpoint_position, BreakpointKind::Standard, cx);
self.edit_breakpoint_at_anchor(
breakpoint_position,
BreakpointKind::Standard,
edit_action,
cx,
);
}
}

pub fn toggle_breakpoint_at_anchor(
pub fn edit_breakpoint_at_anchor(
&mut self,
breakpoint_position: text::Anchor,
kind: BreakpointKind,
edit_action: BreakpointEditAction,
cx: &mut ViewContext<Self>,
) {
let Some(project) = &self.project else {
Expand Down Expand Up @@ -6445,6 +6481,7 @@ impl Editor {
active_position: Some(breakpoint_position),
kind,
},
edit_action,
cx,
);
});
Expand Down Expand Up @@ -14175,6 +14212,7 @@ struct BreakpointPromptEditor {
editor: WeakView<Editor>,
breakpoint_anchor: text::Anchor,
block_ids: HashSet<CustomBlockId>,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
_subscriptions: Vec<Subscription>,
}

Expand All @@ -14184,9 +14222,15 @@ impl BreakpointPromptEditor {
fn new(
editor: WeakView<Editor>,
breakpoint_anchor: text::Anchor,
log_message: Option<Arc<str>>,
cx: &mut ViewContext<Self>,
) -> Self {
let buffer = cx.new_model(|cx| Buffer::local(String::new(), cx));
let buffer = cx.new_model(|cx| {
Buffer::local(
log_message.map(|msg| msg.to_string()).unwrap_or_default(),
cx,
)
});
let buffer = cx.new_model(|cx| MultiBuffer::singleton(buffer, cx));

let prompt = cx.new_view(|cx| {
Expand All @@ -14204,7 +14248,10 @@ impl BreakpointPromptEditor {
// always show the cursor (even when it isn't focused) because
// typing in one will make what you typed appear in all of them.
prompt.set_show_cursor_when_unfocused(true, cx);
prompt.set_placeholder_text("Add a prompt…", cx);
prompt.set_placeholder_text(
"Message to log when breakpoint is hit. Expressions within {} are interpolated.",
cx,
);

prompt
});
Expand All @@ -14213,6 +14260,7 @@ impl BreakpointPromptEditor {
prompt,
editor,
breakpoint_anchor,
gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())),
block_ids: Default::default(),
_subscriptions: vec![],
}
Expand All @@ -14236,9 +14284,10 @@ impl BreakpointPromptEditor {
.to_string();

editor.update(cx, |editor, cx| {
editor.toggle_breakpoint_at_anchor(
editor.edit_breakpoint_at_anchor(
self.breakpoint_anchor,
BreakpointKind::Log(log_message.into()),
BreakpointEditAction::EditLogMessage,
cx,
);

Expand Down Expand Up @@ -14284,6 +14333,7 @@ impl BreakpointPromptEditor {

impl Render for BreakpointPromptEditor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let gutter_dimensions = *self.gutter_dimensions.lock();
h_flex()
.key_context("Editor")
.bg(cx.theme().colors().editor_background)
Expand All @@ -14293,6 +14343,13 @@ impl Render for BreakpointPromptEditor {
.py(cx.line_height() / 2.5)
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.child(h_flex().w(gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)))
.child(div().flex_1().child(self.render_prompt_editor(cx)))
}
}

impl FocusableView for BreakpointPromptEditor {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
self.prompt.focus_handle(cx)
}
}
49 changes: 38 additions & 11 deletions crates/project/src/dap_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,16 +724,21 @@ impl DapStore {
breakpoint: Breakpoint,
buffer_path: PathBuf,
buffer_snapshot: BufferSnapshot,
edit_action: BreakpointEditAction,
cx: &mut ModelContext<Self>,
) {
let breakpoint_set = self.breakpoints.entry(project_path.clone()).or_default();

if let Some(gotten_breakpoint) = breakpoint_set.take(&breakpoint) {
if gotten_breakpoint.kind != breakpoint.kind {
match edit_action {
BreakpointEditAction::Toggle => {
if !breakpoint_set.remove(&breakpoint) {
breakpoint_set.insert(breakpoint);
}
}
BreakpointEditAction::EditLogMessage => {
breakpoint_set.remove(&breakpoint);
breakpoint_set.insert(breakpoint);
}
} else {
breakpoint_set.insert(breakpoint);
}

self.send_changed_breakpoints(project_path, buffer_path, buffer_snapshot, cx)
Expand Down Expand Up @@ -811,9 +816,16 @@ impl DapStore {
})
}
}

type LogMessage = Arc<str>;

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Clone, Debug)]
pub enum BreakpointEditAction {
Toggle,
EditLogMessage,
}

#[derive(Clone, Debug)]
pub enum BreakpointKind {
Standard,
Log(LogMessage),
Expand All @@ -826,6 +838,27 @@ impl BreakpointKind {
BreakpointKind::Log(_) => 1,
}
}

pub fn log_message(&self) -> Option<LogMessage> {
match self {
BreakpointKind::Standard => None,
BreakpointKind::Log(message) => Some(message.clone()),
}
}
}

impl PartialEq for BreakpointKind {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}

impl Eq for BreakpointKind {}

impl Hash for BreakpointKind {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
}
}

#[derive(Clone, Debug)]
Expand All @@ -841,10 +874,6 @@ pub struct Breakpoint {
// overlapping breakpoint's with them being aware.
impl PartialEq for Breakpoint {
fn eq(&self, other: &Self) -> bool {
if self.kind != other.kind {
return false;
}

match (&self.active_position, &other.active_position) {
(None, None) => self.cache_position == other.cache_position,
(None, Some(_)) => false,
Expand All @@ -863,8 +892,6 @@ impl Hash for Breakpoint {
} else {
self.cache_position.hash(state);
}

self.kind.hash(state);
}
}

Expand Down
4 changes: 3 additions & 1 deletion crates/project/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use dap::{
};

use collections::{BTreeSet, HashMap, HashSet};
use dap_store::{Breakpoint, DapStore, DapStoreEvent, SerializedBreakpoint};
use dap_store::{Breakpoint, BreakpointEditAction, DapStore, DapStoreEvent, SerializedBreakpoint};
use debounced_delay::DebouncedDelay;
pub use environment::ProjectEnvironment;
use futures::{
Expand Down Expand Up @@ -1280,6 +1280,7 @@ impl Project {
&self,
buffer_id: BufferId,
breakpoint: Breakpoint,
edit_action: BreakpointEditAction,
cx: &mut ModelContext<Self>,
) {
let Some(buffer) = self.buffer_for_id(buffer_id, cx) else {
Expand All @@ -1303,6 +1304,7 @@ impl Project {
breakpoint,
buffer_path,
buffer.read(cx).snapshot(),
edit_action,
cx,
);
});
Expand Down

0 comments on commit d6d7ceb

Please sign in to comment.