CWDperDrive

This commit is contained in:
pegasus.cadence@gmail.com
2024-11-18 20:42:45 -08:00
parent 9ec152d06d
commit 2a216ea363
7 changed files with 73 additions and 38 deletions

3
Cargo.lock generated
View File

@ -3352,7 +3352,10 @@ version = "0.100.1"
dependencies = [ dependencies = [
"dirs", "dirs",
"omnipath", "omnipath",
"once_cell",
"pwd", "pwd",
"winapi",
"windows-sys 0.48.0",
] ]
[[package]] [[package]]

View File

@ -321,3 +321,6 @@ bench = false
[[bench]] [[bench]]
name = "benchmarks" name = "benchmarks"
harness = false harness = false
[profile.dev]
incremental = true

View File

@ -114,7 +114,19 @@ impl Command for Cd {
//FIXME: this only changes the current scope, but instead this environment variable //FIXME: this only changes the current scope, but instead this environment variable
//should probably be a block that loads the information from the state in the overlay //should probably be a block that loads the information from the state in the overlay
PermissionResult::PermissionOk => { PermissionResult::PermissionOk => {
let path_for_scd = path.clone();
stack.set_cwd(path)?; stack.set_cwd(path)?;
use nu_path::state_driver_pwd::get_drive_pwd_map;
get_drive_pwd_map().lock().unwrap().set_pwd(path_for_scd.as_path())
.map_err(|_e| {
Err(ShellError::GenericError{
error: "Set PWD for drive failed".into(),
msg: "System error".into(),
span: None,
help: None,
inner: vec![],
})
})?;
Ok(PipelineData::empty()) Ok(PipelineData::empty())
} }
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError { PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError {

View File

@ -16,6 +16,9 @@ dirs = { workspace = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
omnipath = { workspace = true } omnipath = { workspace = true }
once_cell = "1.20.1"
winapi = { version = "0.3.9", features = ["fileapi"] }
windows-sys = { workspace = true, features = ["Win32_System_Environment"] }
[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies] [target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies]
pwd = { workspace = true } pwd = { workspace = true }

View File

@ -8,6 +8,7 @@ mod helpers;
mod path; mod path;
mod tilde; mod tilde;
mod trailing_slash; mod trailing_slash;
pub mod state_driver_pwd;
pub use components::components; pub use components::components;
pub use expansions::{canonicalize_with, expand_path_with, expand_to_real_path, locate_in_dirs}; pub use expansions::{canonicalize_with, expand_path_with, expand_to_real_path, locate_in_dirs};

View File

@ -1,11 +1,10 @@
use std::path::Path; use std::path::{ Path, PathBuf };
pub struct DrivePwdMap { pub struct DrivePwdMap {
map: [Option<String>; 26], // Fixed-size array for A-Z map: [Option<String>; 26], // Fixed-size array for A-Z
} }
impl DrivePwdMap { impl DrivePwdMap {
/// Create a new DrivePwdMap
pub fn new() -> Self { pub fn new() -> Self {
DrivePwdMap { DrivePwdMap {
map: Default::default(), // Initialize all to `None` map: Default::default(), // Initialize all to `None`
@ -16,7 +15,8 @@ impl DrivePwdMap {
pub fn set_pwd(&mut self, path: &Path) -> Result<(), String> { pub fn set_pwd(&mut self, path: &Path) -> Result<(), String> {
if let Some(drive_letter) = Self::extract_drive_letter(path) { if let Some(drive_letter) = Self::extract_drive_letter(path) {
if let Some(index) = Self::drive_to_index(drive_letter) { if let Some(index) = Self::drive_to_index(drive_letter) {
self.map[index] = Some(path.to_string_lossy().into_owned()); let normalized = Self::normalize_path(path);
self.map[index] = Some(normalized.to_string_lossy().into_owned());
Ok(()) Ok(())
} else { } else {
Err(format!("Invalid drive letter: {}", drive_letter)) Err(format!("Invalid drive letter: {}", drive_letter))
@ -27,7 +27,6 @@ impl DrivePwdMap {
} }
/// Get the current working directory for a drive letter /// Get the current working directory for a drive letter
/// If no PWD is set, return the root of the drive (e.g., `C:\`)
pub fn get_pwd(&self, drive: char) -> Option<String> { pub fn get_pwd(&self, drive: char) -> Option<String> {
Self::drive_to_index(drive).map(|index| { Self::drive_to_index(drive).map(|index| {
self.map[index] self.map[index]
@ -43,10 +42,10 @@ impl DrivePwdMap {
let is_absolute = path_str.contains(":\\") || path_str.starts_with("\\"); let is_absolute = path_str.contains(":\\") || path_str.starts_with("\\");
if is_absolute { if is_absolute {
// Already an absolute path // Already an absolute path
Some(PathBuf::from(path_str)) Some(PathBuf::from(Self::ensure_trailing_separator(path_str)))
} else if let Some(pwd) = self.get_pwd(drive_letter) { } else if let Some(pwd) = self.get_pwd(drive_letter) {
// Combine current PWD with the relative path // Combine current PWD with the relative path
let mut base = PathBuf::from(pwd); let mut base = PathBuf::from(Self::ensure_trailing_separator(&pwd));
base.push(path_str.split_at(2).1); // Skip the "C:" part of the relative path base.push(path_str.split_at(2).1); // Skip the "C:" part of the relative path
Some(base) Some(base)
} else { } else {
@ -67,17 +66,35 @@ impl DrivePwdMap {
} }
} }
/// Extract the drive letter from a path (e.g., `C:\Users` -> `C`) /// Extract the drive letter from a path (e.g., `C:test` -> `C`)
fn extract_drive_letter(path: &Path) -> Option<char> { fn extract_drive_letter(path: &Path) -> Option<char> {
path.to_str() path.to_str()
.and_then(|s| s.chars().next()) .and_then(|s| s.chars().next())
.filter(|c| c.is_ascii_alphabetic()) .filter(|c| c.is_ascii_alphabetic())
} }
/// Normalize a path by removing any trailing `\` or `/`
fn normalize_path(path: &Path) -> PathBuf {
let mut normalized = path.to_path_buf();
while normalized.to_string_lossy().ends_with(&['\\', '/'][..]) {
normalized.pop();
}
normalized
}
/// Ensure a path has a trailing `\`
fn ensure_trailing_separator(path: &str) -> String {
if !path.ends_with('\\') && !path.ends_with('/') {
format!("{}\\", path)
} else {
path.to_string()
}
}
} }
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Mutex; use std::sync::Mutex;
use crate::ShellError; //use nu_protocol::errors::shell_error::ShellError;
/// Global singleton instance of DrivePwdMap /// Global singleton instance of DrivePwdMap
static DRIVE_PWD_MAP: Lazy<Mutex<DrivePwdMap>> = Lazy::new(|| Mutex::new(DrivePwdMap::new())); static DRIVE_PWD_MAP: Lazy<Mutex<DrivePwdMap>> = Lazy::new(|| Mutex::new(DrivePwdMap::new()));
@ -192,12 +209,11 @@ mod tests {
} }
} }
mod current_directory_specific { pub mod current_directory_specific {
use crate::ShellError;
use std::path::Path; use std::path::Path;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn need_expand_current_directory(path: &Path) -> bool { pub fn need_expand_current_directory_per_drive(path: &Path) -> bool {
if let Some(path_str) = path.to_str() { if let Some(path_str) = path.to_str() {
let chars: Vec<char> = path_str.chars().collect(); let chars: Vec<char> = path_str.chars().collect();
if chars.len() >= 2 { if chars.len() >= 2 {
@ -208,12 +224,12 @@ mod current_directory_specific {
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
fn need_expand_current_directory(path: &Path) -> bool { pub fn need_expand_current_directory(path: &Path) -> bool {
false false
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn get_windows_absolute_path(path: &Path) -> Option<String> { pub fn get_windows_absolute_path(path: &Path) -> Option<String> {
use std::ffi::OsString; use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt; use std::os::windows::ffi::OsStringExt;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
@ -247,12 +263,16 @@ mod current_directory_specific {
None None
} }
pub enum DrivePwdError {
InvalidPath,
SystemError,
}
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
fn get_windows_absolute_path(path: &Path) -> Option<String> { fn get_windows_absolute_path(path: &Path) -> Option<String> {
None None
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn set_current_directory_windows(path: &Path) -> Result<(), ShellError> { pub fn set_current_directory_windows(path: &Path) -> Result<(), DrivePwdError> {
use std::ffi::OsString; use std::ffi::OsString;
use std::os::windows::ffi::OsStrExt; use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::System::Environment::SetCurrentDirectoryW; use windows_sys::Win32::System::Environment::SetCurrentDirectoryW;
@ -266,27 +286,16 @@ mod current_directory_specific {
return Ok(()) return Ok(())
} else { } else {
return return
Err(ShellError::GenericError { Err(DrivePwdError::SystemError)
error: "Failed to set current directory".into(),
msg: "SetCurrentDirectoryW() failed".into(),
span: None,
help: None,
inner: vec![],
})
}; };
} }
} }
Err(ShellError::GenericError { Err(DrivePwdError::InvalidPath)
error: "Failed to set current directory".into(),
msg: "Invalid path".into(),
span: None,
help: None,
inner: vec![],
})
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn set_current_directory_windows(_path: &Path) -> Result<(), ShellError>{ pub fn set_current_directory_windows(_path: &Path) -> Result<(), Error>{
Ok(()) Ok(())
} }
} }

View File

@ -21,17 +21,21 @@ fn expand_tilde_with_home(path: impl AsRef<Path>, home: Option<PathBuf>) -> Path
let path = path.as_ref(); let path = path.as_ref();
if !path.starts_with("~") { if !path.starts_with("~") {
use nu_protocol::engine::state_driver_pwd:: { use crate::state_driver_pwd:: {
need_expand_current_directory, current_directory_specific::need_expand_current_directory_per_drive,
get_windows_absolute_path, //current_directory_specific::get_windows_absolute_path,
get_drive_pwd_map
}; };
if need_expand_current_directory(path) { if need_expand_current_directory_per_drive(path) {
if let Some(current_dir) = get_windows_absolute_path(path) { if let Some(expanded_dir) = get_drive_pwd_map().lock().unwrap().expand_path(path) {
//println!("Absolute path for {} is: {}", path.display(), current_dir); return PathBuf::from(&expanded_dir)
return PathBuf::from(&current_dir)
} else {
println!("Failed to get absolute path for {}", path.display());
} }
// if let Some(current_dir) = get_windows_absolute_path(path) {
// //println!("Absolute path for {} is: {}", path.display(), current_dir);
// return PathBuf::from(&current_dir)
// } else {
// println!("Failed to get absolute path for {}", path.display());
// }
} }
let string = path.to_string_lossy(); let string = path.to_string_lossy();
let mut path_as_string = string.as_ref().bytes(); let mut path_as_string = string.as_ref().bytes();