on master help sign in
dap: Reply to RunInTerminal
2b4de41Blaž Hrastnik
helix-dap/src/client.rs+2−1
@@ -262,10 +262,11 @@ impl Client {
262 pub fn reply(
262 pub fn reply(
263 &self,
263 &self,
264 request_seq: u64,
264 request_seq: u64,
265− command: String,
265+ command: &str,
266 result: core::result::Result<Value, Error>,
266 result: core::result::Result<Value, Error>,
267 ) -> impl Future<Output = Result<()>> {
267 ) -> impl Future<Output = Result<()>> {
268 let server_tx = self.server_tx.clone();
268 let server_tx = self.server_tx.clone();
269+ let command = command.to_string();
269
270
270 async move {
271 async move {
271 let response = match result {
272 let response = match result {
helix-dap/src/types.rs+4−4
@@ -545,11 +545,11 @@ pub mod requests {
545
545
546 // Reverse Requests
546 // Reverse Requests
547
547
548− #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
548+ #[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)]
549 #[serde(rename_all = "camelCase")]
549 #[serde(rename_all = "camelCase")]
550 pub struct RunInTerminalResponse {
550 pub struct RunInTerminalResponse {
551− pub process_id: Option<usize>,
551+ pub process_id: Option<u32>,
552− pub shell_process_id: Option<usize>,
552+ pub shell_process_id: Option<u32>,
553 }
553 }
554
554
555 #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
555 #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
@@ -557,7 +557,7 @@ pub mod requests {
557 pub struct RunInTerminalArguments {
557 pub struct RunInTerminalArguments {
558 pub kind: Option<String>,
558 pub kind: Option<String>,
559 pub title: Option<String>,
559 pub title: Option<String>,
560− pub cwd: String,
560+ pub cwd: Option<String>,
561 pub args: Vec<String>,
561 pub args: Vec<String>,
562 pub env: Option<HashMap<String, Option<String>>>,
562 pub env: Option<HashMap<String, Option<String>>>,
563 }
563 }
helix-term/src/application.rs+142−126
@@ -320,161 +320,177 @@ impl Application {
320
320
321 pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
321 pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
322 use crate::commands::dap::{breakpoints_changed, resume_application, select_thread_id};
322 use crate::commands::dap::{breakpoints_changed, resume_application, select_thread_id};
323+ use dap::requests::RunInTerminal;
323 use helix_dap::{events, Event};
324 use helix_dap::{events, Event};
324
325
326+ let debugger = match self.editor.debugger.as_mut() {
327+ Some(debugger) => debugger,
328+ None => return,
329+ };
325 match payload {
330 match payload {
326− Payload::Event(ev) => {
331+ Payload::Event(ev) => match ev {
327− let debugger = match self.editor.debugger.as_mut() {
328− Some(debugger) => debugger,
329− None => return,
330− };
331− match ev {
332− Event::Stopped(events::Stopped {
332+ Event::Stopped(events::Stopped {
333− thread_id,
333+ thread_id,
334− description,
334+ description,
335− text,
335+ text,
336− reason,
336+ reason,
337− all_threads_stopped,
337+ all_threads_stopped,
338− ..
338+ ..
339− }) => {
339+ }) => {
340− let all_threads_stopped = all_threads_stopped.unwrap_or_default();
340+ let all_threads_stopped = all_threads_stopped.unwrap_or_default();
341−
341+
342− if all_threads_stopped {
342+ if all_threads_stopped {
343− if let Ok(threads) = debugger.threads().await {
343+ if let Ok(threads) = debugger.threads().await {
344− for thread in threads {
344+ for thread in threads {
345− fetch_stack_trace(debugger, thread.id).await;
345+ fetch_stack_trace(debugger, thread.id).await;
346− }
347− select_thread_id(
348− &mut self.editor,
349− thread_id.unwrap_or_default(),
350− false,
351− )
352− .await;
353 }
346 }
354− } else if let Some(thread_id) = thread_id {
355− debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here
356−
357− // whichever thread stops is made "current" (if no previously selected thread).
358− select_thread_id(&mut self.editor, thread_id, false).await;
347+ select_thread_id(
348+ &mut self.editor,
349+ thread_id.unwrap_or_default(),
350+ false,
351+ )
352+ .await;
359 }
353 }
354+ } else if let Some(thread_id) = thread_id {
355+ debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here
360
356
361− let scope = match thread_id {
357+ // whichever thread stops is made "current" (if no previously selected thread).
362− Some(id) => format!("Thread {}", id),
358+ select_thread_id(&mut self.editor, thread_id, false).await;
363− None => "Target".to_owned(),
359+ }
364− };
365
360
366− let mut status = format!("{} stopped because of {}", scope, reason);
361+ let scope = match thread_id {
367− if let Some(desc) = description {
362+ Some(id) => format!("Thread {}", id),
368− status.push_str(&format!(" {}", desc));
363+ None => "Target".to_owned(),
369− }
370− if let Some(text) = text {
371− status.push_str(&format!(" {}", text));
372− }
373− if all_threads_stopped {
374− status.push_str(" (all threads stopped)");
375− }
364+ };
376
365
377− self.editor.set_status(status);
366+ let mut status = format!("{} stopped because of {}", scope, reason);
367+ if let Some(desc) = description {
368+ status.push_str(&format!(" {}", desc));
378 }
369 }
379− Event::Continued(events::Continued { thread_id, .. }) => {
370+ if let Some(text) = text {
380− debugger
371+ status.push_str(&format!(" {}", text));
381− .thread_states
382− .insert(thread_id, "running".to_owned());
383− if debugger.thread_id == Some(thread_id) {
384− resume_application(debugger)
385− }
386 }
372 }
387− Event::Thread(_) => {
373+ if all_threads_stopped {
388− // TODO: update thread_states, make threads request
374+ status.push_str(" (all threads stopped)");
389 }
375 }
376+
377+ self.editor.set_status(status);
378+ }
379+ Event::Continued(events::Continued { thread_id, .. }) => {
380+ debugger
381+ .thread_states
382+ .insert(thread_id, "running".to_owned());
383+ if debugger.thread_id == Some(thread_id) {
384+ resume_application(debugger)
385+ }
386+ }
387+ Event::Thread(_) => {
388+ // TODO: update thread_states, make threads request
389+ }
390− Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => {
390+ Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => {
391− match &reason[..] {
391+ match &reason[..] {
392− "new" => {
392+ "new" => {
393− if let Some(source) = breakpoint.source {
393+ if let Some(source) = breakpoint.source {
394− self.editor
394+ self.editor
395− .breakpoints
395+ .breakpoints
396− .entry(source.path.unwrap()) // TODO: no unwraps
396+ .entry(source.path.unwrap()) // TODO: no unwraps
397− .or_default()
397+ .or_default()
398− .push(Breakpoint {
398+ .push(Breakpoint {
399− id: breakpoint.id,
399+ id: breakpoint.id,
400− verified: breakpoint.verified,
400+ verified: breakpoint.verified,
401− message: breakpoint.message,
401+ message: breakpoint.message,
402− line: breakpoint.line.unwrap().saturating_sub(1), // TODO: no unwrap
402+ line: breakpoint.line.unwrap().saturating_sub(1), // TODO: no unwrap
403− column: breakpoint.column,
403+ column: breakpoint.column,
404− ..Default::default()
404+ ..Default::default()
405− });
405+ });
406− }
407 }
406 }
407+ }
408− "changed" => {
408+ "changed" => {
409− for breakpoints in self.editor.breakpoints.values_mut() {
409+ for breakpoints in self.editor.breakpoints.values_mut() {
410− if let Some(i) =
410+ if let Some(i) =
411− breakpoints.iter().position(|b| b.id == breakpoint.id)
411+ breakpoints.iter().position(|b| b.id == breakpoint.id)
412− {
412+ {
413− breakpoints[i].verified = breakpoint.verified;
413+ breakpoints[i].verified = breakpoint.verified;
414− breakpoints[i].message = breakpoint.message.clone();
414+ breakpoints[i].message = breakpoint.message.clone();
415− breakpoints[i].line =
415+ breakpoints[i].line =
416− breakpoint.line.unwrap().saturating_sub(1); // TODO: no unwrap
416+ breakpoint.line.unwrap().saturating_sub(1); // TODO: no unwrap
417− breakpoints[i].column = breakpoint.column;
417+ breakpoints[i].column = breakpoint.column;
418− }
419 }
418 }
420 }
419 }
420+ }
421− "removed" => {
421+ "removed" => {
422− for breakpoints in self.editor.breakpoints.values_mut() {
422+ for breakpoints in self.editor.breakpoints.values_mut() {
423− if let Some(i) =
423+ if let Some(i) =
424− breakpoints.iter().position(|b| b.id == breakpoint.id)
424+ breakpoints.iter().position(|b| b.id == breakpoint.id)
425− {
425+ {
426− breakpoints.remove(i);
426+ breakpoints.remove(i);
427− }
428 }
427 }
429 }
428 }
429+ }
430− reason => {
430+ reason => {
431− warn!("Unknown breakpoint event: {}", reason);
431+ warn!("Unknown breakpoint event: {}", reason);
432− }
433 }
432 }
434 }
433 }
434+ }
435− Event::Output(events::Output {
435+ Event::Output(events::Output {
436− category, output, ..
436+ category, output, ..
437− }) => {
437+ }) => {
438− let prefix = match category {
438+ let prefix = match category {
439− Some(category) => {
439+ Some(category) => {
440− if &category == "telemetry" {
440+ if &category == "telemetry" {
441− return;
441+ return;
442− }
443− format!("Debug ({}):", category)
444 }
442 }
445− None => "Debug:".to_owned(),
443+ format!("Debug ({}):", category)
446− };
447−
448− log::info!("{}", output);
449− self.editor.set_status(format!("{} {}", prefix, output));
450− }
451− Event::Initialized => {
452− // send existing breakpoints
453− for (path, breakpoints) in &mut self.editor.breakpoints {
454− // TODO: call futures in parallel, await all
455− let _ = breakpoints_changed(debugger, path.clone(), breakpoints);
456 }
444 }
457− // TODO: fetch breakpoints (in case we're attaching)
445+ None => "Debug:".to_owned(),
446+ };
458
447
459− if debugger.configuration_done().await.is_ok() {
448+ log::info!("{}", output);
460− self.editor
449+ self.editor.set_status(format!("{} {}", prefix, output));
461− .set_status("Debugged application started".to_owned());
450+ }
462− }; // TODO: do we need to handle error?
451+ Event::Initialized => {
463− }
452+ // send existing breakpoints
464− ev => {
453+ for (path, breakpoints) in &mut self.editor.breakpoints {
465− log::warn!("Unhandled event {:?}", ev);
454+ // TODO: call futures in parallel, await all
466− return; // return early to skip render
455+ let _ = breakpoints_changed(debugger, path.clone(), breakpoints);
467 }
456 }
457+ // TODO: fetch breakpoints (in case we're attaching)
458+
459+ if debugger.configuration_done().await.is_ok() {
460+ self.editor
461+ .set_status("Debugged application started".to_owned());
462+ }; // TODO: do we need to handle error?
468 }
463 }
464+ ev => {
465+ log::warn!("Unhandled event {:?}", ev);
466+ return; // return early to skip render
467+ }
469− }
468+ },
470 Payload::Response(_) => unreachable!(),
469 Payload::Response(_) => unreachable!(),
471 Payload::Request(request) => match request.command.as_str() {
470 Payload::Request(request) => match request.command.as_str() {
472− dap::requests::RunInTerminal::COMMAND => {
471+ RunInTerminal::COMMAND => {
473 let arguments: dap::requests::RunInTerminalArguments =
472 let arguments: dap::requests::RunInTerminalArguments =
474 serde_json::from_value(request.arguments.unwrap_or_default()).unwrap();
473 serde_json::from_value(request.arguments.unwrap_or_default()).unwrap();
475 // TODO: no unwrap
474 // TODO: no unwrap
476
475
476+ // TODO: handle cwd
477+ let process = std::process::Command::new("tmux")
478+ .arg("split-window")
479+ .arg(arguments.args.join(" ")) // TODO: first arg is wrong, it uses current dir
480+ .spawn()
481+ .unwrap();
482+
483+ let _ = debugger
477− // TODO: dap reply
484+ .reply(
485+ request.seq,
486+ dap::requests::RunInTerminal::COMMAND,
487+ serde_json::to_value(dap::requests::RunInTerminalResponse {
488+ process_id: Some(process.id()),
489+ shell_process_id: None,
490+ })
491+ .map_err(|e| e.into()),
492+ )
493+ .await;
478 }
494 }
479 _ => log::error!("DAP reverse request not implemented: {:?}", request),
495 _ => log::error!("DAP reverse request not implemented: {:?}", request),
480 },
496 },