Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refact: rust fixes and optimizations #933

Merged
merged 24 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1ff69c7
fix: getting locked out when running script
jeevithakannan2 Nov 6, 2024
4a9d726
Merge remote-tracking branch 'upstream/main' into min-tui
jeevithakannan2 Nov 6, 2024
1aa1dfe
Use success and fail colors and reorder imports
jeevithakannan2 Nov 10, 2024
3b7e859
Remove redundant code in themes
jeevithakannan2 Nov 10, 2024
190c26c
Fix scroll beyond list, color bleeding and refact in confirmation.rs
jeevithakannan2 Nov 10, 2024
0b4f33c
Implement case insensitive, fix word disappearing bug
jeevithakannan2 Nov 10, 2024
7e96651
Revert "Remove redundant code in themes"
jeevithakannan2 Nov 11, 2024
35159b8
Reference instead of passing the vector
jeevithakannan2 Nov 11, 2024
2d1f5db
Revert regex and String implementation
jeevithakannan2 Nov 11, 2024
10352c6
Replace ansi and text wrapping code with ratatui
jeevithakannan2 Nov 11, 2024
0be3a44
Merge branch 'main' into optimization
jeevithakannan2 Nov 11, 2024
2f1f5fd
Fix conflicts
jeevithakannan2 Nov 11, 2024
02b0612
Reference instead of borrowing commands, refact mut variables
jeevithakannan2 Nov 11, 2024
df81642
Update tui/src/filter.rs
jeevithakannan2 Nov 12, 2024
79aae9e
Rendering optimizations and function refactors
jeevithakannan2 Nov 12, 2024
06f13dc
Cleanup
jeevithakannan2 Nov 12, 2024
8571925
Update deps, remove unused temp-dir
jeevithakannan2 Nov 12, 2024
f8c6ba1
Add accidentally deleted preview.tape
jeevithakannan2 Nov 13, 2024
1044517
Merge remote-tracking branch 'origin/min-tui' into optimization
jeevithakannan2 Nov 14, 2024
ed01e13
Add fields to config files
jeevithakannan2 Nov 15, 2024
42a7836
Merge branch 'main' into optimization
jeevithakannan2 Nov 15, 2024
78b7c3f
Merge branch 'config-updates' into optimization
jeevithakannan2 Nov 15, 2024
a0752ae
Remove accidentally commited config file
jeevithakannan2 Nov 15, 2024
3e888c3
Merge branch 'main' into optimization
jeevithakannan2 Nov 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ ratatui = "0.29.0"
tui-term = "0.2.0"
temp-dir = "0.1.14"
time = { version = "0.3.36", features = ["local-offset", "macros", "formatting"] }
unicode-width = "0.2.0"
rand = { version = "0.8.5", optional = true }
linutil_core = { path = "../core", version = "24.9.28" }
tree-sitter-highlight = "0.24.3"
Expand All @@ -30,6 +29,7 @@ textwrap = "0.16.1"
anstyle = "1.0.8"
ansi-to-tui = "7.0.0"
zips = "0.1.7"
regex = { version = "1.3", default-features = false, features = ["std"] }

[[bin]]
name = "linutil"
Expand Down
67 changes: 34 additions & 33 deletions tui/src/confirmation.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::borrow::Cow;

use crate::{float::FloatContent, hint::Shortcut};

use crate::{float::FloatContent, hint::Shortcut, theme};
use ratatui::{
crossterm::event::{KeyCode, KeyEvent},
layout::Alignment,
prelude::*,
widgets::{Block, Borders, Clear, List},
};
use std::borrow::Cow;

