@@ -11,11 +11,13 @@ use helix_core::{
1111};
1212use helix_dap::{self as dap, Client, ThreadId};
1313use helix_lsp::block_on;
14+use helix_view::editor::Breakpoint;
1415
1516use serde_json::{to_value, Value};
1617use tokio_stream::wrappers::UnboundedReceiverStream;
1718
1819use std::collections::HashMap;
20+use std::path::PathBuf;
1921
2022// general utils:
2123pub fn dap_pos_to_pos(doc: &helix_core::Rope, line: usize, column: usize) -> Option<usize> {
@@ -406,40 +408,90 @@ pub fn dap_toggle_breakpoint(cx: &mut Context) {
406408 dap_toggle_breakpoint_impl(cx, path, line);
407409}
408410
409−pub fn dap_toggle_breakpoint_impl(cx: &mut Context, path: std::path::PathBuf, line: usize) {
410− let breakpoint = helix_dap::SourceBreakpoint {
411− line: line + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
412− ..Default::default()
411+pub fn breakpoints_changed(
412+ debugger: &mut dap::Client,
413+ path: PathBuf,
414+ breakpoints: &mut [Breakpoint],
415+) -> Result<(), anyhow::Error> {
416+ // TODO: handle capabilities correctly again, by filterin breakpoints when emitting
417+ // if breakpoint.condition.is_some()
418+ // && !debugger
419+ // .caps
420+ // .as_ref()
421+ // .unwrap()
422+ // .supports_conditional_breakpoints
423+ // .unwrap_or_default()
424+ // {
425+ // bail!(
426+ // "Can't edit breakpoint: debugger does not support conditional breakpoints"
427+ // )
428+ // }
429+ // if breakpoint.log_message.is_some()
430+ // && !debugger
431+ // .caps
432+ // .as_ref()
433+ // .unwrap()
434+ // .supports_log_points
435+ // .unwrap_or_default()
436+ // {
437+ // bail!("Can't edit breakpoint: debugger does not support logpoints")
438+ // }
439+ let source_breakpoints = breakpoints
440+ .iter()
441+ .map(|breakpoint| helix_dap::SourceBreakpoint {
442+ line: breakpoint.line + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
443+ ..Default::default()
444+ })
445+ .collect::<Vec<_>>();
446+
447+ let request = debugger.set_breakpoints(path, source_breakpoints);
448+ match block_on(request) {
449+ Ok(Some(dap_breakpoints)) => {
450+ for (breakpoint, dap_breakpoint) in breakpoints.iter_mut().zip(dap_breakpoints) {
451+ breakpoint.id = dap_breakpoint.id;
452+ breakpoint.verified = dap_breakpoint.verified;
453+ breakpoint.message = dap_breakpoint.message;
454+ // TODO: handle breakpoint.message
455+ // TODO: verify source matches
456+ breakpoint.line = dap_breakpoint.line.unwrap_or(0).saturating_sub(1); // convert to 0-indexing
457+ // TODO: no unwrap
458+ breakpoint.column = dap_breakpoint.column;
459+ // TODO: verify end_linef/col instruction reference, offset
460+ }
461+ }
462+ Err(e) => anyhow::bail!("Failed to set breakpoints: {}", e),
463+ _ => {}
413464 };
465+ Ok(())
466+}
414467
468+pub fn dap_toggle_breakpoint_impl(cx: &mut Context, path: PathBuf, line: usize) {
415469 // TODO: need to map breakpoints over edits and update them?
416470 // we shouldn't really allow editing while debug is running though
417471
418472 let breakpoints = cx.editor.breakpoints.entry(path.clone()).or_default();
419− // TODO: always keep breakpoints sorted and use binary search
420− if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
473+ // TODO: always keep breakpoints sorted and use binary search to determine insertion point
474+ if let Some(pos) = breakpoints
475+ .iter()
476+ .position(|breakpoint| breakpoint.line == line)
477+ {
421478 breakpoints.remove(pos);
422479 } else {
423− breakpoints.push(breakpoint);
480+ breakpoints.push(Breakpoint {
481+ line,
482+ ..Default::default()
483+ });
424484 }
425485
426− let breakpoints = breakpoints.clone();
427−
428486 let debugger = match &mut cx.editor.debugger {
429487 Some(debugger) => debugger,
430488 None => return,
431489 };
432− let request = debugger.set_breakpoints(path, breakpoints);
433− match block_on(request) {
434− Ok(Some(breakpoints)) => {
435− // TODO: handle breakpoint.message
436− debugger.breakpoints = breakpoints;
437− }
438− Err(e) => cx
439− .editor
440− .set_error(format!("Failed to set breakpoints: {}", e)),
441− _ => {}
442− };
490+
491+ if let Err(e) = breakpoints_changed(debugger, path, breakpoints) {
492+ cx.editor
493+ .set_error(format!("Failed to set breakpoints: {}", e));
494+ }
443495}
444496
445497pub fn dap_continue(cx: &mut Context) {
@@ -651,36 +703,15 @@ pub fn dap_edit_condition(cx: &mut Context) {
651703 input => Some(input.to_owned()),
652704 };
653705
654− if let Some(debugger) = &mut cx.editor.debugger {
655− // TODO: handle capabilities correctly again, by filterin breakpoints when emitting
656− // if breakpoint.condition.is_some()
657− // && !debugger
658− // .caps
659− // .as_ref()
660− // .unwrap()
661− // .supports_conditional_breakpoints
662− // .unwrap_or_default()
663− // {
664− // bail!(
665− // "Can't edit breakpoint: debugger does not support conditional breakpoints"
666− // )
667− // }
668− // if breakpoint.log_message.is_some()
669− // && !debugger
670− // .caps
671− // .as_ref()
672− // .unwrap()
673− // .supports_log_points
674− // .unwrap_or_default()
675− // {
676− // bail!("Can't edit breakpoint: debugger does not support logpoints")
677− // }
678− let request =
679− debugger.set_breakpoints(path.clone(), breakpoints.clone());
680− if let Err(e) = block_on(request) {
681− cx.editor
682− .set_status(format!("Failed to set breakpoints: {}", e))
683− }
706+ let debugger = match &mut cx.editor.debugger {
707+ Some(debugger) => debugger,
708+ None => return,
709+ };
710+
711+ if let Err(e) = breakpoints_changed(debugger, path.clone(), breakpoints)
712+ {
713+ cx.editor
714+ .set_error(format!("Failed to set breakpoints: {}", e));
684715 }
685716 },
686717 );
@@ -719,36 +750,14 @@ pub fn dap_edit_log(cx: &mut Context) {
719750 input => Some(input.to_owned()),
720751 };
721752
722− if let Some(debugger) = &mut cx.editor.debugger {
723− // TODO: handle capabilities correctly again, by filterin breakpoints when emitting
724− // if breakpoint.condition.is_some()
725− // && !debugger
726− // .caps
727− // .as_ref()
728− // .unwrap()
729− // .supports_conditional_breakpoints
730− // .unwrap_or_default()
731− // {
732− // bail!(
733− // "Can't edit breakpoint: debugger does not support conditional breakpoints"
734− // )
735− // }
736− // if breakpoint.log_message.is_some()
737− // && !debugger
738− // .caps
739− // .as_ref()
740− // .unwrap()
741− // .supports_log_points
742− // .unwrap_or_default()
743− // {
744− // bail!("Can't edit breakpoint: debugger does not support logpoints")
745− // }
746− let request =
747− debugger.set_breakpoints(path.clone(), breakpoints.clone());
748− if let Err(e) = block_on(request) {
749− cx.editor
750− .set_status(format!("Failed to set breakpoints: {}", e))
751− }
753+ let debugger = match &mut cx.editor.debugger {
754+ Some(debugger) => debugger,
755+ None => return,
756+ };
757+ if let Err(e) = breakpoints_changed(debugger, path.clone(), breakpoints)
758+ {
759+ cx.editor
760+ .set_error(format!("Failed to set breakpoints: {}", e));
752761 }
753762 },
754763 );