From e7ebdacc23fb91d79b41274ececcffb295fccbd5 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Mon, 26 Feb 2024 20:39:02 +0100 Subject: [PATCH 1/7] replicate t-monaghan's changes --- helix-view/src/document.rs | 25 ++++++++++++++++++++++--- helix-view/src/editor.rs | 20 +++++++++++++++----- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index f3ace89e59fb..045f8de47fa0 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -37,9 +37,12 @@ use helix_core::{ ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction, }; -use crate::editor::Config; -use crate::events::{DocumentDidChange, SelectionDidChange}; -use crate::{DocumentId, Editor, Theme, View, ViewId}; +use crate::{ + editor::Config, + events::{DocumentDidChange, SelectionDidChange}, + view::ViewPosition, + DocumentId, Editor, Theme, View, ViewId, +}; /// 8kB of buffer space for encoding and decoding `Rope`s. const BUF_SIZE: usize = 8192; @@ -130,6 +133,7 @@ pub struct Document { pub(crate) id: DocumentId, text: Rope, selections: HashMap, + pub view_data: HashMap, /// Inlay hints annotations for the document, by view. /// @@ -265,6 +269,7 @@ impl fmt::Debug for Document { .field("selections", &self.selections) .field("inlay_hints_oudated", &self.inlay_hints_oudated) .field("text_annotations", &self.inlay_hints) + .field("view_data", &self.view_data) .field("path", &self.path) .field("encoding", &self.encoding) .field("restore_cursor", &self.restore_cursor) @@ -656,6 +661,7 @@ impl Document { selections: HashMap::default(), inlay_hints: HashMap::default(), inlay_hints_oudated: false, + view_data: Default::default(), // TODO: verify this is what we want indent_style: DEFAULT_INDENT, line_ending, restore_cursor: false, @@ -1759,6 +1765,14 @@ impl Document { &self.selections } + pub fn view_data(&self, view_id: ViewId) -> Option<&ViewData> { + self.view_data.get(&view_id) + } + + pub fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData { + self.view_data.entry(view_id).or_default() + } + pub fn relative_path(&self) -> Option> { self.path .as_deref() @@ -2034,6 +2048,11 @@ impl Document { } } +#[derive(Debug, Default)] +pub struct ViewData { + pub view_position: ViewPosition, +} + #[derive(Clone, Debug)] pub enum FormatterError { SpawningFailed { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index cead30d7c7e6..28b55fb09315 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1,5 +1,4 @@ use crate::{ - align_view, annotations::diagnostics::{DiagnosticFilter, InlineDiagnosticsConfig}, document::{ DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint, @@ -12,7 +11,7 @@ use crate::{ theme::{self, Theme}, tree::{self, Tree}, view::ViewPosition, - Align, Document, DocumentId, View, ViewId, + Document, DocumentId, View, ViewId, }; use dap::StackFrame; use helix_vcs::DiffProviderRegistry; @@ -1530,16 +1529,27 @@ impl Editor { } fn replace_document_in_view(&mut self, current_view: ViewId, doc_id: DocumentId) { + let scrolloff = self.config().scrolloff; let view = self.tree.get_mut(current_view); - view.doc = doc_id; - view.offset = ViewPosition::default(); + if let Some(old_doc) = self.documents.get_mut(&view.doc) { + old_doc.view_data_mut(current_view).view_position = view.offset; + } + + view.doc = doc_id; let doc = doc_mut!(self, &doc_id); + + view.offset = if let Some(view_data) = doc.view_data(current_view) { + view_data.view_position + } else { + ViewPosition::default() + }; + doc.ensure_view_init(view.id); view.sync_changes(doc); doc.mark_as_focused(); - align_view(doc, view, Align::Center); + view.ensure_cursor_in_view(doc, scrolloff) } pub fn switch(&mut self, id: DocumentId, action: Action) { From 040764d31907f9b28cbe35aa817a6c1dc3c8ebf5 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Mon, 22 Apr 2024 22:10:00 +0200 Subject: [PATCH 2/7] remove View.offset in favour of Document.view_data.view_position --- helix-term/src/application.rs | 15 ++++++- helix-term/src/commands.rs | 55 +++++++++++++++--------- helix-term/src/commands/lsp.rs | 8 +++- helix-term/src/commands/typed.rs | 8 +++- helix-term/src/ui/editor.rs | 21 +++++---- helix-term/src/ui/mod.rs | 6 +-- helix-view/src/document.rs | 14 +++++- helix-view/src/editor.rs | 15 +------ helix-view/src/lib.rs | 7 ++- helix-view/src/view.rs | 73 +++++++++++++++++++------------- 10 files changed, 142 insertions(+), 80 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index b7123e9722a1..02b6dcfb9a82 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -394,10 +394,21 @@ impl Application { self.editor.refresh_config(); // reset view position in case softwrap was enabled/disabled + let mut view_position_resets = Vec::new(); let scrolloff = self.editor.config().scrolloff; - for (view, _) in self.editor.tree.views_mut() { + for (view, _) in self.editor.tree.views() { let doc = &self.editor.documents[&view.doc]; - view.ensure_cursor_in_view(doc, scrolloff) + + if let Some(offset) = view.offset_coords_to_in_view_center::(doc, scrolloff) { + view_position_resets.push((offset, view.id, view.doc)); + } + } + for (offset, view_id, doc_id) in view_position_resets { + self.editor + .document_mut(doc_id) + .unwrap() + .view_data_mut(view_id) + .view_position = offset; } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 7e0bee92baef..d92dd75b2cd1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1032,6 +1032,7 @@ fn goto_window(cx: &mut Context, align: Align) { let count = cx.count() - 1; let config = cx.editor.config(); let (view, doc) = current!(cx.editor); + let view_offset = doc.view_data_mut(view.id).view_position; let height = view.inner_height(); @@ -1044,15 +1045,15 @@ fn goto_window(cx: &mut Context, align: Align) { let last_visual_line = view.last_visual_line(doc); let visual_line = match align { - Align::Top => view.offset.vertical_offset + scrolloff + count, - Align::Center => view.offset.vertical_offset + (last_visual_line / 2), + Align::Top => view_offset.vertical_offset + scrolloff + count, + Align::Center => view_offset.vertical_offset + (last_visual_line / 2), Align::Bottom => { - view.offset.vertical_offset + last_visual_line.saturating_sub(scrolloff + count) + view_offset.vertical_offset + last_visual_line.saturating_sub(scrolloff + count) } }; let visual_line = visual_line - .max(view.offset.vertical_offset + scrolloff) - .min(view.offset.vertical_offset + last_visual_line.saturating_sub(scrolloff)); + .max(view_offset.vertical_offset + scrolloff) + .min(view_offset.vertical_offset + last_visual_line.saturating_sub(scrolloff)); let pos = view .pos_at_visual_coords(doc, visual_line as u16, 0, false) @@ -1665,6 +1666,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor use Direction::*; let config = cx.editor.config(); let (view, doc) = current!(cx.editor); + let view_offset = doc.view_data_mut(view.id).view_position; let range = doc.selection(view.id).primary(); let text = doc.text().slice(..); @@ -1681,15 +1683,21 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor let doc_text = doc.text().slice(..); let viewport = view.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); - let mut annotations = view.text_annotations(&*doc, None); - (view.offset.anchor, view.offset.vertical_offset) = char_idx_at_visual_offset( + let annotations = view.text_annotations(&*doc, None); + let (new_anchor, new_vertical_offset) = char_idx_at_visual_offset( doc_text, - view.offset.anchor, - view.offset.vertical_offset as isize + offset, + view_offset.anchor, + view_offset.vertical_offset as isize + offset, 0, &text_fmt, &annotations, ); + let view_data = doc.view_data_mut(view.id); + view_data.view_position.anchor = new_anchor; + view_data.view_position.vertical_offset = new_vertical_offset; + + let doc_text = doc.text().slice(..); + let mut annotations = view.text_annotations(&*doc, None); if sync_cursor { let movement = match cx.editor.mode { @@ -1716,14 +1724,16 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor return; } + let view_offset = doc.view_data(view.id).unwrap().view_position; + let mut head; match direction { Forward => { let off; (head, off) = char_idx_at_visual_offset( doc_text, - view.offset.anchor, - (view.offset.vertical_offset + scrolloff) as isize, + view_offset.anchor, + (view_offset.vertical_offset + scrolloff) as isize, 0, &text_fmt, &annotations, @@ -1736,8 +1746,8 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor Backward => { head = char_idx_at_visual_offset( doc_text, - view.offset.anchor, - (view.offset.vertical_offset + height - scrolloff - 1) as isize, + view_offset.anchor, + (view_offset.vertical_offset + height - scrolloff - 1) as isize, 0, &text_fmt, &annotations, @@ -5124,7 +5134,7 @@ fn split(editor: &mut Editor, action: Action) { let (view, doc) = current!(editor); let id = doc.id(); let selection = doc.selection(view.id).clone(); - let offset = view.offset; + let offset = doc.view_data(view.id).unwrap().view_position; editor.switch(id, action); @@ -5133,7 +5143,7 @@ fn split(editor: &mut Editor, action: Action) { doc.set_selection(view.id, selection); // match the view scroll offset (switch doesn't handle this fully // since the selection is only matched after the split) - view.offset = offset; + doc.view_data_mut(view.id).view_position = offset; } fn hsplit(cx: &mut Context) { @@ -5230,10 +5240,16 @@ fn align_view_middle(cx: &mut Context) { let doc_text = doc.text().slice(..); let annotations = view.text_annotations(doc, None); let pos = doc.selection(view.id).primary().cursor(doc_text); - let pos = - visual_offset_from_block(doc_text, view.offset.anchor, pos, &text_fmt, &annotations).0; + let pos = visual_offset_from_block( + doc_text, + doc.view_data(view.id).unwrap().view_position.anchor, + pos, + &text_fmt, + &annotations, + ) + .0; - view.offset.horizontal_offset = pos + doc.view_data_mut(view.id).view_position.horizontal_offset = pos .col .saturating_sub((view.inner_area(doc).width as usize) / 2); } @@ -6117,7 +6133,8 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { // This is not necessarily exact if there is virtual text like soft wrap. // It's ok though because the extra jump labels will not be rendered. - let start = text.line_to_char(text.char_to_line(view.offset.anchor)); + let start = + text.line_to_char(text.char_to_line(doc.view_data(view.id).unwrap().view_position.anchor)); let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1); let primary_selection = doc.selection(view.id).primary(); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 3b9efb43175d..6cc5119e23d6 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1299,7 +1299,13 @@ fn compute_inlay_hints_for_view( // than computing all the hints for the full file (which could be dozens of time // longer than the view is). let view_height = view.inner_height(); - let first_visible_line = doc_text.char_to_line(view.offset.anchor.min(doc_text.len_chars())); + let first_visible_line = doc_text.char_to_line( + doc.view_data(view_id) + .unwrap() + .view_position + .anchor + .min(doc_text.len_chars()), + ); let first_line = first_visible_line.saturating_sub(view_height); let last_line = first_visible_line .saturating_add(view_height.saturating_mul(2)) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 530d78097948..59c9948e3323 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1587,7 +1587,13 @@ fn tree_sitter_highlight_name( // Query the same range as the one used in syntax highlighting. let range = { // Calculate viewport byte ranges: - let row = text.char_to_line(view.offset.anchor.min(text.len_chars())); + let row = text.char_to_line( + doc.view_data(view.id) + .unwrap() + .view_position + .anchor + .min(text.len_chars()), + ); // Saturating subs to make it inclusive zero indexing. let last_line = text.len_lines().saturating_sub(1); let height = view.inner_area(doc).height; diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index c151a7dd5c7f..045de0e177f9 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -93,6 +93,8 @@ impl EditorView { let theme = &editor.theme; let config = editor.config(); + let view_offset = doc.view_data(view.id).unwrap().view_position; + let text_annotations = view.text_annotations(doc, Some(theme)); let mut decorations = DecorationManager::default(); @@ -119,13 +121,13 @@ impl EditorView { } let syntax_highlights = - Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme); + Self::doc_syntax_highlights(doc, view_offset.anchor, inner.height, theme); let mut overlay_highlights = - Self::empty_highlight_iter(doc, view.offset.anchor, inner.height); + Self::empty_highlight_iter(doc, view_offset.anchor, inner.height); let overlay_syntax_highlights = Self::overlay_syntax_highlights( doc, - view.offset.anchor, + view_offset.anchor, inner.height, &text_annotations, ); @@ -203,7 +205,7 @@ impl EditorView { surface, inner, doc, - view.offset, + view_offset, &text_annotations, syntax_highlights, overlay_highlights, @@ -259,11 +261,13 @@ impl EditorView { .and_then(|config| config.rulers.as_ref()) .unwrap_or(editor_rulers); + let view_offset = doc.view_data(view.id).unwrap().view_position; + rulers .iter() // View might be horizontally scrolled, convert from absolute distance // from the 1st column to relative distance from left of viewport - .filter_map(|ruler| ruler.checked_sub(1 + view.offset.horizontal_offset as u16)) + .filter_map(|ruler| ruler.checked_sub(1 + view_offset.horizontal_offset as u16)) .filter(|ruler| ruler < &viewport.width) .map(|ruler| viewport.clip_left(ruler).with_width(1)) .for_each(|area| surface.set_style(area, ruler_theme)) @@ -825,6 +829,7 @@ impl EditorView { let inner_area = view.inner_area(doc); let selection = doc.selection(view.id); + let view_offset = doc.view_data(view.id).unwrap().view_position; let primary = selection.primary(); let text_format = doc.text_format(viewport.width, None); for range in selection.iter() { @@ -835,11 +840,11 @@ impl EditorView { visual_offset_from_block(text, cursor, cursor, &text_format, text_annotations).0; // if the cursor is horizontally in the view - if col >= view.offset.horizontal_offset - && inner_area.width > (col - view.offset.horizontal_offset) as u16 + if col >= view_offset.horizontal_offset + && inner_area.width > (col - view_offset.horizontal_offset) as u16 { let area = Rect::new( - inner_area.x + (col - view.offset.horizontal_offset) as u16, + inner_area.x + (col - view_offset.horizontal_offset) as u16, view.area.y, 1, view.area.height, diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 10f4104abf69..3c6c735fec58 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -83,7 +83,7 @@ pub fn raw_regex_prompt( let (view, doc) = current!(cx.editor); let doc_id = view.doc; let snapshot = doc.selection(view.id).clone(); - let offset_snapshot = view.offset; + let offset_snapshot = doc.view_data(view.id).unwrap().view_position; let config = cx.editor.config(); let mut prompt = Prompt::new( @@ -95,7 +95,7 @@ pub fn raw_regex_prompt( PromptEvent::Abort => { let (view, doc) = current!(cx.editor); doc.set_selection(view.id, snapshot.clone()); - view.offset = offset_snapshot; + doc.view_data_mut(view.id).view_position = offset_snapshot; } PromptEvent::Update | PromptEvent::Validate => { // skip empty input @@ -136,7 +136,7 @@ pub fn raw_regex_prompt( Err(err) => { let (view, doc) = current!(cx.editor); doc.set_selection(view.id, snapshot.clone()); - view.offset = offset_snapshot; + doc.view_data_mut(view.id).view_position = offset_snapshot; if event == PromptEvent::Validate { let callback = async move { diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 045f8de47fa0..6b71135e80f1 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1190,12 +1190,16 @@ impl Document { self.set_selection(view_id, Selection::single(origin.anchor, origin.head)); } - /// Initializes a new selection for the given view if it does not - /// already have one. + /// Initializes a new selection and view_data for the given view + /// if it does not already have them. pub fn ensure_view_init(&mut self, view_id: ViewId) { if self.selections.get(&view_id).is_none() { self.reset_selection(view_id); } + + if self.view_data(view_id).is_none() { + self.view_data.insert(view_id, ViewData::default()); + } } /// Mark document as recent used for MRU sorting @@ -1241,6 +1245,12 @@ impl Document { .ensure_invariants(self.text.slice(..)); } + for view_data in self.view_data.values_mut() { + view_data.view_position.anchor = transaction + .changes() + .map_pos(view_data.view_position.anchor, Assoc::Before); + } + // if specified, the current selection should instead be replaced by transaction.selection if let Some(selection) = transaction.selection() { self.selections.insert( diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 28b55fb09315..a43e463587df 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -10,7 +10,6 @@ use crate::{ register::Registers, theme::{self, Theme}, tree::{self, Tree}, - view::ViewPosition, Document, DocumentId, View, ViewId, }; use dap::StackFrame; @@ -1532,19 +1531,9 @@ impl Editor { let scrolloff = self.config().scrolloff; let view = self.tree.get_mut(current_view); - if let Some(old_doc) = self.documents.get_mut(&view.doc) { - old_doc.view_data_mut(current_view).view_position = view.offset; - } - view.doc = doc_id; let doc = doc_mut!(self, &doc_id); - view.offset = if let Some(view_data) = doc.view_data(current_view) { - view_data.view_position - } else { - ViewPosition::default() - }; - doc.ensure_view_init(view.id); view.sync_changes(doc); doc.mark_as_focused(); @@ -1909,8 +1898,8 @@ impl Editor { pub fn ensure_cursor_in_view(&mut self, id: ViewId) { let config = self.config(); - let view = self.tree.get_mut(id); - let doc = &self.documents[&view.doc]; + let view = self.tree.get(id).clone(); + let doc = self.document_mut(view.doc).unwrap(); view.ensure_cursor_in_view(doc, config.scrolloff) } diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 5628c830cfb7..9d2a416ebb5f 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -47,7 +47,7 @@ pub enum Align { Bottom, } -pub fn align_view(doc: &Document, view: &mut View, align: Align) { +pub fn align_view(doc: &mut Document, view: &View, align: Align) { let doc_text = doc.text().slice(..); let cursor = doc.selection(view.id).primary().cursor(doc_text); let viewport = view.inner_area(doc); @@ -61,7 +61,7 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) { let text_fmt = doc.text_format(viewport.width, None); let annotations = view.text_annotations(doc, None); - (view.offset.anchor, view.offset.vertical_offset) = char_idx_at_visual_offset( + let (new_anchor, new_vertical_offset) = char_idx_at_visual_offset( doc_text, cursor, -(relative as isize), @@ -69,6 +69,9 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) { &text_fmt, &annotations, ); + let view_data = doc.view_data_mut(view.id); + view_data.view_position.anchor = new_anchor; + view_data.view_position.vertical_offset = new_vertical_offset; } pub use document::Document; diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index af4fdfe4ebc7..60447b665bfa 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -128,7 +128,6 @@ pub struct ViewPosition { #[derive(Clone)] pub struct View { pub id: ViewId, - pub offset: ViewPosition, pub area: Rect, pub doc: DocumentId, pub jumps: JumpList, @@ -173,11 +172,6 @@ impl View { Self { id: ViewId::default(), doc, - offset: ViewPosition { - anchor: 0, - horizontal_offset: 0, - vertical_offset: 0, - }, area: Rect::default(), // will get calculated upon inserting into tree jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel docs_access_history: Vec::new(), @@ -240,9 +234,10 @@ impl View { doc: &Document, scrolloff: usize, ) -> Option { + let view_offset = doc.view_data(self.id)?.view_position; let doc_text = doc.text().slice(..); let viewport = self.inner_area(doc); - let vertical_viewport_end = self.offset.vertical_offset + viewport.height as usize; + let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize; let text_fmt = doc.text_format(viewport.width, None); let annotations = self.text_annotations(doc, None); @@ -256,7 +251,7 @@ impl View { }; let cursor = doc.selection(self.id).primary().cursor(doc_text); - let mut offset = self.offset; + let mut offset = view_offset; let off = visual_offset_from_anchor( doc_text, offset.anchor, @@ -321,22 +316,22 @@ impl View { } // if we are not centering return None if view position is unchanged - if !CENTERING && offset == self.offset { + if !CENTERING && offset == view_offset { return None; } Some(offset) } - pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) { + pub fn ensure_cursor_in_view(&self, doc: &mut Document, scrolloff: usize) { if let Some(offset) = self.offset_coords_to_in_view_center::(doc, scrolloff) { - self.offset = offset; + doc.view_data_mut(self.id).view_position = offset; } } - pub fn ensure_cursor_in_view_center(&mut self, doc: &Document, scrolloff: usize) { + pub fn ensure_cursor_in_view_center(&self, doc: &mut Document, scrolloff: usize) { if let Some(offset) = self.offset_coords_to_in_view_center::(doc, scrolloff) { - self.offset = offset; + doc.view_data_mut(self.id).view_position = offset; } else { align_view(doc, self, Align::Center); } @@ -354,7 +349,13 @@ impl View { #[inline] pub fn estimate_last_doc_line(&self, doc: &Document) -> usize { let doc_text = doc.text().slice(..); - let line = doc_text.char_to_line(self.offset.anchor.min(doc_text.len_chars())); + let line = doc_text.char_to_line( + doc.view_data(self.id) + .unwrap() + .view_position + .anchor + .min(doc_text.len_chars()), + ); // Saturating subs to make it inclusive zero indexing. (line + self.inner_height()) .min(doc_text.len_lines()) @@ -368,9 +369,15 @@ impl View { let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); let annotations = self.text_annotations(doc, None); + let view_offset = doc.view_data(self.id).unwrap().view_position; // last visual line in view is trivial to compute - let visual_height = self.offset.vertical_offset + viewport.height as usize; + let visual_height = doc + .view_data(self.id) + .unwrap() + .view_position + .vertical_offset + + viewport.height as usize; // fast path when the EOF is not visible on the screen, if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 { @@ -380,7 +387,7 @@ impl View { // translate to document line let pos = visual_offset_from_anchor( doc_text, - self.offset.anchor, + view_offset.anchor, usize::MAX, &text_fmt, &annotations, @@ -388,7 +395,7 @@ impl View { ); match pos { - Ok((Position { row, .. }, _)) => row.saturating_sub(self.offset.vertical_offset), + Ok((Position { row, .. }, _)) => row.saturating_sub(view_offset.vertical_offset), Err(PosAfterMaxRow) => visual_height.saturating_sub(1), Err(PosBeforeAnchorRow) => 0, } @@ -403,13 +410,15 @@ impl View { text: RopeSlice, pos: usize, ) -> Option { + let view_offset = doc.view_data(self.id).unwrap().view_position; + let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); let annotations = self.text_annotations(doc, None); let mut pos = visual_offset_from_anchor( text, - self.offset.anchor, + view_offset.anchor, pos, &text_fmt, &annotations, @@ -417,14 +426,14 @@ impl View { ) .ok()? .0; - if pos.row < self.offset.vertical_offset { + if pos.row < view_offset.vertical_offset { return None; } - pos.row -= self.offset.vertical_offset; + pos.row -= view_offset.vertical_offset; if pos.row >= viewport.height as usize { return None; } - pos.col = pos.col.saturating_sub(self.offset.horizontal_offset); + pos.col = pos.col.saturating_sub(view_offset.horizontal_offset); Some(pos) } @@ -535,13 +544,14 @@ impl View { ignore_virtual_text: bool, ) -> Option { let text = doc.text().slice(..); + let view_offset = doc.view_data(self.id).unwrap().view_position; - let text_row = row as usize + self.offset.vertical_offset; - let text_col = column as usize + self.offset.horizontal_offset; + let text_row = row as usize + view_offset.vertical_offset; + let text_col = column as usize + view_offset.horizontal_offset; let (char_idx, virt_lines) = char_idx_at_visual_offset( text, - self.offset.anchor, + view_offset.anchor, text_row as isize, text_col, &text_fmt, @@ -689,11 +699,12 @@ mod tests { let mut view = View::new(DocumentId::default(), GutterConfig::default()); view.area = Rect::new(40, 40, 40, 40); let rope = Rope::from_str("abc\n\tdef"); - let doc = Document::from( + let mut doc = Document::from( rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), ); + doc.ensure_view_init(view.id); assert_eq!( view.text_pos_at_screen_coords( @@ -863,11 +874,12 @@ mod tests { ); view.area = Rect::new(40, 40, 40, 40); let rope = Rope::from_str("abc\n\tdef"); - let doc = Document::from( + let mut doc = Document::from( rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), ); + doc.ensure_view_init(view.id); assert_eq!( view.text_pos_at_screen_coords( &doc, @@ -892,11 +904,12 @@ mod tests { ); view.area = Rect::new(40, 40, 40, 40); let rope = Rope::from_str("abc\n\tdef"); - let doc = Document::from( + let mut doc = Document::from( rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), ); + doc.ensure_view_init(view.id); assert_eq!( view.text_pos_at_screen_coords( &doc, @@ -915,11 +928,12 @@ mod tests { let mut view = View::new(DocumentId::default(), GutterConfig::default()); view.area = Rect::new(40, 40, 40, 40); let rope = Rope::from_str("Hi! こんにちは皆さん"); - let doc = Document::from( + let mut doc = Document::from( rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), ); + doc.ensure_view_init(view.id); assert_eq!( view.text_pos_at_screen_coords( @@ -998,11 +1012,12 @@ mod tests { let mut view = View::new(DocumentId::default(), GutterConfig::default()); view.area = Rect::new(40, 40, 40, 40); let rope = Rope::from_str("Hèl̀l̀ò world!"); - let doc = Document::from( + let mut doc = Document::from( rope, None, Arc::new(ArcSwap::new(Arc::new(Config::default()))), ); + doc.ensure_view_init(view.id); assert_eq!( view.text_pos_at_screen_coords( From 3126cb3780a224afc877fca949d92d90c7883889 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Thu, 25 Apr 2024 16:23:42 +0200 Subject: [PATCH 3/7] improve access patterns for Document.view_data --- helix-term/src/commands.rs | 9 ++++----- helix-term/src/commands/lsp.rs | 1 - helix-term/src/commands/typed.rs | 1 - helix-term/src/ui/editor.rs | 6 +++--- helix-term/src/ui/mod.rs | 2 +- helix-view/src/document.rs | 15 +++++++++------ helix-view/src/view.rs | 17 ++++++----------- 7 files changed, 23 insertions(+), 28 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d92dd75b2cd1..c566c4a9a57c 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1724,7 +1724,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor return; } - let view_offset = doc.view_data(view.id).unwrap().view_position; + let view_offset = doc.view_data(view.id).view_position; let mut head; match direction { @@ -5134,7 +5134,7 @@ fn split(editor: &mut Editor, action: Action) { let (view, doc) = current!(editor); let id = doc.id(); let selection = doc.selection(view.id).clone(); - let offset = doc.view_data(view.id).unwrap().view_position; + let offset = doc.view_data(view.id).view_position; editor.switch(id, action); @@ -5242,7 +5242,7 @@ fn align_view_middle(cx: &mut Context) { let pos = doc.selection(view.id).primary().cursor(doc_text); let pos = visual_offset_from_block( doc_text, - doc.view_data(view.id).unwrap().view_position.anchor, + doc.view_data(view.id).view_position.anchor, pos, &text_fmt, &annotations, @@ -6133,8 +6133,7 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { // This is not necessarily exact if there is virtual text like soft wrap. // It's ok though because the extra jump labels will not be rendered. - let start = - text.line_to_char(text.char_to_line(doc.view_data(view.id).unwrap().view_position.anchor)); + let start = text.line_to_char(text.char_to_line(doc.view_data(view.id).view_position.anchor)); let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1); let primary_selection = doc.selection(view.id).primary(); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 6cc5119e23d6..91688c99e43f 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1301,7 +1301,6 @@ fn compute_inlay_hints_for_view( let view_height = view.inner_height(); let first_visible_line = doc_text.char_to_line( doc.view_data(view_id) - .unwrap() .view_position .anchor .min(doc_text.len_chars()), diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 59c9948e3323..4f42f26cf991 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1589,7 +1589,6 @@ fn tree_sitter_highlight_name( // Calculate viewport byte ranges: let row = text.char_to_line( doc.view_data(view.id) - .unwrap() .view_position .anchor .min(text.len_chars()), diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 045de0e177f9..ca2c6c5320af 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -93,7 +93,7 @@ impl EditorView { let theme = &editor.theme; let config = editor.config(); - let view_offset = doc.view_data(view.id).unwrap().view_position; + let view_offset = doc.view_data(view.id).view_position; let text_annotations = view.text_annotations(doc, Some(theme)); let mut decorations = DecorationManager::default(); @@ -261,7 +261,7 @@ impl EditorView { .and_then(|config| config.rulers.as_ref()) .unwrap_or(editor_rulers); - let view_offset = doc.view_data(view.id).unwrap().view_position; + let view_offset = doc.view_data(view.id).view_position; rulers .iter() @@ -829,7 +829,7 @@ impl EditorView { let inner_area = view.inner_area(doc); let selection = doc.selection(view.id); - let view_offset = doc.view_data(view.id).unwrap().view_position; + let view_offset = doc.view_data(view.id).view_position; let primary = selection.primary(); let text_format = doc.text_format(viewport.width, None); for range in selection.iter() { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 3c6c735fec58..9498052dcb0a 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -83,7 +83,7 @@ pub fn raw_regex_prompt( let (view, doc) = current!(cx.editor); let doc_id = view.doc; let snapshot = doc.selection(view.id).clone(); - let offset_snapshot = doc.view_data(view.id).unwrap().view_position; + let offset_snapshot = doc.view_data(view.id).view_position; let config = cx.editor.config(); let mut prompt = Prompt::new( diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 6b71135e80f1..0f7f7486dc3e 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -133,7 +133,7 @@ pub struct Document { pub(crate) id: DocumentId, text: Rope, selections: HashMap, - pub view_data: HashMap, + view_data: HashMap, /// Inlay hints annotations for the document, by view. /// @@ -661,7 +661,7 @@ impl Document { selections: HashMap::default(), inlay_hints: HashMap::default(), inlay_hints_oudated: false, - view_data: Default::default(), // TODO: verify this is what we want + view_data: Default::default(), indent_style: DEFAULT_INDENT, line_ending, restore_cursor: false, @@ -1197,9 +1197,7 @@ impl Document { self.reset_selection(view_id); } - if self.view_data(view_id).is_none() { - self.view_data.insert(view_id, ViewData::default()); - } + self.view_data_mut(view_id); } /// Mark document as recent used for MRU sorting @@ -1775,9 +1773,14 @@ impl Document { &self.selections } - pub fn view_data(&self, view_id: ViewId) -> Option<&ViewData> { + pub(crate) fn get_view_data(&self, view_id: ViewId) -> Option<&ViewData> { self.view_data.get(&view_id) } + pub fn view_data(&self, view_id: ViewId) -> &ViewData { + self.view_data + .get(&view_id) + .expect("This should only be called after ensure_view_init") + } pub fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData { self.view_data.entry(view_id).or_default() diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index 60447b665bfa..2a5234cbcff9 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -234,7 +234,7 @@ impl View { doc: &Document, scrolloff: usize, ) -> Option { - let view_offset = doc.view_data(self.id)?.view_position; + let view_offset = doc.get_view_data(self.id)?.view_position; let doc_text = doc.text().slice(..); let viewport = self.inner_area(doc); let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize; @@ -351,7 +351,6 @@ impl View { let doc_text = doc.text().slice(..); let line = doc_text.char_to_line( doc.view_data(self.id) - .unwrap() .view_position .anchor .min(doc_text.len_chars()), @@ -369,15 +368,11 @@ impl View { let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); let annotations = self.text_annotations(doc, None); - let view_offset = doc.view_data(self.id).unwrap().view_position; + let view_offset = doc.view_data(self.id).view_position; // last visual line in view is trivial to compute - let visual_height = doc - .view_data(self.id) - .unwrap() - .view_position - .vertical_offset - + viewport.height as usize; + let visual_height = + doc.view_data(self.id).view_position.vertical_offset + viewport.height as usize; // fast path when the EOF is not visible on the screen, if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 { @@ -410,7 +405,7 @@ impl View { text: RopeSlice, pos: usize, ) -> Option { - let view_offset = doc.view_data(self.id).unwrap().view_position; + let view_offset = doc.view_data(self.id).view_position; let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); @@ -544,7 +539,7 @@ impl View { ignore_virtual_text: bool, ) -> Option { let text = doc.text().slice(..); - let view_offset = doc.view_data(self.id).unwrap().view_position; + let view_offset = doc.view_data(self.id).view_position; let text_row = row as usize + view_offset.vertical_offset; let text_col = column as usize + view_offset.horizontal_offset; From 2b1eb60d0e42489f8060a7ae59780a3e5a6e2c8e Mon Sep 17 00:00:00 2001 From: Ingrid Date: Thu, 25 Apr 2024 16:38:03 +0200 Subject: [PATCH 4/7] better borrow checker wrangling with doc_mut!() --- helix-term/src/application.rs | 13 ++----------- helix-view/src/editor.rs | 4 ++-- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 02b6dcfb9a82..1a8b6d9ebf5e 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -394,22 +394,13 @@ impl Application { self.editor.refresh_config(); // reset view position in case softwrap was enabled/disabled - let mut view_position_resets = Vec::new(); let scrolloff = self.editor.config().scrolloff; for (view, _) in self.editor.tree.views() { - let doc = &self.editor.documents[&view.doc]; - + let doc = doc_mut!(self.editor, &view.doc); if let Some(offset) = view.offset_coords_to_in_view_center::(doc, scrolloff) { - view_position_resets.push((offset, view.id, view.doc)); + doc.view_data_mut(view.id).view_position = offset } } - for (offset, view_id, doc_id) in view_position_resets { - self.editor - .document_mut(doc_id) - .unwrap() - .view_data_mut(view_id) - .view_position = offset; - } } /// refresh language config after config change diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index a43e463587df..ba7337f22339 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1898,8 +1898,8 @@ impl Editor { pub fn ensure_cursor_in_view(&mut self, id: ViewId) { let config = self.config(); - let view = self.tree.get(id).clone(); - let doc = self.document_mut(view.doc).unwrap(); + let view = self.tree.get(id); + let doc = doc_mut!(self, &view.doc); view.ensure_cursor_in_view(doc, config.scrolloff) } From 02e5996b5205b55eb93ace17f543bd7a9d57b6d0 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Fri, 26 Apr 2024 21:37:35 +0200 Subject: [PATCH 5/7] reintroduce ensure_cursor_in_view in handle_config_events since we sorted out the borrow checker issues using partial borrows, there's nothing stopping us from going back to the simpler implementation --- helix-term/src/application.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 1a8b6d9ebf5e..60bc5b7cf23a 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -397,9 +397,7 @@ impl Application { let scrolloff = self.editor.config().scrolloff; for (view, _) in self.editor.tree.views() { let doc = doc_mut!(self.editor, &view.doc); - if let Some(offset) = view.offset_coords_to_in_view_center::(doc, scrolloff) { - doc.view_data_mut(view.id).view_position = offset - } + view.ensure_cursor_in_view(doc, scrolloff); } } From 8391ce99e382ab03e2862dd2d039dddcda253982 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Fri, 26 Apr 2024 21:40:52 +0200 Subject: [PATCH 6/7] introduce helper functions on Document .view_offset, set_view_offset --- helix-term/src/commands.rs | 24 ++++++++++++------------ helix-term/src/commands/lsp.rs | 8 ++------ helix-term/src/commands/typed.rs | 7 +------ helix-term/src/ui/editor.rs | 6 +++--- helix-term/src/ui/mod.rs | 6 +++--- helix-view/src/document.rs | 21 +++++++++++++++------ helix-view/src/lib.rs | 7 +++---- helix-view/src/view.rs | 22 ++++++++-------------- 8 files changed, 47 insertions(+), 54 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c566c4a9a57c..27bc18a21431 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1032,7 +1032,7 @@ fn goto_window(cx: &mut Context, align: Align) { let count = cx.count() - 1; let config = cx.editor.config(); let (view, doc) = current!(cx.editor); - let view_offset = doc.view_data_mut(view.id).view_position; + let view_offset = doc.view_offset(view.id); let height = view.inner_height(); @@ -1666,7 +1666,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor use Direction::*; let config = cx.editor.config(); let (view, doc) = current!(cx.editor); - let view_offset = doc.view_data_mut(view.id).view_position; + let mut view_offset = doc.view_offset(view.id); let range = doc.selection(view.id).primary(); let text = doc.text().slice(..); @@ -1684,7 +1684,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor let viewport = view.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); let annotations = view.text_annotations(&*doc, None); - let (new_anchor, new_vertical_offset) = char_idx_at_visual_offset( + (view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset( doc_text, view_offset.anchor, view_offset.vertical_offset as isize + offset, @@ -1692,9 +1692,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor &text_fmt, &annotations, ); - let view_data = doc.view_data_mut(view.id); - view_data.view_position.anchor = new_anchor; - view_data.view_position.vertical_offset = new_vertical_offset; + doc.set_view_offset(view.id, view_offset); let doc_text = doc.text().slice(..); let mut annotations = view.text_annotations(&*doc, None); @@ -1724,7 +1722,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor return; } - let view_offset = doc.view_data(view.id).view_position; + let view_offset = doc.view_offset(view.id); let mut head; match direction { @@ -5134,7 +5132,7 @@ fn split(editor: &mut Editor, action: Action) { let (view, doc) = current!(editor); let id = doc.id(); let selection = doc.selection(view.id).clone(); - let offset = doc.view_data(view.id).view_position; + let offset = doc.view_offset(view.id); editor.switch(id, action); @@ -5143,7 +5141,7 @@ fn split(editor: &mut Editor, action: Action) { doc.set_selection(view.id, selection); // match the view scroll offset (switch doesn't handle this fully // since the selection is only matched after the split) - doc.view_data_mut(view.id).view_position = offset; + doc.set_view_offset(view.id, offset); } fn hsplit(cx: &mut Context) { @@ -5242,16 +5240,18 @@ fn align_view_middle(cx: &mut Context) { let pos = doc.selection(view.id).primary().cursor(doc_text); let pos = visual_offset_from_block( doc_text, - doc.view_data(view.id).view_position.anchor, + doc.view_offset(view.id).anchor, pos, &text_fmt, &annotations, ) .0; - doc.view_data_mut(view.id).view_position.horizontal_offset = pos + let mut offset = doc.view_offset(view.id); + offset.horizontal_offset = pos .col .saturating_sub((view.inner_area(doc).width as usize) / 2); + doc.set_view_offset(view.id, offset); } fn scroll_up(cx: &mut Context) { @@ -6133,7 +6133,7 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { // This is not necessarily exact if there is virtual text like soft wrap. // It's ok though because the extra jump labels will not be rendered. - let start = text.line_to_char(text.char_to_line(doc.view_data(view.id).view_position.anchor)); + let start = text.line_to_char(text.char_to_line(doc.view_offset(view.id).anchor)); let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1); let primary_selection = doc.selection(view.id).primary(); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 91688c99e43f..9194377c479b 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1299,12 +1299,8 @@ fn compute_inlay_hints_for_view( // than computing all the hints for the full file (which could be dozens of time // longer than the view is). let view_height = view.inner_height(); - let first_visible_line = doc_text.char_to_line( - doc.view_data(view_id) - .view_position - .anchor - .min(doc_text.len_chars()), - ); + let first_visible_line = + doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars())); let first_line = first_visible_line.saturating_sub(view_height); let last_line = first_visible_line .saturating_add(view_height.saturating_mul(2)) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 4f42f26cf991..720d32ac8139 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1587,12 +1587,7 @@ fn tree_sitter_highlight_name( // Query the same range as the one used in syntax highlighting. let range = { // Calculate viewport byte ranges: - let row = text.char_to_line( - doc.view_data(view.id) - .view_position - .anchor - .min(text.len_chars()), - ); + let row = text.char_to_line(doc.view_offset(view.id).anchor.min(text.len_chars())); // Saturating subs to make it inclusive zero indexing. let last_line = text.len_lines().saturating_sub(1); let height = view.inner_area(doc).height; diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index ca2c6c5320af..f7541fe25750 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -93,7 +93,7 @@ impl EditorView { let theme = &editor.theme; let config = editor.config(); - let view_offset = doc.view_data(view.id).view_position; + let view_offset = doc.view_offset(view.id); let text_annotations = view.text_annotations(doc, Some(theme)); let mut decorations = DecorationManager::default(); @@ -261,7 +261,7 @@ impl EditorView { .and_then(|config| config.rulers.as_ref()) .unwrap_or(editor_rulers); - let view_offset = doc.view_data(view.id).view_position; + let view_offset = doc.view_offset(view.id); rulers .iter() @@ -829,7 +829,7 @@ impl EditorView { let inner_area = view.inner_area(doc); let selection = doc.selection(view.id); - let view_offset = doc.view_data(view.id).view_position; + let view_offset = doc.view_offset(view.id); let primary = selection.primary(); let text_format = doc.text_format(viewport.width, None); for range in selection.iter() { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 9498052dcb0a..6a3e198c1051 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -83,7 +83,7 @@ pub fn raw_regex_prompt( let (view, doc) = current!(cx.editor); let doc_id = view.doc; let snapshot = doc.selection(view.id).clone(); - let offset_snapshot = doc.view_data(view.id).view_position; + let offset_snapshot = doc.view_offset(view.id); let config = cx.editor.config(); let mut prompt = Prompt::new( @@ -95,7 +95,7 @@ pub fn raw_regex_prompt( PromptEvent::Abort => { let (view, doc) = current!(cx.editor); doc.set_selection(view.id, snapshot.clone()); - doc.view_data_mut(view.id).view_position = offset_snapshot; + doc.set_view_offset(view.id, offset_snapshot); } PromptEvent::Update | PromptEvent::Validate => { // skip empty input @@ -136,7 +136,7 @@ pub fn raw_regex_prompt( Err(err) => { let (view, doc) = current!(cx.editor); doc.set_selection(view.id, snapshot.clone()); - doc.view_data_mut(view.id).view_position = offset_snapshot; + doc.set_view_offset(view.id, offset_snapshot); if event == PromptEvent::Validate { let callback = async move { diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 0f7f7486dc3e..532f4c344c63 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1773,19 +1773,28 @@ impl Document { &self.selections } - pub(crate) fn get_view_data(&self, view_id: ViewId) -> Option<&ViewData> { - self.view_data.get(&view_id) - } - pub fn view_data(&self, view_id: ViewId) -> &ViewData { + fn view_data(&self, view_id: ViewId) -> &ViewData { self.view_data .get(&view_id) .expect("This should only be called after ensure_view_init") } - pub fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData { + fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData { self.view_data.entry(view_id).or_default() } + pub(crate) fn get_view_offset(&self, view_id: ViewId) -> Option { + Some(self.view_data.get(&view_id)?.view_position) + } + + pub fn view_offset(&self, view_id: ViewId) -> ViewPosition { + self.view_data(view_id).view_position + } + + pub fn set_view_offset(&mut self, view_id: ViewId, new_offset: ViewPosition) { + self.view_data_mut(view_id).view_position = new_offset; + } + pub fn relative_path(&self) -> Option> { self.path .as_deref() @@ -2063,7 +2072,7 @@ impl Document { #[derive(Debug, Default)] pub struct ViewData { - pub view_position: ViewPosition, + view_position: ViewPosition, } #[derive(Clone, Debug)] diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 9d2a416ebb5f..6bf8fd471660 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -52,6 +52,7 @@ pub fn align_view(doc: &mut Document, view: &View, align: Align) { let cursor = doc.selection(view.id).primary().cursor(doc_text); let viewport = view.inner_area(doc); let last_line_height = viewport.height.saturating_sub(1); + let mut view_offset = doc.view_offset(view.id); let relative = match align { Align::Center => last_line_height / 2, @@ -61,7 +62,7 @@ pub fn align_view(doc: &mut Document, view: &View, align: Align) { let text_fmt = doc.text_format(viewport.width, None); let annotations = view.text_annotations(doc, None); - let (new_anchor, new_vertical_offset) = char_idx_at_visual_offset( + (view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset( doc_text, cursor, -(relative as isize), @@ -69,9 +70,7 @@ pub fn align_view(doc: &mut Document, view: &View, align: Align) { &text_fmt, &annotations, ); - let view_data = doc.view_data_mut(view.id); - view_data.view_position.anchor = new_anchor; - view_data.view_position.vertical_offset = new_vertical_offset; + doc.set_view_offset(view.id, view_offset); } pub use document::Document; diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index 2a5234cbcff9..a0a3d462a5c8 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -234,7 +234,7 @@ impl View { doc: &Document, scrolloff: usize, ) -> Option { - let view_offset = doc.get_view_data(self.id)?.view_position; + let view_offset = doc.get_view_offset(self.id)?; let doc_text = doc.text().slice(..); let viewport = self.inner_area(doc); let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize; @@ -325,13 +325,13 @@ impl View { pub fn ensure_cursor_in_view(&self, doc: &mut Document, scrolloff: usize) { if let Some(offset) = self.offset_coords_to_in_view_center::(doc, scrolloff) { - doc.view_data_mut(self.id).view_position = offset; + doc.set_view_offset(self.id, offset); } } pub fn ensure_cursor_in_view_center(&self, doc: &mut Document, scrolloff: usize) { if let Some(offset) = self.offset_coords_to_in_view_center::(doc, scrolloff) { - doc.view_data_mut(self.id).view_position = offset; + doc.set_view_offset(self.id, offset); } else { align_view(doc, self, Align::Center); } @@ -349,12 +349,7 @@ impl View { #[inline] pub fn estimate_last_doc_line(&self, doc: &Document) -> usize { let doc_text = doc.text().slice(..); - let line = doc_text.char_to_line( - doc.view_data(self.id) - .view_position - .anchor - .min(doc_text.len_chars()), - ); + let line = doc_text.char_to_line(doc.view_offset(self.id).anchor.min(doc_text.len_chars())); // Saturating subs to make it inclusive zero indexing. (line + self.inner_height()) .min(doc_text.len_lines()) @@ -368,11 +363,10 @@ impl View { let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); let annotations = self.text_annotations(doc, None); - let view_offset = doc.view_data(self.id).view_position; + let view_offset = doc.view_offset(self.id); // last visual line in view is trivial to compute - let visual_height = - doc.view_data(self.id).view_position.vertical_offset + viewport.height as usize; + let visual_height = doc.view_offset(self.id).vertical_offset + viewport.height as usize; // fast path when the EOF is not visible on the screen, if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 { @@ -405,7 +399,7 @@ impl View { text: RopeSlice, pos: usize, ) -> Option { - let view_offset = doc.view_data(self.id).view_position; + let view_offset = doc.view_offset(self.id); let viewport = self.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); @@ -539,7 +533,7 @@ impl View { ignore_virtual_text: bool, ) -> Option { let text = doc.text().slice(..); - let view_offset = doc.view_data(self.id).view_position; + let view_offset = doc.view_offset(self.id); let text_row = row as usize + view_offset.vertical_offset; let text_col = column as usize + view_offset.horizontal_offset; From b0754edf3a61f93696df88ed03d2fd7f42d094c9 Mon Sep 17 00:00:00 2001 From: Ingrid Date: Tue, 23 Jul 2024 18:52:38 +0200 Subject: [PATCH 7/7] fix rebase breakage --- helix-term/src/commands.rs | 7 +++---- helix-view/src/lib.rs | 3 +-- helix-view/src/view.rs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 27bc18a21431..2c5d2783b261 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1683,14 +1683,14 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor let doc_text = doc.text().slice(..); let viewport = view.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); - let annotations = view.text_annotations(&*doc, None); (view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset( doc_text, view_offset.anchor, view_offset.vertical_offset as isize + offset, 0, &text_fmt, - &annotations, + // &annotations, + &view.text_annotations(&*doc, None), ); doc.set_view_offset(view.id, view_offset); @@ -5236,14 +5236,13 @@ fn align_view_middle(cx: &mut Context) { return; } let doc_text = doc.text().slice(..); - let annotations = view.text_annotations(doc, None); let pos = doc.selection(view.id).primary().cursor(doc_text); let pos = visual_offset_from_block( doc_text, doc.view_offset(view.id).anchor, pos, &text_fmt, - &annotations, + &view.text_annotations(doc, None), ) .0; diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 6bf8fd471660..d54b49ef5400 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -61,14 +61,13 @@ pub fn align_view(doc: &mut Document, view: &View, align: Align) { }; let text_fmt = doc.text_format(viewport.width, None); - let annotations = view.text_annotations(doc, None); (view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset( doc_text, cursor, -(relative as isize), 0, &text_fmt, - &annotations, + &view.text_annotations(doc, None), ); doc.set_view_offset(view.id, view_offset); } diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs index a0a3d462a5c8..fb83c4b86174 100644 --- a/helix-view/src/view.rs +++ b/helix-view/src/view.rs @@ -486,7 +486,7 @@ impl View { doc, cursor, width, - self.offset.horizontal_offset, + doc.view_offset(self.id).horizontal_offset, config, )); }