forked from extern/nushell
Use only $nu.env.PWD for getting the current directory (#587)
* Use only $nu.env.PWD for getting current directory Because setting and reading to/from std::env changes the global state shich is problematic if we call `cd` from multiple threads (e.g., in a `par-each` block). With this change, when engine-q starts, it will either inherit existing PWD env var, or create a new one from `std::env::current_dir()`. Otherwise, everything that needs the current directory will get it from `$nu.env.PWD`. Each spawned external command will get its current directory per-process which should be thread-safe. One thing left to do is to patch nu-path for this as well since it uses `std::env::current_dir()` in its expansions. * Rename nu-path functions *_with is not *_relative which should be more descriptive and frees "with" for use in a followup commit. * Clone stack every each iter; Fix some commands Cloning the stack each iteration of `each` makes sure we're not reusing PWD between iterations. Some fixes in commands to make them use the new PWD. * Post-rebase cleanup, fmt, clippy * Change back _relative to _with in nu-path funcs Didn't use the idea I had for the new "_with". * Remove leftover current_dir from rebase * Add cwd sync at merge_delta() This makes sure the parser and completer always have up-to-date cwd. * Always pass absolute path to glob in ls * Do not allow PWD a relative path; Allow recovery Makes it possible to recover PWD by proceeding with the REPL cycle. * Clone stack in each also for byte/string stream * (WIP) Start moving env variables to engine state * (WIP) Move env vars to engine state (ugly) Quick and dirty code. * (WIP) Remove unused mut and args; Fmt * (WIP) Fix dataframe tests * (WIP) Fix missing args after rebase * (WIP) Clone only env vars, not the whole stack * (WIP) Add env var clone to `for` loop as well * Minor edits * Refactor merge_delta() to include stack merging. Less error-prone than doing it manually. * Clone env for each `update` command iteration * Mark env var hidden only when found in eng. state * Fix clippt warnings * Add TODO about env var reading * Do not clone empty environment in loops * Remove extra cwd collection * Split current_dir() into str and path; Fix autocd * Make completions respect PWD env var
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
use nu_engine::env::current_dir_str;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -32,7 +33,10 @@ impl Command for Cd {
|
||||
|
||||
let (path, span) = match path_val {
|
||||
Some(v) => {
|
||||
let path = nu_path::expand_path(v.as_string()?);
|
||||
let path = nu_path::canonicalize_with(
|
||||
v.as_string()?,
|
||||
current_dir_str(engine_state, stack)?,
|
||||
)?;
|
||||
(path.to_string_lossy().to_string(), v.span()?)
|
||||
}
|
||||
None => {
|
||||
@ -40,7 +44,6 @@ impl Command for Cd {
|
||||
(path.to_string_lossy().to_string(), call.head)
|
||||
}
|
||||
};
|
||||
let _ = std::env::set_current_dir(&path);
|
||||
|
||||
//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
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::env::current_dir;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::util::get_interactive_confirmation;
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::ast::Call;
|
||||
@ -49,7 +49,7 @@ impl Command for Cp {
|
||||
let interactive = call.has_flag("interactive");
|
||||
let force = call.has_flag("force");
|
||||
|
||||
let path = current_dir()?;
|
||||
let path = current_dir(engine_state, stack)?;
|
||||
let source = path.join(source.as_str());
|
||||
let destination = path.join(destination.as_str());
|
||||
|
||||
@ -135,7 +135,7 @@ impl Command for Cp {
|
||||
|
||||
for entry in sources.into_iter().flatten() {
|
||||
let mut sources = FileStructure::new();
|
||||
sources.walk_decorate(&entry)?;
|
||||
sources.walk_decorate(&entry, engine_state, stack)?;
|
||||
|
||||
if entry.is_file() {
|
||||
let sources = sources.paths_applying_with(|(source_file, _depth_level)| {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -10,6 +11,7 @@ use nu_protocol::{
|
||||
use std::io::ErrorKind;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ls;
|
||||
@ -63,10 +65,17 @@ impl Command for Ls {
|
||||
|
||||
let call_span = call.head;
|
||||
|
||||
let pattern = if let Some(mut result) =
|
||||
let (pattern, prefix) = if let Some(result) =
|
||||
call.opt::<Spanned<String>>(engine_state, stack, 0)?
|
||||
{
|
||||
let path = std::path::Path::new(&result.item);
|
||||
let path = PathBuf::from(&result.item);
|
||||
|
||||
let (mut path, prefix) = if path.is_relative() {
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
(cwd.join(path), Some(cwd))
|
||||
} else {
|
||||
(path, None)
|
||||
};
|
||||
|
||||
if path.is_dir() {
|
||||
if permission_denied(&path) {
|
||||
@ -92,16 +101,14 @@ impl Command for Ls {
|
||||
}
|
||||
|
||||
if path.is_dir() {
|
||||
if !result.item.ends_with(std::path::MAIN_SEPARATOR) {
|
||||
result.item.push(std::path::MAIN_SEPARATOR);
|
||||
}
|
||||
result.item.push('*');
|
||||
path = path.join("*");
|
||||
}
|
||||
}
|
||||
|
||||
result.item
|
||||
(path.to_string_lossy().to_string(), prefix)
|
||||
} else {
|
||||
"*".into()
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
(cwd.join("*").to_string_lossy().to_string(), Some(cwd))
|
||||
};
|
||||
|
||||
let glob = glob::glob(&pattern).map_err(|err| {
|
||||
@ -144,11 +151,34 @@ impl Command for Ls {
|
||||
return None;
|
||||
}
|
||||
|
||||
let entry =
|
||||
dir_entry_dict(&path, metadata.as_ref(), call_span, long, short_names);
|
||||
let display_name = if short_names {
|
||||
path.file_name().and_then(|s| s.to_str())
|
||||
} else if let Some(pre) = &prefix {
|
||||
match path.strip_prefix(pre) {
|
||||
Ok(stripped) => stripped.to_str(),
|
||||
Err(_) => path.to_str(),
|
||||
}
|
||||
} else {
|
||||
path.to_str()
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
ShellError::SpannedLabeledError(
|
||||
format!("Invalid file name: {:}", path.to_string_lossy()),
|
||||
"invalid file name".into(),
|
||||
call_span,
|
||||
)
|
||||
});
|
||||
|
||||
match entry {
|
||||
Ok(value) => Some(value),
|
||||
match display_name {
|
||||
Ok(name) => {
|
||||
let entry =
|
||||
dir_entry_dict(&path, name, metadata.as_ref(), call_span, long);
|
||||
|
||||
match entry {
|
||||
Ok(value) => Some(value),
|
||||
Err(err) => Some(Value::Error { error: err }),
|
||||
}
|
||||
}
|
||||
Err(err) => Some(Value::Error { error: err }),
|
||||
}
|
||||
}
|
||||
@ -213,7 +243,7 @@ fn path_contains_hidden_folder(path: &Path, folders: &[PathBuf]) -> bool {
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::FileTypeExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn get_file_type(md: &std::fs::Metadata) -> &str {
|
||||
let ft = md.file_type();
|
||||
@ -243,31 +273,18 @@ pub fn get_file_type(md: &std::fs::Metadata) -> &str {
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn dir_entry_dict(
|
||||
filename: &std::path::Path,
|
||||
filename: &std::path::Path, // absolute path
|
||||
display_name: &str, // gile name to be displayed
|
||||
metadata: Option<&std::fs::Metadata>,
|
||||
span: Span,
|
||||
long: bool,
|
||||
short_name: bool,
|
||||
) -> Result<Value, ShellError> {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let name = if short_name {
|
||||
filename.file_name().and_then(|s| s.to_str())
|
||||
} else {
|
||||
filename.to_str()
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
ShellError::SpannedLabeledError(
|
||||
format!("Invalid file name: {:}", filename.to_string_lossy()),
|
||||
"invalid file name".into(),
|
||||
span,
|
||||
)
|
||||
})?;
|
||||
|
||||
cols.push("name".into());
|
||||
vals.push(Value::String {
|
||||
val: name.to_string(),
|
||||
val: display_name.to_string(),
|
||||
span,
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::env::current_dir;
|
||||
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -39,7 +39,7 @@ impl Command for Mkdir {
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let path = current_dir()?;
|
||||
let path = current_dir(engine_state, stack)?;
|
||||
let mut directories = call
|
||||
.rest::<String>(engine_state, stack, 0)?
|
||||
.into_iter()
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::env::current_dir;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use super::util::get_interactive_confirmation;
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -50,7 +50,7 @@ impl Command for Mv {
|
||||
let interactive = call.has_flag("interactive");
|
||||
let force = call.has_flag("force");
|
||||
|
||||
let path: PathBuf = current_dir()?;
|
||||
let path = current_dir(engine_state, stack)?;
|
||||
let source = path.join(spanned_source.item.as_str());
|
||||
let destination = path.join(destination.as_str());
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
use std::env::current_dir;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::FileTypeExt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::util::get_interactive_confirmation;
|
||||
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -86,7 +86,7 @@ fn rm(
|
||||
));
|
||||
}
|
||||
|
||||
let current_path = current_dir()?;
|
||||
let current_path = current_dir(engine_state, stack)?;
|
||||
let mut paths = call
|
||||
.rest::<String>(engine_state, stack, 0)?
|
||||
.into_iter()
|
||||
|
@ -1,6 +1,8 @@
|
||||
use std::fs::OpenOptions;
|
||||
|
||||
use nu_engine::env::current_dir_str;
|
||||
use nu_engine::CallExt;
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape};
|
||||
@ -39,7 +41,8 @@ impl Command for Touch {
|
||||
let rest: Vec<String> = call.rest(engine_state, stack, 1)?;
|
||||
|
||||
for (index, item) in vec![target].into_iter().chain(rest).enumerate() {
|
||||
match OpenOptions::new().write(true).create(true).open(&item) {
|
||||
let path = expand_path_with(&item, current_dir_str(engine_state, stack)?);
|
||||
match OpenOptions::new().write(true).create(true).open(&path) {
|
||||
Ok(_) => continue,
|
||||
Err(err) => {
|
||||
return Err(ShellError::CreateNotPossible(
|
||||
|
@ -1,6 +1,8 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use nu_engine::env::current_dir_str;
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::engine::{EngineState, Stack};
|
||||
use nu_protocol::ShellError;
|
||||
|
||||
use dialoguer::Input;
|
||||
@ -39,16 +41,27 @@ impl FileStructure {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn walk_decorate(&mut self, start_path: &Path) -> Result<(), ShellError> {
|
||||
pub fn walk_decorate(
|
||||
&mut self,
|
||||
start_path: &Path,
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Result<(), ShellError> {
|
||||
self.resources = Vec::<Resource>::new();
|
||||
self.build(start_path, 0)?;
|
||||
self.build(start_path, 0, engine_state, stack)?;
|
||||
self.resources.sort();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build(&mut self, src: &Path, lvl: usize) -> Result<(), ShellError> {
|
||||
let source = canonicalize_with(src, std::env::current_dir()?)?;
|
||||
fn build(
|
||||
&mut self,
|
||||
src: &Path,
|
||||
lvl: usize,
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
) -> Result<(), ShellError> {
|
||||
let source = canonicalize_with(src, current_dir_str(engine_state, stack)?)?;
|
||||
|
||||
if source.is_dir() {
|
||||
for entry in std::fs::read_dir(src)? {
|
||||
@ -56,7 +69,7 @@ impl FileStructure {
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_dir() {
|
||||
self.build(&path, lvl + 1)?;
|
||||
self.build(&path, lvl + 1, engine_state, stack)?;
|
||||
}
|
||||
|
||||
self.resources.push(Resource {
|
||||
|
Reference in New Issue
Block a user