aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/commands.rs369
-rw-r--r--helix-term/src/commands/dap.rs349
2 files changed, 353 insertions, 365 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index a91490fd..03a71fd3 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1,3 +1,7 @@
+mod dap;
+
+pub use dap::*;
+
use helix_core::{
comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes, indent,
indent::IndentStyle,
@@ -24,13 +28,11 @@ use helix_lsp::{
};
use insert::*;
use movement::Movement;
-use serde_json::Value;
use crate::{
compositor::{self, Component, Compositor},
ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent},
};
-use tokio_stream::wrappers::UnboundedReceiverStream;
use crate::job::{self, Job, Jobs};
use futures_util::FutureExt;
@@ -4490,366 +4492,3 @@ fn suspend(_cx: &mut Context) {
#[cfg(not(windows))]
signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
}
-
-// DAP
-pub fn dap_start_impl(
- editor: &mut Editor,
- name: Option<&str>,
- socket: Option<std::net::SocketAddr>,
- params: Option<Vec<&str>>,
-) {
- use helix_dap::Client;
- use helix_lsp::block_on;
- use serde_json::to_value;
-
- let (_, doc) = current!(editor);
-
- let path = match doc.path() {
- Some(path) => path.to_path_buf(),
- None => {
- editor.set_error("Can't start debug: document has no path".to_string());
- return;
- }
- };
-
- let config = editor
- .syn_loader
- .language_config_for_file_name(&path)
- .and_then(|x| x.debugger.clone());
- let config = match config {
- Some(c) => c,
- None => {
- editor.set_error(
- "Can't start debug: no debug adapter available for language".to_string(),
- );
- return;
- }
- };
-
- let result = match socket {
- Some(socket) => block_on(Client::tcp(socket, 0)),
- None => block_on(Client::process(
- config.transport.clone(),
- config.command.clone(),
- config.args.clone(),
- config.port_arg.clone(),
- 0,
- )),
- };
-
- let (mut debugger, events) = match result {
- Ok(r) => r,
- Err(e) => {
- editor.set_error(format!("Failed to start debug session: {:?}", e));
- return;
- }
- };
-
- let request = debugger.initialize(config.name.clone());
- if let Err(e) = block_on(request) {
- editor.set_error(format!("Failed to initialize debug adapter: {:?}", e));
- return;
- }
-
- let start_config = match name {
- Some(name) => config.templates.iter().find(|t| t.name == name),
- None => config.templates.get(0),
- };
- let start_config = match start_config {
- Some(c) => c,
- None => {
- editor.set_error("Can't start debug: no debug config with given name".to_string());
- return;
- }
- };
-
- let template = start_config.args.clone();
- let mut args: HashMap<String, Value> = HashMap::new();
-
- if let Some(params) = params {
- for (k, t) in template {
- let mut value = t;
- for (i, x) in params.iter().enumerate() {
- // For param #0 replace {0} in args
- value = value.replace(format!("{{{}}}", i).as_str(), x);
- }
-
- if let Ok(integer) = value.parse::<usize>() {
- args.insert(k, Value::Number(serde_json::Number::from(integer)));
- } else {
- args.insert(k, Value::String(value));
- }
- }
- }
-
- let args = to_value(args).unwrap();
-
- let result = match &start_config.request[..] {
- "launch" => block_on(debugger.launch(args)),
- "attach" => block_on(debugger.attach(args)),
- _ => {
- editor.set_error("Unsupported request".to_string());
- return;
- }
- };
- if let Err(e) = result {
- editor.set_error(format!("Failed {} target: {:?}", start_config.request, e));
- return;
- }
-
- // TODO: either await "initialized" or buffer commands until event is received
- editor.debugger = Some(debugger);
- let stream = UnboundedReceiverStream::new(events);
- editor.debugger_events.push(stream);
-}
-
-fn dap_launch(cx: &mut Context) {
- if cx.editor.debugger.is_some() {
- cx.editor
- .set_error("Can't start debug: debugger is running".to_string());
- return;
- }
-
- let (_, doc) = current!(cx.editor);
- let path = match doc.path() {
- Some(path) => path.to_path_buf(),
- None => {
- cx.editor
- .set_error("Can't start debug: document has no path".to_string());
- return;
- }
- };
-
- let config = cx
- .editor
- .syn_loader
- .language_config_for_file_name(&path)
- .and_then(|x| x.debugger.clone());
- let config = match config {
- Some(c) => c,
- None => {
- cx.editor.set_error(
- "Can't start debug: no debug adapter available for language".to_string(),
- );
- return;
- }
- };
-
- cx.editor.debug_config_picker = Some(config.templates.iter().map(|t| t.name.clone()).collect());
- cx.editor.debug_config_completions = Some(
- config
- .templates
- .iter()
- .map(|t| t.completion.clone())
- .collect(),
- );
-}
-
-fn dap_toggle_breakpoint(cx: &mut Context) {
- use helix_lsp::block_on;
-
- let (view, doc) = current!(cx.editor);
- let text = doc.text().slice(..);
- let pos = doc.selection(view.id).primary().cursor(text);
-
- let breakpoint = helix_dap::SourceBreakpoint {
- line: text.char_to_line(pos) + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
- ..Default::default()
- };
-
- let path = match doc.path() {
- Some(path) => path.to_path_buf(),
- None => {
- cx.editor
- .set_error("Can't set breakpoint: document has no path".to_string());
- return;
- }
- };
-
- // TODO: need to map breakpoints over edits and update them?
- // we shouldn't really allow editing while debug is running though
-
- if let Some(debugger) = &mut cx.editor.debugger {
- let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
- if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
- breakpoints.remove(pos);
- } else {
- breakpoints.push(breakpoint);
- }
-
- let breakpoints = breakpoints.clone();
-
- let request = debugger.set_breakpoints(path, breakpoints);
- if let Err(e) = block_on(request) {
- cx.editor
- .set_error(format!("Failed to set breakpoints: {:?}", e));
- }
- }
-}
-
-fn dap_run(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Debuggee is already running".to_owned());
- return;
- }
- let request = debugger.configuration_done();
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to run: {:?}", e));
- return;
- }
- debugger.is_running = true;
- }
-}
-
-fn dap_continue(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Debuggee is already running".to_owned());
- return;
- }
-
- let request = debugger.continue_thread(debugger.stopped_thread.unwrap());
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to continue: {:?}", e));
- return;
- }
- debugger.is_running = true;
- debugger.stack_pointer = None;
- }
-}
-
-fn dap_pause(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if !debugger.is_running {
- cx.editor.set_status("Debuggee is not running".to_owned());
- return;
- }
-
- // FIXME: correct number here
- let request = debugger.pause(0);
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to pause: {:?}", e));
- }
- }
-}
-
-fn dap_step_in(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Debuggee is already running".to_owned());
- return;
- }
-
- let request = debugger.step_in(debugger.stopped_thread.unwrap());
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to step: {:?}", e));
- }
- }
-}
-
-fn dap_step_out(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Debuggee is already running".to_owned());
- return;
- }
-
- let request = debugger.step_out(debugger.stopped_thread.unwrap());
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to step: {:?}", e));
- }
- }
-}
-
-fn dap_next(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Debuggee is already running".to_owned());
- return;
- }
-
- let request = debugger.next(debugger.stopped_thread.unwrap());
- if let Err(e) = block_on(request) {
- cx.editor.set_error(format!("Failed to step: {:?}", e));
- }
- }
-}
-
-fn dap_variables(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- if debugger.is_running {
- cx.editor
- .set_status("Cannot access variables while target is running".to_owned());
- return;
- }
- if debugger.stack_pointer.is_none() {
- cx.editor
- .set_status("Cannot find current stack pointer to access variables".to_owned());
- return;
- }
-
- let frame_id = debugger.stack_pointer.clone().unwrap().id;
- let scopes = match block_on(debugger.scopes(frame_id)) {
- Ok(s) => s,
- Err(e) => {
- cx.editor
- .set_error(format!("Failed to get scopes: {:?}", e));
- return;
- }
- };
- let mut variables = Vec::new();
-
- for scope in scopes.iter() {
- let response = block_on(debugger.variables(scope.variables_reference));
-
- if let Ok(vars) = response {
- for var in vars {
- let prefix = match var.data_type {
- Some(data_type) => format!("{} ", data_type),
- None => "".to_owned(),
- };
- variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
- }
- }
- }
-
- if !variables.is_empty() {
- cx.editor.variables = Some(variables);
- cx.editor.variables_page = 0;
- }
- }
-}
-
-fn dap_terminate(cx: &mut Context) {
- use helix_lsp::block_on;
-
- if let Some(debugger) = &mut cx.editor.debugger {
- let request = debugger.disconnect();
- if let Err(e) = block_on(request) {
- cx.editor
- .set_error(format!("Failed to disconnect: {:?}", e));
- return;
- }
- cx.editor.debugger = None;
- }
-}
diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs
new file mode 100644
index 00000000..9509ada1
--- /dev/null
+++ b/helix-term/src/commands/dap.rs
@@ -0,0 +1,349 @@
+use super::{Context, Editor};
+use helix_dap::Client;
+use helix_lsp::block_on;
+
+use serde_json::{to_value, Value};
+use tokio_stream::wrappers::UnboundedReceiverStream;
+
+use std::collections::HashMap;
+
+// DAP
+pub fn dap_start_impl(
+ editor: &mut Editor,
+ name: Option<&str>,
+ socket: Option<std::net::SocketAddr>,
+ params: Option<Vec<&str>>,
+) {
+ let (_, doc) = current!(editor);
+
+ let path = match doc.path() {
+ Some(path) => path.to_path_buf(),
+ None => {
+ editor.set_error("Can't start debug: document has no path".to_string());
+ return;
+ }
+ };
+
+ let config = editor
+ .syn_loader
+ .language_config_for_file_name(&path)
+ .and_then(|x| x.debugger.clone());
+ let config = match config {
+ Some(c) => c,
+ None => {
+ editor.set_error(
+ "Can't start debug: no debug adapter available for language".to_string(),
+ );
+ return;
+ }
+ };
+
+ let result = match socket {
+ Some(socket) => block_on(Client::tcp(socket, 0)),
+ None => block_on(Client::process(
+ config.transport.clone(),
+ config.command.clone(),
+ config.args.clone(),
+ config.port_arg.clone(),
+ 0,
+ )),
+ };
+
+ let (mut debugger, events) = match result {
+ Ok(r) => r,
+ Err(e) => {
+ editor.set_error(format!("Failed to start debug session: {:?}", e));
+ return;
+ }
+ };
+
+ let request = debugger.initialize(config.name.clone());
+ if let Err(e) = block_on(request) {
+ editor.set_error(format!("Failed to initialize debug adapter: {:?}", e));
+ return;
+ }
+
+ let start_config = match name {
+ Some(name) => config.templates.iter().find(|t| t.name == name),
+ None => config.templates.get(0),
+ };
+ let start_config = match start_config {
+ Some(c) => c,
+ None => {
+ editor.set_error("Can't start debug: no debug config with given name".to_string());
+ return;
+ }
+ };
+
+ let template = start_config.args.clone();
+ let mut args: HashMap<String, Value> = HashMap::new();
+
+ if let Some(params) = params {
+ for (k, t) in template {
+ let mut value = t;
+ for (i, x) in params.iter().enumerate() {
+ // For param #0 replace {0} in args
+ value = value.replace(format!("{{{}}}", i).as_str(), x);
+ }
+
+ if let Ok(integer) = value.parse::<usize>() {
+ args.insert(k, Value::Number(serde_json::Number::from(integer)));
+ } else {
+ args.insert(k, Value::String(value));
+ }
+ }
+ }
+
+ let args = to_value(args).unwrap();
+
+ let result = match &start_config.request[..] {
+ "launch" => block_on(debugger.launch(args)),
+ "attach" => block_on(debugger.attach(args)),
+ _ => {
+ editor.set_error("Unsupported request".to_string());
+ return;
+ }
+ };
+ if let Err(e) = result {
+ editor.set_error(format!("Failed {} target: {:?}", start_config.request, e));
+ return;
+ }
+
+ // TODO: either await "initialized" or buffer commands until event is received
+ editor.debugger = Some(debugger);
+ let stream = UnboundedReceiverStream::new(events);
+ editor.debugger_events.push(stream);
+}
+
+pub fn dap_launch(cx: &mut Context) {
+ if cx.editor.debugger.is_some() {
+ cx.editor
+ .set_error("Can't start debug: debugger is running".to_string());
+ return;
+ }
+
+ let (_, doc) = current!(cx.editor);
+ let path = match doc.path() {
+ Some(path) => path.to_path_buf(),
+ None => {
+ cx.editor
+ .set_error("Can't start debug: document has no path".to_string());
+ return;
+ }
+ };
+
+ let config = cx
+ .editor
+ .syn_loader
+ .language_config_for_file_name(&path)
+ .and_then(|x| x.debugger.clone());
+ let config = match config {
+ Some(c) => c,
+ None => {
+ cx.editor.set_error(
+ "Can't start debug: no debug adapter available for language".to_string(),
+ );
+ return;
+ }
+ };
+
+ cx.editor.debug_config_picker = Some(config.templates.iter().map(|t| t.name.clone()).collect());
+ cx.editor.debug_config_completions = Some(
+ config
+ .templates
+ .iter()
+ .map(|t| t.completion.clone())
+ .collect(),
+ );
+}
+
+pub fn dap_toggle_breakpoint(cx: &mut Context) {
+ let (view, doc) = current!(cx.editor);
+ let text = doc.text().slice(..);
+ let pos = doc.selection(view.id).primary().cursor(text);
+
+ let breakpoint = helix_dap::SourceBreakpoint {
+ line: text.char_to_line(pos) + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
+ ..Default::default()
+ };
+
+ let path = match doc.path() {
+ Some(path) => path.to_path_buf(),
+ None => {
+ cx.editor
+ .set_error("Can't set breakpoint: document has no path".to_string());
+ return;
+ }
+ };
+
+ // TODO: need to map breakpoints over edits and update them?
+ // we shouldn't really allow editing while debug is running though
+
+ if let Some(debugger) = &mut cx.editor.debugger {
+ let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
+ if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
+ breakpoints.remove(pos);
+ } else {
+ breakpoints.push(breakpoint);
+ }
+
+ let breakpoints = breakpoints.clone();
+
+ let request = debugger.set_breakpoints(path, breakpoints);
+ if let Err(e) = block_on(request) {
+ cx.editor
+ .set_error(format!("Failed to set breakpoints: {:?}", e));
+ }
+ }
+}
+
+pub fn dap_run(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Debuggee is already running".to_owned());
+ return;
+ }
+ let request = debugger.configuration_done();
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to run: {:?}", e));
+ return;
+ }
+ debugger.is_running = true;
+ }
+}
+
+pub fn dap_continue(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Debuggee is already running".to_owned());
+ return;
+ }
+
+ let request = debugger.continue_thread(debugger.stopped_thread.unwrap());
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to continue: {:?}", e));
+ return;
+ }
+ debugger.is_running = true;
+ debugger.stack_pointer = None;
+ }
+}
+
+pub fn dap_pause(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if !debugger.is_running {
+ cx.editor.set_status("Debuggee is not running".to_owned());
+ return;
+ }
+
+ // FIXME: correct number here
+ let request = debugger.pause(0);
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to pause: {:?}", e));
+ }
+ }
+}
+
+pub fn dap_step_in(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Debuggee is already running".to_owned());
+ return;
+ }
+
+ let request = debugger.step_in(debugger.stopped_thread.unwrap());
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to step: {:?}", e));
+ }
+ }
+}
+
+pub fn dap_step_out(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Debuggee is already running".to_owned());
+ return;
+ }
+
+ let request = debugger.step_out(debugger.stopped_thread.unwrap());
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to step: {:?}", e));
+ }
+ }
+}
+
+pub fn dap_next(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Debuggee is already running".to_owned());
+ return;
+ }
+
+ let request = debugger.next(debugger.stopped_thread.unwrap());
+ if let Err(e) = block_on(request) {
+ cx.editor.set_error(format!("Failed to step: {:?}", e));
+ }
+ }
+}
+
+pub fn dap_variables(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ if debugger.is_running {
+ cx.editor
+ .set_status("Cannot access variables while target is running".to_owned());
+ return;
+ }
+ if debugger.stack_pointer.is_none() {
+ cx.editor
+ .set_status("Cannot find current stack pointer to access variables".to_owned());
+ return;
+ }
+
+ let frame_id = debugger.stack_pointer.clone().unwrap().id;
+ let scopes = match block_on(debugger.scopes(frame_id)) {
+ Ok(s) => s,
+ Err(e) => {
+ cx.editor
+ .set_error(format!("Failed to get scopes: {:?}", e));
+ return;
+ }
+ };
+ let mut variables = Vec::new();
+
+ for scope in scopes.iter() {
+ let response = block_on(debugger.variables(scope.variables_reference));
+
+ if let Ok(vars) = response {
+ for var in vars {
+ let prefix = match var.data_type {
+ Some(data_type) => format!("{} ", data_type),
+ None => "".to_owned(),
+ };
+ variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
+ }
+ }
+ }
+
+ if !variables.is_empty() {
+ cx.editor.variables = Some(variables);
+ cx.editor.variables_page = 0;
+ }
+ }
+}
+
+pub fn dap_terminate(cx: &mut Context) {
+ if let Some(debugger) = &mut cx.editor.debugger {
+ let request = debugger.disconnect();
+ if let Err(e) = block_on(request) {
+ cx.editor
+ .set_error(format!("Failed to disconnect: {:?}", e));
+ return;
+ }
+ cx.editor.debugger = None;
+ }
+}