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

Improve config defaults #47

Merged
merged 4 commits into from
Apr 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
490 changes: 488 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "xplr"
version = "0.3.13" # Update default_config.rs and default.nix
version = "0.4.0" # Update default_config.rs and default.nix
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer, stealing ideas from nnn and fzf"
Expand All @@ -27,7 +27,13 @@ lazy_static = "1.4.0"

[dev-dependencies]
criterion = "0.3"
cucumber_rust = { git = "https://github.com/bbqsrc/cucumber-rust", branch = "main" }
tokio = { version = "1.4.0", features = ["full"] }

[[bench]]
name = "navigation"
harness = false

[[test]]
name = "cucumber"
harness = false # Allows Cucumber to print output instead of libtest
4 changes: 2 additions & 2 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ with import <nixpkgs> {};

rustPlatform.buildRustPackage rec {
name = "xplr";
version = "0.3.13";
version = "0.4.0";
src = fetchTarball
("https://github.com/sayanarijit/xplr/archive/refs/tags/v0.3.13.tar.gz");
("https://github.com/sayanarijit/xplr/archive/refs/tags/v0.4.0.tar.gz");
buildInputs = [ cargo ];
checkPhase = "";
cargoSha256 = "0000000000000000000000000000000000000000000000000000";
Expand Down
16 changes: 16 additions & 0 deletions features/default_key_bindings.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Feature: Default key bindings

Scenario: xplr exits with debug info when we press '#'
Given xplr is running
When I press "#"
And xplr processes the tasks
Then xplr performs "PrintAppStateAndQuit"
And xplr quits or waits for the next event


Scenario: xplr prints result and quits when I gress 'enter'
Given xplr is running
When I press "enter"
And xplr processes the tasks
Then xplr performs "PrintResultAndQuit"
And xplr quits or waits for the next event
162 changes: 85 additions & 77 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::config::Config;
use crate::config::Mode;
use crate::default_config::DEFAULT_CONFIG;
use crate::input::Key;
use anyhow::{bail, Result};
use chrono::{DateTime, Utc};
Expand Down Expand Up @@ -185,6 +184,7 @@ pub enum InternalMsg {
}

#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum NodeFilter {
RelativePathIs,
RelativePathIsNot,
Expand Down Expand Up @@ -376,6 +376,7 @@ impl NodeFilter {
}

#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NodeFilterApplicable {
filter: NodeFilter,
input: String,
Expand All @@ -398,6 +399,7 @@ impl NodeFilterApplicable {
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NodeFilterFromInput {
filter: NodeFilter,
#[serde(default)]
Expand Down Expand Up @@ -621,6 +623,7 @@ pub enum MsgIn {
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Command {
pub command: String,

Expand Down Expand Up @@ -695,37 +698,6 @@ pub enum HelpMenuLine {
Paragraph(String),
}

/// Major version should be the same.
/// Config minor version should be lower.
/// Patch/fix version can be anything.
pub fn is_compatible(configv: &str, appv: &str) -> bool {
let mut configv = configv
.strip_prefix('v')
.unwrap_or_default()
.split('.')
.map(|c| c.parse::<u64>().unwrap());

let mut appv = appv
.strip_prefix('v')
.unwrap_or_default()
.split('.')
.map(|c| c.parse::<u64>().unwrap());

let mut major_configv = configv.next().unwrap_or_default();
let mut minor_configv = configv.next().unwrap_or_default();
let mut major_appv = appv.next().unwrap_or_default();
let mut minor_appv = appv.next().unwrap_or_default();

if major_configv == 0 && major_appv == 0 {
major_configv = minor_configv;
minor_configv = configv.next().unwrap_or_default();
major_appv = minor_appv;
minor_appv = appv.next().unwrap_or_default();
};

major_configv == major_appv && minor_configv <= minor_appv
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct App {
version: String,
Expand All @@ -750,29 +722,31 @@ impl App {
.join("xplr");

let config_file = config_dir.join("config.yml");
let default_config = Config::default();
let default_config_version = default_config.version.clone();

let config: Config = if config_file.exists() {
serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&config_file)?))?
let c: Config =
serde_yaml::from_reader(io::BufReader::new(&fs::File::open(&config_file)?))?;
c.extended()
} else {
Config::default()
default_config
};

if config.version != DEFAULT_CONFIG.version
&& !is_compatible(&config.version, &DEFAULT_CONFIG.version)
{
if !config.is_compatible()? {
bail!(
"incompatible configuration version in {}
You config version is : {}
Required version is : {}
Visit {}",
config_file.to_string_lossy().to_string(),
config.version,
DEFAULT_CONFIG.version,
default_config_version,
UPGRADE_GUIDE_LINK,
)
};

let mode = match config.modes.get(&"default".to_string()) {
let mode = match config.modes.builtin.get(&"default".to_string()) {
Some(m) => m.clone(),
None => {
bail!("'default' mode is missing")
Expand All @@ -789,17 +763,17 @@ impl App {
.to_string();

let mut explorer_config = ExplorerConfig::default();
if !config.general.show_hidden {
if !config.general.show_hidden.unwrap_or_default() {
explorer_config.filters.insert(NodeFilterApplicable::new(
NodeFilter::RelativePathDoesNotStartWith,
".".into(),
Default::default(),
));
}

Ok(Self {
version: DEFAULT_CONFIG.version.clone(),
config,
let mut app = Self {
version: Config::default().version,
config: config.clone(),
pwd: pwd.to_string_lossy().to_string(),
directory_buffers: Default::default(),
selection: Default::default(),
Expand All @@ -811,7 +785,20 @@ impl App {
pipe: Pipe::from_session_path(&session_path)?,
explorer_config,
logs: Default::default(),
})
};

if let Some(notif) = config.upgrade_notification()? {
let notif = format!(
"{}. To stop seeing this log, update your config version from {} to {}.",
&notif, &config.version, &app.version
);
app = app.enqueue(Task::new(
MsgIn::External(ExternalMsg::LogInfo(notif)),
None,
));
}

Ok(app)
}

pub fn focused_node(&self) -> Option<&Node> {
Expand Down Expand Up @@ -902,10 +889,12 @@ impl App {

fn handle_key(mut self, key: Key) -> Result<Self> {
let kb = self.mode.key_bindings.clone();
let key_str = key.to_string();
let default = kb.default.clone();
let msgs = kb
.on_key
.get(&key.to_string())
.get(&key_str)
.or_else(|| kb.remaps.get(&key_str).and_then(|k| kb.on_key.get(k)))
.map(|a| Some(a.messages.clone()))
.unwrap_or_else(|| {
if key.is_alphabet() {
Expand Down Expand Up @@ -1244,7 +1233,7 @@ impl App {
fn reset_node_filters(mut self) -> Result<Self> {
self.explorer_config.filters.clear();

if !self.config.general.show_hidden {
if !self.config.general.show_hidden.unwrap_or_default() {
self.add_node_filter(NodeFilterApplicable::new(
NodeFilter::RelativePathDoesNotStartWith,
".".into(),
Expand Down Expand Up @@ -1377,14 +1366,6 @@ impl App {
.unwrap_or_default()
}

pub fn logs_str(&self) -> String {
self.logs()
.iter()
.map(|l| format!("{}\n", l))
.collect::<Vec<String>>()
.join("")
}

pub fn selection_str(&self) -> String {
self.selection
.iter()
Expand Down Expand Up @@ -1412,29 +1393,56 @@ impl App {
}

pub fn global_help_menu_str(&self) -> String {
self.config()
.modes
.iter()
.map(|(name, mode)| {
let help = mode
.help_menu()
.iter()
.map(|l| match l {
HelpMenuLine::Paragraph(p) => format!("\t{}\n", p),
HelpMenuLine::KeyMap(k, h) => {
format!(" {:15} | {}\n", k, h)
}
})
.collect::<Vec<String>>()
.join("");

format!(
"### {}\n\n key | action\n --------------- | ------\n{}\n",
name, help
)
})
.collect::<Vec<String>>()
.join("\n")
let builtin = self.config().modes.builtin.clone();
let custom = self.config().modes.custom.clone();

[
(builtin.default.name.clone(), builtin.default),
(builtin.number.name.clone(), builtin.number),
(builtin.go_to.name.clone(), builtin.go_to),
(builtin.search.name.clone(), builtin.search),
(builtin.selection_ops.name.clone(), builtin.selection_ops),
(builtin.action.name.clone(), builtin.action),
(builtin.create.name.clone(), builtin.create),
(builtin.create_file.name.clone(), builtin.create_file),
(
builtin.create_directory.name.clone(),
builtin.create_directory,
),
(builtin.rename.name.clone(), builtin.rename),
(builtin.delete.name.clone(), builtin.delete),
]
.iter()
.chain(custom.into_iter().collect::<Vec<(String, Mode)>>().iter())
.map(|(name, mode)| {
let help = mode
.help_menu()
.iter()
.map(|l| match l {
HelpMenuLine::Paragraph(p) => format!("\t{}\n", p),
HelpMenuLine::KeyMap(k, h) => {
let remaps = self
.mode()
.key_bindings
.remaps
.iter()
.filter(|(_, t)| t == &k)
.map(|(f, _)| f.clone())
.collect::<Vec<String>>()
.join(", ");
format!(" {:15} | {:25} | {}\n", k, remaps, h)
}
})
.collect::<Vec<String>>()
.join("");

format!(
"### {}\n\n key | remaps | action\n --------------- | ------------------------- |------\n{}\n",
name, help
)
})
.collect::<Vec<String>>()
.join("\n")
}

/// Get a reference to the app's version.
Expand Down
Loading