on master help sign in
refactor: handle DAP events in editor main loop
a938f5aDmitry Sharshakov
Cargo.lock+1
@@ -389,6 +389,7 @@ dependencies = [
389 "signal-hook",
389 "signal-hook",
390 "signal-hook-tokio",
390 "signal-hook-tokio",
391 "tokio",
391 "tokio",
392+ "tokio-stream",
392 "toml",
393 "toml",
393]
394]
394
395
helix-dap/src/client.rs+24−50
@@ -1,5 +1,5 @@
1use crate::{
1use crate::{
2− transport::{Event, Payload, Request, Transport},
2+ transport::{Payload, Request, Transport},
3 types::*,
3 types::*,
4 Result,
4 Result,
5};
5};
@@ -9,19 +9,13 @@ use std::{
9 net::{IpAddr, Ipv4Addr, SocketAddr},
9 net::{IpAddr, Ipv4Addr, SocketAddr},
10 path::PathBuf,
10 path::PathBuf,
11 process::Stdio,
11 process::Stdio,
12− sync::{
13− atomic::{AtomicU64, Ordering},
12+ sync::atomic::{AtomicU64, Ordering},
14− Arc,
15− },
16};
13};
17use tokio::{
14use tokio::{
18 io::{AsyncBufRead, AsyncWrite, BufReader, BufWriter},
15 io::{AsyncBufRead, AsyncWrite, BufReader, BufWriter},
19 net::TcpStream,
16 net::TcpStream,
20 process::{Child, Command},
17 process::{Child, Command},
21− sync::{
22− mpsc::{channel, Receiver, Sender, UnboundedReceiver, UnboundedSender},
18+ sync::mpsc::{channel, unbounded_channel, UnboundedReceiver, UnboundedSender},
23− Mutex,
24− },
25 time,
19 time,
26};
20};
27
21
@@ -32,8 +26,6 @@ pub struct Client {
32 server_tx: UnboundedSender<Request>,
26 server_tx: UnboundedSender<Request>,
33 request_counter: AtomicU64,
27 request_counter: AtomicU64,
34 capabilities: Option<DebuggerCapabilities>,
28 capabilities: Option<DebuggerCapabilities>,
35− awaited_events: Arc<Mutex<HashMap<String, Sender<Event>>>>,
36−
37 //
29 //
38 pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
30 pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
39 // TODO: multiple threads support
31 // TODO: multiple threads support
@@ -46,8 +38,9 @@ impl Client {
46 tx: Box<dyn AsyncWrite + Unpin + Send>,
38 tx: Box<dyn AsyncWrite + Unpin + Send>,
47 id: usize,
39 id: usize,
48 process: Option<Child>,
40 process: Option<Child>,
49− ) -> Result<Self> {
41+ ) -> Result<(Self, UnboundedReceiver<Payload>)> {
50 let (server_rx, server_tx) = Transport::start(rx, tx, id);
42 let (server_rx, server_tx) = Transport::start(rx, tx, id);
43+ let (client_rx, client_tx) = unbounded_channel();
51
44
52 let client = Self {
45 let client = Self {
53 id,
46 id,
@@ -55,24 +48,30 @@ impl Client {
55 server_tx,
48 server_tx,
56 request_counter: AtomicU64::new(0),
49 request_counter: AtomicU64::new(0),
57 capabilities: None,
50 capabilities: None,
58− awaited_events: Arc::new(Mutex::new(HashMap::default())),
59 //
51 //
60 breakpoints: HashMap::new(),
52 breakpoints: HashMap::new(),
61 stack_pointer: None,
53 stack_pointer: None,
62 };
54 };
63
55
64− tokio::spawn(Self::recv(Arc::clone(&client.awaited_events), server_rx));
56+ tokio::spawn(Self::recv(server_rx, client_rx));
65
57
66− Ok(client)
58+ Ok((client, client_tx))
67 }
59 }
68
60
61+ pub async fn tcp(
69− pub async fn tcp(addr: std::net::SocketAddr, id: usize) -> Result<Self> {
62+ addr: std::net::SocketAddr,
63+ id: usize,
64+ ) -> Result<(Self, UnboundedReceiver<Payload>)> {
70 let stream = TcpStream::connect(addr).await?;
65 let stream = TcpStream::connect(addr).await?;
71 let (rx, tx) = stream.into_split();
66 let (rx, tx) = stream.into_split();
72 Self::streams(Box::new(BufReader::new(rx)), Box::new(tx), id, None)
67 Self::streams(Box::new(BufReader::new(rx)), Box::new(tx), id, None)
73 }
68 }
74
69
75− pub fn stdio(cmd: &str, args: Vec<&str>, id: usize) -> Result<Self> {
70+ pub fn stdio(
71+ cmd: &str,
72+ args: Vec<&str>,
73+ id: usize,
74+ ) -> Result<(Self, UnboundedReceiver<Payload>)> {
76 let process = Command::new(cmd)
75 let process = Command::new(cmd)
77 .args(args)
76 .args(args)
78 .stdin(Stdio::piped())
77 .stdin(Stdio::piped())
@@ -114,7 +113,7 @@ impl Client {
114 args: Vec<&str>,
113 args: Vec<&str>,
115 port_format: &str,
114 port_format: &str,
116 id: usize,
115 id: usize,
117− ) -> Result<Self> {
116+ ) -> Result<(Self, UnboundedReceiver<Payload>)> {
118 let port = Self::get_port().await.unwrap();
117 let port = Self::get_port().await.unwrap();
119
118
120 let process = Command::new(cmd)
119 let process = Command::new(cmd)
@@ -145,43 +144,22 @@ impl Client {
145 )
144 )
146 }
145 }
147
146
148− async fn recv(
149− awaited_events: Arc<Mutex<HashMap<String, Sender<Event>>>>,
150− mut server_rx: UnboundedReceiver<Payload>,
147+ async fn recv(mut server_rx: UnboundedReceiver<Payload>, client_tx: UnboundedSender<Payload>) {
151− ) {
152 while let Some(msg) = server_rx.recv().await {
148 while let Some(msg) = server_rx.recv().await {
153 match msg {
149 match msg {
154 Payload::Event(ev) => {
150 Payload::Event(ev) => {
155− let name = ev.event.clone();
156− let hashmap = awaited_events.lock().await;
157− let tx = hashmap.get(&name);
158−
159− match tx {
160− Some(tx) => match tx.send(ev).await {
161− Ok(_) => (),
162− Err(_) => error!(
163− "Tried sending event into a closed channel (name={:?})",
164− name
165− ),
166− },
167− None => {
168− info!("unhandled event {}", name);
169− // client_tx.send(Payload::Event(ev)).expect("Failed to send");
151+ client_tx.send(Payload::Event(ev)).expect("Failed to send");
170− }
171− }
172 }
152 }
173 Payload::Response(_) => unreachable!(),
153 Payload::Response(_) => unreachable!(),
174− Payload::Request(_) => todo!(),
154+ Payload::Request(req) => {
155+ client_tx
156+ .send(Payload::Request(req))
157+ .expect("Failed to send");
158+ }
175 }
159 }
176 }
160 }
177 }
161 }
178
162
179− pub async fn listen_for_event(&self, name: String) -> Receiver<Event> {
180− let (rx, tx) = channel(1);
181− self.awaited_events.lock().await.insert(name.clone(), rx);
182− tx
183− }
184−
185 pub fn id(&self) -> usize {
163 pub fn id(&self) -> usize {
186 self.id
164 self.id
187 }
165 }
@@ -248,8 +226,6 @@ impl Client {
248 }
226 }
249
227
250 pub async fn launch(&mut self, args: serde_json::Value) -> Result<()> {
228 pub async fn launch(&mut self, args: serde_json::Value) -> Result<()> {
251− // TODO: buffer these until initialized arrives
252−
253 let response = self.request::<requests::Launch>(args).await?;
229 let response = self.request::<requests::Launch>(args).await?;
254 log::error!("launch response {}", response);
230 log::error!("launch response {}", response);
255
231
@@ -257,8 +233,6 @@ impl Client {
257 }
233 }
258
234
259 pub async fn attach(&mut self, args: serde_json::Value) -> Result<()> {
235 pub async fn attach(&mut self, args: serde_json::Value) -> Result<()> {
260− // TODO: buffer these until initialized arrives
261−
262 let response = self.request::<requests::Attach>(args).await?;
236 let response = self.request::<requests::Attach>(args).await?;
263 log::error!("attach response {}", response);
237 log::error!("attach response {}", response);
264
238
helix-term/Cargo.toml+1−1
@@ -34,7 +34,7 @@ num_cpus = "1"
34tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
34tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
35crossterm = { version = "0.20", features = ["event-stream"] }
35crossterm = { version = "0.20", features = ["event-stream"] }
36signal-hook = "0.3"
36signal-hook = "0.3"
37−
37+tokio-stream = "0.1"
38futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
38futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
39
39
40# Logging
40# Logging
helix-term/src/application.rs+24
@@ -1,4 +1,5 @@
1use helix_core::syntax;
1use helix_core::syntax;
2+use helix_dap::Payload;
2use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
3use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
3use helix_view::{theme, Editor};
4use helix_view::{theme, Editor};
4
5
@@ -184,6 +185,29 @@ impl Application {
184 last_render = Instant::now();
185 last_render = Instant::now();
185 }
186 }
186 }
187 }
188+ Some(payload) = self.editor.debugger_events.next() => {
189+ let mut debugger = self.editor.debugger.as_mut().unwrap();
190+ match payload {
191+ Payload::Event(ev) => {
192+ match &ev.event[..] {
193+ "stopped" => {
194+ let main = debugger
195+ .threads()
196+ .await
197+ .ok()
198+ .and_then(|threads| threads.get(0).cloned());
199+ if let Some(main) = main {
200+ let (bt, _) = debugger.stack_trace(main.id).await.unwrap();
201+ debugger.stack_pointer = bt.get(0).cloned();
202+ }
203+ }
204+ _ => {}
205+ }
206+ },
207+ Payload::Response(_) => unreachable!(),
208+ Payload::Request(_) => todo!(),
209+ }
210+ }
187 Some(callback) = self.jobs.futures.next() => {
211 Some(callback) = self.jobs.futures.next() => {
188 self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
212 self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
189 self.render();
213 self.render();
helix-term/src/commands.rs+6−30
@@ -24,17 +24,17 @@ use helix_lsp::{
24};
24};
25use insert::*;
25use insert::*;
26use movement::Movement;
26use movement::Movement;
27−use tokio::sync::{mpsc::Receiver, Mutex};
28
27
29use crate::{
28use crate::{
30 compositor::{self, Component, Compositor},
29 compositor::{self, Component, Compositor},
31 ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent},
30 ui::{self, FilePicker, Picker, Popup, Prompt, PromptEvent},
32};
31};
32+use tokio_stream::wrappers::UnboundedReceiverStream;
33
33
34use crate::job::{self, Job, Jobs};
34use crate::job::{self, Job, Jobs};
35use futures_util::FutureExt;
35use futures_util::FutureExt;
36+use std::num::NonZeroUsize;
36use std::{collections::HashMap, fmt, future::Future};
37use std::{collections::HashMap, fmt, future::Future};
37−use std::{num::NonZeroUsize, sync::Arc};
38
38
39use std::{
39use std::{
40 borrow::Cow,
40 borrow::Cow,
@@ -4255,8 +4255,8 @@ fn dap_start(cx: &mut Context) {
4255 // look up config for filetype
4255 // look up config for filetype
4256 // if multiple available, open picker
4256 // if multiple available, open picker
4257
4257
4258− let debugger = Client::tcp_process("dlv", vec!["dap"], "-l 127.0.0.1:{}", 0);
4258+ let started = Client::tcp_process("dlv", vec!["dap"], "-l 127.0.0.1:{}", 0);
4259− let mut debugger = block_on(debugger).unwrap();
4259+ let (mut debugger, events) = block_on(started).unwrap();
4260
4260
4261 let request = debugger.initialize("go".to_owned());
4261 let request = debugger.initialize("go".to_owned());
4262 let _ = block_on(request).unwrap();
4262 let _ = block_on(request).unwrap();
@@ -4268,32 +4268,10 @@ fn dap_start(cx: &mut Context) {
4268 let request = debugger.launch(to_value(args).unwrap());
4268 let request = debugger.launch(to_value(args).unwrap());
4269 let _ = block_on(request).unwrap();
4269 let _ = block_on(request).unwrap();
4270
4270
4271− let stopped = block_on(debugger.listen_for_event("stopped".to_owned()));
4272− let debugger = Arc::new(Mutex::new(debugger));
4273− tokio::spawn(dap_listen_stopped(stopped, Arc::clone(&debugger)));
4274−
4275 // TODO: either await "initialized" or buffer commands until event is received
4271 // TODO: either await "initialized" or buffer commands until event is received
4276 cx.editor.debugger = Some(debugger);
4272 cx.editor.debugger = Some(debugger);
4277−}
4273+ let stream = UnboundedReceiverStream::new(events);
4278−
4274+ cx.editor.debugger_events.push(stream);
4279−async fn dap_listen_stopped(
4280− mut stopped: Receiver<helix_dap::Event>,
4281− debugger: Arc<Mutex<helix_dap::Client>>,
4282−) {
4283− loop {
4284− stopped.recv().await;
4285−
4286− let mut dbg = debugger.lock().await;
4287− let main = dbg
4288− .threads()
4289− .await
4290− .ok()
4291− .and_then(|threads| threads.get(0).cloned());
4292− if let Some(main) = main {
4293− let (a, _) = dbg.stack_trace(main.id).await.unwrap();
4294− dbg.stack_pointer = a.get(0).cloned();
4295− }
4296− }
4297}
4275}
4298
4276
4299fn dap_toggle_breakpoint(cx: &mut Context) {
4277fn dap_toggle_breakpoint(cx: &mut Context) {
@@ -4321,7 +4299,6 @@ fn dap_toggle_breakpoint(cx: &mut Context) {
4321 // we shouldn't really allow editing while debug is running though
4299 // we shouldn't really allow editing while debug is running though
4322
4300
4323 if let Some(debugger) = &mut cx.editor.debugger {
4301 if let Some(debugger) = &mut cx.editor.debugger {
4324− let mut debugger = block_on(debugger.lock());
4325 let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
4302 let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
4326 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4303 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4327 breakpoints.remove(pos);
4304 breakpoints.remove(pos);
@@ -4340,7 +4317,6 @@ fn dap_run(cx: &mut Context) {
4340 use helix_lsp::block_on;
4317 use helix_lsp::block_on;
4341
4318
4342 if let Some(debugger) = &mut cx.editor.debugger {
4319 if let Some(debugger) = &mut cx.editor.debugger {
4343− let mut debugger = block_on(debugger.lock());
4344 let request = debugger.configuration_done();
4320 let request = debugger.configuration_done();
4345 let _ = block_on(request).unwrap();
4321 let _ = block_on(request).unwrap();
4346 }
4322 }
helix-term/src/ui/editor.rs+6−13
@@ -25,8 +25,7 @@ use helix_view::{
25 keyboard::{KeyCode, KeyModifiers},
25 keyboard::{KeyCode, KeyModifiers},
26 Document, Editor, Theme, View,
26 Document, Editor, Theme, View,
27};
27};
28−use std::{borrow::Cow, sync::Arc};
28+use std::borrow::Cow;
29−use tokio::sync::Mutex;
30
29
31use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
30use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
32use tui::buffer::Buffer as Surface;
31use tui::buffer::Buffer as Surface;
@@ -73,7 +72,7 @@ impl EditorView {
73 is_focused: bool,
72 is_focused: bool,
74 loader: &syntax::Loader,
73 loader: &syntax::Loader,
75 config: &helix_view::editor::Config,
74 config: &helix_view::editor::Config,
76− debugger: Option<Arc<Mutex<helix_dap::Client>>>,
75+ debugger: &Option<helix_dap::Client>,
77 ) {
76 ) {
78 let inner = view.inner_area();
77 let inner = view.inner_area();
79 let area = view.area;
78 let area = view.area;
@@ -414,9 +413,8 @@ impl EditorView {
414 theme: &Theme,
413 theme: &Theme,
415 is_focused: bool,
414 is_focused: bool,
416 config: &helix_view::editor::Config,
415 config: &helix_view::editor::Config,
417− debugger: Option<Arc<Mutex<helix_dap::Client>>>,
416+ debugger: &Option<helix_dap::Client>,
418 ) {
417 ) {
419− use helix_lsp::block_on;
420 let text = doc.text().slice(..);
418 let text = doc.text().slice(..);
421 let last_line = view.last_line(doc);
419 let last_line = view.last_line(doc);
422
420
@@ -449,9 +447,8 @@ impl EditorView {
449 let mut stack_pointer: Option<StackFrame> = None;
447 let mut stack_pointer: Option<StackFrame> = None;
450 if let Some(debugger) = debugger {
448 if let Some(debugger) = debugger {
451 if let Some(path) = doc.path() {
449 if let Some(path) = doc.path() {
452− let dbg = block_on(debugger.lock());
453− breakpoints = dbg.breakpoints.get(path).cloned();
450+ breakpoints = debugger.breakpoints.get(path).cloned();
454− stack_pointer = dbg.stack_pointer.clone()
451+ stack_pointer = debugger.stack_pointer.clone()
455 }
452 }
456 }
453 }
457
454
@@ -1047,10 +1044,6 @@ impl Component for EditorView {
1047 for (view, is_focused) in cx.editor.tree.views() {
1044 for (view, is_focused) in cx.editor.tree.views() {
1048 let doc = cx.editor.document(view.doc).unwrap();
1045 let doc = cx.editor.document(view.doc).unwrap();
1049 let loader = &cx.editor.syn_loader;
1046 let loader = &cx.editor.syn_loader;
1050− let mut dbg: Option<Arc<Mutex<helix_dap::Client>>> = None;
1051− if let Some(debugger) = &cx.editor.debugger {
1052− dbg = Some(Arc::clone(debugger));
1053− }
1054 self.render_view(
1047 self.render_view(
1055 doc,
1048 doc,
1056 view,
1049 view,
@@ -1060,7 +1053,7 @@ impl Component for EditorView {
1060 is_focused,
1053 is_focused,
1061 loader,
1054 loader,
1062 &cx.editor.config,
1055 &cx.editor.config,
1063− dbg,
1056+ &cx.editor.debugger,
1064 );
1057 );
1065 }
1058 }
1066
1059
helix-view/src/editor.rs+3−4
@@ -8,7 +8,6 @@ use crate::{
8
8
9use futures_util::future;
9use futures_util::future;
10use futures_util::stream::select_all::SelectAll;
10use futures_util::stream::select_all::SelectAll;
11−use tokio::sync::Mutex;
12use tokio_stream::wrappers::UnboundedReceiverStream;
11use tokio_stream::wrappers::UnboundedReceiverStream;
13
12
14use std::{
13use std::{
@@ -75,8 +74,8 @@ pub struct Editor {
75 pub theme: Theme,
74 pub theme: Theme,
76 pub language_servers: helix_lsp::Registry,
75 pub language_servers: helix_lsp::Registry,
77
76
78− pub debugger: Option<Arc<Mutex<helix_dap::Client>>>,
77+ pub debugger: Option<helix_dap::Client>,
79− pub debuggers: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
78+ pub debugger_events: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
80
79
81 pub clipboard_provider: Box<dyn ClipboardProvider>,
80 pub clipboard_provider: Box<dyn ClipboardProvider>,
82
81
@@ -116,7 +115,7 @@ impl Editor {
116 theme: themes.default(),
115 theme: themes.default(),
117 language_servers,
116 language_servers,
118 debugger: None,
117 debugger: None,
119− debuggers: SelectAll::new(),
118+ debugger_events: SelectAll::new(),
120 syn_loader: config_loader,
119 syn_loader: config_loader,
121 theme_loader: themes,
120 theme_loader: themes,
122 registers: Registers::default(),
121 registers: Registers::default(),