diff options
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r-- | helix-term/src/ui/editor.rs | 190 |
1 files changed, 188 insertions, 2 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 72b8adc1..6428870e 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -15,6 +15,7 @@ use helix_core::{ unicode::width::UnicodeWidthStr, LineEnding, Position, Range, Selection, }; +use helix_dap::{SourceBreakpoint, StackFrame}; use helix_view::{ document::Mode, editor::LineNumber, @@ -29,6 +30,8 @@ use std::borrow::Cow; use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind}; use tui::buffer::Buffer as Surface; +use super::{Prompt, PromptEvent}; + pub struct EditorView { keymaps: Keymaps, on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>, @@ -71,6 +74,7 @@ impl EditorView { is_focused: bool, loader: &syntax::Loader, config: &helix_view::editor::Config, + debugger: &Option<helix_dap::Client>, ) { let inner = view.inner_area(); let area = view.area; @@ -87,7 +91,9 @@ impl EditorView { }; Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights); - Self::render_gutter(doc, view, view.area, surface, theme, is_focused, config); + Self::render_gutter( + doc, view, view.area, surface, theme, is_focused, config, debugger, + ); if is_focused { Self::render_focused_view_elements(view, doc, inner, theme, surface); @@ -106,7 +112,7 @@ impl EditorView { } } - self.render_diagnostics(doc, view, inner, surface, theme); + self.render_diagnostics(doc, view, inner, surface, theme, debugger); let statusline_area = view .area @@ -409,6 +415,7 @@ impl EditorView { theme: &Theme, is_focused: bool, config: &helix_view::editor::Config, + debugger: &Option<helix_dap::Client>, ) { let text = doc.text().slice(..); let last_line = view.last_line(doc); @@ -438,6 +445,15 @@ impl EditorView { .map(|range| range.cursor_line(text)) .collect(); + let mut breakpoints: Option<Vec<SourceBreakpoint>> = None; + let mut stack_pointer: Option<StackFrame> = None; + if let Some(debugger) = debugger { + if let Some(path) = doc.path() { + breakpoints = debugger.breakpoints.get(path).cloned(); + stack_pointer = debugger.stack_pointer.clone() + } + } + for (i, line) in (view.offset.row..(last_line + 1)).enumerate() { use helix_core::diagnostic::Severity; if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) { @@ -457,6 +473,36 @@ impl EditorView { let selected = cursors.contains(&line); + if let Some(bps) = breakpoints.as_ref() { + if let Some(breakpoint) = bps.iter().find(|breakpoint| breakpoint.line - 1 == line) + { + if breakpoint.condition.is_some() { + surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, error); + } else if breakpoint.log_message.is_some() { + surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, info); + } else { + surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, warning); + } + } + } + + if let Some(sp) = stack_pointer.as_ref() { + if let Some(src) = sp.source.as_ref() { + if doc + .path() + .map(|path| src.path == Some(path.clone())) + .unwrap_or(false) + && sp.line - 1 == line + { + surface.set_style( + Rect::new(viewport.x, viewport.y + i as u16, 6, 1), + helix_view::graphics::Style::default() + .bg(helix_view::graphics::Color::LightYellow), + ); + } + } + } + let text = if line == last_line && !draw_last { " ~".into() } else { @@ -493,6 +539,7 @@ impl EditorView { viewport: Rect, surface: &mut Surface, theme: &Theme, + debugger: &Option<helix_dap::Client>, ) { use helix_core::diagnostic::Severity; use tui::{ @@ -530,6 +577,31 @@ impl EditorView { lines.extend(text.lines); } + if let Some(debugger) = debugger { + if let Some(path) = doc.path() { + if let Some(breakpoints) = debugger.breakpoints.get(path) { + let line = doc.text().char_to_line(cursor); + if let Some(breakpoint) = breakpoints + .iter() + .find(|breakpoint| breakpoint.line - 1 == line) + { + if let Some(condition) = &breakpoint.condition { + lines.extend( + Text::styled(condition, info.add_modifier(Modifier::UNDERLINED)) + .lines, + ); + } + if let Some(log_message) = &breakpoint.log_message { + lines.extend( + Text::styled(log_message, info.add_modifier(Modifier::UNDERLINED)) + .lines, + ); + } + } + } + } + } + let paragraph = Paragraph::new(lines).alignment(Alignment::Right); let width = 80.min(viewport.width); let height = 15.min(viewport.height); @@ -647,6 +719,70 @@ impl EditorView { event: KeyEvent, ) -> Option<KeymapResult> { self.autoinfo = None; + + if let Some(picker) = cxt.editor.debug_config_picker.clone() { + match event { + KeyEvent { + code: KeyCode::Esc, .. + } => {} + KeyEvent { + code: KeyCode::Char(char), + .. + } => { + let name = match picker.iter().find(|t| t.starts_with(char)) { + Some(n) => n.clone(), + None => return None, + }; + let prompt = Prompt::new( + "arg:".to_owned(), + None, + |_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate + move |cx: &mut crate::compositor::Context, + input: &str, + event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } + commands::dap_start_impl( + cx.editor, + Some(&name), + None, + Some(vec![input]), + ); + }, + ); + cxt.push_layer(Box::new(prompt)); + } + _ => return None, + } + cxt.editor.debug_config_picker = None; + return None; + } + + if cxt.editor.variables.is_some() { + match event { + KeyEvent { + code: KeyCode::Char('h'), + .. + } => { + cxt.editor.variables_page = cxt.editor.variables_page.saturating_sub(1); + } + KeyEvent { + code: KeyCode::Char('l'), + .. + } => { + cxt.editor.variables_page = cxt.editor.variables_page.saturating_add(1); + } + KeyEvent { + code: KeyCode::Esc, .. + } => { + cxt.editor.variables = None; + } + _ => {} + } + return None; + } + match self.keymaps.get_mut(&mode).unwrap().get(event) { KeymapResult::Matched(command) => command.execute(cxt), KeymapResult::Pending(node) => self.autoinfo = Some(node.into()), @@ -1016,9 +1152,59 @@ impl Component for EditorView { is_focused, loader, &cx.editor.config, + &cx.editor.debugger, ); } + if let Some(ref vars) = cx.editor.variables { + let mut text = String::new(); + let mut height = 0; + let mut max_len = 20; + + let per_page = 15; + let num_vars = vars.len(); + let start = (per_page * cx.editor.variables_page).min(num_vars); + let end = (start + per_page).min(num_vars); + for line in vars[start..end].to_vec() { + max_len = max_len.max(line.len() as u16); + height += 1; + text.push_str(&line); + } + + if vars.len() > per_page { + text += "\nMove h, l"; + height += 1; + } + + let mut info = Info { + height: 20.min(height + 2), + width: 70.min(max_len), + title: format!("{} variables", num_vars), + text: text + "\nExit Esc", + }; + info.render(area, surface, cx); + } + + if let Some(ref configs) = cx.editor.debug_config_picker { + let mut text = String::new(); + let mut height = 0; + let mut max_len = 20; + + for line in configs { + max_len = max_len.max(line.len() as u16 + 2); + height += 1; + text.push_str(&format!("{} {}\n", line.chars().next().unwrap(), line)); + } + + let mut info = Info { + height: 20.min(height + 1), + width: 70.min(max_len), + title: "Debug targets".to_owned(), + text: text + "Exit Esc", + }; + info.render(area, surface, cx); + } + if let Some(ref mut info) = self.autoinfo { info.render(area, surface, cx); } |