on master help sign in
Extract dap commands into a separate file
commitd6ccc150c78adf72f77f26185ea7a559983ed2d0 authorBlaž Hrastnik <blaz@mxxn.io> dateSun 29 Aug 2021, 22:43 (5 years ago) parentee2ba74
2 files changed+353−365
helix-term/src/commands.rs+4−365 helix-term/src/commands/dap.rs+349
helix-term/src/commands.rs+4−365
@@ -1,3 +1,7 @@
1+mod dap;
2+
3+pub use dap::*;
4+
15use helix_core::{
26 comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes, indent,
37 indent::IndentStyle,
@@ -24,13 +28,11 @@ use helix_lsp::{
2428};
2529use insert::*;
2630use movement::Movement;
27−use serde_json::Value;
2831
2932use crate::{
3033 compositor::{self, Component, Compositor},
3134 ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent},
3235};
33−use tokio_stream::wrappers::UnboundedReceiverStream;
3436
3537use crate::job::{self, Job, Jobs};
3638use futures_util::FutureExt;
@@ -4490,366 +4492,3 @@ fn suspend(_cx: &mut Context) {
44904492 #[cfg(not(windows))]
44914493 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
44924494}
4493−
4494−// DAP
4495−pub fn dap_start_impl(
4496− editor: &mut Editor,
4497− name: Option<&str>,
4498− socket: Option<std::net::SocketAddr>,
4499− params: Option<Vec<&str>>,
4500−) {
4501− use helix_dap::Client;
4502− use helix_lsp::block_on;
4503− use serde_json::to_value;
4504−
4505− let (_, doc) = current!(editor);
4506−
4507− let path = match doc.path() {
4508− Some(path) => path.to_path_buf(),
4509− None => {
4510− editor.set_error("Can't start debug: document has no path".to_string());
4511− return;
4512− }
4513− };
4514−
4515− let config = editor
4516− .syn_loader
4517− .language_config_for_file_name(&path)
4518− .and_then(|x| x.debugger.clone());
4519− let config = match config {
4520− Some(c) => c,
4521− None => {
4522− editor.set_error(
4523− "Can't start debug: no debug adapter available for language".to_string(),
4524− );
4525− return;
4526− }
4527− };
4528−
4529− let result = match socket {
4530− Some(socket) => block_on(Client::tcp(socket, 0)),
4531− None => block_on(Client::process(
4532− config.transport.clone(),
4533− config.command.clone(),
4534− config.args.clone(),
4535− config.port_arg.clone(),
4536− 0,
4537− )),
4538− };
4539−
4540− let (mut debugger, events) = match result {
4541− Ok(r) => r,
4542− Err(e) => {
4543− editor.set_error(format!("Failed to start debug session: {:?}", e));
4544− return;
4545− }
4546− };
4547−
4548− let request = debugger.initialize(config.name.clone());
4549− if let Err(e) = block_on(request) {
4550− editor.set_error(format!("Failed to initialize debug adapter: {:?}", e));
4551− return;
4552− }
4553−
4554− let start_config = match name {
4555− Some(name) => config.templates.iter().find(|t| t.name == name),
4556− None => config.templates.get(0),
4557− };
4558− let start_config = match start_config {
4559− Some(c) => c,
4560− None => {
4561− editor.set_error("Can't start debug: no debug config with given name".to_string());
4562− return;
4563− }
4564− };
4565−
4566− let template = start_config.args.clone();
4567− let mut args: HashMap<String, Value> = HashMap::new();
4568−
4569− if let Some(params) = params {
4570− for (k, t) in template {
4571− let mut value = t;
4572− for (i, x) in params.iter().enumerate() {
4573− // For param #0 replace {0} in args
4574− value = value.replace(format!("{{{}}}", i).as_str(), x);
4575− }
4576−
4577− if let Ok(integer) = value.parse::<usize>() {
4578− args.insert(k, Value::Number(serde_json::Number::from(integer)));
4579− } else {
4580− args.insert(k, Value::String(value));
4581− }
4582− }
4583− }
4584−
4585− let args = to_value(args).unwrap();
4586−
4587− let result = match &start_config.request[..] {
4588− "launch" => block_on(debugger.launch(args)),
4589− "attach" => block_on(debugger.attach(args)),
4590− _ => {
4591− editor.set_error("Unsupported request".to_string());
4592− return;
4593− }
4594− };
4595− if let Err(e) = result {
4596− editor.set_error(format!("Failed {} target: {:?}", start_config.request, e));
4597− return;
4598− }
4599−
4600− // TODO: either await "initialized" or buffer commands until event is received
4601− editor.debugger = Some(debugger);
4602− let stream = UnboundedReceiverStream::new(events);
4603− editor.debugger_events.push(stream);
4604−}
4605−
4606−fn dap_launch(cx: &mut Context) {
4607− if cx.editor.debugger.is_some() {
4608− cx.editor
4609− .set_error("Can't start debug: debugger is running".to_string());
4610− return;
4611− }
4612−
4613− let (_, doc) = current!(cx.editor);
4614− let path = match doc.path() {
4615− Some(path) => path.to_path_buf(),
4616− None => {
4617− cx.editor
4618− .set_error("Can't start debug: document has no path".to_string());
4619− return;
4620− }
4621− };
4622−
4623− let config = cx
4624− .editor
4625− .syn_loader
4626− .language_config_for_file_name(&path)
4627− .and_then(|x| x.debugger.clone());
4628− let config = match config {
4629− Some(c) => c,
4630− None => {
4631− cx.editor.set_error(
4632− "Can't start debug: no debug adapter available for language".to_string(),
4633− );
4634− return;
4635− }
4636− };
4637−
4638− cx.editor.debug_config_picker = Some(config.templates.iter().map(|t| t.name.clone()).collect());
4639− cx.editor.debug_config_completions = Some(
4640− config
4641− .templates
4642− .iter()
4643− .map(|t| t.completion.clone())
4644− .collect(),
4645− );
4646−}
4647−
4648−fn dap_toggle_breakpoint(cx: &mut Context) {
4649− use helix_lsp::block_on;
4650−
4651− let (view, doc) = current!(cx.editor);
4652− let text = doc.text().slice(..);
4653− let pos = doc.selection(view.id).primary().cursor(text);
4654−
4655− let breakpoint = helix_dap::SourceBreakpoint {
4656− line: text.char_to_line(pos) + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
4657− ..Default::default()
4658− };
4659−
4660− let path = match doc.path() {
4661− Some(path) => path.to_path_buf(),
4662− None => {
4663− cx.editor
4664− .set_error("Can't set breakpoint: document has no path".to_string());
4665− return;
4666− }
4667− };
4668−
4669− // TODO: need to map breakpoints over edits and update them?
4670− // we shouldn't really allow editing while debug is running though
4671−
4672− if let Some(debugger) = &mut cx.editor.debugger {
4673− let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
4674− if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4675− breakpoints.remove(pos);
4676− } else {
4677− breakpoints.push(breakpoint);
4678− }
4679−
4680− let breakpoints = breakpoints.clone();
4681−
4682− let request = debugger.set_breakpoints(path, breakpoints);
4683− if let Err(e) = block_on(request) {
4684− cx.editor
4685− .set_error(format!("Failed to set breakpoints: {:?}", e));
4686− }
4687− }
4688−}
4689−
4690−fn dap_run(cx: &mut Context) {
4691− use helix_lsp::block_on;
4692−
4693− if let Some(debugger) = &mut cx.editor.debugger {
4694− if debugger.is_running {
4695− cx.editor
4696− .set_status("Debuggee is already running".to_owned());
4697− return;
4698− }
4699− let request = debugger.configuration_done();
4700− if let Err(e) = block_on(request) {
4701− cx.editor.set_error(format!("Failed to run: {:?}", e));
4702− return;
4703− }
4704− debugger.is_running = true;
4705− }
4706−}
4707−
4708−fn dap_continue(cx: &mut Context) {
4709− use helix_lsp::block_on;
4710−
4711− if let Some(debugger) = &mut cx.editor.debugger {
4712− if debugger.is_running {
4713− cx.editor
4714− .set_status("Debuggee is already running".to_owned());
4715− return;
4716− }
4717−
4718− let request = debugger.continue_thread(debugger.stopped_thread.unwrap());
4719− if let Err(e) = block_on(request) {
4720− cx.editor.set_error(format!("Failed to continue: {:?}", e));
4721− return;
4722− }
4723− debugger.is_running = true;
4724− debugger.stack_pointer = None;
4725− }
4726−}
4727−
4728−fn dap_pause(cx: &mut Context) {
4729− use helix_lsp::block_on;
4730−
4731− if let Some(debugger) = &mut cx.editor.debugger {
4732− if !debugger.is_running {
4733− cx.editor.set_status("Debuggee is not running".to_owned());
4734− return;
4735− }
4736−
4737− // FIXME: correct number here
4738− let request = debugger.pause(0);
4739− if let Err(e) = block_on(request) {
4740− cx.editor.set_error(format!("Failed to pause: {:?}", e));
4741− }
4742− }
4743−}
4744−
4745−fn dap_step_in(cx: &mut Context) {
4746− use helix_lsp::block_on;
4747−
4748− if let Some(debugger) = &mut cx.editor.debugger {
4749− if debugger.is_running {
4750− cx.editor
4751− .set_status("Debuggee is already running".to_owned());
4752− return;
4753− }
4754−
4755− let request = debugger.step_in(debugger.stopped_thread.unwrap());
4756− if let Err(e) = block_on(request) {
4757− cx.editor.set_error(format!("Failed to step: {:?}", e));
4758− }
4759− }
4760−}
4761−
4762−fn dap_step_out(cx: &mut Context) {
4763− use helix_lsp::block_on;
4764−
4765− if let Some(debugger) = &mut cx.editor.debugger {
4766− if debugger.is_running {
4767− cx.editor
4768− .set_status("Debuggee is already running".to_owned());
4769− return;
4770− }
4771−
4772− let request = debugger.step_out(debugger.stopped_thread.unwrap());
4773− if let Err(e) = block_on(request) {
4774− cx.editor.set_error(format!("Failed to step: {:?}", e));
4775− }
4776− }
4777−}
4778−
4779−fn dap_next(cx: &mut Context) {
4780− use helix_lsp::block_on;
4781−
4782− if let Some(debugger) = &mut cx.editor.debugger {
4783− if debugger.is_running {
4784− cx.editor
4785− .set_status("Debuggee is already running".to_owned());
4786− return;
4787− }
4788−
4789− let request = debugger.next(debugger.stopped_thread.unwrap());
4790− if let Err(e) = block_on(request) {
4791− cx.editor.set_error(format!("Failed to step: {:?}", e));
4792− }
4793− }
4794−}
4795−
4796−fn dap_variables(cx: &mut Context) {
4797− use helix_lsp::block_on;
4798−
4799− if let Some(debugger) = &mut cx.editor.debugger {
4800− if debugger.is_running {
4801− cx.editor
4802− .set_status("Cannot access variables while target is running".to_owned());
4803− return;
4804− }
4805− if debugger.stack_pointer.is_none() {
4806− cx.editor
4807− .set_status("Cannot find current stack pointer to access variables".to_owned());
4808− return;
4809− }
4810−
4811− let frame_id = debugger.stack_pointer.clone().unwrap().id;
4812− let scopes = match block_on(debugger.scopes(frame_id)) {
4813− Ok(s) => s,
4814− Err(e) => {
4815− cx.editor
4816− .set_error(format!("Failed to get scopes: {:?}", e));
4817− return;
4818− }
4819− };
4820− let mut variables = Vec::new();
4821−
4822− for scope in scopes.iter() {
4823− let response = block_on(debugger.variables(scope.variables_reference));
4824−
4825− if let Ok(vars) = response {
4826− for var in vars {
4827− let prefix = match var.data_type {
4828− Some(data_type) => format!("{} ", data_type),
4829− None => "".to_owned(),
4830− };
4831− variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
4832− }
4833− }
4834− }
4835−
4836− if !variables.is_empty() {
4837− cx.editor.variables = Some(variables);
4838− cx.editor.variables_page = 0;
4839− }
4840− }
4841−}
4842−
4843−fn dap_terminate(cx: &mut Context) {
4844− use helix_lsp::block_on;
4845−
4846− if let Some(debugger) = &mut cx.editor.debugger {
4847− let request = debugger.disconnect();
4848− if let Err(e) = block_on(request) {
4849− cx.editor
4850− .set_error(format!("Failed to disconnect: {:?}", e));
4851− return;
4852− }
4853− cx.editor.debugger = None;
4854− }
4855−}
helix-term/src/commands/dap.rsnew file+349
@@ -0,0 +1,349 @@
1+use super::{Context, Editor};
2+use helix_dap::Client;
3+use helix_lsp::block_on;
4+
5+use serde_json::{to_value, Value};
6+use tokio_stream::wrappers::UnboundedReceiverStream;
7+
8+use std::collections::HashMap;
9+
10+// DAP
11+pub fn dap_start_impl(
12+ editor: &mut Editor,
13+ name: Option<&str>,
14+ socket: Option<std::net::SocketAddr>,
15+ params: Option<Vec<&str>>,
16+) {
17+ let (_, doc) = current!(editor);
18+
19+ let path = match doc.path() {
20+ Some(path) => path.to_path_buf(),
21+ None => {
22+ editor.set_error("Can't start debug: document has no path".to_string());
23+ return;
24+ }
25+ };
26+
27+ let config = editor
28+ .syn_loader
29+ .language_config_for_file_name(&path)
30+ .and_then(|x| x.debugger.clone());
31+ let config = match config {
32+ Some(c) => c,
33+ None => {
34+ editor.set_error(
35+ "Can't start debug: no debug adapter available for language".to_string(),
36+ );
37+ return;
38+ }
39+ };
40+
41+ let result = match socket {
42+ Some(socket) => block_on(Client::tcp(socket, 0)),
43+ None => block_on(Client::process(
44+ config.transport.clone(),
45+ config.command.clone(),
46+ config.args.clone(),
47+ config.port_arg.clone(),
48+ 0,
49+ )),
50+ };
51+
52+ let (mut debugger, events) = match result {
53+ Ok(r) => r,
54+ Err(e) => {
55+ editor.set_error(format!("Failed to start debug session: {:?}", e));
56+ return;
57+ }
58+ };
59+
60+ let request = debugger.initialize(config.name.clone());
61+ if let Err(e) = block_on(request) {
62+ editor.set_error(format!("Failed to initialize debug adapter: {:?}", e));
63+ return;
64+ }
65+
66+ let start_config = match name {
67+ Some(name) => config.templates.iter().find(|t| t.name == name),
68+ None => config.templates.get(0),
69+ };
70+ let start_config = match start_config {
71+ Some(c) => c,
72+ None => {
73+ editor.set_error("Can't start debug: no debug config with given name".to_string());
74+ return;
75+ }
76+ };
77+
78+ let template = start_config.args.clone();
79+ let mut args: HashMap<String, Value> = HashMap::new();
80+
81+ if let Some(params) = params {
82+ for (k, t) in template {
83+ let mut value = t;
84+ for (i, x) in params.iter().enumerate() {
85+ // For param #0 replace {0} in args
86+ value = value.replace(format!("{{{}}}", i).as_str(), x);
87+ }
88+
89+ if let Ok(integer) = value.parse::<usize>() {
90+ args.insert(k, Value::Number(serde_json::Number::from(integer)));
91+ } else {
92+ args.insert(k, Value::String(value));
93+ }
94+ }
95+ }
96+
97+ let args = to_value(args).unwrap();
98+
99+ let result = match &start_config.request[..] {
100+ "launch" => block_on(debugger.launch(args)),
101+ "attach" => block_on(debugger.attach(args)),
102+ _ => {
103+ editor.set_error("Unsupported request".to_string());
104+ return;
105+ }
106+ };
107+ if let Err(e) = result {
108+ editor.set_error(format!("Failed {} target: {:?}", start_config.request, e));
109+ return;
110+ }
111+
112+ // TODO: either await "initialized" or buffer commands until event is received
113+ editor.debugger = Some(debugger);
114+ let stream = UnboundedReceiverStream::new(events);
115+ editor.debugger_events.push(stream);
116+}
117+
118+pub fn dap_launch(cx: &mut Context) {
119+ if cx.editor.debugger.is_some() {
120+ cx.editor
121+ .set_error("Can't start debug: debugger is running".to_string());
122+ return;
123+ }
124+
125+ let (_, doc) = current!(cx.editor);
126+ let path = match doc.path() {
127+ Some(path) => path.to_path_buf(),
128+ None => {
129+ cx.editor
130+ .set_error("Can't start debug: document has no path".to_string());
131+ return;
132+ }
133+ };
134+
135+ let config = cx
136+ .editor
137+ .syn_loader
138+ .language_config_for_file_name(&path)
139+ .and_then(|x| x.debugger.clone());
140+ let config = match config {
141+ Some(c) => c,
142+ None => {
143+ cx.editor.set_error(
144+ "Can't start debug: no debug adapter available for language".to_string(),
145+ );
146+ return;
147+ }
148+ };
149+
150+ cx.editor.debug_config_picker = Some(config.templates.iter().map(|t| t.name.clone()).collect());
151+ cx.editor.debug_config_completions = Some(
152+ config
153+ .templates
154+ .iter()
155+ .map(|t| t.completion.clone())
156+ .collect(),
157+ );
158+}
159+
160+pub fn dap_toggle_breakpoint(cx: &mut Context) {
161+ let (view, doc) = current!(cx.editor);
162+ let text = doc.text().slice(..);
163+ let pos = doc.selection(view.id).primary().cursor(text);
164+
165+ let breakpoint = helix_dap::SourceBreakpoint {
166+ line: text.char_to_line(pos) + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init)
167+ ..Default::default()
168+ };
169+
170+ let path = match doc.path() {
171+ Some(path) => path.to_path_buf(),
172+ None => {
173+ cx.editor
174+ .set_error("Can't set breakpoint: document has no path".to_string());
175+ return;
176+ }
177+ };
178+
179+ // TODO: need to map breakpoints over edits and update them?
180+ // we shouldn't really allow editing while debug is running though
181+
182+ if let Some(debugger) = &mut cx.editor.debugger {
183+ let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
184+ if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
185+ breakpoints.remove(pos);
186+ } else {
187+ breakpoints.push(breakpoint);
188+ }
189+
190+ let breakpoints = breakpoints.clone();
191+
192+ let request = debugger.set_breakpoints(path, breakpoints);
193+ if let Err(e) = block_on(request) {
194+ cx.editor
195+ .set_error(format!("Failed to set breakpoints: {:?}", e));
196+ }
197+ }
198+}
199+
200+pub fn dap_run(cx: &mut Context) {
201+ if let Some(debugger) = &mut cx.editor.debugger {
202+ if debugger.is_running {
203+ cx.editor
204+ .set_status("Debuggee is already running".to_owned());
205+ return;
206+ }
207+ let request = debugger.configuration_done();
208+ if let Err(e) = block_on(request) {
209+ cx.editor.set_error(format!("Failed to run: {:?}", e));
210+ return;
211+ }
212+ debugger.is_running = true;
213+ }
214+}
215+
216+pub fn dap_continue(cx: &mut Context) {
217+ if let Some(debugger) = &mut cx.editor.debugger {
218+ if debugger.is_running {
219+ cx.editor
220+ .set_status("Debuggee is already running".to_owned());
221+ return;
222+ }
223+
224+ let request = debugger.continue_thread(debugger.stopped_thread.unwrap());
225+ if let Err(e) = block_on(request) {
226+ cx.editor.set_error(format!("Failed to continue: {:?}", e));
227+ return;
228+ }
229+ debugger.is_running = true;
230+ debugger.stack_pointer = None;
231+ }
232+}
233+
234+pub fn dap_pause(cx: &mut Context) {
235+ if let Some(debugger) = &mut cx.editor.debugger {
236+ if !debugger.is_running {
237+ cx.editor.set_status("Debuggee is not running".to_owned());
238+ return;
239+ }
240+
241+ // FIXME: correct number here
242+ let request = debugger.pause(0);
243+ if let Err(e) = block_on(request) {
244+ cx.editor.set_error(format!("Failed to pause: {:?}", e));
245+ }
246+ }
247+}
248+
249+pub fn dap_step_in(cx: &mut Context) {
250+ if let Some(debugger) = &mut cx.editor.debugger {
251+ if debugger.is_running {
252+ cx.editor
253+ .set_status("Debuggee is already running".to_owned());
254+ return;
255+ }
256+
257+ let request = debugger.step_in(debugger.stopped_thread.unwrap());
258+ if let Err(e) = block_on(request) {
259+ cx.editor.set_error(format!("Failed to step: {:?}", e));
260+ }
261+ }
262+}
263+
264+pub fn dap_step_out(cx: &mut Context) {
265+ if let Some(debugger) = &mut cx.editor.debugger {
266+ if debugger.is_running {
267+ cx.editor
268+ .set_status("Debuggee is already running".to_owned());
269+ return;
270+ }
271+
272+ let request = debugger.step_out(debugger.stopped_thread.unwrap());
273+ if let Err(e) = block_on(request) {
274+ cx.editor.set_error(format!("Failed to step: {:?}", e));
275+ }
276+ }
277+}
278+
279+pub fn dap_next(cx: &mut Context) {
280+ if let Some(debugger) = &mut cx.editor.debugger {
281+ if debugger.is_running {
282+ cx.editor
283+ .set_status("Debuggee is already running".to_owned());
284+ return;
285+ }
286+
287+ let request = debugger.next(debugger.stopped_thread.unwrap());
288+ if let Err(e) = block_on(request) {
289+ cx.editor.set_error(format!("Failed to step: {:?}", e));
290+ }
291+ }
292+}
293+
294+pub fn dap_variables(cx: &mut Context) {
295+ if let Some(debugger) = &mut cx.editor.debugger {
296+ if debugger.is_running {
297+ cx.editor
298+ .set_status("Cannot access variables while target is running".to_owned());
299+ return;
300+ }
301+ if debugger.stack_pointer.is_none() {
302+ cx.editor
303+ .set_status("Cannot find current stack pointer to access variables".to_owned());
304+ return;
305+ }
306+
307+ let frame_id = debugger.stack_pointer.clone().unwrap().id;
308+ let scopes = match block_on(debugger.scopes(frame_id)) {
309+ Ok(s) => s,
310+ Err(e) => {
311+ cx.editor
312+ .set_error(format!("Failed to get scopes: {:?}", e));
313+ return;
314+ }
315+ };
316+ let mut variables = Vec::new();
317+
318+ for scope in scopes.iter() {
319+ let response = block_on(debugger.variables(scope.variables_reference));
320+
321+ if let Ok(vars) = response {
322+ for var in vars {
323+ let prefix = match var.data_type {
324+ Some(data_type) => format!("{} ", data_type),
325+ None => "".to_owned(),
326+ };
327+ variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
328+ }
329+ }
330+ }
331+
332+ if !variables.is_empty() {
333+ cx.editor.variables = Some(variables);
334+ cx.editor.variables_page = 0;
335+ }
336+ }
337+}
338+
339+pub fn dap_terminate(cx: &mut Context) {
340+ if let Some(debugger) = &mut cx.editor.debugger {
341+ let request = debugger.disconnect();
342+ if let Err(e) = block_on(request) {
343+ cx.editor
344+ .set_error(format!("Failed to disconnect: {:?}", e));
345+ return;
346+ }
347+ cx.editor.debugger = None;
348+ }
349+}