aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r--helix-term/src/ui/editor.rs220
-rw-r--r--helix-term/src/ui/mod.rs6
-rw-r--r--helix-term/src/ui/picker.rs12
3 files changed, 224 insertions, 14 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 83be816f..96c5f083 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -15,15 +15,16 @@ use helix_core::{
unicode::width::UnicodeWidthStr,
LineEnding, Position, Range, Selection,
};
+use helix_dap::{Breakpoint, SourceBreakpoint, StackFrame};
use helix_view::{
document::{Mode, SCRATCH_BUFFER_NAME},
- graphics::{CursorKind, Modifier, Rect, Style},
+ graphics::{Color, CursorKind, Modifier, Rect, Style},
info::Info,
input::KeyEvent,
keyboard::{KeyCode, KeyModifiers},
Document, Editor, Theme, View,
};
-use std::borrow::Cow;
+use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
use tui::buffer::Buffer as Surface;
@@ -70,6 +71,9 @@ impl EditorView {
is_focused: bool,
loader: &syntax::Loader,
config: &helix_view::editor::Config,
+ debugger: &Option<helix_dap::Client>,
+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
+ dbg_breakpoints: &Option<Vec<Breakpoint>>,
) {
let inner = view.inner_area();
let area = view.area;
@@ -86,7 +90,18 @@ 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,
+ all_breakpoints,
+ dbg_breakpoints,
+ );
if is_focused {
Self::render_focused_view_elements(view, doc, inner, theme, surface);
@@ -105,7 +120,7 @@ impl EditorView {
}
}
- self.render_diagnostics(doc, view, inner, surface, theme);
+ self.render_diagnostics(doc, view, inner, surface, theme, all_breakpoints);
let statusline_area = view
.area
@@ -407,6 +422,9 @@ impl EditorView {
theme: &Theme,
is_focused: bool,
config: &helix_view::editor::Config,
+ debugger: &Option<helix_dap::Client>,
+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
+ dbg_breakpoints: &Option<Vec<Breakpoint>>,
) {
let text = doc.text().slice(..);
let last_line = view.last_line(doc);
@@ -420,6 +438,113 @@ impl EditorView {
.map(|range| range.cursor_line(text))
.collect();
+ use helix_view::gutter::GutterFn;
+ fn breakpoints<'doc>(
+ doc: &'doc Document,
+ _view: &View,
+ theme: &Theme,
+ _config: &Config,
+ _is_focused: bool,
+ _width: usize,
+ ) -> GutterFn<'doc> {
+ Box::new(move |line: usize, _selected: bool, out: &mut String| {
+ //
+ })
+ }
+ // let mut breakpoints: Option<&Vec<SourceBreakpoint>> = None;
+ // let mut stack_frame: Option<&StackFrame> = None;
+ // if let Some(path) = doc.path() {
+ // breakpoints = all_breakpoints.get(path);
+ // if let Some(debugger) = debugger {
+ // // if we have a frame, and the frame path matches document
+ // if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id)
+ // {
+ // let frame = debugger
+ // .stack_frames
+ // .get(&thread_id)
+ // .and_then(|bt| bt.get(frame)); // TODO: drop the clone..
+ // if let Some(StackFrame {
+ // source: Some(source),
+ // ..
+ // }) = &frame
+ // {
+ // if source.path.as_ref() == Some(path) {
+ // stack_frame = frame;
+ // }
+ // };
+ // };
+ // }
+ // }
+
+ // TODO: debugger should translate received breakpoints to 0-indexing
+
+ // if let Some(user) = breakpoints.as_ref() {
+ // let debugger_breakpoint = if let Some(debugger) = dbg_breakpoints.as_ref() {
+ // debugger.iter().find(|breakpoint| {
+ // if breakpoint.source.is_some()
+ // && doc.path().is_some()
+ // && breakpoint.source.as_ref().unwrap().path == doc.path().cloned()
+ // {
+ // match (breakpoint.line, breakpoint.end_line) {
+ // #[allow(clippy::int_plus_one)]
+ // (Some(l), Some(el)) => l - 1 <= line && line <= el - 1,
+ // (Some(l), None) => l - 1 == line,
+ // _ => false,
+ // }
+ // } else {
+ // false
+ // }
+ // })
+ // } else {
+ // None
+ // };
+
+ // if let Some(breakpoint) = user.iter().find(|breakpoint| breakpoint.line - 1 == line)
+ // {
+ // let verified = debugger_breakpoint.map(|b| b.verified).unwrap_or(false);
+ // 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 !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)
+ // }
+ // };
+ // surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, style);
+ // } else if let Some(breakpoint) = debugger_breakpoint {
+ // let style = if breakpoint.verified {
+ // info
+ // } else {
+ // info.fg(Color::Gray)
+ // };
+ // surface.set_stringn(viewport.x, viewport.y + i as u16, "⊚", 1, style);
+ // }
+ // }
+
+ // if let Some(frame) = stack_frame {
+ // if frame.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 mut offset = 0;
// avoid lots of small allocations by reusing a text buffer for each line
@@ -442,6 +567,7 @@ impl EditorView {
}
text.clear();
}
+
offset += *width as u16;
}
}
@@ -453,6 +579,7 @@ impl EditorView {
viewport: Rect,
surface: &mut Surface,
theme: &Theme,
+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
) {
use helix_core::diagnostic::Severity;
use tui::{
@@ -490,6 +617,29 @@ impl EditorView {
lines.extend(text.lines);
}
+ if let Some(path) = doc.path() {
+ let line = doc.text().char_to_line(cursor);
+ if let Some(breakpoints) = all_breakpoints.get(path) {
+ if let Some(breakpoint) = breakpoints
+ .iter()
+ .find(|breakpoint| breakpoint.line - 1 == line)
+ {
+ if let Some(condition) = &breakpoint.condition {
+ lines.extend(
+ Text::styled(condition, warning.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)
.wrap(Wrap { trim: true });
@@ -653,7 +803,6 @@ impl EditorView {
cxt: &mut commands::Context,
event: KeyEvent,
) -> Option<KeymapResult> {
- self.autoinfo = None;
let key_result = self.keymaps.get_mut(&mode).unwrap().get(event);
self.autoinfo = key_result.sticky.map(|node| node.infobox());
@@ -802,6 +951,31 @@ impl EditorView {
return EventResult::Consumed(None);
}
+ let result = editor.tree.views().find_map(|(view, _focus)| {
+ view.gutter_coords_at_screen_coords(row, column)
+ .map(|coords| (coords, view.id))
+ });
+
+ if let Some((coords, view_id)) = result {
+ editor.tree.focus = view_id;
+
+ let view = editor.tree.get(view_id);
+ let doc = editor.documents.get_mut(&view.doc).unwrap();
+
+ let path = match doc.path() {
+ Some(path) => path.clone(),
+ None => {
+ return EventResult::Ignored;
+ }
+ };
+
+ let line = coords.row + view.offset.row;
+ if line < doc.text().len_lines() {
+ commands::dap_toggle_breakpoint_impl(cxt, path, line);
+ return EventResult::Consumed(None);
+ }
+ }
+
EventResult::Ignored
}
@@ -878,6 +1052,38 @@ impl EditorView {
}
MouseEvent {
+ kind: MouseEventKind::Up(MouseButton::Right),
+ row,
+ column,
+ modifiers,
+ ..
+ } => {
+ let result = cxt.editor.tree.views().find_map(|(view, _focus)| {
+ view.gutter_coords_at_screen_coords(row, column)
+ .map(|coords| (coords, view.id))
+ });
+
+ if let Some((coords, view_id)) = result {
+ cxt.editor.tree.focus = view_id;
+
+ let view = cxt.editor.tree.get(view_id);
+ let doc = cxt.editor.documents.get_mut(&view.doc).unwrap();
+ let line = coords.row + view.offset.row;
+ if let Ok(pos) = doc.text().try_line_to_char(line) {
+ doc.set_selection(view_id, Selection::point(pos));
+ if modifiers == crossterm::event::KeyModifiers::ALT {
+ commands::Command::dap_edit_log.execute(cxt);
+ } else {
+ commands::Command::dap_edit_condition.execute(cxt);
+ }
+
+ return EventResult::Consumed(None);
+ }
+ }
+ EventResult::Ignored
+ }
+
+ MouseEvent {
kind: MouseEventKind::Up(MouseButton::Middle),
row,
column,
@@ -1044,6 +1250,7 @@ impl Component for EditorView {
for (view, is_focused) in cx.editor.tree.views() {
let doc = cx.editor.document(view.doc).unwrap();
let loader = &cx.editor.syn_loader;
+ let dbg_breakpoints = cx.editor.debugger.as_ref().map(|d| d.breakpoints.clone());
self.render_view(
doc,
view,
@@ -1053,6 +1260,9 @@ impl Component for EditorView {
is_focused,
loader,
&cx.editor.config,
+ &cx.editor.debugger,
+ &cx.editor.breakpoints,
+ &dbg_breakpoints,
);
}
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index cdf42311..3c203326 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -21,7 +21,7 @@ pub use text::Text;
use helix_core::regex::Regex;
use helix_core::regex::RegexBuilder;
-use helix_view::{Document, Editor, View};
+use helix_view::{Document, View};
use std::path::PathBuf;
@@ -161,8 +161,8 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
.unwrap()
.into()
},
- move |editor: &mut Editor, path: &PathBuf, action| {
- editor
+ move |cx, path: &PathBuf, action| {
+ cx.editor
.open(path.into(), action)
.expect("editor.open failed");
},
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 6b1c5832..eaca470e 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -86,7 +86,7 @@ impl<T> FilePicker<T> {
pub fn new(
options: Vec<T>,
format_fn: impl Fn(&T) -> Cow<str> + 'static,
- callback_fn: impl Fn(&mut Editor, &T, Action) + 'static,
+ callback_fn: impl Fn(&mut Context, &T, Action) + 'static,
preview_fn: impl Fn(&Editor, &T) -> Option<FileLocation> + 'static,
) -> Self {
Self {
@@ -284,7 +284,7 @@ pub struct Picker<T> {
pub truncate_start: bool,
format_fn: Box<dyn Fn(&T) -> Cow<str>>,
- callback_fn: Box<dyn Fn(&mut Editor, &T, Action)>,
+ callback_fn: Box<dyn Fn(&mut Context, &T, Action)>,
}
impl<T> Picker<T> {
@@ -292,7 +292,7 @@ impl<T> Picker<T> {
render_centered: bool,
options: Vec<T>,
format_fn: impl Fn(&T) -> Cow<str> + 'static,
- callback_fn: impl Fn(&mut Editor, &T, Action) + 'static,
+ callback_fn: impl Fn(&mut Context, &T, Action) + 'static,
) -> Self {
let prompt = Prompt::new(
"".into(),
@@ -421,19 +421,19 @@ impl<T: 'static> Component for Picker<T> {
}
key!(Enter) => {
if let Some(option) = self.selection() {
- (self.callback_fn)(&mut cx.editor, option, Action::Replace);
+ (self.callback_fn)(cx, option, Action::Replace);
}
return close_fn;
}
ctrl!('s') => {
if let Some(option) = self.selection() {
- (self.callback_fn)(&mut cx.editor, option, Action::HorizontalSplit);
+ (self.callback_fn)(cx, option, Action::HorizontalSplit);
}
return close_fn;
}
ctrl!('v') => {
if let Some(option) = self.selection() {
- (self.callback_fn)(&mut cx.editor, option, Action::VerticalSplit);
+ (self.callback_fn)(cx, option, Action::VerticalSplit);
}
return close_fn;
}