diff options
Diffstat (limited to 'helix-view')
-rw-r--r-- | helix-view/Cargo.toml | 2 | ||||
-rw-r--r-- | helix-view/src/editor.rs | 25 | ||||
-rw-r--r-- | helix-view/src/gutter.rs | 82 | ||||
-rw-r--r-- | helix-view/src/view.rs | 25 |
4 files changed, 128 insertions, 6 deletions
diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 3189a19d..932c3321 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -18,6 +18,7 @@ bitflags = "1.3" anyhow = "1" helix-core = { version = "0.6", path = "../helix-core" } helix-lsp = { version = "0.6", path = "../helix-lsp"} +helix-dap = { version = "0.6", path = "../helix-dap"} crossterm = { version = "0.23", optional = true } # Conversion traits @@ -25,6 +26,7 @@ once_cell = "1.9" url = "2" tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } +tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } slotmap = "1" diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 83822c1d..2e6121bc 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -10,6 +10,9 @@ use crate::{ }; use futures_util::future; +use futures_util::stream::select_all::SelectAll; +use tokio_stream::wrappers::UnboundedReceiverStream; + use std::{ collections::{BTreeMap, HashMap}, io::stdin, @@ -27,6 +30,7 @@ pub use helix_core::diagnostic::Severity; pub use helix_core::register::Registers; use helix_core::syntax; use helix_core::{Position, Selection}; +use helix_dap as dap; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize}; @@ -246,6 +250,19 @@ impl std::fmt::Debug for Motion { } } +#[derive(Debug, Clone, Default)] +pub struct Breakpoint { + pub id: Option<usize>, + pub verified: bool, + pub message: Option<String>, + + pub line: usize, + pub column: Option<usize>, + pub condition: Option<String>, + pub hit_condition: Option<String>, + pub log_message: Option<String>, +} + #[derive(Debug)] pub struct Editor { pub tree: Tree, @@ -257,6 +274,11 @@ pub struct Editor { pub macro_recording: Option<(char, Vec<KeyEvent>)>, pub theme: Theme, pub language_servers: helix_lsp::Registry, + + pub debugger: Option<dap::Client>, + pub debugger_events: SelectAll<UnboundedReceiverStream<dap::Payload>>, + pub breakpoints: HashMap<PathBuf, Vec<Breakpoint>>, + pub clipboard_provider: Box<dyn ClipboardProvider>, pub syn_loader: Arc<syntax::Loader>, @@ -302,6 +324,9 @@ impl Editor { macro_recording: None, theme: theme_loader.default(), language_servers, + debugger: None, + debugger_events: SelectAll::new(), + breakpoints: HashMap::new(), syn_loader, theme_loader, registers: Registers::default(), diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs index 113da642..6a77c41f 100644 --- a/helix-view/src/gutter.rs +++ b/helix-view/src/gutter.rs @@ -1,16 +1,19 @@ use std::fmt::Write; -use crate::{editor::Config, graphics::Style, Document, Theme, View}; +use crate::{ + graphics::{Color, Modifier, Style}, + Document, Editor, Theme, View, +}; pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>; pub type Gutter = - for<'doc> fn(&'doc Document, &View, &Theme, &Config, bool, usize) -> GutterFn<'doc>; + for<'doc> fn(&'doc Editor, &'doc Document, &View, &Theme, bool, usize) -> GutterFn<'doc>; pub fn diagnostic<'doc>( + _editor: &'doc Editor, doc: &'doc Document, _view: &View, theme: &Theme, - _config: &Config, _is_focused: bool, _width: usize, ) -> GutterFn<'doc> { @@ -37,10 +40,10 @@ pub fn diagnostic<'doc>( } pub fn line_number<'doc>( + editor: &'doc Editor, doc: &'doc Document, view: &View, theme: &Theme, - config: &Config, is_focused: bool, width: usize, ) -> GutterFn<'doc> { @@ -57,7 +60,7 @@ pub fn line_number<'doc>( .text() .char_to_line(doc.selection(view.id).primary().cursor(text)); - let config = config.line_number; + let config = editor.config.line_number; let mode = doc.mode; Box::new(move |line: usize, selected: bool, out: &mut String| { @@ -96,3 +99,72 @@ const fn abs_diff(a: usize, b: usize) -> usize { b - a } } + +pub fn breakpoints<'doc>( + editor: &'doc Editor, + doc: &'doc Document, + _view: &View, + theme: &Theme, + _is_focused: bool, + _width: usize, +) -> GutterFn<'doc> { + let warning = theme.get("warning"); + let error = theme.get("error"); + let info = theme.get("info"); + + let breakpoints = doc.path().and_then(|path| editor.breakpoints.get(path)); + + let breakpoints = match breakpoints { + Some(breakpoints) => breakpoints, + None => return Box::new(move |_, _, _| None), + }; + + Box::new(move |line: usize, _selected: bool, out: &mut String| { + let breakpoint = breakpoints + .iter() + .find(|breakpoint| breakpoint.line == line)?; + + let mut style = if breakpoint.condition.is_some() && breakpoint.log_message.is_some() { + error.add_modifier(Modifier::UNDERLINED) + } else if breakpoint.condition.is_some() { + error + } else if breakpoint.log_message.is_some() { + info + } else { + warning + }; + + if !breakpoint.verified { + // Faded colors + style = if let Some(Color::Rgb(r, g, b)) = style.fg { + style.fg(Color::Rgb( + ((r as f32) * 0.4).floor() as u8, + ((g as f32) * 0.4).floor() as u8, + ((b as f32) * 0.4).floor() as u8, + )) + } else { + style.fg(Color::Gray) + } + }; + + let sym = if breakpoint.verified { "▲" } else { "⊚" }; + write!(out, "{}", sym).unwrap(); + Some(style) + }) +} + +pub fn diagnostics_or_breakpoints<'doc>( + editor: &'doc Editor, + doc: &'doc Document, + view: &View, + theme: &Theme, + is_focused: bool, + width: usize, +) -> GutterFn<'doc> { + let diagnostics = diagnostic(editor, doc, view, theme, is_focused, width); + let breakpoints = breakpoints(editor, doc, view, theme, is_focused, width); + + Box::new(move |line, selected, out| { + breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out)) + }) +} diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index adfcd071..6bc9435c 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -64,7 +64,10 @@ impl JumpList { } } -const GUTTERS: &[(Gutter, usize)] = &[(gutter::diagnostic, 1), (gutter::line_number, 5)]; +const GUTTERS: &[(Gutter, usize)] = &[ + (gutter::diagnostics_or_breakpoints, 1), + (gutter::line_number, 5), +]; #[derive(Debug)] pub struct View { @@ -273,6 +276,26 @@ impl View { pub fn pos_at_screen_coords(&self, doc: &Document, row: u16, column: u16) -> Option<usize> { self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width()) } + + /// Translates screen coordinates into coordinates on the gutter of the view. + /// Returns a tuple of usize typed line and column numbers starting with 0. + /// Returns None if coordinates are not on the gutter. + pub fn gutter_coords_at_screen_coords(&self, row: u16, column: u16) -> Option<Position> { + // 1 for status + if row < self.area.top() || row >= self.area.bottom() { + return None; + } + + if column < self.area.left() || column > self.area.right() { + return None; + } + + Some(Position::new( + (row - self.area.top()) as usize, + (column - self.area.left()) as usize, + )) + } + // pub fn traverse<F>(&self, text: RopeSlice, start: usize, end: usize, fun: F) // where // F: Fn(usize, usize), |