Refactor various components of Rust code (#490)

* refactor: Simplify hint code, replacing while loop with fold iterator

* refactor: Add spacing to lines in a separate function

* refactor: Replace if let with map_or for better readability

* refactor: Remove unnecessary duplicate body from is_cmd function

* refactor: Create generic function to replace duplicate get_selected code

* refactor: Remove duplicate code, remove unnecessary nesting

* refactor: Move style into its own variable

* refactor: Use constants for min width and height

* refactor: Remove pointless duplicate variable

---------

Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
Liam 2024-09-19 13:18:55 -05:00 committed by GitHub
parent a747f80c85
commit a5b3df0776
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 94 deletions

View File

@ -50,15 +50,7 @@ impl Float {
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) {
let popup_area = self.floating_window(parent_area);
let content_area = Rect {
x: popup_area.x,
y: popup_area.y,
width: popup_area.width,
height: popup_area.height,
};
self.content.draw(frame, content_area);
self.content.draw(frame, popup_area);
}
// Returns true if the floating window is finished.

View File

@ -20,6 +20,15 @@ pub struct Shortcut {
pub desc: &'static str,
}
fn add_spacing(list: Vec<Vec<Span>>) -> Line {
list.into_iter()
.flat_map(|mut s| {
s.push(Span::default().content(" "));
s
})
.collect()
}
pub fn span_vec_len(span_vec: &[Span]) -> usize {
span_vec.iter().rfold(0, |init, s| init + s.width())
}
@ -29,15 +38,14 @@ impl ShortcutList {
.title(self.scope_name)
.borders(Borders::all());
let inner_area = area.inner(Margin::new(1, 1));
let mut shortcut_list: Vec<Vec<Span>> = self.hints.iter().map(|h| h.to_spans()).collect();
let shortcut_spans: Vec<Vec<Span>> = self.hints.iter().map(|h| h.to_spans()).collect();
let mut lines = vec![Line::default(); SHORTCUT_LINES];
let mut idx = 0;
let mut lines: Vec<Line> = Vec::with_capacity(SHORTCUT_LINES);
while idx < SHORTCUT_LINES - 1 {
let split_idx = shortcut_list
let shortcut_list = (0..SHORTCUT_LINES - 1).fold(shortcut_spans, |mut acc, _| {
let split_idx = acc
.iter()
.scan(0usize, |total_len, s| {
.scan(0_usize, |total_len, s| {
*total_len += span_vec_len(s);
if *total_len > inner_area.width as usize {
None
@ -47,25 +55,13 @@ impl ShortcutList {
}
})
.count();
let new_shortcut_list = shortcut_list.split_off(split_idx);
let line: Vec<_> = shortcut_list
.into_iter()
.flat_map(|mut s| {
s.push(Span::default().content(" "));
s
})
.collect();
shortcut_list = new_shortcut_list;
lines[idx] = line.into();
idx += 1;
}
lines[idx] = shortcut_list
.into_iter()
.flat_map(|mut s| {
s.push(Span::default().content(" "));
s
})
.collect();
let new_shortcut_list = acc.split_off(split_idx);
lines.push(add_spacing(acc));
new_shortcut_list
});
lines.push(add_spacing(shortcut_list));
let p = Paragraph::new(lines).block(block);
frame.render_widget(p, area);

View File

@ -17,6 +17,9 @@ use ratatui::{
Frame,
};
const MIN_WIDTH: u16 = 77;
const MIN_HEIGHT: u16 = 19;
pub struct AppState {
/// Selected theme
theme: Theme,
@ -72,16 +75,14 @@ impl AppState {
}
pub fn draw(&mut self, frame: &mut Frame) {
let terminal_size = frame.area();
let min_width = 77; // Minimum width threshold
let min_height = 19; // Minimum height threshold
if terminal_size.width < min_width || terminal_size.height < min_height {
if terminal_size.width < MIN_WIDTH || terminal_size.height < MIN_HEIGHT {
let size_warning_message = format!(
"Terminal size too small:\nWidth = {} Height = {}\n\nMinimum size:\nWidth = {} Height = {}",
terminal_size.width,
terminal_size.height,
min_width,
min_height,
MIN_WIDTH,
MIN_HEIGHT,
);
let warning_paragraph = Paragraph::new(size_warning_message.clone())
@ -230,6 +231,12 @@ impl AppState {
},
));
let style = if let Focus::List = self.focus {
Style::default().reversed()
} else {
Style::new()
};
// Create the list widget with items
let list = List::new(items)
.highlight_style(if let Focus::List = self.focus {
@ -259,12 +266,12 @@ impl AppState {
pub fn handle_key(&mut self, key: &KeyEvent) -> bool {
// This should be defined first to allow closing
// the application even when not drawable ( If terminal is small )
if matches!(self.focus, Focus::TabList | Focus::List) {
if key.code == KeyCode::Char('q')
|| key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL)
{
return false;
}
// Exit on 'q' or 'Ctrl-c' input
if matches!(self.focus, Focus::TabList | Focus::List)
&& (key.code == KeyCode::Char('q')
|| key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c'))
{
return false;
}
// If UI is not drawable returning true will mark as the key handled
@ -303,20 +310,11 @@ impl AppState {
self.focus = Focus::List;
}
}
Focus::Search => match self.filter.handle_key(key) {
SearchAction::Exit => self.exit_search(),
SearchAction::Update => self.update_items(),
_ => {}
},
_ if key.code == KeyCode::Char('q')
|| key.code == KeyCode::Char('c')
&& key.modifiers.contains(KeyModifiers::CONTROL) =>
{
return false;
}
Focus::TabList => match key.code {
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List,
@ -337,7 +335,6 @@ impl AppState {
KeyCode::Char('T') => self.theme.prev(),
_ => {}
},
Focus::List if key.kind != KeyEventKind::Release => match key.code {
KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(),
KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(),
@ -358,7 +355,6 @@ impl AppState {
KeyCode::Char(' ') if self.multi_select => self.toggle_selection(),
_ => {}
},
_ => (),
};
true
@ -410,7 +406,7 @@ impl AppState {
self.selection.select(Some(0));
self.update_items();
}
pub fn get_selected_command(&self) -> Option<Command> {
fn get_selected_node(&self) -> Option<&ListNode> {
let mut selected_index = self.selection.selected().unwrap_or(0);
if !self.at_root() && selected_index == 0 {
@ -422,27 +418,17 @@ impl AppState {
if let Some(item) = self.filter.item_list().get(selected_index) {
if !item.has_children {
return Some(item.node.command.clone());
return Some(&item.node);
}
}
None
}
fn get_selected_description(&mut self) -> Option<String> {
let mut selected_index = self.selection.selected().unwrap_or(0);
if !self.at_root() && selected_index == 0 {
return None;
}
if !self.at_root() {
selected_index = selected_index.saturating_sub(1);
}
if let Some(item) = self.filter.item_list().get(selected_index) {
if !item.has_children {
return Some(item.node.description.clone());
}
}
None
pub fn get_selected_command(&self) -> Option<Command> {
self.get_selected_node().map(|node| node.command.clone())
}
fn get_selected_description(&self) -> Option<String> {
self.get_selected_node()
.map(|node| node.description.clone())
}
pub fn go_to_selected_dir(&mut self) {
let mut selected_index = self.selection.selected().unwrap_or(0);
@ -475,29 +461,14 @@ impl AppState {
selected_index = selected_index.saturating_sub(1);
}
if let Some(item) = self.filter.item_list().get(selected_index) {
item.has_children
} else {
false
}
self.filter
.item_list()
.get(selected_index)
.map_or(false, |item| item.has_children)
}
pub fn selected_item_is_cmd(&self) -> bool {
let mut selected_index = self.selection.selected().unwrap_or(0);
if !self.at_root() && selected_index == 0 {
return false;
}
if !self.at_root() {
selected_index = selected_index.saturating_sub(1);
}
if let Some(item) = self.filter.item_list().get(selected_index) {
!item.has_children
} else {
false
}
!self.selected_item_is_dir()
}
pub fn selected_item_is_up_dir(&self) -> bool {
let selected_index = self.selection.selected().unwrap_or(0);