pub enum ConfirmStatus {
Confirm,
Expand All @@ -16,35 +14,30 @@ pub enum ConfirmStatus {
}

pub struct ConfirmPrompt {
pub names: Box<[String]>,
pub status: ConfirmStatus,
inner_area_height: usize,
names: Box<[String]>,
scroll: usize,
pub status: ConfirmStatus,
}

impl ConfirmPrompt {
pub fn new(names: &[&str]) -> Self {
let max_count_str = format!("{}", names.len());
pub fn new(names: Vec<&str>) -> Self {
jeevithakannan2 marked this conversation as resolved.
Show resolved Hide resolved
let names = names
.iter()
.zip(1..)
.map(|(name, n)| {
let count_str = format!("{n}");
let space_str = (0..(max_count_str.len() - count_str.len()))
.map(|_| ' ')
.collect::<String>();
format!("{space_str}{n}. {name}")
})
jeevithakannan2 marked this conversation as resolved.
Show resolved Hide resolved
.map(|(name, n)| format!(" {n}. {name}"))
.collect();

Self {
inner_area_height: 0,
names,
status: ConfirmStatus::None,
scroll: 0,
status: ConfirmStatus::None,
}
}

pub fn scroll_down(&mut self) {
if self.scroll < self.names.len() - 1 {
if self.scroll + self.inner_area_height < self.names.len() - 1 {
self.scroll += 1;
}
}
Expand All @@ -57,19 +50,28 @@ impl ConfirmPrompt {
}

impl FloatContent for ConfirmPrompt {
fn draw(&mut self, frame: &mut Frame, area: Rect) {
fn draw(&mut self, frame: &mut Frame, area: Rect, theme: &theme::Theme) {
let block = Block::default()
.borders(Borders::ALL)
.border_set(ratatui::symbols::border::ROUNDED)
.title(" Confirm selections ")
.title_bottom(" [y] to continue, [n] to abort ")
.title_bottom(Line::from(vec![
Span::styled(" [", Style::default()),
Span::styled("y", Style::default().fg(theme.success_color())),
Span::styled("] to continue ", Style::default()),
Span::styled("[", Style::default()),
Span::styled("n", Style::default().fg(theme.fail_color())),
Span::styled("] to abort ", Style::default()),
]))
.title_alignment(Alignment::Center)
.title_style(Style::default().bold())
.style(Style::default());

frame.render_widget(block.clone(), area);

let inner_area = block.inner(area);
self.inner_area_height = inner_area.height as usize;

frame.render_widget(Clear, area);
frame.render_widget(block, area);

let paths_text = self
.names
Expand All @@ -81,26 +83,25 @@ impl FloatContent for ConfirmPrompt {
})
.collect::<Text>();

frame.render_widget(Clear, inner_area);
frame.render_widget(List::new(paths_text), inner_area);
}

fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
use KeyCode::*;
use ConfirmStatus::*;
jeevithakannan2 marked this conversation as resolved.
Show resolved Hide resolved
use KeyCode::{Char, Down, Esc, Up};
self.status = match key.code {
Char('y') | Char('Y') => ConfirmStatus::Confirm,
Char('n') | Char('N') | Esc | Char('q') => ConfirmStatus::Abort,
Char('j') => {
Char('y') | Char('Y') => Confirm,
Char('n') | Char('N') | Esc | Char('q') => Abort,
Char('j') | Char('J') | Down => {
self.scroll_down();
ConfirmStatus::None
None
}
Char('k') => {
Char('k') | Char('K') | Up => {
self.scroll_up();
ConfirmStatus::None
None
}
_ => ConfirmStatus::None,
_ => None,
};

false
}

Expand All @@ -118,8 +119,8 @@ impl FloatContent for ConfirmPrompt {
Box::new([
Shortcut::new("Continue", ["Y", "y"]),
Shortcut::new("Abort", ["N", "n", "q", "Esc"]),
Shortcut::new("Scroll up", ["k"]),
Shortcut::new("Scroll down", ["j"]),
Shortcut::new("Scroll up", ["k", "Up"]),
Shortcut::new("Scroll down", ["j", "Down"]),
Shortcut::new("Close linutil", ["CTRL-c"]),
]),
)
Expand Down
125 changes: 69 additions & 56 deletions tui/src/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use linutil_core::{ego_tree::NodeId, Tab};
use ratatui::{
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
layout::{Position, Rect},
style::{Color, Style},
style::Style,
text::Span,
widgets::{Block, Borders, Paragraph},
Frame,
};
use unicode_width::UnicodeWidthChar;
use regex::RegexBuilder;

pub enum SearchAction {
None,
Expand All @@ -17,7 +17,7 @@ pub enum SearchAction {
}

pub struct Filter {
search_input: Vec<char>,
search_input: String,
in_search_mode: bool,
input_position: usize,
items: Vec<ListEntry>,
Expand All @@ -27,7 +27,7 @@ pub struct Filter {
impl Filter {
pub fn new() -> Self {
Self {
search_input: vec![],
search_input: String::new(),
in_search_mode: false,
input_position: 0,
items: vec![],
Expand Down Expand Up @@ -62,56 +62,56 @@ impl Filter {
.collect();
} else {
self.items.clear();

let query_lower = self.search_input.iter().collect::<String>().to_lowercase();
for tab in tabs.iter() {
let mut stack = vec![tab.tree.root().id()];
while let Some(node_id) = stack.pop() {
let node = tab.tree.get(node_id).unwrap();

if node.value().name.to_lowercase().contains(&query_lower)
&& !node.has_children()
{
self.items.push(ListEntry {
node: node.value().clone(),
id: node.id(),
has_children: false,
});
if let Ok(regex) = self.regex_builder(&regex::escape(&self.search_input)) {
for tab in tabs {
let mut stack = vec![tab.tree.root().id()];
while let Some(node_id) = stack.pop() {
let node = tab.tree.get(node_id).unwrap();
if regex.is_match(&node.value().name) && !node.has_children() {
self.items.push(ListEntry {
node: node.value().clone(),
id: node.id(),
has_children: false,
});
}
stack.extend(node.children().map(|child| child.id()));
}

stack.extend(node.children().map(|child| child.id()));
}
self.items
.sort_unstable_by(|a, b| a.node.name.cmp(&b.node.name));
} else {
self.search_input.clear();
}
self.items.sort_by(|a, b| a.node.name.cmp(&b.node.name));
}

self.update_completion_preview();
}

fn update_completion_preview(&mut self) {
if self.search_input.is_empty() {
self.completion_preview = None;
return;
}

let input = self.search_input.iter().collect::<String>().to_lowercase();
self.completion_preview = self.items.iter().find_map(|item| {
let item_name_lower = item.node.name.to_lowercase();
if item_name_lower.starts_with(&input) {
Some(item_name_lower[input.len()..].to_string())
self.completion_preview = if self.items.is_empty() || self.search_input.is_empty() {
None
} else {
let pattern = format!("(?i)^{}", regex::escape(&self.search_input));
if let Ok(regex) = self.regex_builder(&pattern) {
self.items.iter().find_map(|item| {
regex
.find(&item.node.name)
.map(|mat| item.node.name[mat.end()..].to_string())
})
} else {
None
}
});
}
jeevithakannan2 marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn draw_searchbar(&self, frame: &mut Frame, area: Rect, theme: &Theme) {
//Set the search bar text (If empty use the placeholder)
let display_text = if !self.in_search_mode && self.search_input.is_empty() {
Span::raw("Press / to search")
} else {
let input_text = self.search_input.iter().collect::<String>();
Span::styled(input_text, Style::default().fg(theme.focused_color()))
Span::styled(
&self.search_input,
Style::default().fg(theme.focused_color()),
)
};

let search_color = if self.in_search_mode {
Expand All @@ -135,24 +135,16 @@ impl Filter {

// Render cursor in search bar
if self.in_search_mode {
let cursor_position: usize = self.search_input[..self.input_position]
.iter()
.map(|c| c.width().unwrap_or(1))
.sum();
let x = area.x + cursor_position as u16 + 1;
let x = area.x + self.input_position as u16 + 1;
let y = area.y + 1;
frame.set_cursor_position(Position::new(x, y));

if let Some(preview) = &self.completion_preview {
let preview_span = Span::styled(preview, Style::default().fg(Color::DarkGray));
let preview_paragraph = Paragraph::new(preview_span).style(Style::default());
let preview_area = Rect::new(
x,
y,
(preview.len() as u16).min(area.width - cursor_position as u16 - 1),
1,
);
frame.render_widget(preview_paragraph, preview_area);
let preview_x = area.x + self.search_input.len() as u16 + 1;
let preview_span =
Span::styled(preview, Style::default().fg(theme.search_preview_color()));
let preview_area = Rect::new(preview_x, y, preview.len() as u16, 1);
frame.render_widget(Paragraph::new(preview_span), preview_area);
}
}
}
Expand Down Expand Up @@ -219,14 +211,35 @@ impl Filter {
}
}

fn regex_builder(&self, pattern: &str) -> Result<regex::Regex, regex::Error> {
RegexBuilder::new(pattern).case_insensitive(true).build()
}

fn complete_search(&mut self) -> SearchAction {
if let Some(completion) = self.completion_preview.take() {
self.search_input.extend(completion.chars());
self.input_position = self.search_input.len();
self.update_completion_preview();
SearchAction::Update
} else {
if self.completion_preview.is_none() {
SearchAction::None
} else {
let pattern = format!("(?i)^{}", self.search_input);
if let Ok(regex) = self.regex_builder(&pattern) {
self.search_input = self
.items
.iter()
.find_map(|item| {
if regex.is_match(&item.node.name) {
Some(item.node.name.clone())
} else {
None
}
})
.unwrap_or_default();

self.completion_preview = None;
self.input_position = self.search_input.len();

SearchAction::Update
} else {
SearchAction::None
}
}
}

Expand Down
9 changes: 4 additions & 5 deletions tui/src/float.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use crate::{hint::Shortcut, theme::Theme};
use ratatui::{
crossterm::event::{KeyCode, KeyEvent},
layout::{Constraint, Direction, Layout, Rect},
Frame,
};

use crate::hint::Shortcut;

pub trait FloatContent {
fn draw(&mut self, frame: &mut Frame, area: Rect);
fn draw(&mut self, frame: &mut Frame, area: Rect, theme: &Theme);
fn handle_key_event(&mut self, key: &KeyEvent) -> bool;
fn is_finished(&self) -> bool;
fn get_shortcut_list(&self) -> (&str, Box<[Shortcut]>);
Expand Down Expand Up @@ -48,9 +47,9 @@ impl<Content: FloatContent + ?Sized> Float<Content> {
.split(hor_float)[1]
}

pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) {
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect, theme: &Theme) {
let popup_area = self.floating_window(parent_area);
self.content.draw(frame, popup_area);
self.content.draw(frame, popup_area, theme);
}

// Returns true if the floating window is finished.
Expand Down
Loading