From 7e8291be221a8f126e2e24c253e3c7ddfb8e2f0f Mon Sep 17 00:00:00 2001
From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com>
Date: Sun, 25 Jul 2021 19:22:31 +0900
Subject: [PATCH] Select multiple cells (#30)
* select multiple cells
* define `expand_selected_area_x`, `expand_selected_area_y`
* make some table component fields private
* fix keymap
* fix a cell selection bug in page 2 and after
* add tests for `is_selected_cell` and `selected_cells`
* select cells while scrolling right
* calculate the number of cells considering cell spacing
* add tests for expand_selected_area
* add comments
* implement `reset`
* fix clippy warnings
---
README.md | 4 +-
src/app.rs | 19 +-
src/components/record_table.rs | 4 +-
src/components/table.rs | 447 ++++++++++++++++++++++++++++-----
src/components/table_filter.rs | 10 +-
src/utils.rs | 16 +-
6 files changed, 412 insertions(+), 88 deletions(-)
diff --git a/README.md b/README.md
index 0e7e880..9d92fc5 100644
--- a/README.md
+++ b/README.md
@@ -31,9 +31,9 @@ $ cargo install --version 0.1.0-alpha.0 gobang
| Key | Description |
| ---- | ---- |
-| h | move right |
+| h | move left |
| j | move down |
| k | move up |
-| l | move left |
+| l | move right |
| Ctrl + d | scroll down multiple lines |
| Ctrl + u | scroll up multiple lines |
diff --git a/src/app.rs b/src/app.rs
index ff69fbd..2e38d45 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -191,7 +191,6 @@ impl App {
.get_columns(&database, &table.name)
.await?;
self.structure_table = TableComponent::new(records, headers);
-
self.table_status
.update(self.record_table.len() as u64, table);
}
@@ -206,7 +205,7 @@ impl App {
};
if let Key::Char('y') = key {
- if let Some(text) = self.record_table.table.selected_cell() {
+ if let Some(text) = self.record_table.table.selected_cells() {
self.clipboard.store(text)
}
}
@@ -276,7 +275,7 @@ impl App {
};
if let Key::Char('y') = key {
- if let Some(text) = self.structure_table.selected_cell() {
+ if let Some(text) = self.structure_table.selected_cells() {
self.clipboard.store(text)
}
};
@@ -302,20 +301,18 @@ impl App {
return Ok(EventState::Consumed);
}
}
- Focus::DabataseList => match key {
- Key::Right if self.databases.tree_focused() => {
+ Focus::DabataseList => {
+ if matches!(key, Key::Right) && self.databases.tree_focused() {
self.focus = Focus::Table;
return Ok(EventState::Consumed);
}
- _ => (),
- },
- Focus::Table => match key {
- Key::Left => {
+ }
+ Focus::Table => {
+ if let Key::Left = key {
self.focus = Focus::DabataseList;
return Ok(EventState::Consumed);
}
- _ => (),
- },
+ }
}
Ok(EventState::NotConsumed)
}
diff --git a/src/components/record_table.rs b/src/components/record_table.rs
index 3e30304..9ed757b 100644
--- a/src/components/record_table.rs
+++ b/src/components/record_table.rs
@@ -91,8 +91,8 @@ impl Component for RecordTableComponent {
self.focus = Focus::Filter;
return Ok(EventState::Consumed);
}
- key if matches!(self.focus, Focus::Filter) => return Ok(self.filter.event(key)?),
- key if matches!(self.focus, Focus::Table) => return Ok(self.table.event(key)?),
+ key if matches!(self.focus, Focus::Filter) => return self.filter.event(key),
+ key if matches!(self.focus, Focus::Table) => return self.table.event(key),
_ => (),
}
Ok(EventState::NotConsumed)
diff --git a/src/components/table.rs b/src/components/table.rs
index 1bba7a0..a4ae13a 100644
--- a/src/components/table.rs
+++ b/src/components/table.rs
@@ -18,22 +18,24 @@ pub struct TableComponent {
pub state: TableState,
pub headers: Vec,
pub rows: Vec>,
- pub column_index: usize,
- pub column_page: usize,
- pub column_page_start: std::cell::Cell,
- pub scroll: VerticalScroll,
- pub select_entire_row: bool,
pub eod: bool,
+ select_state: TableState,
+ selected_left_column_index: usize,
+ selected_right_cell: Option<(usize, usize)>,
+ column_page_start: std::cell::Cell,
+ scroll: VerticalScroll,
+ select_entire_row: bool,
}
impl Default for TableComponent {
fn default() -> Self {
Self {
state: TableState::default(),
+ select_state: TableState::default(),
headers: vec![],
rows: vec![],
- column_page: 0,
- column_index: 0,
+ selected_left_column_index: 0,
+ selected_right_cell: None,
column_page_start: std::cell::Cell::new(0),
scroll: VerticalScroll::new(),
select_entire_row: false,
@@ -50,13 +52,19 @@ impl TableComponent {
state.select(Some(0))
}
Self {
- rows,
- headers,
state,
+ headers,
+ rows,
..Self::default()
}
}
+ pub fn reset(&mut self) {
+ self.select_state.select(None);
+ self.selected_right_cell = None;
+ self.select_entire_row = false;
+ }
+
pub fn end(&mut self) {
self.eod = true;
}
@@ -72,10 +80,24 @@ impl TableComponent {
}
None => None,
};
- self.select_entire_row = false;
+ self.reset();
self.state.select(i);
}
+ pub fn next_select(&mut self, lines: usize) {
+ let i = match self.select_state.selected() {
+ Some(i) => {
+ if i + lines >= self.rows.len() {
+ Some(self.rows.len() - 1)
+ } else {
+ Some(i + lines)
+ }
+ }
+ None => None,
+ };
+ self.select_state.select(i);
+ }
+
pub fn previous(&mut self, lines: usize) {
let i = match self.state.selected() {
Some(i) => {
@@ -87,15 +109,29 @@ impl TableComponent {
}
None => None,
};
- self.select_entire_row = false;
+ self.reset();
self.state.select(i);
}
+ pub fn previout_select(&mut self, lines: usize) {
+ let i = match self.select_state.selected() {
+ Some(i) => {
+ if i <= lines {
+ Some(0)
+ } else {
+ Some(i - lines)
+ }
+ }
+ None => None,
+ };
+ self.select_state.select(i);
+ }
+
pub fn scroll_top(&mut self) {
if self.rows.is_empty() {
return;
}
- self.state.select(None);
+ self.reset();
self.state.select(Some(0));
}
@@ -103,6 +139,7 @@ impl TableComponent {
if self.rows.is_empty() {
return;
}
+ self.reset();
self.state.select(Some(self.rows.len() - 1));
}
@@ -110,37 +147,124 @@ impl TableComponent {
if self.rows.is_empty() {
return;
}
- if self.column_index >= self.headers().len().saturating_sub(1) {
+ if self.selected_left_column_index >= self.headers.len().saturating_sub(1) {
return;
}
- self.select_entire_row = false;
- self.column_index += 1;
+ self.reset();
+ self.selected_left_column_index += 1;
}
pub fn previous_column(&mut self) {
if self.rows.is_empty() {
return;
}
- if self.column_index == 0 {
+ if self.selected_left_column_index == 0 {
return;
}
- self.select_entire_row = false;
- self.column_index -= 1;
+ self.reset();
+ self.selected_left_column_index -= 1;
+ }
+
+ pub fn expand_selected_area_x(&mut self, positive: bool) {
+ if self.selected_right_cell.is_none() {
+ self.selected_right_cell = Some((
+ self.selected_left_column_index,
+ self.state.selected().unwrap_or(0),
+ ));
+ }
+ if let Some((x, y)) = self.selected_right_cell {
+ self.selected_right_cell = Some((
+ if positive {
+ (x + 1).min(self.headers.len().saturating_sub(1))
+ } else {
+ x.saturating_sub(1)
+ },
+ y,
+ ));
+ }
+ }
+
+ pub fn expand_selected_area_y(&mut self, positive: bool) {
+ if self.select_state.selected().is_none() {
+ self.select_state = self.state.clone();
+ }
+
+ if positive {
+ self.next_select(1);
+ } else {
+ self.previout_select(1);
+ }
+
+ if self.selected_right_cell.is_none() {
+ self.selected_right_cell = Some((
+ self.selected_left_column_index,
+ self.state.selected().unwrap_or(0),
+ ));
+ }
+ if let Some((x, y)) = self.selected_right_cell {
+ self.selected_right_cell = Some((
+ x,
+ if positive {
+ (y + 1).min(self.rows.len().saturating_sub(1))
+ } else {
+ y.saturating_sub(1)
+ },
+ ));
+ }
}
pub fn is_row_number_clumn(&self, row_index: usize, column_index: usize) -> bool {
matches!(self.state.selected(), Some(selected_row_index) if row_index == selected_row_index && 0 == column_index)
}
- pub fn selected_cell(&self) -> Option {
+ pub fn selected_cells(&self) -> Option {
+ if let Some((x, y)) = self.selected_right_cell {
+ let selected_row_index = self.state.selected()?;
+ return Some(
+ self.rows[y.min(selected_row_index)..y.max(selected_row_index) + 1]
+ .iter()
+ .map(|row| {
+ row[x.min(self.selected_left_column_index)
+ ..x.max(self.selected_left_column_index) + 1]
+ .join(",")
+ })
+ .collect::>()
+ .join("\n"),
+ );
+ }
self.rows
.get(self.state.selected()?)?
- .get(self.column_index)
+ .get(self.selected_left_column_index)
.map(|cell| cell.to_string())
}
- pub fn headers(&self) -> Vec {
- self.headers.clone()
+ pub fn selected_column_index(&self) -> usize {
+ if let Some((x, _)) = self.selected_right_cell {
+ return x;
+ }
+ self.selected_left_column_index
+ }
+
+ pub fn is_selected_cell(
+ &self,
+ row_index: usize,
+ column_index: usize,
+ selected_column_index: usize,
+ ) -> bool {
+ if let Some((x, y)) = self.selected_right_cell {
+ let x_in_page = x
+ .saturating_add(1)
+ .saturating_sub(self.column_page_start.get());
+ return matches!(
+ self.state.selected(),
+ Some(selected_row_index)
+ if (x_in_page.min(selected_column_index).max(1)..x_in_page.max(selected_column_index) + 1)
+ .contains(&column_index)
+ && (y.min(selected_row_index)..y.max(selected_row_index) + 1)
+ .contains(&row_index)
+ );
+ }
+ matches!(self.state.selected(), Some(selected_row_index) if row_index == selected_row_index && column_index == selected_column_index)
}
pub fn headers_with_number(&self, left: usize, right: usize) -> Vec {
@@ -149,10 +273,6 @@ impl TableComponent {
headers
}
- pub fn rows(&self) -> Vec> {
- self.rows.clone()
- }
-
pub fn rows_with_number(&self, left: usize, right: usize) -> Vec> {
let rows = self
.rows
@@ -174,17 +294,17 @@ impl TableComponent {
if self.rows.is_empty() {
return (0, Vec::new(), Vec::new(), Vec::new());
}
- if self.column_index < self.column_page_start.get() {
- self.column_page_start.set(self.column_index);
+ if self.selected_column_index() < self.column_page_start.get() {
+ self.column_page_start.set(self.selected_column_index());
}
- let right_column_index = self.column_index.clone();
- let mut column_index = self.column_index;
+ let right_column_index = self.selected_column_index();
+ let mut column_index = self.selected_column_index();
let number_clomn_width = (self.rows.len() + 1).to_string().width() as u16;
let mut widths = vec![];
loop {
let length = self
- .rows()
+ .rows
.iter()
.map(|row| {
row.get(column_index)
@@ -197,16 +317,14 @@ impl TableComponent {
.map_or(3, |v| {
*v.max(
&self
- .headers()
+ .headers
.get(column_index)
.map_or(3, |header| header.to_string().width()),
)
- .clamp(&3, &20) as u16
+ .clamp(&3, &20)
});
- if widths.iter().map(|(_, width)| width).sum::() + length
- > area_width
- .saturating_sub(7)
- .saturating_sub(number_clomn_width)
+ if widths.iter().map(|(_, width)| width).sum::() + length + widths.len()
+ > area_width.saturating_sub(number_clomn_width) as usize
{
column_index += 1;
break;
@@ -221,13 +339,11 @@ impl TableComponent {
widths.reverse();
let selected_column_index = widths.len().saturating_sub(1);
let mut column_index = right_column_index + 1;
- while widths.iter().map(|(_, width)| width).sum::()
- <= area_width
- .saturating_sub(7)
- .saturating_sub(number_clomn_width)
+ while widths.iter().map(|(_, width)| width).sum::() + widths.len()
+ <= area_width.saturating_sub(number_clomn_width) as usize
{
let length = self
- .rows()
+ .rows
.iter()
.map(|row| {
row.get(column_index)
@@ -239,14 +355,14 @@ impl TableComponent {
.max()
.map_or(3, |v| {
*v.max(
- self.headers()
+ self.headers
.iter()
.map(|header| header.to_string().width())
.collect::>()
.get(column_index)
.unwrap_or(&3),
)
- .clamp(&3, &20) as u16
+ .clamp(&3, &20)
});
match self.headers.get(column_index) {
Some(header) => {
@@ -256,21 +372,30 @@ impl TableComponent {
}
column_index += 1
}
- if self.column_index != self.headers.len().saturating_sub(1) {
+ if self.selected_column_index() != self.headers.len().saturating_sub(1) {
widths.pop();
}
let right_column_index = column_index;
let mut constraints = widths
.iter()
- .map(|(_, width)| Constraint::Length(*width))
+ .map(|(_, width)| Constraint::Length(*width as u16))
.collect::>();
- if self.column_index != self.headers.len().saturating_sub(1) {
+ if self.selected_column_index() != self.headers.len().saturating_sub(1) {
constraints.push(Constraint::Min(10));
}
constraints.insert(0, Constraint::Length(number_clomn_width));
self.column_page_start.set(left_column_index);
(
- selected_column_index + 1,
+ self.selected_right_cell
+ .map_or(selected_column_index + 1, |(x, _)| {
+ if x > self.selected_left_column_index {
+ (selected_column_index + 1)
+ .saturating_sub(x.saturating_sub(self.selected_left_column_index))
+ } else {
+ (selected_column_index + 1)
+ .saturating_add(self.selected_left_column_index.saturating_sub(x))
+ }
+ }),
self.headers_with_number(left_column_index, right_column_index),
self.rows_with_number(left_column_index, right_column_index),
constraints,
@@ -298,7 +423,7 @@ impl DrawableComponent for TableComponent {
},
);
- TableValueComponent::new(self.selected_cell().unwrap_or_default())
+ TableValueComponent::new(self.selected_cells().unwrap_or_default())
.draw(f, layout[0], focused)?;
let block = Block::default().borders(Borders::ALL).title("Records");
@@ -320,13 +445,15 @@ impl DrawableComponent for TableComponent {
.unwrap_or(0)
+ 1;
let cells = item.iter().enumerate().map(|(column_index, c)| {
- Cell::from(c.to_string()).style(if matches!(self.state.selected(), Some(selected_row_index) if row_index == selected_row_index && selected_column_index == column_index) {
- Style::default().bg(Color::Blue)
- } else if self.is_row_number_clumn(row_index, column_index) {
- Style::default().add_modifier(Modifier::BOLD)
- } else {
- Style::default()
- })
+ Cell::from(c.to_string()).style(
+ if self.is_selected_cell(row_index, column_index, selected_column_index) {
+ Style::default().bg(Color::Blue)
+ } else if self.is_row_number_clumn(row_index, column_index) {
+ Style::default().add_modifier(Modifier::BOLD)
+ } else {
+ Style::default()
+ },
+ )
});
Row::new(cells).height(height as u16).bottom_margin(1)
});
@@ -345,7 +472,15 @@ impl DrawableComponent for TableComponent {
Style::default().fg(Color::DarkGray)
})
.widths(&constraints);
- f.render_stateful_widget(table, layout[1], &mut self.state);
+ f.render_stateful_widget(
+ table,
+ layout[1],
+ if self.select_state.selected().is_some() {
+ &mut self.select_state
+ } else {
+ &mut self.state
+ },
+ );
self.scroll.draw(f, layout[1]);
Ok(())
@@ -391,6 +526,22 @@ impl Component for TableComponent {
self.next_column();
return Ok(EventState::Consumed);
}
+ Key::Char('H') => {
+ self.expand_selected_area_x(false);
+ return Ok(EventState::Consumed);
+ }
+ Key::Char('K') => {
+ self.expand_selected_area_y(false);
+ return Ok(EventState::Consumed);
+ }
+ Key::Char('J') => {
+ self.expand_selected_area_y(true);
+ return Ok(EventState::Consumed);
+ }
+ Key::Char('L') => {
+ self.expand_selected_area_x(true);
+ return Ok(EventState::Consumed);
+ }
_ => (),
}
Ok(EventState::NotConsumed)
@@ -422,6 +573,184 @@ mod test {
)
}
+ #[test]
+ fn test_expand_selected_area_x_left() {
+ // before
+ // 1 2 3
+ // 1 a b c
+ // 2 d |e| f
+
+ // after
+ // 1 2 3
+ // 1 a b c
+ // 2 |d e| f
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(1));
+ component.selected_left_column_index = 1;
+ component.expand_selected_area_x(false);
+ assert_eq!(component.selected_right_cell, Some((0, 1)));
+ assert_eq!(component.selected_cells(), Some("d,e".to_string()));
+ }
+
+ #[test]
+ fn test_expand_selected_area_x_right() {
+ // before
+ // 1 2 3
+ // 1 a b c
+ // 2 d |e| f
+
+ // after
+ // 1 2 3
+ // 1 a b c
+ // 2 d |e f|
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(1));
+ component.selected_left_column_index = 1;
+ component.expand_selected_area_x(true);
+ assert_eq!(component.selected_right_cell, Some((2, 1)));
+ assert_eq!(component.selected_cells(), Some("e,f".to_string()));
+ }
+
+ #[test]
+ fn test_expand_selected_area_y_up() {
+ // before
+ // 1 2 3
+ // 1 a b c
+ // 2 d |e| f
+
+ // after
+ // 1 2 3
+ // 1 a |b| c
+ // 2 d |e| f
+
+ let mut component = TableComponent::default();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(1));
+ component.selected_left_column_index = 1;
+ component.expand_selected_area_y(false);
+ assert_eq!(component.selected_right_cell, Some((1, 0)));
+ assert_eq!(component.selected_cells(), Some("b\ne".to_string()));
+ }
+
+ #[test]
+ fn test_expand_selected_area_y_down() {
+ // before
+ // 1 2 3
+ // 1 a |b| c
+ // 2 d e f
+
+ // after
+ // 1 2 3
+ // 1 a |b| c
+ // 2 d |e| f
+
+ let mut component = TableComponent::default();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(0));
+ component.selected_left_column_index = 1;
+ component.expand_selected_area_y(true);
+ assert_eq!(component.selected_right_cell, Some((1, 1)));
+ assert_eq!(component.selected_cells(), Some("b\ne".to_string()));
+ }
+
+ #[test]
+ fn test_selected_cell_when_one_cell_selected() {
+ // 1 2 3
+ // 1 |a| b c
+ // 2 d e f
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(0));
+ assert_eq!(component.selected_cells(), Some("a".to_string()));
+ }
+
+ #[test]
+ fn test_selected_cell_when_multiple_cells_selected() {
+ // 1 2 3
+ // 1 |a b| c
+ // 2 |d e| f
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(0));
+ component.selected_right_cell = Some((1, 1));
+ assert_eq!(component.selected_cells(), Some("a,b\nd,e".to_string()));
+ }
+
+ #[test]
+ fn test_is_selected_cell_when_one_cell_selected() {
+ // 1 2 3
+ // 1 |a| b c
+ // 2 d e f
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(0));
+ // a
+ assert!(component.is_selected_cell(0, 1, 1));
+ // d
+ assert!(!component.is_selected_cell(1, 1, 1));
+ // e
+ assert!(!component.is_selected_cell(1, 2, 1));
+ }
+
+ #[test]
+ fn test_is_selected_cell_when_multiple_cells_selected() {
+ // 1 2 3
+ // 1 |a b| c
+ // 2 |d e| f
+
+ let mut component = TableComponent::default();
+ component.headers = vec!["1", "2", "3"].iter().map(|h| h.to_string()).collect();
+ component.rows = vec![
+ vec!["a", "b", "c"].iter().map(|h| h.to_string()).collect(),
+ vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
+ ];
+ component.state.select(Some(0));
+ component.selected_right_cell = Some((1, 1));
+ // a
+ assert!(component.is_selected_cell(0, 1, 1));
+ // b
+ assert!(component.is_selected_cell(0, 2, 1));
+ // d
+ assert!(component.is_selected_cell(1, 1, 1));
+ // e
+ assert!(component.is_selected_cell(1, 2, 1));
+ // f
+ assert!(!component.is_selected_cell(1, 3, 1));
+ }
+
#[test]
fn test_calculate_widths() {
let mut component = TableComponent::default();
@@ -433,7 +762,7 @@ mod test {
.collect(),
vec!["d", "e", "f"].iter().map(|h| h.to_string()).collect(),
];
- let (selected_column_index, headers, rows, constraints) = component.calculate_widths(17);
+ let (selected_column_index, headers, rows, constraints) = component.calculate_widths(10);
assert_eq!(selected_column_index, 1);
assert_eq!(headers, vec!["", "1", "2"]);
assert_eq!(rows, vec![vec!["1", "aaaaa", "bbbbb"], vec!["2", "d", "e"]]);
@@ -446,7 +775,7 @@ mod test {
]
);
- let (selected_column_index, headers, rows, constraints) = component.calculate_widths(27);
+ let (selected_column_index, headers, rows, constraints) = component.calculate_widths(20);
assert_eq!(selected_column_index, 1);
assert_eq!(headers, vec!["", "1", "2", "3"]);
assert_eq!(
diff --git a/src/components/table_filter.rs b/src/components/table_filter.rs
index 8c878cb..b64e006 100644
--- a/src/components/table_filter.rs
+++ b/src/components/table_filter.rs
@@ -89,12 +89,10 @@ impl Component for TableFilterComponent {
return Ok(EventState::Consumed);
}
Key::Delete | Key::Backspace => {
- if input_str.width() > 0 {
- if !self.input.is_empty() && self.input_idx > 0 {
- let last_c = self.input.remove(self.input_idx - 1);
- self.input_idx -= 1;
- self.input_cursor_position -= compute_character_width(last_c);
- }
+ if input_str.width() > 0 && !self.input.is_empty() && self.input_idx > 0 {
+ let last_c = self.input.remove(self.input_idx - 1);
+ self.input_idx -= 1;
+ self.input_cursor_position -= compute_character_width(last_c);
}
return Ok(EventState::Consumed);
}
diff --git a/src/utils.rs b/src/utils.rs
index a09e62d..56c3c9d 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -13,15 +13,15 @@ pub trait Pool {
async fn get_tables(&self, database: String) -> anyhow::Result>;
async fn get_records(
&self,
- database: &String,
- table: &String,
+ database: &str,
+ table: &str,
page: u16,
filter: Option,
) -> anyhow::Result<(Vec, Vec>)>;
async fn get_columns(
&self,
- database: &String,
- table: &String,
+ database: &str,
+ table: &str,
) -> anyhow::Result<(Vec, Vec>)>;
async fn close(&self);
}
@@ -67,8 +67,8 @@ impl Pool for MySqlPool {
async fn get_records(
&self,
- database: &String,
- table: &String,
+ database: &str,
+ table: &str,
page: u16,
filter: Option,
) -> anyhow::Result<(Vec, Vec>)> {
@@ -111,8 +111,8 @@ impl Pool for MySqlPool {
async fn get_columns(
&self,
- database: &String,
- table: &String,
+ database: &str,
+ table: &str,
) -> anyhow::Result<(Vec, Vec>)> {
let query = format!("SHOW FULL COLUMNS FROM `{}`.`{}`", database, table);
let mut rows = sqlx::query(query.as_str()).fetch(&self.pool);