nushell on your web browser
nushell wasm terminal

fix source command not sourcing (lol)

ptr.pet f07685c9 2a6c2efa

verified
Changed files
+90 -69
src
www
src
+2 -1
.gitignore
··· 13 # Env 14 .env 15 .env.* 16 !.env.example 17 !.env.test 18 ··· 26 target 27 28 # ai scratchpad 29 - /.scratchpad
··· 13 # Env 14 .env 15 .env.* 16 + !.env.nu 17 !.env.example 18 !.env.test 19 ··· 27 target 28 29 # ai scratchpad 30 + /.scratchpad
+52 -53
src/cmd/source.rs
··· 1 use crate::globals::{get_pwd, queue_delta, to_shell_err}; 2 - use nu_engine::{CallExt, command_prelude::IoError, eval_block}; 3 use nu_parser::parse; 4 use nu_protocol::{ 5 - Category, PipelineData, ShellError, Signature, SyntaxShape, Type, 6 debugger::WithoutDebug, 7 engine::{Command, EngineState, Stack, StateWorkingSet}, 8 }; 9 - use std::{io::Read, path::PathBuf, str::FromStr}; 10 11 #[derive(Clone)] 12 pub struct Source; 13 14 impl Command for Source { 15 fn name(&self) -> &str { 16 - "source" 17 } 18 19 fn signature(&self) -> Signature { 20 Signature::build(self.name()) 21 - .required("filename", SyntaxShape::Filepath, "the file to source") 22 .input_output_type(Type::Nothing, Type::Nothing) 23 .category(Category::Core) 24 } ··· 32 engine_state: &EngineState, 33 stack: &mut Stack, 34 call: &nu_protocol::engine::Call, 35 - input: PipelineData, 36 ) -> Result<PipelineData, ShellError> { 37 - let filename: String = call.req(engine_state, stack, 0)?; 38 39 - // 1. Read file from VFS 40 - let path = get_pwd().join(&filename).map_err(to_shell_err(call.head))?; 41 - let mut file = path.open_file().map_err(to_shell_err(call.head))?; 42 - let mut contents = String::new(); 43 - file.read_to_string(&mut contents).map_err(|e| { 44 - ShellError::Io(IoError::new( 45 - e, 46 - call.head, 47 - PathBuf::from_str(path.as_str()).unwrap(), 48 - )) 49 - })?; 50 51 - // 2. Parse the content 52 - // We create a new working set based on the CURRENT engine state. 53 - let mut working_set = StateWorkingSet::new(engine_state); 54 55 - // We must add the file to the working set so the parser can track spans correctly 56 - let _file_id = working_set.add_file(filename.clone(), contents.as_bytes()); 57 58 - // Parse the block 59 - let block = parse( 60 - &mut working_set, 61 - Some(&filename), 62 - contents.as_bytes(), 63 - false, 64 - ); 65 - 66 - if let Some(err) = working_set.parse_errors.first() { 67 - return Err(ShellError::GenericError { 68 - error: "Parse error".into(), 69 - msg: err.to_string(), 70 - span: Some(call.head), 71 - help: None, 72 - inner: vec![], 73 - }); 74 - } 75 - 76 - // 3. Prepare execution context 77 - // We clone the engine state to merge the new definitions (delta) locally. 78 - // This ensures the script can call its own defined functions immediately. 79 - let mut local_state = engine_state.clone(); 80 - local_state.merge_delta(working_set.delta.clone())?; 81 82 - // 4. Queue the delta for the global engine state 83 - // This allows definitions to be available in the next command execution cycle (REPL behavior). 84 - queue_delta(working_set.delta); 85 86 - // 5. Evaluate the block 87 - // We pass the MUTABLE stack, so environment variable changes (PWD, load-env) WILL persist. 88 - eval_block::<WithoutDebug>(&local_state, stack, &block, input).map(|data| data.body) 89 - } 90 }
··· 1 use crate::globals::{get_pwd, queue_delta, to_shell_err}; 2 + use nu_engine::{CallExt, eval_block}; 3 use nu_parser::parse; 4 use nu_protocol::{ 5 + Category, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, 6 debugger::WithoutDebug, 7 engine::{Command, EngineState, Stack, StateWorkingSet}, 8 }; 9 + use vfs::VfsPath; 10 11 #[derive(Clone)] 12 pub struct Source; 13 14 impl Command for Source { 15 fn name(&self) -> &str { 16 + "source-file" 17 } 18 19 fn signature(&self) -> Signature { 20 Signature::build(self.name()) 21 + .required("path", SyntaxShape::Filepath, "the file to source") 22 .input_output_type(Type::Nothing, Type::Nothing) 23 .category(Category::Core) 24 } ··· 32 engine_state: &EngineState, 33 stack: &mut Stack, 34 call: &nu_protocol::engine::Call, 35 + _input: PipelineData, 36 ) -> Result<PipelineData, ShellError> { 37 + let path: String = call.req(engine_state, stack, 0)?; 38 + 39 + let path = get_pwd() 40 + .join(&path) 41 + .map_err(to_shell_err(call.arguments_span()))?; 42 + eval_file(engine_state, stack, &path, call.arguments_span()) 43 + } 44 + } 45 46 + pub fn eval_file<'s>( 47 + engine_state: &'s EngineState, 48 + stack: &mut Stack, 49 + path: &VfsPath, 50 + span: Span, 51 + ) -> Result<PipelineData, ShellError> { 52 + let contents = path.read_to_string().map_err(to_shell_err(span))?; 53 54 + let mut working_set= StateWorkingSet::new(engine_state); 55 + let _ = working_set.add_file(path.filename(), contents.as_bytes()); 56 57 + // we dont need the block here just the delta 58 + let block = parse( 59 + &mut working_set, 60 + Some(&path.filename()), 61 + contents.as_bytes(), 62 + false, 63 + ); 64 65 + if let Some(err) = working_set.parse_errors.first() { 66 + web_sys::console::error_1(&err.to_string().into()); 67 + return Err(ShellError::GenericError { 68 + error: "parse error".into(), 69 + msg: err.to_string(), 70 + span: Some(span), 71 + help: None, 72 + inner: vec![], 73 + }); 74 + } 75 + if let Some(err) = working_set.compile_errors.first() { 76 + web_sys::console::error_1(&err.to_string().into()); 77 + return Err(ShellError::GenericError { 78 + error: "compile error".into(), 79 + msg: err.to_string(), 80 + span: Some(span), 81 + help: None, 82 + inner: vec![], 83 + }); 84 + } 85 86 + queue_delta(working_set.delta); 87 88 + eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::Empty).map(|d| d.body) 89 }
+1 -1
src/default_context.rs
··· 264 bind_command! { 265 ExportEnv, 266 LoadEnv, 267 - SourceEnv, 268 WithEnv, 269 ConfigNu, 270 ConfigEnv,
··· 264 bind_command! { 265 ExportEnv, 266 LoadEnv, 267 + // SourceEnv, 268 WithEnv, 269 ConfigNu, 270 ConfigEnv,
+31 -13
src/lib.rs
··· 29 use crate::{ 30 cmd::{ 31 Cd, Fetch, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Pwd, Random, Rm, Save, Source, Sys, 32 }, 33 default_context::add_shell_command_context, 34 error::format_error, 35 globals::{ 36 - InterruptBool, apply_pending_deltas, get_pwd, print_to_console, set_interrupt, 37 }, 38 }; 39 use error::CommandError; ··· 67 static ENGINE_STATE: OnceLock<RwLock<EngineState>> = OnceLock::new(); 68 #[inline] 69 async fn read_engine_state() -> RwLockReadGuard<'static, EngineState> { 70 - ENGINE_STATE.get().unwrap().read().await 71 } 72 #[inline] 73 async fn write_engine_state() -> RwLockWriteGuard<'static, EngineState> { 74 - ENGINE_STATE.get().unwrap().write().await 75 } 76 77 static STACK: OnceLock<RwLock<Stack>> = OnceLock::new(); 78 #[inline] 79 async fn read_stack() -> RwLockReadGuard<'static, Stack> { 80 - STACK.get().unwrap().read().await 81 } 82 #[inline] 83 async fn write_stack() -> RwLockWriteGuard<'static, Stack> { 84 - STACK.get().unwrap().write().await 85 } 86 87 fn init_engine_internal() -> Result<(), Report> { 88 let mut engine_state = create_default_context(); 89 engine_state = add_shell_command_context(engine_state); 90 engine_state = add_extra_command_context(engine_state); ··· 121 122 engine_state.set_signals(Signals::new(Arc::new(InterruptBool))); 123 124 ENGINE_STATE 125 .set(RwLock::new(engine_state)) 126 .map_err(|_| miette::miette!("ENGINE_STATE was already set!?"))?; 127 STACK 128 - .set(RwLock::new(Stack::new())) 129 .map_err(|_| miette::miette!("STACK was already set!?"))?; 130 131 // web_sys::console::log_1(&"Hello, World!".into()); ··· 133 Ok(()) 134 } 135 136 - #[wasm_bindgen] 137 - pub fn init_engine() -> String { 138 - std::panic::set_hook(Box::new(panic_hook)); 139 - init_engine_internal().map_or_else(|err| format!("error: {err}"), |_| String::new()) 140 - } 141 - 142 async fn run_command_internal(input: &str) -> Result<(), CommandError> { 143 - let mut engine_state = ENGINE_STATE.get().unwrap().upgradable_read().await; 144 let (mut working_set, signals, config) = { 145 let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 146 apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { ··· 191 &block, 192 PipelineData::Empty, 193 ); 194 engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 195 res 196 };
··· 29 use crate::{ 30 cmd::{ 31 Cd, Fetch, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Pwd, Random, Rm, Save, Source, Sys, 32 + source::eval_file, 33 }, 34 default_context::add_shell_command_context, 35 error::format_error, 36 globals::{ 37 + InterruptBool, apply_pending_deltas, get_pwd, get_vfs, print_to_console, set_interrupt, 38 }, 39 }; 40 use error::CommandError; ··· 68 static ENGINE_STATE: OnceLock<RwLock<EngineState>> = OnceLock::new(); 69 #[inline] 70 async fn read_engine_state() -> RwLockReadGuard<'static, EngineState> { 71 + unsafe { ENGINE_STATE.get().unwrap_unchecked() } 72 + .read() 73 + .await 74 } 75 #[inline] 76 async fn write_engine_state() -> RwLockWriteGuard<'static, EngineState> { 77 + unsafe { ENGINE_STATE.get().unwrap_unchecked() } 78 + .write() 79 + .await 80 } 81 82 static STACK: OnceLock<RwLock<Stack>> = OnceLock::new(); 83 #[inline] 84 async fn read_stack() -> RwLockReadGuard<'static, Stack> { 85 + unsafe { STACK.get().unwrap_unchecked() }.read().await 86 } 87 #[inline] 88 async fn write_stack() -> RwLockWriteGuard<'static, Stack> { 89 + unsafe { STACK.get().unwrap_unchecked() }.write().await 90 + } 91 + 92 + #[wasm_bindgen] 93 + pub fn init_engine() -> String { 94 + std::panic::set_hook(Box::new(panic_hook)); 95 + init_engine_internal().map_or_else(|err| format!("error: {err}"), |_| String::new()) 96 } 97 98 fn init_engine_internal() -> Result<(), Report> { 99 + let mut stack = Stack::new(); 100 let mut engine_state = create_default_context(); 101 engine_state = add_shell_command_context(engine_state); 102 engine_state = add_extra_command_context(engine_state); ··· 133 134 engine_state.set_signals(Signals::new(Arc::new(InterruptBool))); 135 136 + // source our "nu rc" 137 + let rc_path = get_vfs().join("/.env.nu").ok(); 138 + let rc = rc_path.and_then(|env| env.exists().ok().and_then(|ok| ok.then_some(env))); 139 + if let Some(env) = rc { 140 + web_sys::console::log_1(&format!("Loading rc file: {}", env.as_str()).into()); 141 + eval_file(&engine_state, &mut stack, &env, Span::unknown())?; 142 + } 143 + 144 ENGINE_STATE 145 .set(RwLock::new(engine_state)) 146 .map_err(|_| miette::miette!("ENGINE_STATE was already set!?"))?; 147 STACK 148 + .set(RwLock::new(stack)) 149 .map_err(|_| miette::miette!("STACK was already set!?"))?; 150 151 // web_sys::console::log_1(&"Hello, World!".into()); ··· 153 Ok(()) 154 } 155 156 async fn run_command_internal(input: &str) -> Result<(), CommandError> { 157 + let mut engine_state = unsafe { ENGINE_STATE.get().unwrap_unchecked() } 158 + .upgradable_read() 159 + .await; 160 let (mut working_set, signals, config) = { 161 let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 162 apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { ··· 207 &block, 208 PipelineData::Empty, 209 ); 210 + // Apply any deltas queued during command execution (e.g., from source-file) 211 + apply_pending_deltas(&mut write_engine_state).map_err(cmd_err)?; 212 engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 213 res 214 };
+4 -1
www/src/worker.ts
··· 11 12 // Initialize WASM 13 await init(); 14 - init_engine(); 15 16 // Setup Callbacks to proxy messages back to Main Thread 17 register_console_callback((msg: string, isCmd: boolean) => {
··· 11 12 // Initialize WASM 13 await init(); 14 + const error = init_engine(); 15 + if (error) { 16 + console.error(error); 17 + } 18 19 // Setup Callbacks to proxy messages back to Main Thread 20 register_console_callback((msg: string, isCmd: boolean) => {