mod enter; mod exit; mod g; mod n; mod p; mod shells_; pub use enter::Enter; pub use exit::Exit; pub use g::GotoShell; pub use n::NextShell; use nu_engine::current_dir; use nu_protocol::ast::Call; use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, ShellError, Span, Value}; pub use p::PrevShell; pub use shells_::Shells; enum SwitchTo { Next, Prev, Last, Nth(usize), } pub fn get_shells(engine_state: &EngineState, stack: &mut Stack, cwd: Value) -> Vec { let shells = stack.get_env_var(engine_state, "NUSHELL_SHELLS"); let shells = if let Some(v) = shells { v.as_list() .map(|x| x.to_vec()) .unwrap_or_else(|_| vec![cwd]) } else { vec![cwd] }; shells } pub fn get_current_shell(engine_state: &EngineState, stack: &mut Stack) -> usize { let current_shell = stack.get_env_var(engine_state, "NUSHELL_CURRENT_SHELL"); if let Some(v) = current_shell { v.as_integer().unwrap_or_default() as usize } else { 0 } } fn get_last_shell(engine_state: &EngineState, stack: &mut Stack) -> usize { let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL"); if let Some(v) = last_shell { v.as_integer().unwrap_or_default() as usize } else { 0 } } fn switch_shell( engine_state: &EngineState, stack: &mut Stack, call: &Call, span: Span, switch_to: SwitchTo, ) -> Result { let cwd = current_dir(engine_state, stack)?; let cwd = Value::string(cwd.to_string_lossy(), call.head); let shells = get_shells(engine_state, stack, cwd); let current_shell = get_current_shell(engine_state, stack); let new_shell = match switch_to { SwitchTo::Next => { let mut new_shell = current_shell + 1; if new_shell == shells.len() { new_shell = 0; } new_shell } SwitchTo::Prev => { if current_shell == 0 { shells.len() - 1 } else { current_shell - 1 } } SwitchTo::Last => get_last_shell(engine_state, stack), SwitchTo::Nth(n) => n, }; let new_path = shells .get(new_shell) .ok_or(ShellError::NotFound { span })? .to_owned(); stack.add_env_var( "NUSHELL_SHELLS".into(), Value::List { vals: shells, span: call.head, }, ); stack.add_env_var( "NUSHELL_CURRENT_SHELL".into(), Value::int(new_shell as i64, call.head), ); stack.add_env_var( "NUSHELL_LAST_SHELL".into(), Value::int(current_shell as i64, call.head), ); stack.add_env_var("PWD".into(), new_path); Ok(PipelineData::empty()) } fn list_shells( engine_state: &EngineState, stack: &mut Stack, span: Span, ) -> Result { let cwd = current_dir(engine_state, stack)?; let cwd = Value::String { val: cwd.to_string_lossy().to_string(), span, }; let shells = get_shells(engine_state, stack, cwd); let current_shell = get_current_shell(engine_state, stack); Ok(shells .into_iter() .enumerate() .map(move |(idx, val)| Value::Record { cols: vec!["active".to_string(), "path".to_string()], vals: vec![ Value::Bool { val: idx == current_shell, span, }, val, ], span, }) .into_pipeline_data(None)) }