|
|
|
@ -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<ViewPosition> {
|
|
|
|
|
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 = 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::<false>(doc, scrolloff) {
|
|
|
|
|
self.offset = offset;
|
|
|
|
|
doc.set_view_offset(self.id, 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.set_view_offset(self.id, offset);
|
|
|
|
|
} else {
|
|
|
|
|
align_view(doc, self, Align::Center);
|
|
|
|
|
}
|
|
|
|
@ -354,7 +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(self.offset.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,9 +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_offset(self.id);
|
|
|
|
|
|
|
|
|
|
// 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_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 {
|
|
|
|
@ -380,7 +376,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 +384,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 +399,15 @@ impl View {
|
|
|
|
|
text: RopeSlice,
|
|
|
|
|
pos: usize,
|
|
|
|
|
) -> Option<Position> {
|
|
|
|
|
let view_offset = doc.view_offset(self.id);
|
|
|
|
|
|
|
|
|
|
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 +415,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)
|
|
|
|
|
}
|
|
|
|
@ -488,7 +486,7 @@ impl View {
|
|
|
|
|
doc,
|
|
|
|
|
cursor,
|
|
|
|
|
width,
|
|
|
|
|
self.offset.horizontal_offset,
|
|
|
|
|
doc.view_offset(self.id).horizontal_offset,
|
|
|
|
|
config,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
@ -535,13 +533,14 @@ impl View {
|
|
|
|
|
ignore_virtual_text: bool,
|
|
|
|
|
) -> Option<usize> {
|
|
|
|
|
let text = doc.text().slice(..);
|
|
|
|
|
let view_offset = doc.view_offset(self.id);
|
|
|
|
|
|
|
|
|
|
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 +688,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 +863,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 +893,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 +917,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 +1001,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(
|
|
|
|
|