Extract view.inner_area(), simplify render_focused_view_elements
This commit is contained in:
parent
9776553ad0
commit
557fd86e71
5 changed files with 97 additions and 134 deletions
|
@ -110,7 +110,7 @@ fn align_view(doc: &Document, view: &mut View, align: Align) {
|
|||
.cursor(doc.text().slice(..));
|
||||
let line = doc.text().char_to_line(pos);
|
||||
|
||||
let height = view.area.height.saturating_sub(1) as usize; // -1 for statusline
|
||||
let height = view.inner_area().height as usize;
|
||||
|
||||
let relative = match align {
|
||||
Align::Center => height / 2,
|
||||
|
@ -455,7 +455,7 @@ fn goto_first_nonwhitespace(cx: &mut Context) {
|
|||
fn goto_window(cx: &mut Context, align: Align) {
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
let height = view.area.height.saturating_sub(1) as usize; // -1 for statusline
|
||||
let height = view.inner_area().height as usize;
|
||||
|
||||
// - 1 so we have at least one gap in the middle.
|
||||
// a height of 6 with padding of 3 on each side will keep shifting the view back and forth
|
||||
|
@ -898,11 +898,9 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
|
|||
return;
|
||||
}
|
||||
|
||||
let scrolloff = cx
|
||||
.editor
|
||||
.config
|
||||
.scrolloff
|
||||
.min(view.area.height as usize / 2);
|
||||
let height = view.inner_area().height;
|
||||
|
||||
let scrolloff = cx.editor.config.scrolloff.min(height as usize / 2);
|
||||
|
||||
view.offset.row = match direction {
|
||||
Forward => view.offset.row + offset,
|
||||
|
@ -928,25 +926,25 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
|
|||
|
||||
fn page_up(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.area.height as usize;
|
||||
let offset = view.inner_area().height as usize;
|
||||
scroll(cx, offset, Direction::Backward);
|
||||
}
|
||||
|
||||
fn page_down(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.area.height as usize;
|
||||
let offset = view.inner_area().height as usize;
|
||||
scroll(cx, offset, Direction::Forward);
|
||||
}
|
||||
|
||||
fn half_page_up(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.area.height as usize / 2;
|
||||
let offset = view.inner_area().height as usize / 2;
|
||||
scroll(cx, offset, Direction::Backward);
|
||||
}
|
||||
|
||||
fn half_page_down(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.area.height as usize / 2;
|
||||
let offset = view.inner_area().height as usize / 2;
|
||||
scroll(cx, offset, Direction::Forward);
|
||||
}
|
||||
|
||||
|
@ -4113,9 +4111,9 @@ fn align_view_middle(cx: &mut Context) {
|
|||
.cursor(doc.text().slice(..));
|
||||
let pos = coords_at_pos(doc.text().slice(..), pos);
|
||||
|
||||
view.offset.col = pos.col.saturating_sub(
|
||||
((view.area.width as usize).saturating_sub(crate::ui::editor::GUTTER_OFFSET as usize)) / 2,
|
||||
);
|
||||
view.offset.col = pos
|
||||
.col
|
||||
.saturating_sub((view.inner_area().width as usize) / 2);
|
||||
}
|
||||
|
||||
fn scroll_up(cx: &mut Context) {
|
||||
|
|
|
@ -314,7 +314,7 @@ impl Component for Completion {
|
|||
let half = area.height / 2;
|
||||
let height = 15.min(half);
|
||||
// we want to make sure the cursor is visible (not hidden behind the documentation)
|
||||
let y = if cursor_pos + view.area.y
|
||||
let y = if cursor_pos + area.y
|
||||
>= (cx.editor.tree.area().height - height - 2/* statusline + commandline */)
|
||||
{
|
||||
0
|
||||
|
|
|
@ -38,8 +38,6 @@ pub struct EditorView {
|
|||
autoinfo: Option<Info>,
|
||||
}
|
||||
|
||||
pub const GUTTER_OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
|
||||
impl Default for EditorView {
|
||||
fn default() -> Self {
|
||||
Self::new(Keymaps::default())
|
||||
|
@ -74,15 +72,10 @@ impl EditorView {
|
|||
loader: &syntax::Loader,
|
||||
config: &helix_view::editor::Config,
|
||||
) {
|
||||
let area = Rect::new(
|
||||
view.area.x + GUTTER_OFFSET,
|
||||
view.area.y,
|
||||
view.area.width - GUTTER_OFFSET,
|
||||
view.area.height.saturating_sub(1),
|
||||
); // - 1 for statusline
|
||||
let height = view.area.height.saturating_sub(1); // - 1 for statusline
|
||||
let inner = view.inner_area();
|
||||
let area = view.area;
|
||||
|
||||
let highlights = Self::doc_syntax_highlights(doc, view.offset, height, theme, loader);
|
||||
let highlights = Self::doc_syntax_highlights(doc, view.offset, inner.height, theme, loader);
|
||||
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(
|
||||
|
@ -93,11 +86,11 @@ impl EditorView {
|
|||
Box::new(highlights)
|
||||
};
|
||||
|
||||
Self::render_text_highlights(doc, view.offset, area, surface, theme, highlights);
|
||||
Self::render_gutter(doc, view, area, surface, theme, config);
|
||||
Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
|
||||
Self::render_gutter(doc, view, view.area, surface, theme, is_focused, config);
|
||||
|
||||
if is_focused {
|
||||
Self::render_focused_view_elements(view, doc, area, theme, surface);
|
||||
Self::render_focused_view_elements(view, doc, inner, theme, surface);
|
||||
}
|
||||
|
||||
// if we're not at the edge of the screen, draw a right border
|
||||
|
@ -113,7 +106,7 @@ impl EditorView {
|
|||
}
|
||||
}
|
||||
|
||||
self.render_diagnostics(doc, view, area, surface, theme);
|
||||
self.render_diagnostics(doc, view, inner, surface, theme);
|
||||
|
||||
let area = Rect::new(
|
||||
view.area.x,
|
||||
|
@ -369,7 +362,7 @@ impl EditorView {
|
|||
}
|
||||
}
|
||||
|
||||
/// Render brace match, selected line numbers, etc (meant for the focused view only)
|
||||
/// Render brace match, etc (meant for the focused view only)
|
||||
pub fn render_focused_view_elements(
|
||||
view: &View,
|
||||
doc: &Document,
|
||||
|
@ -377,58 +370,12 @@ impl EditorView {
|
|||
theme: &Theme,
|
||||
surface: &mut Surface,
|
||||
) {
|
||||
let text = doc.text().slice(..);
|
||||
let selection = doc.selection(view.id);
|
||||
let last_line = view.last_line(doc);
|
||||
let screen = {
|
||||
let start = text.line_to_char(view.offset.row);
|
||||
let end = text.line_to_char(last_line + 1) + 1; // +1 for cursor at end of text.
|
||||
Range::new(start, end)
|
||||
};
|
||||
|
||||
// render selected linenr(s)
|
||||
let linenr_select: Style = theme
|
||||
.try_get("ui.linenr.selected")
|
||||
.unwrap_or_else(|| theme.get("ui.linenr"));
|
||||
|
||||
// Whether to draw the line number for the last line of the
|
||||
// document or not. We only draw it if it's not an empty line.
|
||||
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
|
||||
|
||||
for selection in selection.iter().filter(|range| range.overlaps(&screen)) {
|
||||
let head = view.screen_coords_at_pos(
|
||||
doc,
|
||||
text,
|
||||
if selection.head > selection.anchor {
|
||||
selection.head - 1
|
||||
} else {
|
||||
selection.head
|
||||
},
|
||||
);
|
||||
if let Some(head) = head {
|
||||
// Highlight line number for selected lines.
|
||||
let line_number = view.offset.row + head.row;
|
||||
let line_number_text = if line_number == last_line && !draw_last {
|
||||
" ~".into()
|
||||
} else {
|
||||
format!("{:>5}", line_number + 1)
|
||||
};
|
||||
surface.set_stringn(
|
||||
viewport.x - GUTTER_OFFSET + 1,
|
||||
viewport.y + head.row as u16,
|
||||
line_number_text,
|
||||
5,
|
||||
linenr_select,
|
||||
);
|
||||
|
||||
// Highlight matching braces
|
||||
// TODO: set cursor position for IME
|
||||
if let Some(syntax) = doc.syntax() {
|
||||
let text = doc.text().slice(..);
|
||||
use helix_core::match_brackets;
|
||||
let pos = doc
|
||||
.selection(view.id)
|
||||
.primary()
|
||||
.cursor(doc.text().slice(..));
|
||||
let pos = doc.selection(view.id).primary().cursor(text);
|
||||
|
||||
let pos = match_brackets::find(syntax, doc.text(), pos)
|
||||
.and_then(|pos| view.screen_coords_at_pos(doc, text, pos));
|
||||
|
||||
|
@ -450,8 +397,6 @@ impl EditorView {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn render_gutter(
|
||||
|
@ -460,12 +405,15 @@ impl EditorView {
|
|||
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);
|
||||
|
||||
let linenr = theme.get("ui.linenr");
|
||||
let linenr_select: Style = theme.try_get("ui.linenr.selected").unwrap_or(linenr);
|
||||
|
||||
let warning = theme.get("warning");
|
||||
let error = theme.get("error");
|
||||
let info = theme.get("info");
|
||||
|
@ -478,11 +426,21 @@ impl EditorView {
|
|||
let current_line = doc
|
||||
.text()
|
||||
.char_to_line(doc.selection(view.id).primary().anchor);
|
||||
|
||||
// it's used inside an iterator so the collect isn't needless:
|
||||
// https://github.com/rust-lang/rust-clippy/issues/6164
|
||||
#[allow(clippy::clippy::needless_collect)]
|
||||
let cursors: Vec<_> = doc
|
||||
.selection(view.id)
|
||||
.iter()
|
||||
.map(|range| range.cursor_line(text))
|
||||
.collect();
|
||||
|
||||
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
|
||||
use helix_core::diagnostic::Severity;
|
||||
if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
|
||||
surface.set_stringn(
|
||||
viewport.x - GUTTER_OFFSET,
|
||||
viewport.x,
|
||||
viewport.y + i as u16,
|
||||
"●",
|
||||
1,
|
||||
|
@ -495,25 +453,27 @@ impl EditorView {
|
|||
);
|
||||
}
|
||||
|
||||
// Line numbers having selections are rendered
|
||||
// differently, further below.
|
||||
let line_number_text = if line == last_line && !draw_last {
|
||||
let selected = cursors.contains(&line);
|
||||
|
||||
let text = if line == last_line && !draw_last {
|
||||
" ~".into()
|
||||
} else {
|
||||
match config.line_number {
|
||||
LineNumber::Absolute => format!("{:>5}", line + 1),
|
||||
LineNumber::Relative => {
|
||||
let relative_line = abs_diff(current_line, line);
|
||||
format!("{:>5}", relative_line)
|
||||
}
|
||||
}
|
||||
let line = match config.line_number {
|
||||
LineNumber::Absolute => line + 1,
|
||||
LineNumber::Relative => abs_diff(current_line, line),
|
||||
};
|
||||
format!("{:>5}", line)
|
||||
};
|
||||
surface.set_stringn(
|
||||
viewport.x + 1 - GUTTER_OFFSET,
|
||||
viewport.x + 1,
|
||||
viewport.y + i as u16,
|
||||
line_number_text,
|
||||
text,
|
||||
5,
|
||||
linenr,
|
||||
if selected && is_focused {
|
||||
linenr_select
|
||||
} else {
|
||||
linenr
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ impl Editor {
|
|||
.primary()
|
||||
.cursor(doc.text().slice(..));
|
||||
let line = doc.text().char_to_line(pos);
|
||||
view.offset.row = line.saturating_sub(view.area.height as usize / 2);
|
||||
view.offset.row = line.saturating_sub(view.inner_area().height as usize / 2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -344,7 +344,6 @@ impl Editor {
|
|||
// }
|
||||
|
||||
pub fn cursor(&self) -> (Option<Position>, CursorKind) {
|
||||
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
let view = view!(self);
|
||||
let doc = &self.documents[view.doc];
|
||||
let cursor = doc
|
||||
|
@ -352,8 +351,9 @@ impl Editor {
|
|||
.primary()
|
||||
.cursor(doc.text().slice(..));
|
||||
if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
|
||||
pos.col += view.area.x as usize + OFFSET as usize;
|
||||
pos.row += view.area.y as usize;
|
||||
let inner = view.inner_area();
|
||||
pos.col += inner.x as usize;
|
||||
pos.row += inner.y as usize;
|
||||
(Some(pos), CursorKind::Hidden)
|
||||
} else {
|
||||
(None, CursorKind::Hidden)
|
||||
|
|
|
@ -80,25 +80,32 @@ impl View {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inner_area(&self) -> Rect {
|
||||
// TODO: not ideal
|
||||
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
Rect::new(
|
||||
self.area.x + OFFSET,
|
||||
self.area.y,
|
||||
self.area.width - OFFSET,
|
||||
self.area.height.saturating_sub(1), // -1 for statusline
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) {
|
||||
let cursor = doc
|
||||
.selection(self.id)
|
||||
.primary()
|
||||
.cursor(doc.text().slice(..));
|
||||
let pos = coords_at_pos(doc.text().slice(..), cursor);
|
||||
let line = pos.row;
|
||||
let col = pos.col;
|
||||
let height = self.area.height.saturating_sub(1); // - 1 for statusline
|
||||
let last_line = (self.offset.row + height as usize).saturating_sub(1);
|
||||
let Position { col, row: line } = coords_at_pos(doc.text().slice(..), cursor);
|
||||
let inner_area = self.inner_area();
|
||||
let last_line = (self.offset.row + inner_area.height as usize).saturating_sub(1);
|
||||
|
||||
// - 1 so we have at least one gap in the middle.
|
||||
// a height of 6 with padding of 3 on each side will keep shifting the view back and forth
|
||||
// as we type
|
||||
let scrolloff = scrolloff.min(height.saturating_sub(1) as usize / 2);
|
||||
let scrolloff = scrolloff.min(inner_area.height.saturating_sub(1) as usize / 2);
|
||||
|
||||
// TODO: not ideal
|
||||
const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
let last_col = (self.offset.col + self.area.width as usize).saturating_sub(OFFSET + 1);
|
||||
let last_col = self.offset.col + inner_area.width.saturating_sub(1) as usize;
|
||||
|
||||
if line > last_line.saturating_sub(scrolloff) {
|
||||
// scroll down
|
||||
|
@ -120,7 +127,7 @@ impl View {
|
|||
/// Calculates the last visible line on screen
|
||||
#[inline]
|
||||
pub fn last_line(&self, doc: &Document) -> usize {
|
||||
let height = self.area.height.saturating_sub(1); // - 1 for statusline
|
||||
let height = self.inner_area().height;
|
||||
std::cmp::min(
|
||||
// Saturating subs to make it inclusive zero indexing.
|
||||
(self.offset.row + height as usize).saturating_sub(1),
|
||||
|
@ -172,19 +179,17 @@ impl View {
|
|||
column: u16,
|
||||
tab_width: usize,
|
||||
) -> Option<usize> {
|
||||
// TODO: not ideal
|
||||
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
|
||||
// 2 for status
|
||||
if row < self.area.top() || row > self.area.bottom().saturating_sub(2) {
|
||||
let inner = self.inner_area();
|
||||
// 1 for status
|
||||
if row < inner.top() || row >= inner.bottom() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if column < self.area.left() + OFFSET || column > self.area.right() {
|
||||
if column < inner.left() || column > inner.right() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let line_number = (row - self.area.y) as usize + self.offset.row;
|
||||
let line_number = (row - inner.y) as usize + self.offset.row;
|
||||
|
||||
if line_number > text.len_lines() - 1 {
|
||||
return Some(text.len_chars());
|
||||
|
@ -194,7 +199,7 @@ impl View {
|
|||
|
||||
let current_line = text.line(line_number);
|
||||
|
||||
let target = (column - OFFSET - self.area.x) as usize + self.offset.col;
|
||||
let target = (column - inner.x) as usize + self.offset.col;
|
||||
let mut selected = 0;
|
||||
|
||||
for grapheme in RopeGraphemes::new(current_line) {
|
||||
|
|
Loading…
Add table
Reference in a new issue