forked from extern/nushell
Refactor nu-cli/env* (#3041)
* Revert "History, more test coverage improvements, and refactorings. (#3217)"
This reverts commit 8fc8fc89aa
.
* Add tests
* Refactor .nu-env
* Change logic of Config write to logic of read()
* Fix reload always appends to old vars
* Fix reload always takes last_modified of global config
* Add reload_config in evaluation context
* Reload config after writing to it in cfg set / cfg set_into
* Add --no-history to cli options
* Use --no-history in tests
* Add comment about maybe_print_errors
* Get ctrl_exit var from context.global_config
* Use context.global_config in command "config"
* Add Readme in engine how env vars are now handled
* Update docs from autoenv command
* Move history_path from engine to nu_data
* Move load history out of if
* No let before return
* Add import for indexmap
This commit is contained in:
26
crates/nu-engine/src/basic_evaluation_context.rs
Normal file
26
crates/nu-engine/src/basic_evaluation_context.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::EvaluationContext;
|
||||
use crate::Scope;
|
||||
use crate::{basic_shell_manager, config_holder::ConfigHolder};
|
||||
use crate::{env::basic_host::BasicHost, Host};
|
||||
use indexmap::IndexMap;
|
||||
use parking_lot::Mutex;
|
||||
use std::error::Error;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn basic_evaluation_context() -> Result<EvaluationContext, Box<dyn Error>> {
|
||||
let scope = Scope::new();
|
||||
let mut host = BasicHost {};
|
||||
let env_vars = host.vars().iter().cloned().collect::<IndexMap<_, _>>();
|
||||
scope.add_env(env_vars);
|
||||
|
||||
Ok(EvaluationContext {
|
||||
scope,
|
||||
host: Arc::new(parking_lot::Mutex::new(Box::new(host))),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
ctrl_c: Arc::new(AtomicBool::new(false)),
|
||||
configs: Arc::new(Mutex::new(ConfigHolder::new())),
|
||||
shell_manager: basic_shell_manager::basic_shell_manager()?,
|
||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
})
|
||||
}
|
16
crates/nu-engine/src/basic_shell_manager.rs
Normal file
16
crates/nu-engine/src/basic_shell_manager.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use crate::filesystem::filesystem_shell::{FilesystemShell, FilesystemShellMode};
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::error::Error;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn basic_shell_manager() -> Result<ShellManager, Box<dyn Error>> {
|
||||
Ok(ShellManager {
|
||||
current_shell: Arc::new(AtomicUsize::new(0)),
|
||||
shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic(
|
||||
FilesystemShellMode::Cli,
|
||||
)?)])),
|
||||
})
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
use crate::call_info::UnevaluatedCallInfo;
|
||||
use crate::deserializer::ConfigDeserializer;
|
||||
use crate::env::host::Host;
|
||||
use crate::evaluate::scope::Scope;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
use crate::{call_info::UnevaluatedCallInfo, config_holder::ConfigHolder};
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use nu_errors::ShellError;
|
||||
@ -22,6 +22,7 @@ use std::sync::Arc;
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub configs: Arc<Mutex<ConfigHolder>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
@ -35,6 +36,7 @@ pub struct RawCommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub configs: Arc<Mutex<ConfigHolder>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub scope: Scope,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
@ -45,6 +47,7 @@ impl RawCommandArgs {
|
||||
CommandArgs {
|
||||
host: self.host,
|
||||
ctrl_c: self.ctrl_c,
|
||||
configs: self.configs,
|
||||
current_errors: self.current_errors,
|
||||
shell_manager: self.shell_manager,
|
||||
call_info: self.call_info,
|
||||
@ -65,6 +68,7 @@ impl CommandArgs {
|
||||
let ctx = EvaluationContext::from_args(&self);
|
||||
let host = self.host.clone();
|
||||
let ctrl_c = self.ctrl_c.clone();
|
||||
let configs = self.configs.clone();
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let input = self.input;
|
||||
let call_info = self.call_info.evaluate(&ctx).await?;
|
||||
@ -73,6 +77,7 @@ impl CommandArgs {
|
||||
Ok(EvaluatedWholeStreamCommandArgs::new(
|
||||
host,
|
||||
ctrl_c,
|
||||
configs,
|
||||
shell_manager,
|
||||
call_info,
|
||||
input,
|
||||
@ -106,6 +111,7 @@ impl EvaluatedWholeStreamCommandArgs {
|
||||
pub fn new(
|
||||
host: Arc<parking_lot::Mutex<dyn Host>>,
|
||||
ctrl_c: Arc<AtomicBool>,
|
||||
configs: Arc<Mutex<ConfigHolder>>,
|
||||
shell_manager: ShellManager,
|
||||
call_info: CallInfo,
|
||||
input: impl Into<InputStream>,
|
||||
@ -115,6 +121,7 @@ impl EvaluatedWholeStreamCommandArgs {
|
||||
args: EvaluatedCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
configs,
|
||||
shell_manager,
|
||||
call_info,
|
||||
scope,
|
||||
@ -145,6 +152,7 @@ impl EvaluatedWholeStreamCommandArgs {
|
||||
pub struct EvaluatedCommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<dyn Host>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub configs: Arc<Mutex<ConfigHolder>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: CallInfo,
|
||||
pub scope: Scope,
|
||||
|
53
crates/nu-engine/src/config_holder.rs
Normal file
53
crates/nu-engine/src/config_holder.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use std::path::Path;
|
||||
|
||||
use nu_data::config::NuConfig;
|
||||
use nu_protocol::ConfigPath;
|
||||
|
||||
/// ConfigHolder holds information which configs have been loaded.
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigHolder {
|
||||
pub global_config: Option<NuConfig>,
|
||||
pub local_configs: Vec<NuConfig>,
|
||||
}
|
||||
|
||||
impl Default for ConfigHolder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigHolder {
|
||||
pub fn new() -> ConfigHolder {
|
||||
ConfigHolder {
|
||||
global_config: None,
|
||||
local_configs: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_local_cfg(&mut self, cfg: NuConfig) {
|
||||
self.local_configs.push(cfg);
|
||||
}
|
||||
|
||||
pub fn set_global_cfg(&mut self, cfg: NuConfig) {
|
||||
self.global_config = Some(cfg);
|
||||
}
|
||||
|
||||
pub fn remove_cfg(&mut self, cfg_path: &ConfigPath) {
|
||||
match cfg_path {
|
||||
ConfigPath::Global(_) => self.global_config = None,
|
||||
ConfigPath::Local(p) => self.remove_local_cfg(p),
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_local_cfg<P: AsRef<Path>>(&mut self, cfg_path: P) {
|
||||
// Remove the first loaded local config with specified cfg_path
|
||||
if let Some(index) = self
|
||||
.local_configs
|
||||
.iter()
|
||||
.rev()
|
||||
.position(|cfg| cfg.file_path == cfg_path.as_ref())
|
||||
{
|
||||
self.local_configs.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
28
crates/nu-engine/src/env/basic_host.rs
vendored
28
crates/nu-engine/src/env/basic_host.rs
vendored
@ -7,21 +7,6 @@ use std::ffi::OsString;
|
||||
#[derive(Debug)]
|
||||
pub struct BasicHost;
|
||||
|
||||
pub fn print_err(err: ShellError, source: &Text) {
|
||||
if let Some(diag) = err.into_diagnostic() {
|
||||
let source = source.to_string();
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
files.add("shell", source);
|
||||
|
||||
let writer = termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
|
||||
let _ = std::panic::catch_unwind(move || {
|
||||
let _ = codespan_reporting::term::emit(&mut writer.lock(), &config, &files, &diag);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Host for BasicHost {
|
||||
fn stdout(&mut self, out: &str) {
|
||||
match out {
|
||||
@ -38,7 +23,18 @@ impl Host for BasicHost {
|
||||
}
|
||||
|
||||
fn print_err(&mut self, err: ShellError, source: &Text) {
|
||||
print_err(err, source);
|
||||
if let Some(diag) = err.into_diagnostic() {
|
||||
let source = source.to_string();
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
files.add("shell", source);
|
||||
|
||||
let writer = termcolor::StandardStream::stderr(termcolor::ColorChoice::Auto);
|
||||
let config = codespan_reporting::term::Config::default();
|
||||
|
||||
let _ = std::panic::catch_unwind(move || {
|
||||
let _ = codespan_reporting::term::emit(&mut writer.lock(), &config, &files, &diag);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
|
30
crates/nu-engine/src/env/environment.rs
vendored
30
crates/nu-engine/src/env/environment.rs
vendored
@ -1,30 +0,0 @@
|
||||
use nu_protocol::Value;
|
||||
use std::ffi::OsString;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Env: Debug + Send {
|
||||
fn env(&self) -> Option<Value>;
|
||||
fn path(&self) -> Option<Value>;
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str);
|
||||
fn add_path(&mut self, new_path: OsString);
|
||||
}
|
||||
|
||||
impl Env for Box<dyn Env> {
|
||||
fn env(&self) -> Option<Value> {
|
||||
(**self).env()
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<Value> {
|
||||
(**self).path()
|
||||
}
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str) {
|
||||
(**self).add_env(key, value);
|
||||
}
|
||||
|
||||
fn add_path(&mut self, new_path: OsString) {
|
||||
(**self).add_path(new_path);
|
||||
}
|
||||
}
|
1
crates/nu-engine/src/env/mod.rs
vendored
1
crates/nu-engine/src/env/mod.rs
vendored
@ -1,3 +1,2 @@
|
||||
pub(crate) mod basic_host;
|
||||
pub(crate) mod environment;
|
||||
pub(crate) mod host;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::call_info::UnevaluatedCallInfo;
|
||||
use crate::command_args::RawCommandArgs;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::filesystem::filesystem_shell::FilesystemShell;
|
||||
use crate::filesystem::filesystem_shell::{FilesystemShell, FilesystemShellMode};
|
||||
use crate::shell::help_shell::HelpShell;
|
||||
use crate::shell::value_shell::ValueShell;
|
||||
use futures::StreamExt;
|
||||
@ -11,7 +11,6 @@ use nu_protocol::hir::{ExternalRedirection, InternalCommand};
|
||||
use nu_protocol::{CommandAction, Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_source::{PrettyDebug, Span, Tag};
|
||||
use nu_stream::{trace_stream, InputStream, ToInputStream};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) async fn run_internal_command(
|
||||
@ -28,12 +27,6 @@ pub(crate) async fn run_internal_command(
|
||||
|
||||
let internal_command = context.scope.expect_command(&command.name);
|
||||
|
||||
if command.name == "autoenv untrust" {
|
||||
context
|
||||
.user_recently_used_autoenv_untrust
|
||||
.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let result = {
|
||||
context
|
||||
.run_command(
|
||||
@ -75,6 +68,7 @@ pub(crate) async fn run_internal_command(
|
||||
let new_args = RawCommandArgs {
|
||||
host: context.host.clone(),
|
||||
ctrl_c: context.ctrl_c.clone(),
|
||||
configs: context.configs.clone(),
|
||||
current_errors: context.current_errors.clone(),
|
||||
shell_manager: context.shell_manager.clone(),
|
||||
call_info: UnevaluatedCallInfo {
|
||||
@ -172,8 +166,13 @@ pub(crate) async fn run_internal_command(
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
CommandAction::EnterShell(location) => {
|
||||
let mode = if context.shell_manager.is_interactive() {
|
||||
FilesystemShellMode::Cli
|
||||
} else {
|
||||
FilesystemShellMode::Script
|
||||
};
|
||||
context.shell_manager.insert_at_current(Box::new(
|
||||
match FilesystemShell::with_location(location) {
|
||||
match FilesystemShell::with_location(location, mode) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
context.error(err.into());
|
||||
@ -220,6 +219,17 @@ pub(crate) async fn run_internal_command(
|
||||
}
|
||||
InputStream::empty()
|
||||
}
|
||||
CommandAction::UnloadConfig(cfg_path) => {
|
||||
context.unload_config(&cfg_path).await;
|
||||
InputStream::empty()
|
||||
}
|
||||
CommandAction::LoadConfig(cfg_path) => {
|
||||
if let Err(e) = context.load_config(&cfg_path).await {
|
||||
InputStream::one(UntaggedValue::Error(e).into_untagged_value())
|
||||
} else {
|
||||
InputStream::empty()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Ok(ReturnSuccess::Value(Value {
|
||||
|
@ -177,6 +177,16 @@ impl Scope {
|
||||
output
|
||||
}
|
||||
|
||||
pub fn get_env(&self, name: &str) -> Option<String> {
|
||||
for frame in self.frames.lock().iter().rev() {
|
||||
if let Some(v) = frame.env.get(name) {
|
||||
return Some(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_var(&self, name: &str) -> Option<Value> {
|
||||
for frame in self.frames.lock().iter().rev() {
|
||||
if let Some(v) = frame.vars.get(name) {
|
||||
@ -224,6 +234,66 @@ impl Scope {
|
||||
frame.env.insert(name.into(), value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_exit_scripts(&self, scripts: Vec<String>) {
|
||||
if let Some(frame) = self.frames.lock().last_mut() {
|
||||
frame.exitscripts = scripts
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enter_scope_with_tag(&self, tag: String) {
|
||||
self.frames.lock().push(ScopeFrame::with_tag(tag));
|
||||
}
|
||||
|
||||
//Removes the scopeframe with tag.
|
||||
pub fn exit_scope_with_tag(&self, tag: &str) {
|
||||
let mut frames = self.frames.lock();
|
||||
let tag = Some(tag);
|
||||
if let Some(i) = frames.iter().rposition(|f| f.tag.as_deref() == tag) {
|
||||
frames.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_exitscripts_of_frame_with_tag(&self, tag: &str) -> Option<Vec<String>> {
|
||||
let frames = self.frames.lock();
|
||||
let tag = Some(tag);
|
||||
frames.iter().find_map(|f| {
|
||||
if f.tag.as_deref() == tag {
|
||||
Some(f.exitscripts.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_frame_with_tag(&self, tag: &str) -> Option<ScopeFrame> {
|
||||
let frames = self.frames.lock();
|
||||
let tag = Some(tag);
|
||||
frames.iter().rev().find_map(|f| {
|
||||
if f.tag.as_deref() == tag {
|
||||
Some(f.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_frame_with_tag(&self, frame: ScopeFrame, tag: &str) -> Result<(), ShellError> {
|
||||
let mut frames = self.frames.lock();
|
||||
let tag = Some(tag);
|
||||
for f in frames.iter_mut().rev() {
|
||||
if f.tag.as_deref() == tag {
|
||||
*f = frame;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Frame not found, return err
|
||||
Err(ShellError::untagged_runtime_error(format!(
|
||||
"Can't update frame with tag {:?}. No such frame present!",
|
||||
tag
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ParserScope for Scope {
|
||||
@ -286,6 +356,15 @@ pub struct ScopeFrame {
|
||||
pub commands: IndexMap<String, Command>,
|
||||
pub custom_commands: IndexMap<String, Block>,
|
||||
pub aliases: IndexMap<String, Vec<Spanned<String>>>,
|
||||
///Optional tag to better identify this scope frame later
|
||||
pub tag: Option<String>,
|
||||
pub exitscripts: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for ScopeFrame {
|
||||
fn default() -> Self {
|
||||
ScopeFrame::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ScopeFrame {
|
||||
@ -324,6 +403,15 @@ impl ScopeFrame {
|
||||
commands: IndexMap::new(),
|
||||
custom_commands: IndexMap::new(),
|
||||
aliases: IndexMap::new(),
|
||||
tag: None,
|
||||
exitscripts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_tag(tag: String) -> ScopeFrame {
|
||||
let mut scope = ScopeFrame::new();
|
||||
scope.tag = Some(tag);
|
||||
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,13 @@
|
||||
use crate::evaluate::scope::Scope;
|
||||
use indexmap::IndexMap;
|
||||
use nu_data::config::NuConfig;
|
||||
use nu_data::config::path::history_path;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::{Spanned, Tag};
|
||||
|
||||
pub fn nu(scope: &Scope, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let tag = tag.into();
|
||||
|
||||
let env = &scope.get_env_vars();
|
||||
|
||||
let config = if let Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) = scope.get_var("config-path")
|
||||
{
|
||||
NuConfig::with(Some(path).map(|p| p.into_os_string()))
|
||||
} else {
|
||||
NuConfig::new()
|
||||
};
|
||||
let tag = tag.into();
|
||||
|
||||
let mut nu_dict = TaggedDictBuilder::new(&tag);
|
||||
|
||||
@ -30,10 +19,16 @@ pub fn nu(scope: &Scope, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
}
|
||||
nu_dict.insert_value("env", dict.into_value());
|
||||
|
||||
nu_dict.insert_value(
|
||||
"config",
|
||||
UntaggedValue::row(config.vars.clone()).into_value(&tag),
|
||||
);
|
||||
let config_file = match scope.get_var("config-path") {
|
||||
Some(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
|
||||
..
|
||||
}) => Some(path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let config = nu_data::config::read(&tag, &config_file)?;
|
||||
nu_dict.insert_value("config", UntaggedValue::row(config).into_value(&tag));
|
||||
|
||||
let mut table = vec![];
|
||||
for v in env.iter() {
|
||||
@ -55,9 +50,15 @@ pub fn nu(scope: &Scope, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let temp = std::env::temp_dir();
|
||||
nu_dict.insert_value("temp-dir", UntaggedValue::filepath(temp).into_value(&tag));
|
||||
|
||||
let config = if let Some(path) = config_file {
|
||||
path
|
||||
} else {
|
||||
nu_data::config::default_path()?
|
||||
};
|
||||
|
||||
nu_dict.insert_value(
|
||||
"config-path",
|
||||
UntaggedValue::filepath(nu_data::config::path::source_file(&config)).into_value(&tag),
|
||||
UntaggedValue::filepath(config).into_value(&tag),
|
||||
);
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
@ -69,9 +70,11 @@ pub fn nu(scope: &Scope, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
);
|
||||
}
|
||||
|
||||
let config: Box<dyn nu_data::config::Conf> = Box::new(nu_data::config::NuConfig::new());
|
||||
let history = history_path(&config);
|
||||
nu_dict.insert_value(
|
||||
"history-path",
|
||||
UntaggedValue::filepath(nu_data::config::path::history(&config)).into_value(&tag),
|
||||
UntaggedValue::filepath(history).into_value(&tag),
|
||||
);
|
||||
|
||||
Ok(nu_dict.into_value())
|
||||
|
@ -1,17 +1,18 @@
|
||||
use crate::call_info::UnevaluatedCallInfo;
|
||||
use crate::command_args::CommandArgs;
|
||||
use crate::env::{basic_host::BasicHost, host::Host};
|
||||
use crate::evaluate::scope::Scope;
|
||||
use crate::env::host::Host;
|
||||
use crate::evaluate::scope::{Scope, ScopeFrame};
|
||||
use crate::shell::shell_manager::ShellManager;
|
||||
use crate::whole_stream_command::Command;
|
||||
use indexmap::IndexMap;
|
||||
use crate::{call_info::UnevaluatedCallInfo, config_holder::ConfigHolder};
|
||||
use crate::{command_args::CommandArgs, script};
|
||||
use log::trace;
|
||||
use nu_data::config::{self, Conf, NuConfig};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir;
|
||||
use nu_source::{Tag, Text};
|
||||
use nu_protocol::{hir, ConfigPath};
|
||||
use nu_source::{Span, Tag};
|
||||
use nu_stream::{InputStream, OutputStream};
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EvaluationContext {
|
||||
@ -19,7 +20,7 @@ pub struct EvaluationContext {
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub user_recently_used_autoenv_untrust: Arc<AtomicBool>,
|
||||
pub configs: Arc<Mutex<ConfigHolder>>,
|
||||
pub shell_manager: ShellManager,
|
||||
|
||||
/// Windows-specific: keep track of previous cwd on each drive
|
||||
@ -27,26 +28,14 @@ pub struct EvaluationContext {
|
||||
}
|
||||
|
||||
impl EvaluationContext {
|
||||
pub fn basic() -> Result<EvaluationContext, ShellError> {
|
||||
Ok(EvaluationContext {
|
||||
scope: Scope::new(),
|
||||
host: Arc::new(parking_lot::Mutex::new(Box::new(BasicHost))),
|
||||
current_errors: Arc::new(Mutex::new(vec![])),
|
||||
ctrl_c: Arc::new(AtomicBool::new(false)),
|
||||
user_recently_used_autoenv_untrust: Arc::new(AtomicBool::new(false)),
|
||||
shell_manager: ShellManager::basic()?,
|
||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_args(args: &CommandArgs) -> EvaluationContext {
|
||||
EvaluationContext {
|
||||
scope: args.scope.clone(),
|
||||
host: args.host.clone(),
|
||||
current_errors: args.current_errors.clone(),
|
||||
ctrl_c: args.ctrl_c.clone(),
|
||||
configs: args.configs.clone(),
|
||||
shell_manager: args.shell_manager.clone(),
|
||||
user_recently_used_autoenv_untrust: Arc::new(AtomicBool::new(false)),
|
||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
}
|
||||
}
|
||||
@ -117,6 +106,7 @@ impl EvaluationContext {
|
||||
CommandArgs {
|
||||
host: self.host.clone(),
|
||||
ctrl_c: self.ctrl_c.clone(),
|
||||
configs: self.configs.clone(),
|
||||
current_errors: self.current_errors.clone(),
|
||||
shell_manager: self.shell_manager.clone(),
|
||||
call_info: self.call_info(args, name_tag),
|
||||
@ -125,26 +115,187 @@ impl EvaluationContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_env(&self) -> IndexMap<String, String> {
|
||||
let mut output = IndexMap::new();
|
||||
for (var, value) in self.host.lock().vars() {
|
||||
output.insert(var, value);
|
||||
/// Loads config under cfg_path.
|
||||
/// If an error occurs while loading the config:
|
||||
/// The config is not loaded
|
||||
/// The error is returned
|
||||
/// After successfull loading of the config the startup scripts are run
|
||||
/// as normal scripts (Errors are printed out, ...)
|
||||
/// After executing the startup scripts, true is returned to indicate successfull loading
|
||||
/// of the config
|
||||
//
|
||||
// The rational here is that, we should not partially load any config
|
||||
// that might be damaged. However, startup scripts might fail for various reasons.
|
||||
// A failure there is not as crucial as wrong config files.
|
||||
pub async fn load_config(&self, cfg_path: &ConfigPath) -> Result<(), ShellError> {
|
||||
trace!("Loading cfg {:?}", cfg_path);
|
||||
|
||||
let cfg = NuConfig::load(Some(cfg_path.get_path().clone()))?;
|
||||
let exit_scripts = cfg.exit_scripts()?;
|
||||
let startup_scripts = cfg.startup_scripts()?;
|
||||
let cfg_paths = cfg.path()?;
|
||||
|
||||
let joined_paths = cfg_paths
|
||||
.map(|mut cfg_paths| {
|
||||
//existing paths are prepended to path
|
||||
if let Some(env_paths) = self.scope.get_env("PATH") {
|
||||
let mut env_paths = std::env::split_paths(&env_paths).collect::<Vec<_>>();
|
||||
//No duplicates! Remove env_paths already existing in cfg_paths
|
||||
env_paths.retain(|env_path| !cfg_paths.contains(env_path));
|
||||
//env_paths entries are appended at the end
|
||||
//nu config paths have a higher priority
|
||||
cfg_paths.extend(env_paths);
|
||||
}
|
||||
cfg_paths
|
||||
})
|
||||
.map(|paths| {
|
||||
std::env::join_paths(paths)
|
||||
.map(|s| s.to_string_lossy().to_string())
|
||||
.map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
&format!("Error while joining paths from config: {:?}", e),
|
||||
"Config path error",
|
||||
Span::unknown(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let tag = config::cfg_path_to_scope_tag(cfg_path);
|
||||
|
||||
self.scope.enter_scope_with_tag(tag);
|
||||
self.scope.add_env(cfg.env_map());
|
||||
if let Some(path) = joined_paths {
|
||||
self.scope.add_env_var("PATH", path);
|
||||
}
|
||||
self.scope.set_exit_scripts(exit_scripts);
|
||||
|
||||
match cfg_path {
|
||||
ConfigPath::Global(_) => self.configs.lock().set_global_cfg(cfg),
|
||||
ConfigPath::Local(_) => {
|
||||
self.configs.lock().add_local_cfg(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
if !startup_scripts.is_empty() {
|
||||
self.run_scripts(startup_scripts, cfg_path.get_path().parent())
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reloads config with a path of cfg_path.
|
||||
/// If an error occurs while reloading the config:
|
||||
/// The config is not reloaded
|
||||
/// The error is returned
|
||||
pub async fn reload_config(&self, cfg_path: &ConfigPath) -> Result<(), ShellError> {
|
||||
trace!("Reloading cfg {:?}", cfg_path);
|
||||
|
||||
let mut configs = self.configs.lock();
|
||||
let cfg = match cfg_path {
|
||||
ConfigPath::Global(path) => {
|
||||
configs.global_config.iter_mut().find(|cfg| &cfg.file_path == path).ok_or_else(||
|
||||
ShellError::labeled_error(
|
||||
&format!("Error reloading global config with path of {}. No such global config present.", path.display()),
|
||||
"Config path error",
|
||||
Span::unknown(),
|
||||
)
|
||||
)?
|
||||
}
|
||||
ConfigPath::Local(path) => {
|
||||
configs.local_configs.iter_mut().find(|cfg| &cfg.file_path == path).ok_or_else(||
|
||||
ShellError::labeled_error(
|
||||
&format!("Error reloading local config with path of {}. No such local config present.", path.display()),
|
||||
"Config path error",
|
||||
Span::unknown(),
|
||||
)
|
||||
)?
|
||||
}
|
||||
};
|
||||
|
||||
cfg.reload();
|
||||
|
||||
let exit_scripts = cfg.exit_scripts()?;
|
||||
let cfg_paths = cfg.path()?;
|
||||
|
||||
let joined_paths = cfg_paths
|
||||
.map(|mut cfg_paths| {
|
||||
//existing paths are prepended to path
|
||||
if let Some(env_paths) = self.scope.get_env("PATH") {
|
||||
let mut env_paths = std::env::split_paths(&env_paths).collect::<Vec<_>>();
|
||||
//No duplicates! Remove env_paths already existing in cfg_paths
|
||||
env_paths.retain(|env_path| !cfg_paths.contains(env_path));
|
||||
//env_paths entries are appended at the end
|
||||
//nu config paths have a higher priority
|
||||
cfg_paths.extend(env_paths);
|
||||
}
|
||||
cfg_paths
|
||||
})
|
||||
.map(|paths| {
|
||||
std::env::join_paths(paths)
|
||||
.map(|s| s.to_string_lossy().to_string())
|
||||
.map_err(|e| {
|
||||
ShellError::labeled_error(
|
||||
&format!("Error while joining paths from config: {:?}", e),
|
||||
"Config path error",
|
||||
Span::unknown(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let tag = config::cfg_path_to_scope_tag(cfg_path);
|
||||
let mut frame = ScopeFrame::new();
|
||||
|
||||
frame.env = cfg.env_map();
|
||||
if let Some(path) = joined_paths {
|
||||
frame.env.insert("PATH".to_string(), path);
|
||||
}
|
||||
frame.exitscripts = exit_scripts;
|
||||
|
||||
self.scope.update_frame_with_tag(frame, &tag)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Runs all exit_scripts before unloading the config with path of cfg_path
|
||||
/// If an error occurs while running exit scripts:
|
||||
/// The error is added to `self.current_errors`
|
||||
/// If no config with path of `cfg_path` is present, this method does nothing
|
||||
pub async fn unload_config(&self, cfg_path: &ConfigPath) {
|
||||
trace!("UnLoading cfg {:?}", cfg_path);
|
||||
|
||||
let tag = config::cfg_path_to_scope_tag(cfg_path);
|
||||
|
||||
//Run exitscripts with scope frame and cfg still applied
|
||||
if let Some(scripts) = self.scope.get_exitscripts_of_frame_with_tag(&tag) {
|
||||
self.run_scripts(scripts, cfg_path.get_path().parent())
|
||||
.await;
|
||||
}
|
||||
|
||||
//Unload config
|
||||
self.configs.lock().remove_cfg(&cfg_path);
|
||||
self.scope.exit_scope_with_tag(&tag);
|
||||
}
|
||||
|
||||
/// Runs scripts with cwd of dir. If dir is None, this method does nothing.
|
||||
/// Each error is added to `self.current_errors`
|
||||
pub async fn run_scripts(&self, scripts: Vec<String>, dir: Option<&Path>) {
|
||||
if let Some(dir) = dir {
|
||||
for script in scripts {
|
||||
match script::run_script_in_dir(script.clone(), dir, &self).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
let err = ShellError::untagged_runtime_error(format!(
|
||||
"Err while executing exitscript. Err was\n{:?}",
|
||||
e
|
||||
));
|
||||
let text = script.into();
|
||||
self.host.lock().print_err(err, &text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_print_errors(context: &EvaluationContext, source: Text) -> bool {
|
||||
let errors = context.current_errors.clone();
|
||||
let mut errors = errors.lock();
|
||||
|
||||
if errors.len() > 0 {
|
||||
let error = errors[0].clone();
|
||||
*errors = vec![];
|
||||
|
||||
context.host.lock().print_err(error, &source);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ use futures::stream::BoxStream;
|
||||
use futures::StreamExt;
|
||||
use futures_codec::FramedRead;
|
||||
use futures_util::TryStreamExt;
|
||||
use nu_protocol::{TaggedDictBuilder, Value};
|
||||
use nu_data::config::LocalConfigDiff;
|
||||
use nu_protocol::{CommandAction, ConfigPath, TaggedDictBuilder, Value};
|
||||
use nu_source::{Span, Tag};
|
||||
use nu_stream::{Interruptible, OutputStream, ToOutputStream};
|
||||
use std::collections::HashMap;
|
||||
@ -27,9 +28,16 @@ use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy)]
|
||||
pub enum FilesystemShellMode {
|
||||
Cli,
|
||||
Script,
|
||||
}
|
||||
|
||||
pub struct FilesystemShell {
|
||||
pub(crate) path: String,
|
||||
pub(crate) last_path: String,
|
||||
pub(crate) mode: FilesystemShellMode,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FilesystemShell {
|
||||
@ -43,12 +51,13 @@ impl Clone for FilesystemShell {
|
||||
FilesystemShell {
|
||||
path: self.path.clone(),
|
||||
last_path: self.path.clone(),
|
||||
mode: self.mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FilesystemShell {
|
||||
pub fn basic() -> Result<FilesystemShell, Error> {
|
||||
pub fn basic(mode: FilesystemShellMode) -> Result<FilesystemShell, Error> {
|
||||
let path = match std::env::current_dir() {
|
||||
Ok(path) => path,
|
||||
Err(_) => PathBuf::from("/"),
|
||||
@ -57,15 +66,23 @@ impl FilesystemShell {
|
||||
Ok(FilesystemShell {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
last_path: path.to_string_lossy().to_string(),
|
||||
mode,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_location(path: String) -> Result<FilesystemShell, std::io::Error> {
|
||||
pub fn with_location(
|
||||
path: String,
|
||||
mode: FilesystemShellMode,
|
||||
) -> Result<FilesystemShell, std::io::Error> {
|
||||
let path = canonicalize(std::env::current_dir()?, &path)?;
|
||||
let path = path.display().to_string();
|
||||
let last_path = path.clone();
|
||||
|
||||
Ok(FilesystemShell { path, last_path })
|
||||
Ok(FilesystemShell {
|
||||
path,
|
||||
last_path,
|
||||
mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,6 +272,43 @@ impl Shell for FilesystemShell {
|
||||
path.to_string_lossy().to_string(),
|
||||
));
|
||||
|
||||
//Loading local configs in script mode, makes scripts behave different on different
|
||||
//filesystems and might therefore surprise users. That's why we only load them in cli mode.
|
||||
if self.mode == FilesystemShellMode::Cli {
|
||||
match dunce::canonicalize(self.path()) {
|
||||
Err(e) => {
|
||||
let err = ShellError::untagged_runtime_error(format!(
|
||||
"Could not get absolute path from current fs shell. The error was: {:?}",
|
||||
e
|
||||
));
|
||||
stream.push_back(ReturnSuccess::value(
|
||||
UntaggedValue::Error(err).into_value(Tag::unknown()),
|
||||
));
|
||||
}
|
||||
Ok(current_pwd) => {
|
||||
let (changes, errs) = LocalConfigDiff::between(current_pwd, path);
|
||||
|
||||
for err in errs {
|
||||
stream.push_back(ReturnSuccess::value(
|
||||
UntaggedValue::Error(err).into_value(Tag::unknown()),
|
||||
));
|
||||
}
|
||||
|
||||
for unload_cfg in changes.cfgs_to_unload {
|
||||
stream.push_back(ReturnSuccess::action(CommandAction::UnloadConfig(
|
||||
ConfigPath::Local(unload_cfg),
|
||||
)));
|
||||
}
|
||||
|
||||
for load_cfg in changes.cfgs_to_load {
|
||||
stream.push_back(ReturnSuccess::action(CommandAction::LoadConfig(
|
||||
ConfigPath::Local(load_cfg),
|
||||
)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(stream.into())
|
||||
}
|
||||
|
||||
@ -777,6 +831,10 @@ impl Shell for FilesystemShell {
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_interactive(&self) -> bool {
|
||||
self.mode == FilesystemShellMode::Cli
|
||||
}
|
||||
}
|
||||
|
||||
struct TaggedPathBuf<'a>(&'a PathBuf, &'a Tag);
|
||||
|
@ -1,25 +1,31 @@
|
||||
pub mod basic_evaluation_context;
|
||||
pub mod basic_shell_manager;
|
||||
mod call_info;
|
||||
mod command_args;
|
||||
mod config_holder;
|
||||
pub mod deserializer;
|
||||
pub mod documentation;
|
||||
mod env;
|
||||
mod evaluate;
|
||||
pub mod evaluation_context;
|
||||
mod evaluation_context;
|
||||
mod example;
|
||||
pub mod filesystem;
|
||||
mod maybe_text_codec;
|
||||
pub mod plugin;
|
||||
mod print;
|
||||
mod runnable_context;
|
||||
pub mod script;
|
||||
pub mod shell;
|
||||
mod whole_stream_command;
|
||||
|
||||
pub use crate::basic_evaluation_context::basic_evaluation_context;
|
||||
pub use crate::basic_shell_manager::basic_shell_manager;
|
||||
pub use crate::call_info::UnevaluatedCallInfo;
|
||||
pub use crate::command_args::{
|
||||
CommandArgs, EvaluatedCommandArgs, EvaluatedWholeStreamCommandArgs, RawCommandArgs,
|
||||
};
|
||||
pub use crate::config_holder::ConfigHolder;
|
||||
pub use crate::documentation::{generate_docs, get_brief_help, get_documentation, get_full_help};
|
||||
pub use crate::env::environment::Env;
|
||||
pub use crate::env::host::FakeHost;
|
||||
pub use crate::env::host::Host;
|
||||
pub use crate::evaluate::block::run_block;
|
||||
@ -31,6 +37,7 @@ pub use crate::filesystem::dir_info::{DirBuilder, DirInfo, FileInfo};
|
||||
pub use crate::filesystem::filesystem_shell::FilesystemShell;
|
||||
pub use crate::filesystem::path;
|
||||
pub use crate::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
|
||||
pub use crate::print::maybe_print_errors;
|
||||
pub use crate::runnable_context::RunnableContext;
|
||||
pub use crate::shell::help_shell::{command_dict, HelpShell};
|
||||
pub use crate::shell::painter::Painter;
|
||||
|
18
crates/nu-engine/src/print.rs
Normal file
18
crates/nu-engine/src/print.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use nu_source::Text;
|
||||
|
||||
use crate::EvaluationContext;
|
||||
|
||||
pub fn maybe_print_errors(context: &EvaluationContext, source: Text) -> bool {
|
||||
let errors = context.current_errors.clone();
|
||||
let mut errors = errors.lock();
|
||||
|
||||
if errors.len() > 0 {
|
||||
let error = errors[0].clone();
|
||||
*errors = vec![];
|
||||
|
||||
context.host.lock().print_err(error, &source);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use crate::{Command, Host, Scope, ShellManager};
|
||||
use crate::{Command, CommandArgs, EvaluationContext};
|
||||
use crate::{ConfigHolder, Host, Scope, ShellManager};
|
||||
use nu_errors::ShellError;
|
||||
use nu_source::Tag;
|
||||
use nu_stream::InputStream;
|
||||
@ -10,12 +11,39 @@ pub struct RunnableContext {
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub configs: Arc<Mutex<ConfigHolder>>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn from_command_args(args: CommandArgs) -> Self {
|
||||
RunnableContext {
|
||||
input: args.input,
|
||||
scope: args.scope.clone(),
|
||||
shell_manager: args.shell_manager,
|
||||
host: args.host,
|
||||
ctrl_c: args.ctrl_c,
|
||||
configs: args.configs,
|
||||
current_errors: args.current_errors,
|
||||
name: args.call_info.name_tag,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_evaluation_context(input: InputStream, ctx: &EvaluationContext) -> Self {
|
||||
RunnableContext {
|
||||
input,
|
||||
shell_manager: ctx.shell_manager.clone(),
|
||||
host: ctx.host.clone(),
|
||||
ctrl_c: ctx.ctrl_c.clone(),
|
||||
configs: ctx.configs.clone(),
|
||||
current_errors: ctx.current_errors.clone(),
|
||||
scope: ctx.scope.clone(),
|
||||
name: Tag::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
self.scope.get_command(name)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::path::canonicalize;
|
||||
use crate::run_block;
|
||||
use crate::{maybe_print_errors, path::canonicalize, run_block};
|
||||
use crate::{MaybeTextCodec, StringOrBinary};
|
||||
use futures::StreamExt;
|
||||
use futures_codec::FramedRead;
|
||||
@ -11,7 +10,7 @@ use nu_protocol::hir::{
|
||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
use nu_stream::{InputStream, ToInputStream};
|
||||
|
||||
use crate::{evaluation_context, EvaluationContext};
|
||||
use crate::EvaluationContext;
|
||||
use log::{debug, trace};
|
||||
use nu_source::{Span, Tag, Text};
|
||||
use std::iter::Iterator;
|
||||
@ -36,6 +35,22 @@ fn chomp_newline(s: &str) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_script_in_dir(
|
||||
script: String,
|
||||
dir: &Path,
|
||||
ctx: &EvaluationContext,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
//Save path before to switch back to it after executing script
|
||||
let path_before = ctx.shell_manager.path();
|
||||
|
||||
ctx.shell_manager
|
||||
.set_path(dir.to_string_lossy().to_string());
|
||||
run_script_standalone(script, false, ctx, false).await?;
|
||||
ctx.shell_manager.set_path(path_before);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process the line by parsing the text to turn it into commands, classify those commands so that we understand what is being called in the pipeline, and then run this pipeline
|
||||
pub async fn process_script(
|
||||
script_text: &str,
|
||||
@ -168,9 +183,7 @@ pub async fn process_script(
|
||||
};
|
||||
|
||||
trace!("{:#?}", block);
|
||||
let env = ctx.get_env();
|
||||
|
||||
ctx.scope.add_env_to_base(env);
|
||||
let result = run_block(&block, ctx, input_stream).await;
|
||||
|
||||
match result {
|
||||
@ -229,6 +242,10 @@ pub async fn run_script_standalone(
|
||||
context: &EvaluationContext,
|
||||
exit_on_error: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
context
|
||||
.shell_manager
|
||||
.enter_script_mode()
|
||||
.map_err(Box::new)?;
|
||||
let line = process_script(&script_text, context, redirect_stdin, 0, false).await;
|
||||
|
||||
match line {
|
||||
@ -244,16 +261,19 @@ pub async fn run_script_standalone(
|
||||
}
|
||||
};
|
||||
|
||||
evaluation_context::maybe_print_errors(&context, Text::from(line));
|
||||
maybe_print_errors(&context, Text::from(line));
|
||||
if error_code != 0 && exit_on_error {
|
||||
std::process::exit(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
LineResult::Error(line, err) => {
|
||||
context.with_host(|host| host.print_err(err, &Text::from(line.clone())));
|
||||
context
|
||||
.host
|
||||
.lock()
|
||||
.print_err(err, &Text::from(line.clone()));
|
||||
|
||||
evaluation_context::maybe_print_errors(&context, Text::from(line));
|
||||
maybe_print_errors(&context, Text::from(line));
|
||||
if exit_on_error {
|
||||
std::process::exit(1);
|
||||
}
|
||||
@ -261,5 +281,9 @@ pub async fn run_script_standalone(
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
//exit script mode shell
|
||||
context.shell_manager.remove_at_current();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -244,4 +244,9 @@ impl Shell for HelpShell {
|
||||
"save on help shell is not supported",
|
||||
))
|
||||
}
|
||||
|
||||
fn is_interactive(&self) -> bool {
|
||||
//Help shell is always interactive
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ pub(crate) mod shell_manager;
|
||||
pub(crate) mod value_shell;
|
||||
|
||||
pub trait Shell: std::fmt::Debug {
|
||||
fn is_interactive(&self) -> bool;
|
||||
fn name(&self) -> String;
|
||||
fn homedir(&self) -> Option<PathBuf>;
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
use crate::command_args::EvaluatedWholeStreamCommandArgs;
|
||||
use crate::maybe_text_codec::StringOrBinary;
|
||||
use crate::shell::Shell;
|
||||
use crate::{command_args::EvaluatedWholeStreamCommandArgs, FilesystemShell};
|
||||
use crate::{filesystem::filesystem_shell::FilesystemShellMode, maybe_text_codec::StringOrBinary};
|
||||
use futures::Stream;
|
||||
use nu_stream::OutputStream;
|
||||
|
||||
use crate::filesystem::filesystem_shell::FilesystemShell;
|
||||
use crate::shell::shell_args::{CdArgs, CopyArgs, LsArgs, MkdirArgs, MvArgs, RemoveArgs};
|
||||
use crate::shell::Shell;
|
||||
|
||||
use encoding_rs::Encoding;
|
||||
use nu_errors::ShellError;
|
||||
use nu_source::{Span, Tag};
|
||||
@ -22,11 +20,11 @@ pub struct ShellManager {
|
||||
}
|
||||
|
||||
impl ShellManager {
|
||||
pub fn basic() -> Result<Self, ShellError> {
|
||||
Ok(ShellManager {
|
||||
current_shell: Arc::new(AtomicUsize::new(0)),
|
||||
shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic()?)])),
|
||||
})
|
||||
pub fn enter_script_mode(&self) -> Result<(), std::io::Error> {
|
||||
//New fs_shell starting from current path
|
||||
let fs_shell = FilesystemShell::with_location(self.path(), FilesystemShellMode::Script)?;
|
||||
self.insert_at_current(Box::new(fs_shell));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_at_current(&self, shell: Box<dyn Shell + Send>) {
|
||||
@ -64,6 +62,10 @@ impl ShellManager {
|
||||
self.shells.lock().is_empty()
|
||||
}
|
||||
|
||||
pub fn is_interactive(&self) -> bool {
|
||||
self.shells.lock()[self.current_shell()].is_interactive()
|
||||
}
|
||||
|
||||
pub fn path(&self) -> String {
|
||||
self.shells.lock()[self.current_shell()].path()
|
||||
}
|
||||
|
@ -254,4 +254,9 @@ impl Shell for ValueShell {
|
||||
"save on help shell is not supported",
|
||||
))
|
||||
}
|
||||
|
||||
fn is_interactive(&self) -> bool {
|
||||
//Value shell is always interactive
|
||||
true
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user