@@ -1,7 +1,7 @@
1use super::{align_view, Align, Context, Editor};
1use super::{align_view, Align, Context, Editor};
2use crate::{
2use crate::{
3− compositor::Compositor,
3+ compositor::{self, Compositor},
4− job::Callback,
4+ job::{Callback, Jobs},
5 ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent, Text},
5 ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent, Text},
7use helix_core::{
7use helix_core::{
@@ -16,8 +16,11 @@ use serde_json::{to_value, Value};
16use tokio_stream::wrappers::UnboundedReceiverStream;
16use tokio_stream::wrappers::UnboundedReceiverStream;
18use std::collections::HashMap;
18use std::collections::HashMap;
19+use std::future::Future;
19use std::path::PathBuf;
20use std::path::PathBuf;
21// general utils:
24// general utils:
22pub fn dap_pos_to_pos(doc: &helix_core::Rope, line: usize, column: usize) -> Option<usize> {
25pub fn dap_pos_to_pos(doc: &helix_core::Rope, line: usize, column: usize) -> Option<usize> {
23 // 1-indexing to 0 indexing
26 // 1-indexing to 0 indexing
@@ -174,23 +177,39 @@ fn get_breakpoint_at_current_line(editor: &mut Editor) -> Option<(usize, Breakpo
180+fn dap_callback<T, F>(
182+ call: impl Future<Output = helix_dap::Result<serde_json::Value>> + 'static + Send,
185+ T: for<'de> serde::Deserialize<'de> + Send + 'static,
186+ F: FnOnce(&mut Editor, &mut Compositor, T) + Send + 'static,
188+ let callback = Box::pin(async move {
189+ let json = call.await?;
190+ let response = serde_json::from_value(json)?;
191+ let call: Callback = Box::new(move |editor: &mut Editor, compositor: &mut Compositor| {
192+ callback(editor, compositor, response)
196+ jobs.callback(callback);
177pub fn dap_start_impl(
199pub fn dap_start_impl(
178− editor: &mut Editor,
200+ cx: &mut compositor::Context,
179 name: Option<&str>,
201 name: Option<&str>,
180 socket: Option<std::net::SocketAddr>,
202 socket: Option<std::net::SocketAddr>,
181 params: Option<Vec<&str>>,
203 params: Option<Vec<&str>>,
182−) {
204+) -> Result<(), anyhow::Error> {
183− let doc = doc!(editor);
205+ let doc = doc!(cx.editor);
185 let config = match doc
207 let config = match doc
186 .language_config()
208 .language_config()
187 .and_then(|config| config.debugger.as_ref())
209 .and_then(|config| config.debugger.as_ref())
189 Some(c) => c,
211 Some(c) => c,
191− editor.set_error("No debug adapter available for language".to_string());
212+ None => bail!("No debug adapter available for language"),
196 let result = match socket {
215 let result = match socket {
@@ -206,16 +225,12 @@ pub fn dap_start_impl(
207 let (mut debugger, events) = match result {
226 let (mut debugger, events) = match result {
208 Ok(r) => r,
227 Ok(r) => r,
210− editor.set_error(format!("Failed to start debug session: {}", e));
228+ Err(e) => bail!("Failed to start debug session: {}", e),
215 let request = debugger.initialize(config.name.clone());
231 let request = debugger.initialize(config.name.clone());
216 if let Err(e) = block_on(request) {
232 if let Err(e) = block_on(request) {
217− editor.set_error(format!("Failed to initialize debug adapter: {}", e));
233+ bail!("Failed to initialize debug adapter: {}", e);
221 debugger.quirks = config.quirks.clone();
236 debugger.quirks = config.quirks.clone();
@@ -227,10 +242,7 @@ pub fn dap_start_impl(
228 let template = match template {
243 let template = match template {
229 Some(template) => template,
244 Some(template) => template,
231− editor.set_error("No debug config with given name".to_string());
245+ None => bail!("No debug config with given name"),
236 let mut args: HashMap<&str, Value> = HashMap::new();
248 let mut args: HashMap<&str, Value> = HashMap::new();
@@ -282,28 +294,29 @@ pub fn dap_start_impl(
283 let args = to_value(args).unwrap();
295 let args = to_value(args).unwrap();
285− // problem: this blocks for too long while we get back the startInTerminal REQ
297+ let callback = |_editor: &mut Editor, _compositor: &mut Compositor, _response: Value| {
298+ // if let Err(e) = result {
299+ // editor.set_error(format!("Failed {} target: {}", template.request, e));
287− log::error!("pre start");
288− let result = match &template.request[..] {
303+ match &template.request[..] {
289− "launch" => block_on(debugger.launch(args)),
305+ let call = debugger.launch(args);
306+ dap_callback(cx.jobs, call, callback);
290− "attach" => block_on(debugger.attach(args)),
309+ let call = debugger.attach(args);
291− _ => {
310+ dap_callback(cx.jobs, call, callback);
292− editor.set_error("Unsupported request".to_string());
312+ request => bail!("Unsupported request '{}'", request),
296− log::error!("post start");
297− if let Err(e) = result {
298− let msg = format!("Failed {} target: {}", template.request, e);
299− editor.set_error(msg);
303 // TODO: either await "initialized" or buffer commands until event is received
315 // TODO: either await "initialized" or buffer commands until event is received
304− editor.debugger = Some(debugger);
316+ cx.editor.debugger = Some(debugger);
305 let stream = UnboundedReceiverStream::new(events);
317 let stream = UnboundedReceiverStream::new(events);
306− editor.debugger_events.push(stream);
318+ cx.editor.debugger_events.push(stream);
309pub fn dap_launch(cx: &mut Context) {
322pub fn dap_launch(cx: &mut Context) {
@@ -404,12 +417,14 @@ fn debug_parameter_prompt(
405 cx.jobs.callback(callback);
418 cx.jobs.callback(callback);
407− dap_start_impl(
420+ if let Err(e) = dap_start_impl(
409 Some(&config_name),
422 Some(&config_name),
411 Some(params.iter().map(|x| x.as_str()).collect()),
424 Some(params.iter().map(|x| x.as_str()).collect()),
426+ cx.editor.set_error(e.to_string());