diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..f646a08 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,91 @@ +name: Continuous Deployment + +on: + push: + tags: + - "v*.*.*" + workflow_dispatch: + +jobs: + publish: + name: Publishing for ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + rust: [stable] + include: + - os: macos-latest + artifact_prefix: macos + target: x86_64-apple-darwin + binary_postfix: "" + - os: ubuntu-latest + artifact_prefix: linux + target: x86_64-unknown-linux-gnu + binary_postfix: "" + # - os: windows-latest + # artifact_prefix: windows + # target: x86_64-pc-windows-msvc + # binary_postfix: ".exe" + + steps: + - name: Installing Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + override: true + - name: Installing needed macOS dependencies + if: matrix.os == 'macos-latest' + run: brew install openssl@1.1 + - name: Installing needed Ubuntu dependencies + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + - name: Checking out sources + uses: actions/checkout@v1 + - name: Running cargo build + uses: actions-rs/cargo@v1 + with: + command: build + toolchain: ${{ matrix.rust }} + args: --release --target ${{ matrix.target }} + + - name: Packaging final binary + shell: bash + run: | + cd target/${{ matrix.target }}/release + BINARY_NAME=spt${{ matrix.binary_postfix }} + strip $BINARY_NAME + RELEASE_NAME=xplr-${{ matrix.artifact_prefix }} + tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME + if [[ ${{ runner.os }} == 'Windows' ]]; then + certutil -hashfile $RELEASE_NAME.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256 + else + shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256 + fi + - name: Releasing assets + uses: softprops/action-gh-release@v1 + with: + files: | + target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.tar.gz + target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.sha256 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-cargo: + name: Publishing to Cargo + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - run: | + sudo apt-get update + sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + - uses: actions-rs/cargo@v1 + with: + command: publish + args: --token ${{ secrets.CARGO_API_KEY }} --allow-dirty diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..eec2d14 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,104 @@ +on: + pull_request: + push: + branches: master + workflow_dispatch: + +name: Continuous Integration + +jobs: + # Workaround for making Github Actions skip based on commit message `[skip ci]` + # Source https://gist.github.com/ybiquitous/c80f15c18319c63cae8447a3be341267 + prepare: + runs-on: ubuntu-latest + if: | + !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title, github.event.pull_request.body), '[skip ci]') + steps: + - run: | + cat <<'MESSAGE' + github.event_name: ${{ toJson(github.event_name) }} + github.event: + ${{ toJson(github.event) }} + MESSAGE + check: + name: Check + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + + test: + name: Test Suite + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + # These dependencies are required for `clipboard` + - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + - uses: actions-rs/cargo@v1 + with: + command: test + + bench: + name: Benchmarks + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + # These dependencies are required for `clipboard` + - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + - uses: actions-rs/cargo@v1 + with: + command: bench + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + components: rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + needs: prepare + steps: + - uses: actions/checkout@master + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + components: clippy + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings diff --git a/README.md b/README.md index 64e395a..bda2031 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ A hackable, minimal, fast TUI file explorer, stealing ideas from Self { + fn from_session_path(path: &str) -> Self { let pipesdir = PathBuf::from(path).join("pipe"); fs::create_dir_all(&pipesdir).unwrap(); @@ -191,11 +189,11 @@ pub enum NodeFilter { } impl NodeFilter { - fn apply(&self, node: &Node, input: &String, case_sensitive: bool) -> bool { + fn apply(&self, node: &Node, input: &str, case_sensitive: bool) -> bool { match self { Self::RelativePathIs => { if case_sensitive { - &node.relative_path == input + node.relative_path == input } else { node.relative_path.to_lowercase() == input.to_lowercase() } @@ -203,7 +201,7 @@ impl NodeFilter { Self::RelativePathIsNot => { if case_sensitive { - &node.relative_path != input + node.relative_path != input } else { node.relative_path.to_lowercase() != input.to_lowercase() } @@ -274,7 +272,7 @@ impl NodeFilter { Self::AbsolutePathIs => { if case_sensitive { - &node.absolute_path == input + node.absolute_path == input } else { node.absolute_path.to_lowercase() == input.to_lowercase() } @@ -282,7 +280,7 @@ impl NodeFilter { Self::AbsolutePathIsNot => { if case_sensitive { - &node.absolute_path != input + node.absolute_path != input } else { node.absolute_path.to_lowercase() != input.to_lowercase() } @@ -691,19 +689,9 @@ pub struct App { } impl App { - pub fn create() -> Result { - let mut pwd = PathBuf::from(env::args().skip(1).next().unwrap_or(".".into())) - .canonicalize() - .unwrap_or_default(); - - if pwd.is_file() { - pwd = pwd.parent().map(|p| p.into()).unwrap_or_default(); - } - - let pwd = pwd.to_string_lossy().to_string(); - + pub fn create(pwd: PathBuf) -> Result { let config_dir = dirs::config_dir() - .unwrap_or(PathBuf::from(".")) + .unwrap_or_else(|| PathBuf::from(".")) .join("xplr"); let config_file = config_dir.join("config.yml"); @@ -733,7 +721,7 @@ impl App { let pid = std::process::id(); let session_path = dirs::runtime_dir() - .unwrap_or("/tmp".into()) + .unwrap_or_else(|| "/tmp".into()) .join("xplr") .join("session") .join(&pid.to_string()) @@ -751,7 +739,7 @@ impl App { Ok(Self { config, - pwd, + pwd: pwd.to_string_lossy().to_string(), directory_buffers: Default::default(), tasks: Default::default(), selection: Default::default(), @@ -842,7 +830,7 @@ impl App { ExternalMsg::LogError(l) => self.log_error(l), ExternalMsg::PrintResultAndQuit => self.print_result_and_quit(), ExternalMsg::PrintAppStateAndQuit => self.print_app_state_and_quit(), - ExternalMsg::Debug(path) => self.debug(&path), + ExternalMsg::Debug(path) => self.debug(path), ExternalMsg::Terminate => bail!("terminated"), } } @@ -953,7 +941,7 @@ impl App { } } - fn change_directory(mut self, dir: &String) -> Result { + fn change_directory(mut self, dir: &str) -> Result { if PathBuf::from(dir).is_dir() { self.pwd = dir.to_owned(); self.msg_out.push_back(MsgOut::Refresh); @@ -978,9 +966,9 @@ impl App { .unwrap_or(Ok(self)) } - fn buffer_input(mut self, input: &String) -> Result { + fn buffer_input(mut self, input: &str) -> Result { if let Some(buf) = self.input_buffer.as_mut() { - buf.extend(input.chars()); + buf.push_str(input) } else { self.input_buffer = Some(input.to_owned()); }; @@ -1024,14 +1012,14 @@ impl App { } } - fn focus_by_file_name(mut self, name: &String) -> Result { + fn focus_by_file_name(mut self, name: &str) -> Result { if let Some(dir_buf) = self.directory_buffer_mut() { if let Some(focus) = dir_buf .clone() .nodes .iter() .enumerate() - .find(|(_, n)| &n.relative_path == name) + .find(|(_, n)| n.relative_path == name) .map(|(i, _)| i) { dir_buf.focus = focus; @@ -1041,7 +1029,7 @@ impl App { Ok(self) } - fn focus_path(self, path: &String) -> Result { + fn focus_path(self, path: &str) -> Result { let pathbuf = PathBuf::from(path); if let Some(parent) = pathbuf.parent() { if let Some(filename) = pathbuf.file_name() { @@ -1063,7 +1051,7 @@ impl App { } } - fn switch_mode(mut self, mode: &String) -> Result { + fn switch_mode(mut self, mode: &str) -> Result { if let Some(mode) = self.config.modes.get(mode) { self.input_buffer = None; self.mode = mode.to_owned(); @@ -1085,7 +1073,7 @@ impl App { fn select(mut self) -> Result { if let Some(n) = self.focused_node().map(|n| n.to_owned()) { - self.selection.push(n.clone()); + self.selection.push(n); self.msg_out.push_back(MsgOut::Refresh); } Ok(self) @@ -1198,8 +1186,8 @@ impl App { Ok(self) } - fn debug(mut self, path: &String) -> Result { - self.msg_out.push_back(MsgOut::Debug(path.to_owned())); + fn debug(mut self, path: String) -> Result { + self.msg_out.push_back(MsgOut::Debug(path)); Ok(self) } @@ -1275,7 +1263,7 @@ impl App { if self.selection.is_empty() { self.focused_node().map(|n| vec![n]).unwrap_or_default() } else { - self.selection.iter().map(|n| n).collect() + self.selection.iter().collect() } } diff --git a/src/config.rs b/src/config.rs index 21acfb8..07a7232 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,10 +2,9 @@ use crate::app::ExternalMsg; use crate::app::HelpMenuLine; use crate::app::VERSION; use serde::{Deserialize, Serialize}; -use serde_yaml; use std::collections::BTreeMap; use std::collections::HashMap; -use tui::layout::Constraint as TUIConstraint; +use tui::layout::Constraint as TuiConstraint; use tui::style::Color; use tui::style::Modifier; use tui::style::Style; @@ -73,7 +72,7 @@ impl Default for FileTypesConfig { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct UIConfig { +pub struct UiConfig { #[serde(default)] pub prefix: String, #[serde(default)] @@ -83,7 +82,7 @@ pub struct UIConfig { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct UIElement { +pub struct UiElement { #[serde(default)] pub format: String, #[serde(default)] @@ -93,7 +92,7 @@ pub struct UIElement { #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct TableRowConfig { #[serde(default)] - pub cols: Vec, + pub cols: Vec, #[serde(default)] pub style: Style, #[serde(default)] @@ -116,14 +115,14 @@ impl Default for Constraint { } } -impl Into for Constraint { - fn into(self) -> TUIConstraint { +impl Into for Constraint { + fn into(self) -> TuiConstraint { match self { - Self::Length(n) => TUIConstraint::Length(n), - Self::Percentage(n) => TUIConstraint::Percentage(n), - Self::Ratio(x, y) => TUIConstraint::Ratio(x, y), - Self::Max(n) => TUIConstraint::Max(n), - Self::Min(n) => TUIConstraint::Min(n), + Self::Length(n) => TuiConstraint::Length(n), + Self::Percentage(n) => TuiConstraint::Percentage(n), + Self::Ratio(x, y) => TuiConstraint::Ratio(x, y), + Self::Max(n) => TuiConstraint::Max(n), + Self::Min(n) => TuiConstraint::Min(n), } } } @@ -137,7 +136,7 @@ pub struct TableConfig { #[serde(default)] pub style: Style, #[serde(default)] - pub tree: Option<(UIElement, UIElement, UIElement)>, + pub tree: Option<(UiElement, UiElement, UiElement)>, #[serde(default)] pub col_spacing: u16, #[serde(default)] @@ -153,13 +152,13 @@ pub struct GeneralConfig { pub table: TableConfig, #[serde(default)] - pub normal_ui: UIConfig, + pub normal_ui: UiConfig, #[serde(default)] - pub focused_ui: UIConfig, + pub focused_ui: UiConfig, #[serde(default)] - pub selection_ui: UIConfig, + pub selection_ui: UiConfig, } impl Default for GeneralConfig { @@ -441,45 +440,35 @@ impl Mode { .unwrap_or_default() .into_iter() .chain(self.key_bindings.on_key.iter().filter_map(|(k, a)| { - a.help - .clone() - .map(|h| HelpMenuLine::KeyMap(k.into(), h.into())) + a.help.clone().map(|h| HelpMenuLine::KeyMap(k.into(), h)) })) .chain( self.key_bindings .on_alphabet .iter() .map(|a| ("[a-Z]", a.help.clone())) - .filter_map(|(k, mh)| { - mh.map(|h| HelpMenuLine::KeyMap(k.into(), h.into())) - }), + .filter_map(|(k, mh)| mh.map(|h| HelpMenuLine::KeyMap(k.into(), h))), ) .chain( self.key_bindings .on_number .iter() .map(|a| ("[0-9]", a.help.clone())) - .filter_map(|(k, mh)| { - mh.map(|h| HelpMenuLine::KeyMap(k.into(), h.into())) - }), + .filter_map(|(k, mh)| mh.map(|h| HelpMenuLine::KeyMap(k.into(), h))), ) .chain( self.key_bindings .on_special_character .iter() .map(|a| ("[spcl chars]", a.help.clone())) - .filter_map(|(k, mh)| { - mh.map(|h| HelpMenuLine::KeyMap(k.into(), h.into())) - }), + .filter_map(|(k, mh)| mh.map(|h| HelpMenuLine::KeyMap(k.into(), h))), ) .chain( self.key_bindings .default .iter() .map(|a| ("[default]", a.help.clone())) - .filter_map(|(k, mh)| { - mh.map(|h| HelpMenuLine::KeyMap(k.into(), h.into())) - }), + .filter_map(|(k, mh)| mh.map(|h| HelpMenuLine::KeyMap(k.into(), h))), ) .collect() }) diff --git a/src/event_reader.rs b/src/event_reader.rs index 9708385..427fa57 100644 --- a/src/event_reader.rs +++ b/src/event_reader.rs @@ -9,33 +9,31 @@ pub fn keep_reading(tx_msg_in: Sender, rx_event_reader: Receiver) { thread::spawn(move || { let mut is_paused = false; loop { - if let Some(paused) = rx_event_reader.try_recv().ok() { + if let Ok(paused) = rx_event_reader.try_recv() { is_paused = paused; }; - if !is_paused { - if event::poll(std::time::Duration::from_millis(1)).unwrap() { - match event::read() { - Ok(Event::Key(key)) => { - let key = Key::from_event(key); - let msg = MsgIn::Internal(InternalMsg::HandleKey(key)); - tx_msg_in.send(Task::new(0, msg, Some(key))).unwrap(); - } + if !is_paused && event::poll(std::time::Duration::from_millis(1)).unwrap() { + match event::read() { + Ok(Event::Key(key)) => { + let key = Key::from_event(key); + let msg = MsgIn::Internal(InternalMsg::HandleKey(key)); + tx_msg_in.send(Task::new(0, msg, Some(key))).unwrap(); + } - Ok(Event::Resize(_, _)) => { - let msg = MsgIn::External(ExternalMsg::Refresh); - tx_msg_in.send(Task::new(0, msg, None)).unwrap(); - } - Ok(_) => {} - Err(e) => { - tx_msg_in - .send(Task::new( - 0, - MsgIn::External(ExternalMsg::LogError(e.to_string())), - None, - )) - .unwrap(); - } + Ok(Event::Resize(_, _)) => { + let msg = MsgIn::External(ExternalMsg::Refresh); + tx_msg_in.send(Task::new(0, msg, None)).unwrap(); + } + Ok(_) => {} + Err(e) => { + tx_msg_in + .send(Task::new( + 0, + MsgIn::External(ExternalMsg::LogError(e.to_string())), + None, + )) + .unwrap(); } } } diff --git a/src/input.rs b/src/input.rs index 3aeb102..907ed9a 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,6 +1,5 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use serde::{Deserialize, Serialize}; -use serde_yaml; use std::cmp::Ordering; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -302,86 +301,81 @@ impl Key { } pub fn is_alphabet(&self) -> bool { - match self { - Self::A => true, - Self::B => true, - Self::C => true, - Self::D => true, - Self::E => true, - Self::F => true, - Self::G => true, - Self::H => true, - Self::I => true, - Self::J => true, - Self::K => true, - Self::L => true, - Self::M => true, - Self::N => true, - Self::O => true, - Self::P => true, - Self::Q => true, - Self::R => true, - Self::S => true, - Self::T => true, - Self::U => true, - Self::V => true, - Self::W => true, - Self::X => true, - Self::Y => true, - Self::Z => true, - - Self::ShiftA => true, - Self::ShiftB => true, - Self::ShiftC => true, - Self::ShiftD => true, - Self::ShiftE => true, - Self::ShiftF => true, - Self::ShiftG => true, - Self::ShiftH => true, - Self::ShiftI => true, - Self::ShiftJ => true, - Self::ShiftK => true, - Self::ShiftL => true, - Self::ShiftM => true, - Self::ShiftN => true, - Self::ShiftO => true, - Self::ShiftP => true, - Self::ShiftQ => true, - Self::ShiftR => true, - Self::ShiftS => true, - Self::ShiftT => true, - Self::ShiftU => true, - Self::ShiftV => true, - Self::ShiftW => true, - Self::ShiftX => true, - Self::ShiftY => true, - Self::ShiftZ => true, - - _ => false, - } + matches!( + self, + Self::A + | Self::B + | Self::C + | Self::D + | Self::E + | Self::F + | Self::G + | Self::H + | Self::I + | Self::J + | Self::K + | Self::L + | Self::M + | Self::N + | Self::O + | Self::P + | Self::Q + | Self::R + | Self::S + | Self::T + | Self::U + | Self::V + | Self::W + | Self::X + | Self::Y + | Self::Z + | Self::ShiftA + | Self::ShiftB + | Self::ShiftC + | Self::ShiftD + | Self::ShiftE + | Self::ShiftF + | Self::ShiftG + | Self::ShiftH + | Self::ShiftI + | Self::ShiftJ + | Self::ShiftK + | Self::ShiftL + | Self::ShiftM + | Self::ShiftN + | Self::ShiftO + | Self::ShiftP + | Self::ShiftQ + | Self::ShiftR + | Self::ShiftS + | Self::ShiftT + | Self::ShiftU + | Self::ShiftV + | Self::ShiftW + | Self::ShiftX + | Self::ShiftY + | Self::ShiftZ + ) } pub fn is_number(&self) -> bool { - match self { - Self::Num0 => true, - Self::Num1 => true, - Self::Num2 => true, - Self::Num3 => true, - Self::Num4 => true, - Self::Num5 => true, - Self::Num6 => true, - Self::Num7 => true, - Self::Num8 => true, - Self::Num9 => true, - _ => false, - } + matches!( + self, + Self::Num0 + | Self::Num1 + | Self::Num2 + | Self::Num3 + | Self::Num4 + | Self::Num5 + | Self::Num6 + | Self::Num7 + | Self::Num8 + | Self::Num9, + ) } pub fn is_special_character(&self) -> bool { - match self { - Self::Special(_) => true, - _ => false, - } + matches!(self, Self::Special(_)) } pub fn to_char(&self) -> Option { diff --git a/src/lib.rs b/src/lib.rs index ecd2d56..04e32e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ +#![allow(clippy::too_many_arguments)] +#![allow(clippy::from_over_into)] +#![allow(clippy::unnecessary_wraps)] + pub mod app; pub mod config; +pub mod event_reader; pub mod explorer; pub mod input; -pub mod ui; pub mod pipe_reader; -pub mod event_reader; +pub mod ui; diff --git a/src/main.rs b/src/main.rs index 9ffe978..7ba43ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ +#![allow(clippy::too_many_arguments)] + use anyhow::Result; use crossterm::execute; use crossterm::terminal as term; use handlebars::Handlebars; +use std::env; use std::fs; use std::io::prelude::*; use std::path::PathBuf; @@ -16,7 +19,32 @@ use xplr::pipe_reader; use xplr::ui; fn main() -> Result<()> { - let mut app = app::App::create()?; + let (tx_msg_in, rx_msg_in) = mpsc::channel(); + let (tx_event_reader, rx_event_reader) = mpsc::channel(); + + let mut pwd = PathBuf::from(env::args().nth(1).unwrap_or_else(|| ".".into())) + .canonicalize() + .unwrap_or_default(); + let mut focused_path = None; + + if pwd.is_file() { + focused_path = Some( + pwd.file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + ); + pwd = pwd.parent().map(|p| p.into()).unwrap_or_default(); + } + + let mut app = app::App::create(pwd)?; + + explorer::explore( + app.explorer_config().clone(), + app.pwd().clone(), + focused_path, + tx_msg_in.clone(), + ); let mut hb = Handlebars::new(); hb.register_template_string( @@ -35,8 +63,6 @@ fn main() -> Result<()> { let mut result = Ok(()); let mut output = None; - let (tx_msg_in, rx_msg_in) = mpsc::channel(); - term::enable_raw_mode()?; let mut stdout = get_tty()?; // let mut stdout = stdout.lock(); @@ -46,21 +72,8 @@ fn main() -> Result<()> { let mut terminal = Terminal::new(backend)?; terminal.hide_cursor()?; - let focused_path = std::env::args().skip(1).next().and_then(|p| { - PathBuf::from(p) - .file_name() - .map(|n| n.to_string_lossy().to_string()) - }); - explorer::explore( - app.explorer_config().clone(), - app.pwd().clone(), - focused_path, - tx_msg_in.clone(), - ); - pipe_reader::keep_reading(app.pipe().msg_in.clone(), tx_msg_in.clone()); - let (tx_event_reader, rx_event_reader) = mpsc::channel(); event_reader::keep_reading(tx_msg_in.clone(), rx_event_reader); let mut last_pwd = app.pwd().clone(); @@ -139,7 +152,7 @@ fn main() -> Result<()> { terminal.show_cursor()?; let pid = std::process::id().to_string(); - let input_buffer = app.input_buffer().map(|i| i.to_owned()).unwrap_or_default(); + let input_buffer = app.input_buffer().unwrap_or_default(); let focus_path = app .focused_node() diff --git a/src/pipe_reader.rs b/src/pipe_reader.rs index 2af63e2..c6239c3 100644 --- a/src/pipe_reader.rs +++ b/src/pipe_reader.rs @@ -1,5 +1,4 @@ use crate::app::{ExternalMsg, MsgIn, Task}; -use serde_yaml; use std::fs; use std::sync::mpsc::Sender; use std::thread; diff --git a/src/ui.rs b/src/ui.rs index be2bdd4..a88099a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -3,9 +3,10 @@ use crate::app::HelpMenuLine; use crate::app::Node; use handlebars::Handlebars; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; use tui::backend::Backend; use tui::layout::Rect; -use tui::layout::{Constraint as TUIConstraint, Direction, Layout}; +use tui::layout::{Constraint as TuiConstraint, Direction, Layout}; use tui::style::{Color, Style}; use tui::widgets::{ Block, Borders, Cell, List, ListItem, ListState, Paragraph, Row, Table, TableState, @@ -16,7 +17,7 @@ const TOTAL_ROWS: usize = 50; #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -struct NodeUIMetadata { +struct NodeUiMetadata { // From Node pub parent: String, pub relative_path: String, @@ -42,7 +43,7 @@ struct NodeUIMetadata { pub total: usize, } -impl NodeUIMetadata { +impl NodeUiMetadata { fn new( node: &Node, index: usize, @@ -122,11 +123,11 @@ fn draw_table(f: &mut Frame, rect: Rect, app: &app::App, hb: &Han .clone() .map(|t| { if is_last { - t.2.format.clone() + t.2.format } else if is_first { - t.0.format.clone() + t.0.format } else { - t.1.format.clone() + t.1.format } }) .unwrap_or_default(); @@ -147,15 +148,14 @@ fn draw_table(f: &mut Frame, rect: Rect, app: &app::App, hb: &Han } }); - let (relative_index, is_before_focus, is_after_focus) = if dir.focus > index { - (dir.focus - index, true, false) - } else if dir.focus < index { - (index - dir.focus, false, true) - } else { - (0, false, false) - }; + let (relative_index, is_before_focus, is_after_focus) = + match dir.focus.cmp(&index) { + Ordering::Greater => (dir.focus - index, true, false), + Ordering::Less => (index - dir.focus, false, true), + Ordering::Equal => (0, false, false), + }; - let meta = NodeUIMetadata::new( + let meta = NodeUiMetadata::new( &node, index, relative_index, @@ -174,7 +174,7 @@ fn draw_table(f: &mut Frame, rect: Rect, app: &app::App, hb: &Han .render(app::TEMPLATE_TABLE_ROW, &meta) .ok() .unwrap_or_else(|| app::UNSUPPORTED_STR.into()) - .split("\t") + .split('\t') .map(|x| Cell::from(x.to_string())) .collect::>(); @@ -207,7 +207,7 @@ fn draw_table(f: &mut Frame, rect: Rect, app: &app::App, hb: &Han }) .unwrap_or_default(); - let table_constraints: Vec = config + let table_constraints: Vec = config .general .table .col_widths @@ -293,13 +293,16 @@ fn draw_help_menu(f: &mut Frame, rect: Rect, app: &app::App, _: & .borders(Borders::ALL) .title(format!(" Help [{}] ", &app.mode().name)), ) - .widths(&[TUIConstraint::Percentage(30), TUIConstraint::Percentage(70)]); + .widths(&[TuiConstraint::Percentage(30), TuiConstraint::Percentage(70)]); f.render_widget(help_menu, rect); } fn draw_input_buffer(f: &mut Frame, rect: Rect, app: &app::App, _: &Handlebars) { - let input_buf = Paragraph::new(format!("> {}", app.input_buffer().unwrap_or("".into()))) - .block(Block::default().borders(Borders::ALL).title(" Input ")); + let input_buf = Paragraph::new(format!( + "> {}", + app.input_buffer().unwrap_or_else(|| "".into()) + )) + .block(Block::default().borders(Borders::ALL).title(" Input ")); f.render_widget(input_buf, rect); } @@ -333,15 +336,15 @@ pub fn draw(f: &mut Frame, app: &app::App, hb: &Handlebars) { let chunks = Layout::default() .direction(Direction::Horizontal) - .constraints([TUIConstraint::Percentage(70), TUIConstraint::Percentage(30)].as_ref()) + .constraints([TuiConstraint::Percentage(70), TuiConstraint::Percentage(30)].as_ref()) .split(rect); let left_chunks = Layout::default() .direction(Direction::Vertical) .constraints( [ - TUIConstraint::Length(rect.height - 3), - TUIConstraint::Length(3), + TuiConstraint::Length(rect.height - 3), + TuiConstraint::Length(3), ] .as_ref(), ) @@ -357,7 +360,7 @@ pub fn draw(f: &mut Frame, app: &app::App, hb: &Handlebars) { let right_chunks = Layout::default() .direction(Direction::Vertical) - .constraints([TUIConstraint::Percentage(50), TUIConstraint::Percentage(50)].as_ref()) + .constraints([TuiConstraint::Percentage(50), TuiConstraint::Percentage(50)].as_ref()) .split(chunks[1]); draw_selection(f, right_chunks[0], app, hb);