Skip to content

Commit

Permalink
Remove all dynamic allocations from shortcut creation
Browse files Browse the repository at this point in the history
  • Loading branch information
cartercanedy committed Sep 25, 2024
1 parent cab5796 commit 0765953
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 54 deletions.
10 changes: 5 additions & 5 deletions tui/src/floating_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,11 @@ impl FloatContent for FloatingText {
(
self.mode_title,
Box::new([
Shortcut::new(vec!["j", "Down"], "Scroll down"),
Shortcut::new(vec!["k", "Up"], "Scroll up"),
Shortcut::new(vec!["h", "Left"], "Scroll left"),
Shortcut::new(vec!["l", "Right"], "Scroll right"),
Shortcut::new(vec!["Enter", "p", "d", "g"], "Close window"),
Shortcut::new("Scroll down", ["j", "Down"]),
Shortcut::new("Scroll up", ["k", "Up"]),
Shortcut::new("Scroll left", ["h", "Left"]),
Shortcut::new("Scroll right", ["l", "Right"]),
Shortcut::new("Close window", ["Enter", "p", "d", "g"]),
]),
)
}
Expand Down
25 changes: 17 additions & 8 deletions tui/src/hint.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use ratatui::{
style::{Style, Stylize},
text::{Line, Span},
Expand Down Expand Up @@ -31,16 +33,23 @@ pub fn create_shortcut_list(

let mut lines: Vec<Line<'static>> = vec![];

for _ in 0.. {
loop {
let split_idx = shortcut_spans
.iter()
.scan(0usize, |total_len, s| {
*total_len += span_vec_len(s);
if *total_len > render_width as usize {
None
// take at least one so that we guarantee that we drain the list
// otherwise, this might lock up the
if *total_len == 0 {
*total_len += span_vec_len(s) + 4;
Some(())
} else {
*total_len += 4;
Some(1)
*total_len += span_vec_len(s);
if *total_len > render_width as usize {
None
} else {
*total_len += 4;
Some(())
}
}
})
.count();
Expand All @@ -59,11 +68,11 @@ pub fn create_shortcut_list(
}

impl Shortcut {
pub fn new(key_sequences: Vec<&'static str>, desc: &'static str) -> Self {
pub fn new<const LEN: usize>(desc: &'static str, key_sequences: [&'static str; LEN]) -> Self {
Self {
key_sequences: key_sequences
.iter()
.map(|s| Span::styled(*s, Style::default().bold()))
.map(|s| Span::styled(Cow::<'static, str>::Borrowed(s), Style::default().bold()))
.collect(),
desc,
}
Expand Down
4 changes: 2 additions & 2 deletions tui/src/running_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ impl FloatContent for RunningCommand {
if self.is_finished() {
(
"Finished command",
Box::new([Shortcut::new(vec!["Enter", "q"], "Close window")]),
Box::new([Shortcut::new("Close window", ["Enter", "q"])]),
)
} else {
(
"Running command",
Box::new([Shortcut::new(vec!["CTRL-c"], "Kill the command")]),
Box::new([Shortcut::new("Kill the command", ["CTRL-c"])]),
)
}
}
Expand Down
88 changes: 49 additions & 39 deletions tui/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl AppState {
pub fn new(theme: Theme, override_validation: bool) -> Self {
let tabs = linutil_core::get_tabs(!override_validation);
let root_id = tabs[0].tree.root().id();

let mut state = Self {
theme,
focus: Focus::List,
Expand All @@ -90,77 +91,75 @@ impl AppState {
#[cfg(feature = "tips")]
tip: get_random_tip(),
};

state.update_items();
state
}

fn get_list_item_shortcut(&self) -> Vec<Shortcut> {
fn get_list_item_shortcut(&self) -> Box<[Shortcut]> {
if self.selected_item_is_dir() {
vec![Shortcut::new(
vec!["l", "Right", "Enter"],
"Go to selected dir",
)]
Box::new([Shortcut::new("Go to selected dir", ["l", "Right", "Enter"])])
} else {
vec![
Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command"),
Shortcut::new(vec!["p"], "Enable preview"),
Shortcut::new(vec!["d"], "Command Description"),
]
Box::new([
Shortcut::new("Run selected command", ["l", "Right", "Enter"]),
Shortcut::new("Enable preview", ["p"]),
Shortcut::new("Command Description", ["d"]),
])
}
}

pub fn get_keybinds(&self) -> (&str, Box<[Shortcut]>) {
match self.focus {
Focus::Search => (
"Search bar",
Box::new([Shortcut::new(vec!["Enter"], "Finish search")]),
Box::new([Shortcut::new("Finish search", ["Enter"])]),
),

Focus::List => {
let mut hints = Vec::new();
hints.push(Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"));
hints.push(Shortcut::new("Exit linutil", ["q", "CTRL-c"]));

if self.at_root() {
hints.push(Shortcut::new(vec!["h", "Left"], "Focus tab list"));
hints.push(Shortcut::new("Focus tab list", ["h", "Left"]));
hints.extend(self.get_list_item_shortcut());
} else if self.selected_item_is_up_dir() {
hints.push(Shortcut::new(
vec!["l", "Right", "Enter", "h", "Left"],
"Go to parent directory",
["l", "Right", "Enter", "h", "Left"],
));
} else {
hints.push(Shortcut::new(vec!["h", "Left"], "Go to parent directory"));
hints.push(Shortcut::new("Go to parent directory", ["h", "Left"]));
hints.extend(self.get_list_item_shortcut());
}

hints.push(Shortcut::new(vec!["k", "Up"], "Select item above"));
hints.push(Shortcut::new(vec!["j", "Down"], "Select item below"));
hints.push(Shortcut::new(vec!["t"], "Next theme"));
hints.push(Shortcut::new(vec!["T"], "Previous theme"));
hints.push(Shortcut::new("Select item above", ["k", "Up"]));
hints.push(Shortcut::new("Select item below", ["j", "Down"]));
hints.push(Shortcut::new("Next theme", ["t"]));
hints.push(Shortcut::new("Previous theme", ["T"]));

if self.is_current_tab_multi_selectable() {
hints.push(Shortcut::new(vec!["v"], "Toggle multi-selection mode"));
hints.push(Shortcut::new(vec!["Space"], "Select multiple commands"));
hints.push(Shortcut::new("Toggle multi-selection mode", ["v"]));
hints.push(Shortcut::new("Select multiple commands", ["Space"]));
}

hints.push(Shortcut::new(vec!["Tab"], "Next tab"));
hints.push(Shortcut::new(vec!["Shift-Tab"], "Previous tab"));
hints.push(Shortcut::new(vec!["g"], "Important actions guide"));
hints.push(Shortcut::new("Next tab", ["Tab"]));
hints.push(Shortcut::new("Previous tab", ["Shift-Tab"]));
hints.push(Shortcut::new("Important actions guide", ["g"]));

("Command list", hints.into_boxed_slice())
}

Focus::TabList => (
"Tab list",
Box::new([
Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"),
Shortcut::new(vec!["l", "Right", "Enter"], "Focus action list"),
Shortcut::new(vec!["k", "Up"], "Select item above"),
Shortcut::new(vec!["j", "Down"], "Select item below"),
Shortcut::new(vec!["t"], "Next theme"),
Shortcut::new(vec!["T"], "Previous theme"),
Shortcut::new(vec!["Tab"], "Next tab"),
Shortcut::new(vec!["Shift-Tab"], "Previous tab"),
Shortcut::new("Exit linutil", ["q", "CTRL-c"]),
Shortcut::new("Focus action list", ["l", "Right", "Enter"]),
Shortcut::new("Select item above", ["k", "Up"]),
Shortcut::new("Select item below", ["j", "Down"]),
Shortcut::new("Next theme", ["t"]),
Shortcut::new("Previous theme", ["T"]),
Shortcut::new("Next tab", ["Tab"]),
Shortcut::new("Previous tab", ["Shift-Tab"]),
]),
),

Expand Down Expand Up @@ -444,11 +443,13 @@ impl AppState {
self.focus = Focus::List;
}
}

Focus::Search => match self.filter.handle_key(key) {
SearchAction::Exit => self.exit_search(),
SearchAction::Update => self.update_items(),
SearchAction::None => {}
},

Focus::TabList => match key.code {
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List,

Expand All @@ -470,19 +471,14 @@ impl AppState {
KeyCode::Char('g') => self.toggle_task_list_guide(),
_ => {}
},

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(),
KeyCode::Char('p') | KeyCode::Char('P') => self.enable_preview(),
KeyCode::Char('d') | KeyCode::Char('D') => self.enable_description(),
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.handle_enter(),
KeyCode::Char('h') | KeyCode::Left => {
if self.at_root() {
self.focus = Focus::TabList;
} else {
self.enter_parent_directory();
}
}
KeyCode::Char('h') | KeyCode::Left => self.go_back(),
KeyCode::Char('/') => self.enter_search(),
KeyCode::Char('t') => self.theme.next(),
KeyCode::Char('T') => self.theme.prev(),
Expand All @@ -491,10 +487,12 @@ impl AppState {
KeyCode::Char(' ') if self.multi_select => self.toggle_selection(),
_ => {}
},

_ => (),
};
true
}

fn toggle_multi_select(&mut self) {
if self.is_current_tab_multi_selectable() {
self.multi_select = !self.multi_select;
Expand All @@ -503,6 +501,7 @@ impl AppState {
}
}
}

fn toggle_selection(&mut self) {
if let Some(command) = self.get_selected_command() {
if self.selected_commands.contains(&command) {
Expand All @@ -512,12 +511,14 @@ impl AppState {
}
}
}

pub fn is_current_tab_multi_selectable(&self) -> bool {
let index = self.current_tab.selected().unwrap_or(0);
self.tabs
.get(index)
.map_or(false, |tab| tab.multi_selectable)
}

fn update_items(&mut self) {
self.filter.update_items(
&self.tabs,
Expand All @@ -537,11 +538,20 @@ impl AppState {
self.visit_stack.len() == 1
}

fn go_back(&mut self) {
if self.at_root() {
self.focus = Focus::TabList;
} else {
self.enter_parent_directory();
}
}

fn enter_parent_directory(&mut self) {
self.visit_stack.pop();
self.selection.select(Some(0));
self.update_items();
}

fn get_selected_node(&self) -> Option<&ListNode> {
let mut selected_index = self.selection.selected().unwrap_or(0);

Expand Down

0 comments on commit 0765953

Please sign in to comment.