fix(performance): do not hang when resizing large line wraps (#814)
* fix(performance): do not hang when resizing large line wraps * style(fmt): make rustfmt happy * style(clippy): make clippy happy
This commit is contained in:
parent
14df486353
commit
42b66644ae
1 changed files with 42 additions and 46 deletions
|
|
@ -658,11 +658,7 @@ impl Grid {
|
||||||
canonical_line_parts.push(Row::new(new_columns).canonical());
|
canonical_line_parts.push(Row::new(new_columns).canonical());
|
||||||
}
|
}
|
||||||
while !canonical_line.columns.is_empty() {
|
while !canonical_line.columns.is_empty() {
|
||||||
let next_wrap = if canonical_line.width() > new_columns {
|
let next_wrap = canonical_line.drain_until(new_columns);
|
||||||
canonical_line.drain_until(new_columns)
|
|
||||||
} else {
|
|
||||||
canonical_line.columns.drain(..).collect()
|
|
||||||
};
|
|
||||||
// If the next character is wider than the grid (i.e. there is nothing in
|
// If the next character is wider than the grid (i.e. there is nothing in
|
||||||
// `next_wrap`, then just abort the resizing
|
// `next_wrap`, then just abort the resizing
|
||||||
if next_wrap.is_empty() {
|
if next_wrap.is_empty() {
|
||||||
|
|
@ -821,7 +817,7 @@ impl Grid {
|
||||||
pub fn rotate_scroll_region_up(&mut self, count: usize) {
|
pub fn rotate_scroll_region_up(&mut self, count: usize) {
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
|
let columns = VecDeque::from(vec![EMPTY_TERMINAL_CHARACTER; self.width]);
|
||||||
if scroll_region_bottom < self.viewport.len() {
|
if scroll_region_bottom < self.viewport.len() {
|
||||||
self.viewport.remove(scroll_region_bottom);
|
self.viewport.remove(scroll_region_bottom);
|
||||||
}
|
}
|
||||||
|
|
@ -836,7 +832,7 @@ impl Grid {
|
||||||
pub fn rotate_scroll_region_down(&mut self, count: usize) {
|
pub fn rotate_scroll_region_down(&mut self, count: usize) {
|
||||||
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
if let Some((scroll_region_top, scroll_region_bottom)) = self.scroll_region {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
|
let columns = VecDeque::from(vec![EMPTY_TERMINAL_CHARACTER; self.width]);
|
||||||
self.viewport.remove(scroll_region_top);
|
self.viewport.remove(scroll_region_top);
|
||||||
if self.viewport.len() > scroll_region_top {
|
if self.viewport.len() > scroll_region_top {
|
||||||
self.viewport
|
self.viewport
|
||||||
|
|
@ -857,7 +853,7 @@ impl Grid {
|
||||||
self.width,
|
self.width,
|
||||||
);
|
);
|
||||||
for _ in 0..self.height {
|
for _ in 0..self.height {
|
||||||
let columns = vec![character; self.width];
|
let columns = VecDeque::from(vec![character; self.width]);
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
self.output_buffer.update_all_lines();
|
self.output_buffer.update_all_lines();
|
||||||
|
|
@ -876,7 +872,7 @@ impl Grid {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.viewport.remove(scroll_region_top);
|
self.viewport.remove(scroll_region_top);
|
||||||
let columns = vec![EMPTY_TERMINAL_CHARACTER; self.width];
|
let columns = VecDeque::from(vec![EMPTY_TERMINAL_CHARACTER; self.width]);
|
||||||
if self.viewport.len() >= scroll_region_bottom {
|
if self.viewport.len() >= scroll_region_bottom {
|
||||||
self.viewport
|
self.viewport
|
||||||
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
|
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
|
||||||
|
|
@ -1012,7 +1008,7 @@ impl Grid {
|
||||||
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all_after_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
|
if let Some(cursor_row) = self.viewport.get_mut(self.cursor.y) {
|
||||||
cursor_row.truncate(self.cursor.x);
|
cursor_row.truncate(self.cursor.x);
|
||||||
let replace_with_columns = vec![replace_with; self.width];
|
let replace_with_columns = VecDeque::from(vec![replace_with; self.width]);
|
||||||
self.replace_characters_in_line_after_cursor(replace_with);
|
self.replace_characters_in_line_after_cursor(replace_with);
|
||||||
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
|
for row in self.viewport.iter_mut().skip(self.cursor.y + 1) {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
|
|
@ -1023,7 +1019,7 @@ impl Grid {
|
||||||
pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all_before_cursor(&mut self, replace_with: TerminalCharacter) {
|
||||||
if self.viewport.get(self.cursor.y).is_some() {
|
if self.viewport.get(self.cursor.y).is_some() {
|
||||||
self.replace_characters_in_line_before_cursor(replace_with);
|
self.replace_characters_in_line_before_cursor(replace_with);
|
||||||
let replace_with_columns = vec![replace_with; self.width];
|
let replace_with_columns = VecDeque::from(vec![replace_with; self.width]);
|
||||||
for row in self.viewport.iter_mut().take(self.cursor.y) {
|
for row in self.viewport.iter_mut().take(self.cursor.y) {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -1035,7 +1031,7 @@ impl Grid {
|
||||||
self.output_buffer.update_line(self.cursor.y);
|
self.output_buffer.update_line(self.cursor.y);
|
||||||
}
|
}
|
||||||
pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
|
pub fn clear_all(&mut self, replace_with: TerminalCharacter) {
|
||||||
let replace_with_columns = vec![replace_with; self.width];
|
let replace_with_columns = VecDeque::from(vec![replace_with; self.width]);
|
||||||
self.replace_characters_in_line_after_cursor(replace_with);
|
self.replace_characters_in_line_after_cursor(replace_with);
|
||||||
for row in self.viewport.iter_mut() {
|
for row in self.viewport.iter_mut() {
|
||||||
row.replace_columns(replace_with_columns.clone());
|
row.replace_columns(replace_with_columns.clone());
|
||||||
|
|
@ -1051,7 +1047,7 @@ impl Grid {
|
||||||
}
|
}
|
||||||
fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
|
fn pad_lines_until(&mut self, position: usize, pad_character: TerminalCharacter) {
|
||||||
for _ in self.viewport.len()..=position {
|
for _ in self.viewport.len()..=position {
|
||||||
let columns = vec![pad_character; self.width];
|
let columns = VecDeque::from(vec![pad_character; self.width]);
|
||||||
self.viewport.push(Row::from_columns(columns).canonical());
|
self.viewport.push(Row::from_columns(columns).canonical());
|
||||||
self.output_buffer.update_line(self.viewport.len() - 1);
|
self.output_buffer.update_line(self.viewport.len() - 1);
|
||||||
}
|
}
|
||||||
|
|
@ -1172,7 +1168,7 @@ impl Grid {
|
||||||
// region
|
// region
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.viewport.remove(current_line_index);
|
self.viewport.remove(current_line_index);
|
||||||
let columns = vec![pad_character; self.width];
|
let columns = VecDeque::from(vec![pad_character; self.width]);
|
||||||
if self.viewport.len() > scroll_region_bottom {
|
if self.viewport.len() > scroll_region_bottom {
|
||||||
self.viewport
|
self.viewport
|
||||||
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
|
.insert(scroll_region_bottom, Row::from_columns(columns).canonical());
|
||||||
|
|
@ -1201,7 +1197,7 @@ impl Grid {
|
||||||
if scroll_region_bottom < self.viewport.len() {
|
if scroll_region_bottom < self.viewport.len() {
|
||||||
self.viewport.remove(scroll_region_bottom);
|
self.viewport.remove(scroll_region_bottom);
|
||||||
}
|
}
|
||||||
let columns = vec![pad_character; self.width];
|
let columns = VecDeque::from(vec![pad_character; self.width]);
|
||||||
self.viewport
|
self.viewport
|
||||||
.insert(current_line_index, Row::from_columns(columns).canonical());
|
.insert(current_line_index, Row::from_columns(columns).canonical());
|
||||||
}
|
}
|
||||||
|
|
@ -1325,7 +1321,8 @@ impl Grid {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let empty_row = Row::from_columns(vec![EMPTY_TERMINAL_CHARACTER; self.width]);
|
let empty_row =
|
||||||
|
Row::from_columns(VecDeque::from(vec![EMPTY_TERMINAL_CHARACTER; self.width]));
|
||||||
|
|
||||||
// get the row from lines_above, viewport, or lines below depending on index
|
// get the row from lines_above, viewport, or lines below depending on index
|
||||||
let row = if l < 0 && self.lines_above.len() > l.abs() as usize {
|
let row = if l < 0 && self.lines_above.len() > l.abs() as usize {
|
||||||
|
|
@ -2017,7 +2014,7 @@ impl Perform for Grid {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
pub columns: Vec<TerminalCharacter>,
|
pub columns: VecDeque<TerminalCharacter>,
|
||||||
pub is_canonical: bool,
|
pub is_canonical: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2033,11 +2030,11 @@ impl Debug for Row {
|
||||||
impl Row {
|
impl Row {
|
||||||
pub fn new(width: usize) -> Self {
|
pub fn new(width: usize) -> Self {
|
||||||
Row {
|
Row {
|
||||||
columns: Vec::with_capacity(width),
|
columns: VecDeque::with_capacity(width),
|
||||||
is_canonical: false,
|
is_canonical: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_columns(columns: Vec<TerminalCharacter>) -> Self {
|
pub fn from_columns(columns: VecDeque<TerminalCharacter>) -> Self {
|
||||||
Row {
|
Row {
|
||||||
columns,
|
columns,
|
||||||
is_canonical: false,
|
is_canonical: false,
|
||||||
|
|
@ -2055,7 +2052,7 @@ impl Row {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn with_character(mut self, terminal_character: TerminalCharacter) -> Self {
|
pub fn with_character(mut self, terminal_character: TerminalCharacter) -> Self {
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn canonical(mut self) -> Self {
|
pub fn canonical(mut self) -> Self {
|
||||||
|
|
@ -2103,13 +2100,13 @@ impl Row {
|
||||||
pub fn add_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
pub fn add_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
||||||
match self.width().cmp(&x) {
|
match self.width().cmp(&x) {
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let width_offset = self.excess_width_until(x);
|
let width_offset = self.excess_width_until(x);
|
||||||
self.columns
|
self.columns
|
||||||
.resize(x.saturating_sub(width_offset), EMPTY_TERMINAL_CHARACTER);
|
.resize(x.saturating_sub(width_offset), EMPTY_TERMINAL_CHARACTER);
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
// wide-character-aware index, where each character is counted once
|
// wide-character-aware index, where each character is counted once
|
||||||
|
|
@ -2142,10 +2139,10 @@ impl Row {
|
||||||
}
|
}
|
||||||
pub fn insert_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
pub fn insert_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
||||||
match self.columns.len().cmp(&x) {
|
match self.columns.len().cmp(&x) {
|
||||||
Ordering::Equal => self.columns.push(terminal_character),
|
Ordering::Equal => self.columns.push_back(terminal_character),
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
|
self.columns.resize(x, EMPTY_TERMINAL_CHARACTER);
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
self.columns.insert(x, terminal_character);
|
self.columns.insert(x, terminal_character);
|
||||||
|
|
@ -2155,19 +2152,20 @@ impl Row {
|
||||||
pub fn replace_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
pub fn replace_character_at(&mut self, terminal_character: TerminalCharacter, x: usize) {
|
||||||
// this is much more performant than remove/insert
|
// this is much more performant than remove/insert
|
||||||
if x < self.columns.len() {
|
if x < self.columns.len() {
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
let character = self.columns.swap_remove(x);
|
// let character = self.columns.swap_remove_back(x);
|
||||||
|
let character = self.columns.swap_remove_back(x).unwrap(); // TODO: if let?
|
||||||
let excess_width = character.width.saturating_sub(1);
|
let excess_width = character.width.saturating_sub(1);
|
||||||
for _ in 0..excess_width {
|
for _ in 0..excess_width {
|
||||||
self.columns.insert(x, terminal_character);
|
self.columns.insert(x, terminal_character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn replace_columns(&mut self, columns: Vec<TerminalCharacter>) {
|
pub fn replace_columns(&mut self, columns: VecDeque<TerminalCharacter>) {
|
||||||
self.columns = columns;
|
self.columns = columns;
|
||||||
}
|
}
|
||||||
pub fn push(&mut self, terminal_character: TerminalCharacter) {
|
pub fn push(&mut self, terminal_character: TerminalCharacter) {
|
||||||
self.columns.push(terminal_character);
|
self.columns.push_back(terminal_character);
|
||||||
}
|
}
|
||||||
pub fn truncate(&mut self, x: usize) {
|
pub fn truncate(&mut self, x: usize) {
|
||||||
let width_offset = self.excess_width_until(x);
|
let width_offset = self.excess_width_until(x);
|
||||||
|
|
@ -2198,26 +2196,23 @@ impl Row {
|
||||||
let to_position_accounting_for_widechars = self.position_accounting_for_widechars(to);
|
let to_position_accounting_for_widechars = self.position_accounting_for_widechars(to);
|
||||||
let replacement_length = to_position_accounting_for_widechars
|
let replacement_length = to_position_accounting_for_widechars
|
||||||
.saturating_sub(from_position_accounting_for_widechars);
|
.saturating_sub(from_position_accounting_for_widechars);
|
||||||
let mut replace_with = vec![terminal_character; replacement_length];
|
let mut replace_with = VecDeque::from(vec![terminal_character; replacement_length]);
|
||||||
self.columns
|
self.columns
|
||||||
.truncate(from_position_accounting_for_widechars);
|
.truncate(from_position_accounting_for_widechars);
|
||||||
self.columns.append(&mut replace_with);
|
self.columns.append(&mut replace_with);
|
||||||
}
|
}
|
||||||
pub fn append(&mut self, to_append: &mut Vec<TerminalCharacter>) {
|
pub fn append(&mut self, to_append: &mut VecDeque<TerminalCharacter>) {
|
||||||
self.columns.append(to_append);
|
self.columns.append(to_append);
|
||||||
}
|
}
|
||||||
pub fn drain_until(&mut self, x: usize) -> Vec<TerminalCharacter> {
|
pub fn drain_until(&mut self, x: usize) -> VecDeque<TerminalCharacter> {
|
||||||
let mut drained_part: Vec<TerminalCharacter> = vec![];
|
let mut drained_part: VecDeque<TerminalCharacter> = VecDeque::new();
|
||||||
let mut drained_part_len = 0;
|
let mut drained_part_len = 0;
|
||||||
loop {
|
while let Some(next_character) = self.columns.remove(0) {
|
||||||
if self.columns.is_empty() {
|
if drained_part_len + next_character.width <= x {
|
||||||
break;
|
drained_part.push_back(next_character);
|
||||||
}
|
drained_part_len += next_character.width;
|
||||||
let next_character_len = self.columns.get(0).unwrap().width;
|
|
||||||
if drained_part_len + next_character_len <= x {
|
|
||||||
drained_part.push(self.columns.remove(0));
|
|
||||||
drained_part_len += next_character_len;
|
|
||||||
} else {
|
} else {
|
||||||
|
self.columns.push_front(next_character); // put it back
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2230,7 +2225,8 @@ impl Row {
|
||||||
.get(to_position_accounting_for_widechars)
|
.get(to_position_accounting_for_widechars)
|
||||||
.map(|character| character.width)
|
.map(|character| character.width)
|
||||||
.unwrap_or(1);
|
.unwrap_or(1);
|
||||||
let mut replace_with = vec![terminal_character; to + width_of_current_character];
|
let mut replace_with =
|
||||||
|
VecDeque::from(vec![terminal_character; to + width_of_current_character]);
|
||||||
if to_position_accounting_for_widechars > self.columns.len() {
|
if to_position_accounting_for_widechars > self.columns.len() {
|
||||||
self.columns.clear();
|
self.columns.clear();
|
||||||
} else if to_position_accounting_for_widechars >= self.columns.len() {
|
} else if to_position_accounting_for_widechars >= self.columns.len() {
|
||||||
|
|
@ -2241,7 +2237,7 @@ impl Row {
|
||||||
replace_with.append(&mut self.columns);
|
replace_with.append(&mut self.columns);
|
||||||
self.columns = replace_with;
|
self.columns = replace_with;
|
||||||
}
|
}
|
||||||
pub fn replace_beginning_with(&mut self, mut line_part: Vec<TerminalCharacter>) {
|
pub fn replace_beginning_with(&mut self, mut line_part: VecDeque<TerminalCharacter>) {
|
||||||
// this assumes line_part has no wide characters
|
// this assumes line_part has no wide characters
|
||||||
if line_part.len() > self.columns.len() {
|
if line_part.len() > self.columns.len() {
|
||||||
self.columns.clear();
|
self.columns.clear();
|
||||||
|
|
@ -2259,22 +2255,22 @@ impl Row {
|
||||||
}
|
}
|
||||||
pub fn delete_and_return_character(&mut self, x: usize) -> Option<TerminalCharacter> {
|
pub fn delete_and_return_character(&mut self, x: usize) -> Option<TerminalCharacter> {
|
||||||
if x < self.columns.len() {
|
if x < self.columns.len() {
|
||||||
Some(self.columns.remove(x))
|
Some(self.columns.remove(x).unwrap()) // TODO: just return the remove part?
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn split_to_rows_of_length(&mut self, max_row_length: usize) -> Vec<Row> {
|
pub fn split_to_rows_of_length(&mut self, max_row_length: usize) -> Vec<Row> {
|
||||||
let mut parts: Vec<Row> = vec![];
|
let mut parts: Vec<Row> = vec![];
|
||||||
let mut current_part: Vec<TerminalCharacter> = vec![];
|
let mut current_part: VecDeque<TerminalCharacter> = VecDeque::new();
|
||||||
let mut current_part_len = 0;
|
let mut current_part_len = 0;
|
||||||
for character in self.columns.drain(..) {
|
for character in self.columns.drain(..) {
|
||||||
if current_part_len + character.width > max_row_length {
|
if current_part_len + character.width > max_row_length {
|
||||||
parts.push(Row::from_columns(current_part));
|
parts.push(Row::from_columns(current_part));
|
||||||
current_part = vec![];
|
current_part = VecDeque::new();
|
||||||
current_part_len = 0;
|
current_part_len = 0;
|
||||||
}
|
}
|
||||||
current_part.push(character);
|
current_part.push_back(character);
|
||||||
current_part_len += character.width;
|
current_part_len += character.width;
|
||||||
}
|
}
|
||||||
if !current_part.is_empty() {
|
if !current_part.is_empty() {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue