aboutsummaryrefslogtreecommitdiff
path: root/helix-view
diff options
context:
space:
mode:
Diffstat (limited to 'helix-view')
-rw-r--r--helix-view/Cargo.toml2
-rw-r--r--helix-view/src/editor.rs25
-rw-r--r--helix-view/src/gutter.rs82
-rw-r--r--helix-view/src/view.rs25
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),