Add CI/CD gh actions

Mostly stolen from https://github.com/Rigellute/spotify-tui.
pull/4/head
Arijit Basu 3 years ago committed by Arijit Basu
parent 8dae2fef4d
commit dba8631911

@ -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

@ -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

@ -21,6 +21,11 @@ A hackable, minimal, fast TUI file explorer, stealing ideas from <a href="https:
<br>
[![Continuous Integration](https://github.com/sayanarijit/xplr/actions/workflows/ci.yml/badge.svg)](https://github.com/sayanarijit/xplr/actions/workflows/ci.yml)
[![Continuous Delivery](https://github.com/sayanarijit/xplr/actions/workflows/cd.yml/badge.svg)](https://github.com/sayanarijit/xplr/actions/workflows/cd.yml)
<br>
Though [xplr](https://github.com/sayanarijit/xplr) strives to be fast and minimalist, it's speciality is it's hackability.
Once you read the [documentation](https://github.com/sayanarijit/xplr/wiki), you should be able to configure the key bindings,

@ -8,7 +8,7 @@ fn criterion_benchmark(c: &mut Criterion) {
fs::File::create(format!("/tmp/xplr_bench/{}", i)).unwrap();
});
let app = app::App::create()
let app = app::App::create("/tmp/xplr_bench".into())
.expect("failed to create app")
.enqueue(app::Task::new(
1,

@ -3,13 +3,11 @@ use crate::config::Mode;
use crate::input::Key;
use anyhow::{bail, Result};
use chrono::{DateTime, Utc};
use mime_guess;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::env;
use std::fs;
use std::io;
use std::path::PathBuf;
@ -29,7 +27,7 @@ pub struct Pipe {
}
impl Pipe {
fn from_session_path(path: &String) -> 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<Self> {
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<Self> {
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<Self> {
fn change_directory(mut self, dir: &str) -> Result<Self> {
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<Self> {
fn buffer_input(mut self, input: &str) -> Result<Self> {
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<Self> {
fn focus_by_file_name(mut self, name: &str) -> Result<Self> {
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<Self> {
fn focus_path(self, path: &str) -> Result<Self> {
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<Self> {
fn switch_mode(mut self, mode: &str) -> Result<Self> {
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<Self> {
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> {
self.msg_out.push_back(MsgOut::Debug(path.to_owned()));
fn debug(mut self, path: String) -> Result<Self> {
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()
}
}

@ -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<UIElement>,
pub cols: Vec<UiElement>,
#[serde(default)]
pub style: Style,
#[serde(default)]
@ -116,14 +115,14 @@ impl Default for Constraint {
}
}
impl Into<TUIConstraint> for Constraint {
fn into(self) -> TUIConstraint {
impl Into<TuiConstraint> 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()
})

@ -9,33 +9,31 @@ pub fn keep_reading(tx_msg_in: Sender<Task>, rx_event_reader: Receiver<bool>) {
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();
}
}
}

@ -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<char> {

@ -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;

@ -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()

@ -1,5 +1,4 @@
use crate::app::{ExternalMsg, MsgIn, Task};
use serde_yaml;
use std::fs;
use std::sync::mpsc::Sender;
use std::thread;

@ -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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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::<Vec<Cell>>();
@ -207,7 +207,7 @@ fn draw_table<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, hb: &Han
})
.unwrap_or_default();
let table_constraints: Vec<TUIConstraint> = config
let table_constraints: Vec<TuiConstraint> = config
.general
.table
.col_widths
@ -293,13 +293,16 @@ fn draw_help_menu<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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);

Loading…
Cancel
Save