summaryrefslogtreecommitdiff
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.rs115
-rw-r--r--helix-term/src/ui/mod.rs6
-rw-r--r--helix-term/src/ui/picker.rs12
-rw-r--r--helix-term/src/ui/text.rs21
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;
}