Fix layout panic on empty editors with blocks
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
parent
40a4c18ee4
commit
e392368d89
2 changed files with 86 additions and 13 deletions
|
@ -26,6 +26,7 @@ use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
|
iter,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -610,11 +611,10 @@ impl EditorElement {
|
||||||
|
|
||||||
fn layout_lines(
|
fn layout_lines(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
snapshot: &mut EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
cx: &LayoutContext,
|
cx: &LayoutContext,
|
||||||
) -> Vec<text_layout::Line> {
|
) -> Vec<text_layout::Line> {
|
||||||
rows.end = cmp::min(rows.end, snapshot.max_point().row() + 1);
|
|
||||||
if rows.start >= rows.end {
|
if rows.start >= rows.end {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
@ -632,6 +632,7 @@ impl EditorElement {
|
||||||
.map_or("", AsRef::as_ref)
|
.map_or("", AsRef::as_ref)
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.skip(rows.start as usize)
|
.skip(rows.start as usize)
|
||||||
|
.chain(iter::repeat(""))
|
||||||
.take(rows.len());
|
.take(rows.len());
|
||||||
return placeholder_lines
|
return placeholder_lines
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
|
@ -880,7 +881,12 @@ impl Element for EditorElement {
|
||||||
let scroll_position = snapshot.scroll_position();
|
let scroll_position = snapshot.scroll_position();
|
||||||
let start_row = scroll_position.y() as u32;
|
let start_row = scroll_position.y() as u32;
|
||||||
let scroll_top = scroll_position.y() * line_height;
|
let scroll_top = scroll_position.y() * line_height;
|
||||||
let end_row = ((scroll_top + size.y()) / line_height).ceil() as u32 + 1; // Add 1 to ensure selections bleed off screen
|
|
||||||
|
// Add 1 to ensure selections bleed off screen
|
||||||
|
let end_row = 1 + cmp::min(
|
||||||
|
((scroll_top + size.y()) / line_height).ceil() as u32,
|
||||||
|
snapshot.max_point().row(),
|
||||||
|
);
|
||||||
|
|
||||||
let start_anchor = if start_row == 0 {
|
let start_anchor = if start_row == 0 {
|
||||||
Anchor::min()
|
Anchor::min()
|
||||||
|
@ -963,7 +969,7 @@ impl Element for EditorElement {
|
||||||
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
|
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx);
|
||||||
|
|
||||||
let mut max_visible_line_width = 0.0;
|
let mut max_visible_line_width = 0.0;
|
||||||
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
|
||||||
for line in &line_layouts {
|
for line in &line_layouts {
|
||||||
if line.width() > max_visible_line_width {
|
if line.width() > max_visible_line_width {
|
||||||
max_visible_line_width = line.width();
|
max_visible_line_width = line.width();
|
||||||
|
@ -1466,8 +1472,13 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Editor, MultiBuffer};
|
use crate::{
|
||||||
|
display_map::{BlockDisposition, BlockProperties},
|
||||||
|
Editor, MultiBuffer,
|
||||||
|
};
|
||||||
use util::test::sample_text;
|
use util::test::sample_text;
|
||||||
use workspace::Settings;
|
use workspace::Settings;
|
||||||
|
|
||||||
|
@ -1492,4 +1503,58 @@ mod tests {
|
||||||
});
|
});
|
||||||
assert_eq!(layouts.len(), 6);
|
assert_eq!(layouts.len(), 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_layout_with_placeholder_text_and_blocks(cx: &mut gpui::MutableAppContext) {
|
||||||
|
cx.add_app_state(Settings::test(cx));
|
||||||
|
let buffer = MultiBuffer::build_simple("", cx);
|
||||||
|
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
|
||||||
|
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
editor.set_placeholder_text("hello", cx);
|
||||||
|
editor.insert_blocks(
|
||||||
|
[BlockProperties {
|
||||||
|
disposition: BlockDisposition::Above,
|
||||||
|
height: 3,
|
||||||
|
position: Anchor::min(),
|
||||||
|
render: Arc::new(|_| Empty::new().boxed()),
|
||||||
|
}],
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Blur the editor so that it displays placeholder text.
|
||||||
|
cx.blur();
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut element = EditorElement::new(
|
||||||
|
editor.downgrade(),
|
||||||
|
editor.read(cx).style(cx),
|
||||||
|
CursorShape::Bar,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut scene = Scene::new(1.0);
|
||||||
|
let mut presenter = cx.build_presenter(window_id, 30.);
|
||||||
|
let mut layout_cx = presenter.build_layout_context(false, cx);
|
||||||
|
let (size, mut state) = element.layout(
|
||||||
|
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
|
||||||
|
&mut layout_cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(state.line_layouts.len(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
state
|
||||||
|
.line_number_layouts
|
||||||
|
.iter()
|
||||||
|
.map(Option::is_some)
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
&[false, false, false, true]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Don't panic.
|
||||||
|
let bounds = RectF::new(Default::default(), size);
|
||||||
|
let mut paint_cx = presenter.build_paint_context(&mut scene, cx);
|
||||||
|
element.paint(bounds, bounds, &mut state, &mut paint_cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,13 +110,7 @@ impl Presenter {
|
||||||
|
|
||||||
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
|
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
|
||||||
self.layout(window_size, refreshing, cx);
|
self.layout(window_size, refreshing, cx);
|
||||||
let mut paint_cx = PaintContext {
|
let mut paint_cx = self.build_paint_context(&mut scene, cx);
|
||||||
scene: &mut scene,
|
|
||||||
font_cache: &self.font_cache,
|
|
||||||
text_layout_cache: &self.text_layout_cache,
|
|
||||||
rendered_views: &mut self.rendered_views,
|
|
||||||
app: cx.as_ref(),
|
|
||||||
};
|
|
||||||
paint_cx.paint(
|
paint_cx.paint(
|
||||||
root_view_id,
|
root_view_id,
|
||||||
Vector2F::zero(),
|
Vector2F::zero(),
|
||||||
|
@ -159,6 +153,20 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_paint_context<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
scene: &'a mut Scene,
|
||||||
|
cx: &'a mut MutableAppContext,
|
||||||
|
) -> PaintContext {
|
||||||
|
PaintContext {
|
||||||
|
scene,
|
||||||
|
font_cache: &self.font_cache,
|
||||||
|
text_layout_cache: &self.text_layout_cache,
|
||||||
|
rendered_views: &mut self.rendered_views,
|
||||||
|
app: cx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) {
|
pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) {
|
||||||
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
|
if let Some(root_view_id) = cx.root_view_id(self.window_id) {
|
||||||
match event {
|
match event {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue