diff options
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r-- | helix-term/src/ui/editor.rs | 115 | ||||
-rw-r--r-- | helix-term/src/ui/mod.rs | 6 | ||||
-rw-r--r-- | helix-term/src/ui/picker.rs | 12 | ||||
-rw-r--r-- | helix-term/src/ui/text.rs | 21 |
4 files changed, 123 insertions, 31 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index e68f5ff0..a2131abe 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -59,33 +59,66 @@ impl EditorView { &mut self.spinners } - #[allow(clippy::too_many_arguments)] pub fn render_view( &self, + editor: &Editor, doc: &Document, view: &View, viewport: Rect, surface: &mut Surface, - theme: &Theme, is_focused: bool, - config: &helix_view::editor::Config, ) { let inner = view.inner_area(); let area = view.area; + let theme = &editor.theme; + + // DAP: Highlight current stack frame position + let stack_frame = editor.debugger.as_ref().and_then(|debugger| { + if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id) { + debugger + .stack_frames + .get(&thread_id) + .and_then(|bt| bt.get(frame)) + } else { + None + } + }); + if let Some(frame) = stack_frame { + if doc.path().is_some() + && frame + .source + .as_ref() + .and_then(|source| source.path.as_ref()) + == doc.path() + { + let line = frame.line - 1; // convert to 0-indexing + if line >= view.offset.row && line < view.offset.row + area.height as usize { + surface.set_style( + Rect::new( + area.x, + area.y + (line - view.offset.row) as u16, + area.width, + 1, + ), + theme.get("ui.highlight"), + ); + } + } + } let highlights = Self::doc_syntax_highlights(doc, view.offset, inner.height, theme); let highlights = syntax::merge(highlights, Self::doc_diagnostics_highlights(doc, theme)); let highlights: Box<dyn Iterator<Item = HighlightEvent>> = if is_focused { Box::new(syntax::merge( highlights, - Self::doc_selection_highlights(doc, view, theme, &config.cursor_shape), + Self::doc_selection_highlights(doc, view, theme, &editor.config.cursor_shape), )) } else { Box::new(highlights) }; 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(editor, doc, view, view.area, surface, theme, is_focused); if is_focused { Self::render_focused_view_elements(view, doc, inner, theme, surface); @@ -404,13 +437,13 @@ impl EditorView { } pub fn render_gutter( + editor: &Editor, doc: &Document, view: &View, viewport: Rect, surface: &mut Surface, theme: &Theme, is_focused: bool, - config: &helix_view::editor::Config, ) { let text = doc.text().slice(..); let last_line = view.last_line(doc); @@ -432,7 +465,7 @@ impl EditorView { let mut text = String::with_capacity(8); for (constructor, width) in view.gutters() { - let gutter = constructor(doc, view, theme, config, is_focused, *width); + let gutter = constructor(editor, doc, view, theme, is_focused, *width); text.reserve(*width); // ensure there's enough space for the gutter for (i, line) in (view.offset.row..(last_line + 1)).enumerate() { let selected = cursors.contains(&line); @@ -448,6 +481,7 @@ impl EditorView { } text.clear(); } + offset += *width as u16; } } @@ -825,6 +859,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 } @@ -901,6 +960,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::MappableCommand::dap_edit_log.execute(cxt); + } else { + commands::MappableCommand::dap_edit_condition.execute(cxt); + } + + return EventResult::Consumed(None); + } + } + EventResult::Ignored + } + + MouseEvent { kind: MouseEventKind::Up(MouseButton::Middle), row, column, @@ -1078,15 +1169,7 @@ impl Component for EditorView { for (view, is_focused) in cx.editor.tree.views() { let doc = cx.editor.document(view.doc).unwrap(); - self.render_view( - doc, - view, - area, - surface, - &cx.editor.theme, - is_focused, - &cx.editor.config, - ); + self.render_view(cx.editor, doc, view, area, surface, is_focused); } if cx.editor.config.auto_info { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 7bbd9ced..49f7b2fa 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 4068a2d4..2c7db7f2 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 { @@ -280,7 +280,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> { @@ -288,7 +288,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(), @@ -427,19 +427,19 @@ impl<T: 'static> Component for Picker<T> { } key!(Enter) => { if let Some(option) = self.selection() { - (self.callback_fn)(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)(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)(cx.editor, option, Action::VerticalSplit); + (self.callback_fn)(cx, option, Action::VerticalSplit); } return close_fn; } diff --git a/helix-term/src/ui/text.rs b/helix-term/src/ui/text.rs index 4641fae1..caece049 100644 --- a/helix-term/src/ui/text.rs +++ b/helix-term/src/ui/text.rs @@ -4,7 +4,7 @@ use tui::buffer::Buffer as Surface; use helix_view::graphics::Rect; pub struct Text { - contents: String, + contents: tui::text::Text<'static>, size: (u16, u16), viewport: (u16, u16), } @@ -12,18 +12,28 @@ pub struct Text { impl Text { pub fn new(contents: String) -> Self { Self { + contents: tui::text::Text::from(contents), + size: (0, 0), + viewport: (0, 0), + } + } +} + +impl From<tui::text::Text<'static>> for Text { + fn from(contents: tui::text::Text<'static>) -> Self { + Self { contents, size: (0, 0), viewport: (0, 0), } } } + impl Component for Text { fn render(&mut self, area: Rect, surface: &mut Surface, _cx: &mut Context) { use tui::widgets::{Paragraph, Widget, Wrap}; - let contents = tui::text::Text::from(self.contents.clone()); - let par = Paragraph::new(contents).wrap(Wrap { trim: false }); + let par = Paragraph::new(self.contents.clone()).wrap(Wrap { trim: false }); // .scroll(x, y) offsets par.render(area, surface); @@ -31,9 +41,8 @@ impl Component for Text { fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { if viewport != self.viewport { - let contents = tui::text::Text::from(self.contents.clone()); - let width = std::cmp::min(contents.width() as u16, viewport.0); - let height = std::cmp::min(contents.height() as u16, viewport.1); + let width = std::cmp::min(self.contents.width() as u16, viewport.0); + let height = std::cmp::min(self.contents.height() as u16, viewport.1); self.size = (width, height); self.viewport = viewport; } |