diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index fa6bcb0c..b2792720 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -118,7 +118,7 @@ fn align_view(doc: &Document, view: &mut View, align: Align) { Align::Bottom => height, }; - view.first_line = line.saturating_sub(relative); + view.offset.row = line.saturating_sub(relative); } /// A command is composed of a static name, and a function that takes the current state plus a count, @@ -465,8 +465,8 @@ fn goto_window(cx: &mut Context, align: Align) { let last_line = view.last_line(doc); let line = match align { - Align::Top => (view.first_line + scrolloff), - Align::Center => (view.first_line + (height / 2)), + Align::Top => (view.offset.row + scrolloff), + Align::Center => (view.offset.row + (height / 2)), Align::Bottom => last_line.saturating_sub(scrolloff), } .min(last_line.saturating_sub(scrolloff)); @@ -892,7 +892,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { let last_line = view.last_line(doc); - if direction == Backward && view.first_line == 0 + if direction == Backward && view.offset.row == 0 || direction == Forward && last_line == doc_last_line { return; @@ -904,9 +904,9 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { .scrolloff .min(view.area.height as usize / 2); - view.first_line = match direction { - Forward => view.first_line + offset, - Backward => view.first_line.saturating_sub(offset), + view.offset.row = match direction { + Forward => view.offset.row + offset, + Backward => view.offset.row.saturating_sub(offset), } .min(doc_last_line); @@ -916,7 +916,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { // clamp into viewport let line = cursor .row - .max(view.first_line + scrolloff) + .max(view.offset.row + scrolloff) .min(last_line.saturating_sub(scrolloff)); let text = doc.text().slice(..); @@ -4058,13 +4058,13 @@ fn split(cx: &mut Context, action: Action) { let (view, doc) = current!(cx.editor); let id = doc.id(); let selection = doc.selection(view.id).clone(); - let first_line = view.first_line; + let offset = view.offset; cx.editor.switch(id, action); // match the selection in the previous view let (view, doc) = current!(cx.editor); - view.first_line = first_line; + view.offset = offset; doc.set_selection(view.id, selection); } @@ -4113,7 +4113,7 @@ fn align_view_middle(cx: &mut Context) { .cursor(doc.text().slice(..)); let pos = coords_at_pos(doc.text().slice(..), pos); - view.first_col = pos.col.saturating_sub( + view.offset.col = pos.col.saturating_sub( ((view.area.width as usize).saturating_sub(crate::ui::editor::GUTTER_OFFSET as usize)) / 2, ); } diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 4e01ce1c..985d4e48 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -261,7 +261,7 @@ impl Component for Completion { .primary() .cursor(doc.text().slice(..)); let cursor_pos = (helix_core::coords_at_pos(doc.text().slice(..), cursor_pos).row - - view.first_line) as u16; + - view.offset.row) as u16; let mut doc = match &option.documentation { Some(lsp::Documentation::String(contents)) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 5e1e8f50..98462e26 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -80,10 +80,9 @@ impl EditorView { view.area.width - GUTTER_OFFSET, view.area.height.saturating_sub(1), ); // - 1 for statusline - let offset = Position::new(view.first_line, view.first_col); let height = view.area.height.saturating_sub(1); // - 1 for statusline - let highlights = Self::doc_syntax_highlights(doc, offset, height, theme, loader); + let highlights = Self::doc_syntax_highlights(doc, view.offset, height, theme, loader); let highlights = syntax::merge(highlights, Self::doc_diagnostics_highlights(doc, theme)); let highlights: Box> = if is_focused { Box::new(syntax::merge( @@ -94,7 +93,7 @@ impl EditorView { Box::new(highlights) }; - Self::render_text_highlights(doc, offset, area, surface, theme, highlights); + Self::render_text_highlights(doc, view.offset, area, surface, theme, highlights); Self::render_gutter(doc, view, area, surface, theme, config); if is_focused { @@ -382,7 +381,7 @@ impl EditorView { let selection = doc.selection(view.id); let last_line = view.last_line(doc); let screen = { - let start = text.line_to_char(view.first_line); + 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) }; @@ -408,7 +407,7 @@ impl EditorView { ); if let Some(head) = head { // Highlight line number for selected lines. - let line_number = view.first_line + head.row; + let line_number = view.offset.row + head.row; let line_number_text = if line_number == last_line && !draw_last { " ~".into() } else { @@ -435,8 +434,8 @@ impl EditorView { if let Some(pos) = pos { // ensure col is on screen - if (pos.col as u16) < viewport.width + view.first_col as u16 - && pos.col >= view.first_col + if (pos.col as u16) < viewport.width + view.offset.col as u16 + && pos.col >= view.offset.col { let style = theme.try_get("ui.cursor.match").unwrap_or_else(|| { Style::default() @@ -479,7 +478,7 @@ impl EditorView { let current_line = doc .text() .char_to_line(doc.selection(view.id).primary().anchor); - for (i, line) in (view.first_line..(last_line + 1)).enumerate() { + 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( diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 32f5fe86..478f3818 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -180,7 +180,7 @@ impl Editor { view.jumps.push(jump); view.last_accessed_doc = Some(view.doc); view.doc = id; - view.first_line = 0; + view.offset = Position::default(); let (view, doc) = current!(self); @@ -194,7 +194,7 @@ impl Editor { .primary() .cursor(doc.text().slice(..)); let line = doc.text().char_to_line(pos); - view.first_line = line.saturating_sub(view.area.height as usize / 2); + view.offset.row = line.saturating_sub(view.area.height as usize / 2); return; } diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index c7309fe9..f688dd7f 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -61,8 +61,7 @@ impl JumpList { pub struct View { pub id: ViewId, pub doc: DocumentId, - pub first_line: usize, - pub first_col: usize, + pub offset: Position, pub area: Rect, pub jumps: JumpList, /// the last accessed file before the current one @@ -74,8 +73,7 @@ impl View { Self { id: ViewId::default(), doc, - first_line: 0, - first_col: 0, + offset: Position::new(0, 0), area: Rect::default(), // will get calculated upon inserting into tree jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel last_accessed_doc: None, @@ -91,7 +89,7 @@ impl View { let line = pos.row; let col = pos.col; let height = self.area.height.saturating_sub(1); // - 1 for statusline - let last_line = (self.first_line + height as usize).saturating_sub(1); + let last_line = (self.offset.row + 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 @@ -100,22 +98,22 @@ impl View { // TODO: not ideal const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter - let last_col = (self.first_col + self.area.width as usize).saturating_sub(OFFSET + 1); + let last_col = (self.offset.col + self.area.width as usize).saturating_sub(OFFSET + 1); if line > last_line.saturating_sub(scrolloff) { // scroll down - self.first_line += line - (last_line.saturating_sub(scrolloff)); - } else if line < self.first_line + scrolloff { + self.offset.row += line - (last_line.saturating_sub(scrolloff)); + } else if line < self.offset.row + scrolloff { // scroll up - self.first_line = line.saturating_sub(scrolloff); + self.offset.row = line.saturating_sub(scrolloff); } if col > last_col.saturating_sub(scrolloff) { // scroll right - self.first_col += col - (last_col.saturating_sub(scrolloff)); - } else if col < self.first_col + scrolloff { + self.offset.col += col - (last_col.saturating_sub(scrolloff)); + } else if col < self.offset.col + scrolloff { // scroll left - self.first_col = col.saturating_sub(scrolloff); + self.offset.col = col.saturating_sub(scrolloff); } } @@ -125,7 +123,7 @@ impl View { let height = self.area.height.saturating_sub(1); // - 1 for statusline std::cmp::min( // Saturating subs to make it inclusive zero indexing. - (self.first_line + height as usize).saturating_sub(1), + (self.offset.row + height as usize).saturating_sub(1), doc.text().len_lines().saturating_sub(1), ) } @@ -141,7 +139,7 @@ impl View { ) -> Option { let line = text.char_to_line(pos); - if line < self.first_line || line > self.last_line(doc) { + if line < self.offset.row || line > self.last_line(doc) { // Line is not visible on screen return None; } @@ -161,8 +159,8 @@ impl View { } // It is possible for underflow to occur if the buffer length is larger than the terminal width. - let row = line.saturating_sub(self.first_line); - let col = col.saturating_sub(self.first_col); + let row = line.saturating_sub(self.offset.row); + let col = col.saturating_sub(self.offset.col); Some(Position::new(row, col)) } @@ -186,7 +184,7 @@ impl View { return None; } - let line_number = (row - self.area.y) as usize + self.first_line; + let line_number = (row - self.area.y) as usize + self.offset.row; if line_number > text.len_lines() - 1 { return Some(text.len_chars()); @@ -196,7 +194,7 @@ impl View { let current_line = text.line(line_number); - let target = (column - OFFSET - self.area.x) as usize + self.first_col; + let target = (column - OFFSET - self.area.x) as usize + self.offset.col; let mut selected = 0; for grapheme in RopeGraphemes::new(current_line) {