|
|
|
@ -110,7 +110,6 @@ pub struct ViewPosition {
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct View {
|
|
|
|
|
pub id: ViewId,
|
|
|
|
|
pub offset: ViewPosition,
|
|
|
|
|
pub area: Rect,
|
|
|
|
|
pub doc: DocumentId,
|
|
|
|
|
pub jumps: JumpList,
|
|
|
|
@ -147,11 +146,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(),
|
|
|
|
@ -213,9 +207,10 @@ impl View {
|
|
|
|
|
doc: &Document,
|
|
|
|
|
scrolloff: usize,
|
|
|
|
|
) -> Option<ViewPosition> {
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
@ -229,7 +224,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,
|
|
|
|
@ -294,22 +289,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::<false>(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::<true>(doc, scrolloff) {
|
|
|
|
|
self.offset = offset;
|
|
|
|
|
doc.view_data_mut(self.id).view_position = offset;
|
|
|
|
|
} else {
|
|
|
|
|
align_view(doc, self, Align::Center);
|
|
|
|
|
}
|
|
|
|
@ -327,7 +322,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())
|
|
|
|
@ -341,9 +342,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 {
|
|
|
|
@ -353,7 +360,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,
|
|
|
|
@ -361,7 +368,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,
|
|
|
|
|
}
|
|
|
|
@ -376,7 +383,9 @@ impl View {
|
|
|
|
|
text: RopeSlice,
|
|
|
|
|
pos: usize,
|
|
|
|
|
) -> Option<Position> {
|
|
|
|
|
if pos < self.offset.anchor {
|
|
|
|
|
let view_offset = doc.view_data(self.id).unwrap().view_position;
|
|
|
|
|
|
|
|
|
|
if pos < view_offset.anchor {
|
|
|
|
|
// Line is not visible on screen
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
@ -387,7 +396,7 @@ impl View {
|
|
|
|
|
|
|
|
|
|
let mut pos = visual_offset_from_anchor(
|
|
|
|
|
text,
|
|
|
|
|
self.offset.anchor,
|
|
|
|
|
view_offset.anchor,
|
|
|
|
|
pos,
|
|
|
|
|
&text_fmt,
|
|
|
|
|
&annotations,
|
|
|
|
@ -395,14 +404,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)
|
|
|
|
|
}
|
|
|
|
@ -496,13 +505,14 @@ impl View {
|
|
|
|
|
ignore_virtual_text: bool,
|
|
|
|
|
) -> Option<usize> {
|
|
|
|
|
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,
|
|
|
|
@ -650,11 +660,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(
|
|
|
|
@ -824,11 +835,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,
|
|
|
|
@ -853,11 +865,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,
|
|
|
|
@ -876,11 +889,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(
|
|
|
|
@ -959,11 +973,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(
|
|
|
|
|