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

Fix multiple paths: define the platform-specific join separator #840

Merged
merged 10 commits into from
Dec 10, 2023
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
**/*.rs.bk
navi.log
11 changes: 9 additions & 2 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ dns_common_derive = { version = "0.2.1" }
dns_common = { version = "0.2.1", default-features = false, features = ["yaml", "json"] }
unicode-width = "0.1.10"

[target.'cfg(windows)'.dependencies]
dunce = "1"

[lib]
name = "navi"
path = "src/lib.rs"
Expand Down
6 changes: 6 additions & 0 deletions docs/config_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@ Run the following command to generate a config file with the default parameters:
```sh
navi info config-example > "$(navi info config-path)"
```

### Logging

The log file will be created under the same directory where the config locates.

And you can use the `RUST_LOG` env to set the log level, e.g. `RUST_LOG=debug navi`.
11 changes: 7 additions & 4 deletions docs/config_file_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ finder:
# overrides_var: --tac # equivalent to the --fzf-overrides-var option

# cheats:
# paths:
# - /path/to/some/dir
# - /path/to/another/dir
# paths:
# - /path/to/some/dir # on unix-like os
# - F:\\path\\to\\dir # on Windows
# path: /path/to/some/dir # (DEPRECATED) equivalent to the --path option

# search:
# tags: git,!checkout # equivalent to the --tag-rules option

shell:
command: bash # shell used for shell out. possible values: bash, zsh, dash, ...
# Shell used for shell out. Possible values: bash, zsh, dash, ...
# For Windows, use `cmd.exe` instead.
command: bash

# finder_command: bash # similar, but for fzf's internals
36 changes: 33 additions & 3 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extern crate navi;

use std::fmt::Debug;
use dns_common::prelude::*;
use thiserror::Error;

#[derive(Error, Debug)]
Expand All @@ -24,6 +24,36 @@ impl FileAnIssue {
}
}

fn main() -> Result<(), anyhow::Error> {
navi::handle().map_err(|e| FileAnIssue::new(e).into())
fn main() -> anyhow::Result<()> {
if let Err(err) = init_logger() {
// may need redir stderr to a file to show this log initialization error
eprintln!("failed to initialize logging: {err:?}");
}
navi::handle().map_err(|e| {
error!("{e:?}");
FileAnIssue::new(e).into()
})
}

fn init_logger() -> anyhow::Result<()> {
const FILE_NAME: &str = "navi.log";
let mut file = navi::default_config_pathbuf()?;
file.set_file_name(FILE_NAME);

// If config path doesn't exist, navi won't log.
if file.parent().map(|p| !p.exists()).unwrap_or(true) {
return Ok(());
}

let writer = std::fs::File::create(&file).with_context(|| format!("{file:?} is not created"))?;
tracing::subscriber::set_global_default(
tracing_subscriber::fmt()
.with_ansi(false)
.with_writer(writer)
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.finish(),
)?;
debug!("tracing initialized");

Ok(())
}
14 changes: 8 additions & 6 deletions src/commands/core/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ fn prompt_finder(
}
}

let child = shell::out()
.stdout(Stdio::piped())
.arg(suggestion_command)
let mut cmd = shell::out();
cmd.stdout(Stdio::piped()).arg(suggestion_command);
debug!(cmd = ?cmd);
let child = cmd
.spawn()
.map_err(|e| ShellSpawnError::new(suggestion_command, e))?;

Expand Down Expand Up @@ -236,9 +237,10 @@ pub fn act(
clipboard::copy(interpolated_snippet)?;
}
_ => {
shell::out()
.arg(&interpolated_snippet[..])
.spawn()
let mut cmd = shell::out();
cmd.arg(&interpolated_snippet[..]);
debug!(cmd = ?cmd);
cmd.spawn()
.map_err(|e| ShellSpawnError::new(&interpolated_snippet[..], e))?
.wait()
.context("bash was not running")?;
Expand Down
6 changes: 5 additions & 1 deletion src/commands/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::welcome;
pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
let config = &CONFIG;
let opts = FinderOpts::snippet_default();
debug!("opts = {opts:#?}");
// let fetcher = config.fetcher();

let (raw_selection, (variables, files)) = config
Expand All @@ -32,6 +33,7 @@ pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
})
.context("Failed getting selection and variables from finder")?;

debug!(raw_selection = ?raw_selection);
let extractions = deser::terminal::read(&raw_selection, config.best_match());

if extractions.is_err() {
Expand All @@ -44,7 +46,9 @@ pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
}

pub fn get_fetcher() -> Result<Box<dyn Fetcher>> {
match CONFIG.source() {
let source = CONFIG.source();
debug!(source = ?source);
match source {
Source::Cheats(query) => {
let lines = cheatsh::call(&query)?;
let fetcher = Box::new(StaticFetcher::new(lines));
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::prelude::*;
pub fn handle() -> Result<()> {
use crate::config::Command::*;

debug!("CONFIG = {:#?}", &*CONFIG);
match CONFIG.cmd() {
None => commands::core::main(),

Expand Down
9 changes: 4 additions & 5 deletions src/commands/preview/var_stdin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ impl Runnable for Input {
if !extra.is_empty() {
print!("");

shell::out()
.arg(extra)
.spawn()
.map_err(|e| ShellSpawnError::new(extra, e))?
.wait()?;
let mut cmd = shell::out();
cmd.arg(extra);
debug!(?cmd);
cmd.spawn().map_err(|e| ShellSpawnError::new(extra, e))?.wait()?;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/common/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ fn follow_symlink(pathbuf: PathBuf) -> Result<PathBuf> {

fn exe_pathbuf() -> Result<PathBuf> {
let pathbuf = std::env::current_exe().context("Unable to acquire executable's path")?;

#[cfg(target_family = "windows")]
let pathbuf = dunce::canonicalize(pathbuf)?;

debug!(current_exe = ?pathbuf);
follow_symlink(pathbuf)
}

Expand Down
1 change: 1 addition & 0 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub enum Command {
Info(commands::info::Input),
}

#[derive(Debug)]
pub enum Source {
Filesystem(Option<String>),
Tldr(String),
Expand Down
1 change: 1 addition & 0 deletions src/config/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::env_var;
use crate::finder::FinderChoice;
use crate::prelude::*;

#[derive(Debug)]
pub struct EnvConfig {
pub config_yaml: Option<String>,
pub config_path: Option<String>,
Expand Down
3 changes: 2 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use yaml::YamlConfig;
lazy_static! {
pub static ref CONFIG: Config = Config::new();
}
#[derive(Debug)]
pub struct Config {
yaml: YamlConfig,
clap: ClapConfig,
Expand Down Expand Up @@ -69,7 +70,7 @@ impl Config {
if p.is_empty() {
None
} else {
Some(p.join(":"))
Some(p.join(crate::filesystem::JOIN_SEPARATOR))
}
})
.or_else(|| self.yaml.cheats.path.clone())
Expand Down
16 changes: 8 additions & 8 deletions src/config/yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::prelude::*;
use crossterm::style::Color as TerminalColor;
use serde::de;

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
pub struct Color(#[serde(deserialize_with = "color_deserialize")] TerminalColor);

impl Color {
Expand All @@ -24,23 +24,23 @@ where
.map_err(|_| de::Error::custom(format!("Failed to deserialize color: {s}")))
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct ColorWidth {
pub color: Color,
pub width_percentage: u16,
pub min_width: u16,
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct Style {
pub tag: ColorWidth,
pub comment: ColorWidth,
pub snippet: ColorWidth,
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct Finder {
#[serde(deserialize_with = "finder_deserialize")]
Expand All @@ -58,27 +58,27 @@ where
.map_err(|_| de::Error::custom(format!("Failed to deserialize finder: {s}")))
}

#[derive(Deserialize, Default)]
#[derive(Deserialize, Default, Debug)]
#[serde(default)]
pub struct Cheats {
pub path: Option<String>,
pub paths: Vec<String>,
}

#[derive(Deserialize, Default)]
#[derive(Deserialize, Default, Debug)]
#[serde(default)]
pub struct Search {
pub tags: Option<String>,
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[serde(default)]
pub struct Shell {
pub command: String,
pub finder_command: Option<String>,
}

#[derive(Deserialize, Default)]
#[derive(Deserialize, Default, Debug)]
#[serde(default)]
pub struct YamlConfig {
pub style: Style,
Expand Down
23 changes: 21 additions & 2 deletions src/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ use std::path::MAIN_SEPARATOR;

use walkdir::WalkDir;

/// Multiple paths are joint by a platform-specific separator.
/// FIXME: it's actually incorrect to assume a path doesn't containing this separator
#[cfg(target_family = "windows")]
pub const JOIN_SEPARATOR: &str = ";";
#[cfg(not(target_family = "windows"))]
pub const JOIN_SEPARATOR: &str = ":";

pub fn all_cheat_files(path: &Path) -> Vec<String> {
WalkDir::new(path)
.follow_links(true)
Expand All @@ -23,7 +30,7 @@ pub fn all_cheat_files(path: &Path) -> Vec<String> {
}

fn paths_from_path_param(env_var: &str) -> impl Iterator<Item = &str> {
env_var.split(':').filter(|folder| folder != &"")
env_var.split(JOIN_SEPARATOR).filter(|folder| folder != &"")
}

fn compiled_default_path(path: Option<&str>) -> Option<PathBuf> {
Expand Down Expand Up @@ -125,6 +132,7 @@ fn interpolate_paths(paths: String) -> String {
newtext
}

#[derive(Debug)]
pub struct Fetcher {
path: Option<String>,
files: RefCell<Vec<String>>,
Expand Down Expand Up @@ -165,7 +173,9 @@ impl fetcher::Fetcher for Fetcher {
None => folder.to_string(),
};
let folder_pathbuf = PathBuf::from(interpolated_folder);
for file in all_cheat_files(&folder_pathbuf) {
let cheat_files = all_cheat_files(&folder_pathbuf);
debug!("read cheat files in `{folder_pathbuf:?}`: {cheat_files:#?}");
for file in cheat_files {
self.files.borrow_mut().push(file.clone());
let index = self.files.borrow().len() - 1;
let read_file_result = {
Expand All @@ -180,6 +190,7 @@ impl fetcher::Fetcher for Fetcher {
}
}

debug!("FilesystemFetcher = {self:#?}");
Ok(found_something)
}

Expand Down Expand Up @@ -280,4 +291,12 @@ mod tests {

assert_eq!(expected, cheats.to_string_lossy().to_string())
}

#[test]
#[cfg(target_family = "windows")]
fn multiple_paths() {
let p = r#"C:\Users\Administrator\AppData\Roaming\navi\config.yaml"#;
let paths = &[p; 2].join(JOIN_SEPARATOR);
assert_eq!(paths_from_path_param(paths).collect::<Vec<_>>(), [p; 2]);
}
}
8 changes: 5 additions & 3 deletions src/finder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,13 @@ impl FinderChoice {
});
}

let child = command
command
.env("SHELL", CONFIG.finder_shell())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn();
.stdout(Stdio::piped());
debug!(cmd = ?command);

let child = command.spawn();

let mut child = match child {
Ok(x) => x,
Expand Down
Loading