diff options
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r-- | helix-term/src/ui/editor.rs | 220 | ||||
-rw-r--r-- | helix-term/src/ui/mod.rs | 6 | ||||
-rw-r--r-- | helix-term/src/ui/picker.rs | 12 |
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; } |