diff --git a/Cargo.lock b/Cargo.lock index cf02dbe5c2..e271ad63ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2605,6 +2605,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +[[package]] +name = "is_executable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "isahc" version = "0.9.14" @@ -3498,6 +3507,7 @@ version = "0.32.1" dependencies = [ "dirs-next", "indexmap", + "is_executable", "nu-data", "nu-errors", "nu-parser", diff --git a/Cargo.toml b/Cargo.toml index f611f8882c..864a4297d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"] term-support = ["nu-cli/term", "nu-command/term"] uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"] which-support = ["nu-cli/which", "nu-command/which", "nu-engine/which"] +executable-support = ["nu-completion/is_executable"] default = [ "nu-cli/shadow-rs", @@ -85,6 +86,7 @@ default = [ "post", "fetch", "zip-support", + "executable-support", ] stable = ["default"] diff --git a/crates/nu-completion/Cargo.toml b/crates/nu-completion/Cargo.toml index 6f218b8bc5..8e8a312cf5 100644 --- a/crates/nu-completion/Cargo.toml +++ b/crates/nu-completion/Cargo.toml @@ -20,3 +20,4 @@ nu-test-support = { version = "0.32.1", path = "../nu-test-support" } dirs-next = "2.0.0" indexmap = { version = "1.6.1", features = ["serde-1"] } +is_executable = { version = "1.0.1", optional = true } diff --git a/crates/nu-completion/src/command.rs b/crates/nu-completion/src/command.rs index da67ad935d..452664e88b 100644 --- a/crates/nu-completion/src/command.rs +++ b/crates/nu-completion/src/command.rs @@ -1,12 +1,12 @@ -use nu_test_support::NATIVE_PATH_ENV_VAR; - -use std::iter::FromIterator; -use std::path::Path; - -use indexmap::set::IndexSet; - use super::matchers::Matcher; use crate::{Completer, CompletionContext, Suggestion}; +use indexmap::set::IndexSet; +#[cfg(feature = "is_executable")] +#[allow(unused)] +use is_executable::IsExecutable; +use nu_test_support::NATIVE_PATH_ENV_VAR; +use std::iter::FromIterator; +use std::path::Path; pub struct CommandCompleter; @@ -56,45 +56,12 @@ where } } -// TODO create a struct for "is executable" and store this information in it so we don't recompute -// on every dir entry - -#[cfg(windows)] -fn pathext() -> Option> { - std::env::var_os("PATHEXT").map(|v| { - v.to_string_lossy() - .split(';') - // Filter out empty tokens and ';' at the end - .filter(|f| f.len() > 1) - // Cut off the leading '.' character - .map(|ext| ext[1..].to_string()) - .collect::>() - }) -} - #[cfg(windows)] fn is_executable(path: &Path) -> bool { - if let Ok(metadata) = path.metadata() { - let file_type = metadata.file_type(); - - // If the entry isn't a file, it cannot be executable - if !(file_type.is_file() || file_type.is_symlink()) { - return false; - } - - if let Some(extension) = path.extension() { - if let Some(exts) = pathext() { - exts.iter() - .any(|ext| extension.to_string_lossy().eq_ignore_ascii_case(ext)) - } else { - false - } - } else { - false - } - } else { - false - } + // This call to a crate essentially checks the PATHEXT on Windows and does some + // low level WinAPI calls to determine if the file is executable. It seems quite + // a bit faster than calling path.metadata(). + path.is_executable() } #[cfg(target_arch = "wasm32")]