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 13 # Env 14 14 .env 15 15 .env.* 16 + !.env.nu 16 17 !.env.example 17 18 !.env.test 18 19 ··· 26 27 target 27 28 28 29 # ai scratchpad 29 - /.scratchpad 30 + /.scratchpad
+52 -53
src/cmd/source.rs
··· 1 1 use crate::globals::{get_pwd, queue_delta, to_shell_err}; 2 - use nu_engine::{CallExt, command_prelude::IoError, eval_block}; 2 + use nu_engine::{CallExt, eval_block}; 3 3 use nu_parser::parse; 4 4 use nu_protocol::{ 5 - Category, PipelineData, ShellError, Signature, SyntaxShape, Type, 5 + Category, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, 6 6 debugger::WithoutDebug, 7 7 engine::{Command, EngineState, Stack, StateWorkingSet}, 8 8 }; 9 - use std::{io::Read, path::PathBuf, str::FromStr}; 9 + use vfs::VfsPath; 10 10 11 11 #[derive(Clone)] 12 12 pub struct Source; 13 13 14 14 impl Command for Source { 15 15 fn name(&self) -> &str { 16 - "source" 16 + "source-file" 17 17 } 18 18 19 19 fn signature(&self) -> Signature { 20 20 Signature::build(self.name()) 21 - .required("filename", SyntaxShape::Filepath, "the file to source") 21 + .required("path", SyntaxShape::Filepath, "the file to source") 22 22 .input_output_type(Type::Nothing, Type::Nothing) 23 23 .category(Category::Core) 24 24 } ··· 32 32 engine_state: &EngineState, 33 33 stack: &mut Stack, 34 34 call: &nu_protocol::engine::Call, 35 - input: PipelineData, 35 + _input: PipelineData, 36 36 ) -> Result<PipelineData, ShellError> { 37 - let filename: String = call.req(engine_state, stack, 0)?; 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 + } 38 45 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 - })?; 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))?; 50 53 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 + let mut working_set= StateWorkingSet::new(engine_state); 55 + let _ = working_set.add_file(path.filename(), contents.as_bytes()); 54 56 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 + // 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 + ); 57 64 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())?; 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 + } 81 85 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); 86 + queue_delta(working_set.delta); 85 87 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 - } 88 + eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::Empty).map(|d| d.body) 90 89 }
+1 -1
src/default_context.rs
··· 264 264 bind_command! { 265 265 ExportEnv, 266 266 LoadEnv, 267 - SourceEnv, 267 + // SourceEnv, 268 268 WithEnv, 269 269 ConfigNu, 270 270 ConfigEnv,
+31 -13
src/lib.rs
··· 29 29 use crate::{ 30 30 cmd::{ 31 31 Cd, Fetch, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Pwd, Random, Rm, Save, Source, Sys, 32 + source::eval_file, 32 33 }, 33 34 default_context::add_shell_command_context, 34 35 error::format_error, 35 36 globals::{ 36 - InterruptBool, apply_pending_deltas, get_pwd, print_to_console, set_interrupt, 37 + InterruptBool, apply_pending_deltas, get_pwd, get_vfs, print_to_console, set_interrupt, 37 38 }, 38 39 }; 39 40 use error::CommandError; ··· 67 68 static ENGINE_STATE: OnceLock<RwLock<EngineState>> = OnceLock::new(); 68 69 #[inline] 69 70 async fn read_engine_state() -> RwLockReadGuard<'static, EngineState> { 70 - ENGINE_STATE.get().unwrap().read().await 71 + unsafe { ENGINE_STATE.get().unwrap_unchecked() } 72 + .read() 73 + .await 71 74 } 72 75 #[inline] 73 76 async fn write_engine_state() -> RwLockWriteGuard<'static, EngineState> { 74 - ENGINE_STATE.get().unwrap().write().await 77 + unsafe { ENGINE_STATE.get().unwrap_unchecked() } 78 + .write() 79 + .await 75 80 } 76 81 77 82 static STACK: OnceLock<RwLock<Stack>> = OnceLock::new(); 78 83 #[inline] 79 84 async fn read_stack() -> RwLockReadGuard<'static, Stack> { 80 - STACK.get().unwrap().read().await 85 + unsafe { STACK.get().unwrap_unchecked() }.read().await 81 86 } 82 87 #[inline] 83 88 async fn write_stack() -> RwLockWriteGuard<'static, Stack> { 84 - STACK.get().unwrap().write().await 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()) 85 96 } 86 97 87 98 fn init_engine_internal() -> Result<(), Report> { 99 + let mut stack = Stack::new(); 88 100 let mut engine_state = create_default_context(); 89 101 engine_state = add_shell_command_context(engine_state); 90 102 engine_state = add_extra_command_context(engine_state); ··· 121 133 122 134 engine_state.set_signals(Signals::new(Arc::new(InterruptBool))); 123 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 + 124 144 ENGINE_STATE 125 145 .set(RwLock::new(engine_state)) 126 146 .map_err(|_| miette::miette!("ENGINE_STATE was already set!?"))?; 127 147 STACK 128 - .set(RwLock::new(Stack::new())) 148 + .set(RwLock::new(stack)) 129 149 .map_err(|_| miette::miette!("STACK was already set!?"))?; 130 150 131 151 // web_sys::console::log_1(&"Hello, World!".into()); ··· 133 153 Ok(()) 134 154 } 135 155 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 156 async fn run_command_internal(input: &str) -> Result<(), CommandError> { 143 - let mut engine_state = ENGINE_STATE.get().unwrap().upgradable_read().await; 157 + let mut engine_state = unsafe { ENGINE_STATE.get().unwrap_unchecked() } 158 + .upgradable_read() 159 + .await; 144 160 let (mut working_set, signals, config) = { 145 161 let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 146 162 apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { ··· 191 207 &block, 192 208 PipelineData::Empty, 193 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)?; 194 212 engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 195 213 res 196 214 };
+4 -1
www/src/worker.ts
··· 11 11 12 12 // Initialize WASM 13 13 await init(); 14 - init_engine(); 14 + const error = init_engine(); 15 + if (error) { 16 + console.error(error); 17 + } 15 18 16 19 // Setup Callbacks to proxy messages back to Main Thread 17 20 register_console_callback((msg: string, isCmd: boolean) => {