nushell on your web browser
nushell wasm terminal

better sourcing (now called eval)

ptr.pet bab1df6f 76dc74a3

verified
+355 -257
+4 -1
src/cmd/cd.rs
··· 1 - use crate::globals::{get_pwd, get_vfs, set_pwd, to_shell_err}; 1 + use crate::{ 2 + error::to_shell_err, 3 + globals::{get_pwd, get_vfs, set_pwd}, 4 + }; 2 5 use nu_engine::CallExt; 3 6 use nu_protocol::{ 4 7 Category, IntoValue, PipelineData, ShellError, Signature, SyntaxShape, Type,
+61
src/cmd/eval.rs
··· 1 + use crate::globals::print_to_console; 2 + use nu_engine::CallExt; 3 + use nu_protocol::engine::Call; 4 + use nu_protocol::{ 5 + Category, PipelineData, ShellError, Signature, 6 + engine::{Command, EngineState, Stack}, 7 + }; 8 + use nu_protocol::{SyntaxShape, Type}; 9 + 10 + #[derive(Clone)] 11 + pub struct Eval; 12 + 13 + impl Command for Eval { 14 + fn name(&self) -> &str { 15 + "eval" 16 + } 17 + 18 + fn signature(&self) -> Signature { 19 + Signature::build(self.name()) 20 + .optional("code", SyntaxShape::String, "code to evaluate") 21 + .input_output_type(Type::one_of([Type::Nothing, Type::String]), Type::Nothing) 22 + .category(Category::FileSystem) 23 + } 24 + 25 + fn description(&self) -> &str { 26 + "evaluates a string as nushell code." 27 + } 28 + 29 + fn run( 30 + &self, 31 + engine_state: &EngineState, 32 + stack: &mut Stack, 33 + call: &Call, 34 + input: PipelineData, 35 + ) -> Result<PipelineData, ShellError> { 36 + let code: Option<String> = call.opt(engine_state, stack, 0)?; 37 + 38 + let (span, code) = match code { 39 + Some(c) => (Some(call.arguments_span()), c), 40 + None => ( 41 + input.span(), 42 + input.collect_string("\n", &engine_state.config)?, 43 + ), 44 + }; 45 + 46 + match super::source_file::eval(engine_state, stack, &code, None) { 47 + Ok(d) => Ok(d), 48 + Err(err) => { 49 + let msg: String = err.into(); 50 + print_to_console(&msg, true); 51 + Err(ShellError::GenericError { 52 + error: "source error".into(), 53 + msg: "can't source string".into(), 54 + span, 55 + help: None, 56 + inner: vec![], 57 + }) 58 + } 59 + } 60 + } 61 + }
+2 -1
src/cmd/fetch.rs
··· 1 - use crate::globals::{get_pwd, print_to_console, register_task, remove_task, to_shell_err}; 1 + use crate::error::to_shell_err; 2 + use crate::globals::{get_pwd, print_to_console, register_task, remove_task}; 2 3 use anyhow::{Result, anyhow}; 3 4 use futures::future::{AbortHandle, Abortable}; 4 5 use jacquard::types::aturi::AtUri;
+1 -1
src/cmd/ls.rs
··· 3 3 time::{SystemTime, UNIX_EPOCH}, 4 4 }; 5 5 6 - use crate::globals::{get_pwd, to_shell_err}; 6 + use crate::{error::to_shell_err, globals::get_pwd}; 7 7 use jacquard::chrono; 8 8 use nu_engine::CallExt; 9 9 use nu_protocol::{
+1 -1
src/cmd/mkdir.rs
··· 1 - use crate::globals::{get_pwd, to_shell_err}; 1 + use crate::{error::to_shell_err, globals::get_pwd}; 2 2 use nu_engine::CallExt; 3 3 use nu_protocol::{ 4 4 Category, PipelineData, ShellError, Signature, SyntaxShape, Type,
+4 -2
src/cmd/mod.rs
··· 1 1 pub mod cd; 2 + pub mod eval; 2 3 pub mod fetch; 3 4 pub mod job; 4 5 pub mod job_kill; ··· 12 13 pub mod random; 13 14 pub mod rm; 14 15 pub mod save; 15 - pub mod source; 16 + pub mod source_file; 16 17 pub mod sys; 17 18 18 19 pub use cd::Cd; 20 + pub use eval::Eval; 19 21 pub use fetch::Fetch; 20 22 pub use job::Job; 21 23 pub use job_kill::JobKill; ··· 29 31 pub use random::Random; 30 32 pub use rm::Rm; 31 33 pub use save::Save; 32 - pub use source::Source; 34 + pub use source_file::SourceFile; 33 35 pub use sys::Sys;
+1 -1
src/cmd/mv.rs
··· 1 1 use std::io::{Read, Write}; 2 2 3 - use crate::globals::{get_pwd, to_shell_err}; 3 + use crate::{error::to_shell_err, globals::get_pwd}; 4 4 use nu_engine::CallExt; 5 5 use nu_protocol::{ 6 6 Category, PipelineData, ShellError, Signature, SyntaxShape, Type,
+1 -1
src/cmd/open.rs
··· 1 1 use std::ops::Not; 2 2 3 - use crate::globals::{get_pwd, to_shell_err}; 3 + use crate::{error::to_shell_err, globals::get_pwd}; 4 4 use nu_command::{FromCsv, FromJson, FromOds, FromToml, FromTsv, FromXlsx, FromXml, FromYaml}; 5 5 use nu_engine::CallExt; 6 6 use nu_protocol::{
+3 -4
src/cmd/print.rs
··· 32 32 _input: PipelineData, 33 33 ) -> Result<PipelineData, ShellError> { 34 34 let rest: Vec<Value> = call.rest(engine_state, stack, 0)?; 35 - 35 + 36 36 let mut parts = Vec::new(); 37 37 for value in rest { 38 38 let s = value.to_expanded_string(" ", &engine_state.config); 39 39 parts.push(s); 40 40 } 41 41 let output = parts.join(" "); 42 - print_to_console(&output, true)?; 43 - 42 + print_to_console(&output, true); 43 + 44 44 Ok(PipelineData::Empty) 45 45 } 46 46 } 47 -
+1 -1
src/cmd/rm.rs
··· 1 - use crate::globals::{get_pwd, to_shell_err}; 1 + use crate::{error::to_shell_err, globals::get_pwd}; 2 2 use nu_engine::CallExt; 3 3 use nu_protocol::{ 4 4 Category, PipelineData, ShellError, Signature, SyntaxShape, Type,
+1 -1
src/cmd/save.rs
··· 1 - use crate::globals::{get_pwd, to_shell_err}; 1 + use crate::{error::to_shell_err, globals::get_pwd}; 2 2 use nu_engine::CallExt; 3 3 use nu_protocol::{ 4 4 Category, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
-89
src/cmd/source.rs
··· 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 - } 25 - 26 - fn description(&self) -> &str { 27 - "source a file from the virtual filesystem." 28 - } 29 - 30 - fn run( 31 - &self, 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 - }
+104
src/cmd/source_file.rs
··· 1 + use crate::{ 2 + error::{CommandError, to_shell_err}, 3 + globals::{get_pwd, print_to_console}, 4 + }; 5 + use nu_engine::{CallExt, get_eval_block_with_early_return}; 6 + use nu_parser::parse; 7 + use nu_protocol::{ 8 + Category, PipelineData, ShellError, Signature, SyntaxShape, Type, 9 + engine::{Command, EngineState, Stack, StateWorkingSet}, 10 + }; 11 + 12 + #[derive(Clone)] 13 + pub struct SourceFile; 14 + 15 + impl Command for SourceFile { 16 + fn name(&self) -> &str { 17 + "eval file" 18 + } 19 + 20 + fn signature(&self) -> Signature { 21 + Signature::build(self.name()) 22 + .required("path", SyntaxShape::Filepath, "the file to source") 23 + .input_output_type(Type::Nothing, Type::Nothing) 24 + .category(Category::Core) 25 + } 26 + 27 + fn description(&self) -> &str { 28 + "sources a file from the virtual filesystem." 29 + } 30 + 31 + fn run( 32 + &self, 33 + engine_state: &EngineState, 34 + stack: &mut Stack, 35 + call: &nu_protocol::engine::Call, 36 + _input: PipelineData, 37 + ) -> Result<PipelineData, ShellError> { 38 + let span = call.arguments_span(); 39 + let path: String = call.req(engine_state, stack, 0)?; 40 + 41 + let path = get_pwd().join(&path).map_err(to_shell_err(span))?; 42 + let contents = path.read_to_string().map_err(to_shell_err(span))?; 43 + 44 + let res = eval(engine_state, stack, &contents, Some(&path.filename())); 45 + 46 + match res { 47 + Ok(d) => Ok(d), 48 + Err(err) => { 49 + let msg: String = err.into(); 50 + print_to_console(&msg, true); 51 + Err(ShellError::GenericError { 52 + error: "source error".into(), 53 + msg: "can't source file".into(), 54 + span: Some(span), 55 + help: None, 56 + inner: vec![], 57 + }) 58 + } 59 + } 60 + } 61 + } 62 + 63 + pub fn eval( 64 + engine_state: &EngineState, 65 + stack: &mut Stack, 66 + contents: &str, 67 + filename: Option<&str>, 68 + ) -> Result<PipelineData, CommandError> { 69 + let filename = filename.unwrap_or("<piped data>"); 70 + let mut working_set = StateWorkingSet::new(engine_state); 71 + let start_offset = working_set.next_span_start(); 72 + let _ = working_set.add_file(filename.into(), contents.as_bytes()); 73 + 74 + // we dont need the block here just the delta 75 + let block = parse(&mut working_set, Some(filename), contents.as_bytes(), false); 76 + 77 + if let Some(err) = working_set.parse_errors.into_iter().next() { 78 + web_sys::console::error_1(&err.to_string().into()); 79 + return Err(CommandError::new(err, contents).with_start_offset(start_offset)); 80 + } 81 + if let Some(err) = working_set.compile_errors.into_iter().next() { 82 + web_sys::console::error_1(&err.to_string().into()); 83 + return Err(CommandError::new(err, contents).with_start_offset(start_offset)); 84 + } 85 + 86 + // uhhhhh this is safe prolly cuz we are single threaded 87 + // i mean still shouldnt do this but i lowkey dont care so :3 88 + let engine_state = unsafe { 89 + std::ptr::from_ref(engine_state) 90 + .cast_mut() 91 + .as_mut() 92 + .unwrap() 93 + }; 94 + engine_state 95 + .merge_delta(working_set.delta) 96 + .map_err(|err| CommandError::new(err, contents).with_start_offset(start_offset))?; 97 + 98 + // queue_delta(working_set.delta.clone()); 99 + 100 + let eval_block_with_early_return = get_eval_block_with_early_return(&engine_state); 101 + eval_block_with_early_return(&engine_state, stack, &block, PipelineData::Empty) 102 + .map(|d| d.body) 103 + .map_err(|err| CommandError::new(err, contents).with_start_offset(start_offset)) 104 + }
+71 -17
src/error.rs
··· 1 - use miette::{GraphicalReportHandler, Report, SourceCode, SourceSpan, SpanContents}; 1 + use miette::{Diagnostic, GraphicalReportHandler, Report, SourceCode, SourceSpan, SpanContents}; 2 + use nu_protocol::{ShellError, Span}; 3 + use vfs::{VfsError, error::VfsErrorKind}; 2 4 3 5 pub struct CommandError { 4 6 pub error: Report, 5 7 pub start_offset: usize, 8 + pub input: String, 9 + } 10 + 11 + impl CommandError { 12 + pub fn new<E>(error: E, input: impl Into<String>) -> Self 13 + where 14 + E: Diagnostic + Clone + Send + Sync + 'static, 15 + { 16 + Self { 17 + error: Report::new(error), 18 + start_offset: 0, 19 + input: input.into(), 20 + } 21 + } 22 + 23 + pub fn with_start_offset(mut self, start_offset: usize) -> Self { 24 + self.start_offset = start_offset; 25 + self 26 + } 27 + } 28 + 29 + impl From<ShellError> for CommandError { 30 + fn from(value: ShellError) -> Self { 31 + CommandError::new(value, String::new()) 32 + } 33 + } 34 + 35 + impl From<CommandError> for String { 36 + fn from(value: CommandError) -> Self { 37 + let handler = GraphicalReportHandler::new() 38 + .with_theme(miette::GraphicalTheme::unicode()) 39 + .with_cause_chain(); 40 + 41 + if value.input.is_empty() { 42 + let mut msg = String::new(); 43 + handler 44 + .render_report(&mut msg, value.error.as_ref()) 45 + .unwrap(); 46 + return msg; 47 + } 48 + 49 + let source = OffsetSource { 50 + source: value.input, 51 + start_offset: value.start_offset, 52 + }; 53 + 54 + let report_with_source = value.error.with_source_code(source); 55 + let mut msg = String::new(); 56 + handler 57 + .render_report(&mut msg, report_with_source.as_ref()) 58 + .unwrap(); 59 + msg 60 + } 6 61 } 7 62 8 63 pub struct OffsetSource { ··· 59 114 } 60 115 } 61 116 62 - pub fn format_error(error: Report, input: String, start_offset: usize) -> String { 63 - let handler = GraphicalReportHandler::new() 64 - .with_theme(miette::GraphicalTheme::unicode()) 65 - .with_cause_chain(); 66 - 67 - let source = OffsetSource { 68 - source: input, 69 - start_offset, 70 - }; 71 - 72 - let report_with_source = error.with_source_code(source); 73 - let mut output = String::new(); 74 - handler 75 - .render_report(&mut output, report_with_source.as_ref()) 76 - .unwrap(); 77 - output 117 + pub fn to_shell_err(span: Span) -> impl Fn(VfsError) -> ShellError { 118 + move |vfs_error: VfsError| ShellError::GenericError { 119 + error: (match vfs_error.kind() { 120 + VfsErrorKind::DirectoryExists 121 + | VfsErrorKind::FileExists 122 + | VfsErrorKind::FileNotFound 123 + | VfsErrorKind::InvalidPath => "path error", 124 + _ => "io error", 125 + }) 126 + .to_string(), 127 + msg: vfs_error.to_string(), 128 + span: Some(span), 129 + help: None, 130 + inner: vec![], 131 + } 78 132 }
+20 -51
src/globals.rs
··· 1 1 use futures::stream::AbortHandle; 2 - use nu_protocol::{ 3 - ShellError, Signal, Span, 4 - engine::{EngineState, StateDelta}, 5 - }; 2 + use nu_protocol::Signal; 6 3 use rust_embed::RustEmbed; 7 4 use std::{ 8 5 collections::HashMap, ··· 12 9 }, 13 10 time::{Duration, SystemTime, UNIX_EPOCH}, 14 11 }; 15 - use vfs::{EmbeddedFS, OverlayFS, VfsError, VfsPath, error::VfsErrorKind}; 12 + use vfs::{EmbeddedFS, OverlayFS, VfsPath}; 16 13 use wasm_bindgen::prelude::*; 17 14 18 15 use crate::memory_fs::MemoryFS; ··· 46 43 47 44 pub fn set_pwd(path: Arc<VfsPath>) { 48 45 *PWD.get_or_init(|| RwLock::new(get_vfs())).write().unwrap() = path; 49 - } 50 - 51 - pub fn to_shell_err(span: Span) -> impl Fn(VfsError) -> ShellError { 52 - move |vfs_error: VfsError| ShellError::GenericError { 53 - error: (match vfs_error.kind() { 54 - VfsErrorKind::DirectoryExists 55 - | VfsErrorKind::FileExists 56 - | VfsErrorKind::FileNotFound 57 - | VfsErrorKind::InvalidPath => "path error", 58 - _ => "io error", 59 - }) 60 - .to_string(), 61 - msg: vfs_error.to_string(), 62 - span: Some(span), 63 - help: None, 64 - inner: vec![], 65 - } 66 46 } 67 47 68 48 pub struct TaskInfo { ··· 153 133 false 154 134 } 155 135 156 - static PENDING_DELTAS: OnceLock<Mutex<Vec<StateDelta>>> = OnceLock::new(); 136 + // static PENDING_DELTAS: OnceLock<Mutex<Vec<StateDelta>>> = OnceLock::new(); 157 137 158 - pub fn queue_delta(delta: StateDelta) { 159 - let _ = PENDING_DELTAS.get_or_init(|| Mutex::new(Vec::new())); 160 - if let Ok(mut guard) = PENDING_DELTAS.get().unwrap().lock() { 161 - guard.push(delta); 162 - } 163 - } 138 + // pub fn queue_delta(delta: StateDelta) { 139 + // let _ = PENDING_DELTAS.get_or_init(|| Mutex::new(Vec::new())); 140 + // if let Ok(mut guard) = PENDING_DELTAS.get().unwrap().lock() { 141 + // guard.push(delta); 142 + // } 143 + // } 164 144 165 - pub fn apply_pending_deltas(engine_state: &mut EngineState) -> Result<(), ShellError> { 166 - if let Some(mutex) = PENDING_DELTAS.get() { 167 - if let Ok(mut guard) = mutex.lock() { 168 - for delta in guard.drain(..) { 169 - engine_state.merge_delta(delta)?; 170 - } 171 - } 172 - } 173 - Ok(()) 174 - } 145 + // pub fn apply_pending_deltas(engine_state: &mut EngineState) -> Result<(), ShellError> { 146 + // if let Some(mutex) = PENDING_DELTAS.get() { 147 + // if let Ok(mut guard) = mutex.lock() { 148 + // for delta in guard.drain(..) { 149 + // engine_state.merge_delta(delta)?; 150 + // } 151 + // } 152 + // } 153 + // Ok(()) 154 + // } 175 155 176 156 pub static CONSOLE_CALLBACK: OnceLock<Mutex<Option<CallbackWrapper>>> = OnceLock::new(); 177 157 ··· 183 163 } 184 164 } 185 165 186 - pub fn print_to_console(msg: &str, is_cmd: bool) -> Result<(), ShellError> { 187 - // if is_interrupted() { 188 - // return Err(ShellError::Interrupted { 189 - // span: Span::unknown(), 190 - // }); 191 - // } 166 + pub fn print_to_console(msg: &str, is_cmd: bool) { 192 167 if let Some(mutex) = CONSOLE_CALLBACK.get() { 193 168 if let Ok(guard) = mutex.lock() { 194 169 if let Some(cb) = guard.as_ref() { ··· 199 174 } 200 175 } 201 176 } 202 - Ok(()) 203 177 } 204 178 205 179 pub fn current_time() -> Option<SystemTime> { ··· 215 189 pub static INTERRUPT_BUFFER: RefCell<Option<Int32Array>> = RefCell::new(None); 216 190 } 217 191 218 - /// Called from JS to pass the SharedArrayBuffer view 219 192 #[wasm_bindgen] 220 193 pub fn set_interrupt_buffer(buffer: Int32Array) { 221 194 INTERRUPT_BUFFER.with(|b| { ··· 223 196 }); 224 197 } 225 198 226 - /// Call this function periodically in your long-running loops! 227 - /// Returns `true` if an interrupt was requested. 228 199 pub fn check_interrupt() -> bool { 229 200 INTERRUPT_BUFFER.with(|b| { 230 201 if let Some(buffer) = b.borrow().as_ref() { 231 - // Check index 0. If it's 1, an interrupt occurred. 232 - // We use Atomics to ensure we see the value written by the main thread. 233 202 match js_sys::Atomics::load(buffer, 0) { 234 203 Ok(1) => true, 235 204 _ => false,
+55 -66
src/lib.rs
··· 1 1 use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard}; 2 - use futures::FutureExt; 2 + use futures::TryFutureExt; 3 3 use js_sys::Promise; 4 - use miette::Report; 5 4 use nu_cmd_base::hook::eval_hook; 6 5 use nu_cmd_extra::add_extra_command_context; 7 6 use nu_cmd_lang::create_default_context; ··· 12 11 engine::{EngineState, Stack, StateWorkingSet}, 13 12 }; 14 13 use std::{ 14 + fmt::Write, 15 15 io::Cursor, 16 16 sync::{Arc, OnceLock}, 17 17 }; ··· 28 28 29 29 use crate::{ 30 30 cmd::{ 31 - Cd, Fetch, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm, Save, Source, Sys, 32 - source::eval_file, 31 + Cd, Eval, Fetch, Job, JobKill, JobList, Ls, Mkdir, Mv, Open, Print, Pwd, Random, Rm, Save, 32 + SourceFile, Sys, 33 33 }, 34 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 - }, 35 + globals::{InterruptBool, get_pwd, get_vfs, print_to_console, set_interrupt}, 39 36 }; 40 37 use error::CommandError; 41 38 ··· 90 87 } 91 88 92 89 #[wasm_bindgen] 93 - pub fn init_engine() -> String { 90 + pub fn init_engine() -> Promise { 94 91 std::panic::set_hook(Box::new(panic_hook)); 95 - init_engine_internal().map_or_else(|err| format!("error: {err}"), |_| String::new()) 92 + future_to_promise( 93 + init_engine_internal() 94 + .map_ok(|_| JsValue::null()) 95 + .map_err(|s| JsValue::from_str(&s)), 96 + ) 96 97 } 97 98 98 - fn init_engine_internal() -> Result<(), Report> { 99 - let mut stack = Stack::new(); 99 + async fn init_engine_internal() -> Result<(), String> { 100 100 let mut engine_state = create_default_context(); 101 101 engine_state = add_shell_command_context(engine_state); 102 102 engine_state = add_extra_command_context(engine_state); 103 103 104 104 let mut working_set = StateWorkingSet::new(&engine_state); 105 - let decls: [Box<dyn Command>; 16] = [ 105 + let decls: [Box<dyn Command>; 17] = [ 106 106 Box::new(Ls), 107 107 Box::new(Open), 108 108 Box::new(Save), ··· 112 112 Box::new(Cd), 113 113 Box::new(Rm), 114 114 Box::new(Fetch), 115 - Box::new(Source), 115 + Box::new(SourceFile), 116 + Box::new(Eval), 116 117 Box::new(Job), 117 118 Box::new(JobList), 118 119 Box::new(JobKill), ··· 123 124 for decl in decls { 124 125 working_set.add_decl(decl); 125 126 } 126 - engine_state.merge_delta(working_set.delta)?; 127 + engine_state 128 + .merge_delta(working_set.delta) 129 + .map_err(CommandError::from)?; 127 130 128 131 let mut config = Config::default(); 129 132 config.use_ansi_coloring = true.into(); ··· 134 137 135 138 engine_state.set_signals(Signals::new(Arc::new(InterruptBool))); 136 139 140 + ENGINE_STATE 141 + .set(RwLock::new(engine_state)) 142 + .map_err(|_| "ENGINE_STATE was already set!?".to_string())?; 143 + STACK 144 + .set(RwLock::new(Stack::new())) 145 + .map_err(|_| "STACK was already set!?".to_string())?; 146 + 147 + let mut startup_script = String::new(); 148 + 137 149 // source our "nu rc" 138 150 let rc_path = get_vfs().join("/.env.nu").ok(); 139 151 let rc = rc_path.and_then(|env| env.exists().ok().and_then(|ok| ok.then_some(env))); 140 152 if let Some(env) = rc { 141 - web_sys::console::log_1(&format!("Loading rc file: {}", env.as_str()).into()); 142 - eval_file(&engine_state, &mut stack, &env, Span::unknown())?; 153 + writeln!(&mut startup_script, "eval file {path}", path = env.as_str()).unwrap(); 143 154 } 144 155 145 - ENGINE_STATE 146 - .set(RwLock::new(engine_state)) 147 - .map_err(|_| miette::miette!("ENGINE_STATE was already set!?"))?; 148 - STACK 149 - .set(RwLock::new(stack)) 150 - .map_err(|_| miette::miette!("STACK was already set!?"))?; 156 + // add some aliases for some commands 157 + let aliases = ["alias l = ls", "alias la = ls -a", "alias . = eval file"]; 158 + for alias in aliases { 159 + writeln!(&mut startup_script, "{alias}").unwrap(); 160 + } 151 161 152 - // web_sys::console::log_1(&"Hello, World!".into()); 162 + run_command_internal(&startup_script).await?; 153 163 154 164 Ok(()) 155 165 } 156 166 157 - async fn run_command_internal(input: &str) -> Result<(), CommandError> { 167 + async fn run_command_internal(input: &str) -> Result<(), String> { 158 168 let mut engine_state = unsafe { ENGINE_STATE.get().unwrap_unchecked() } 159 169 .upgradable_read() 160 170 .await; 161 171 let (mut working_set, signals, config) = { 162 172 let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 163 - apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { 164 - error: Report::new(e), 165 - start_offset: 0, 166 - })?; 173 + // apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { 174 + // error: Report::new(e), 175 + // start_offset: 0, 176 + // })?; 167 177 write_engine_state.add_env_var( 168 178 "PWD".to_string(), 169 179 get_pwd_string().into_value(Span::unknown()), ··· 179 189 let start_offset = working_set.next_span_start(); 180 190 let block = parse(&mut working_set, Some("entry"), input.as_bytes(), false); 181 191 182 - let cmd_err = |err: ShellError| CommandError { 183 - error: Report::new(err), 184 - start_offset, 185 - }; 192 + let cmd_err = |err: ShellError| CommandError::new(err, input).with_start_offset(start_offset); 186 193 187 194 if let Some(err) = working_set.parse_errors.into_iter().next() { 188 - return Err(CommandError { 189 - error: Report::new(err), 190 - start_offset, 191 - }); 195 + return Err(CommandError::new(err, input) 196 + .with_start_offset(start_offset) 197 + .into()); 192 198 } 193 199 if let Some(err) = working_set.compile_errors.into_iter().next() { 194 - return Err(CommandError { 195 - error: Report::new(err), 196 - start_offset, 197 - }); 200 + return Err(CommandError::new(err, input) 201 + .with_start_offset(start_offset) 202 + .into()); 198 203 } 199 204 let delta = working_set.delta; 200 205 ··· 202 207 let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 203 208 let mut stack = write_stack().await; 204 209 write_engine_state.merge_delta(delta).map_err(cmd_err)?; 210 + engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 205 211 let res = eval_block::<nu_protocol::debugger::WithoutDebug>( 206 - &mut write_engine_state, 212 + &engine_state, 207 213 &mut stack, 208 214 &block, 209 215 PipelineData::Empty, 210 216 ); 211 - // Apply any deltas queued during command execution (e.g., from source-file) 212 - apply_pending_deltas(&mut write_engine_state).map_err(cmd_err)?; 213 - engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 217 + // apply_pending_deltas(&mut write_engine_state).map_err(cmd_err)?; 214 218 res 215 219 }; 216 220 ··· 224 228 let pipeline_data = match pipeline_data { 225 229 PipelineData::Empty => return Ok(()), 226 230 PipelineData::Value(Value::Error { error, .. }, _) => { 227 - return Err(cmd_err(*error)); 231 + return Err(cmd_err(*error).into()); 228 232 } 229 233 PipelineData::ByteStream(s, m) => match (s.span(), s.type_(), s.reader()) { 230 234 (span, ty, Some(r)) => { ··· 272 276 match res { 273 277 PipelineData::Empty => {} 274 278 PipelineData::Value(v, _) => { 275 - print_to_console(&v.to_expanded_string("\n", &config), true).map_err(cmd_err)?; 279 + print_to_console(&v.to_expanded_string("\n", &config), true); 276 280 } 277 281 PipelineData::ByteStream(s, _) => { 278 282 for line in s.lines().into_iter().flatten() { 279 283 let out = line.map_err(cmd_err)?; // TODO: do we turn this into a Value ??? or is returning err fine 280 - print_to_console(&out, true).map_err(cmd_err)?; 284 + print_to_console(&out, true); 281 285 } 282 286 } 283 287 PipelineData::ListStream(s, _) => { ··· 286 290 .unwrap_error() 287 291 .map_err(cmd_err)? 288 292 .to_expanded_string("\n", &config); 289 - print_to_console(&out, true).map_err(cmd_err)?; 293 + print_to_console(&out, true); 290 294 } 291 295 } 292 296 } ··· 300 304 301 305 future_to_promise(async move { 302 306 run_command_internal(&input) 303 - .map(|res| { 304 - res.map_or_else( 305 - |cmd_err| { 306 - Some(format_error( 307 - cmd_err.error, 308 - input.to_owned(), 309 - cmd_err.start_offset, 310 - )) 311 - }, 312 - |_| None, 313 - ) 314 - }) 315 - .map(|res| { 316 - Ok(res 317 - .map(|s| JsValue::from_str(&s)) 318 - .unwrap_or_else(JsValue::null)) 319 - }) 307 + .map_ok(|_| JsValue::null()) 308 + .map_err(|s| JsValue::from_str(&s)) 320 309 .await 321 310 }) 322 311 }
+22 -18
www/src/main.ts
··· 404 404 "\x1b[33mengine is still loading, please wait (_ _ )zZ...\x1b[0m\r\n", 405 405 ); 406 406 } else { 407 - // ASYNC execution 408 - const output: string | undefined = await callWorker( 409 - "run", 410 - trimmed, 411 - ); 412 - if (output) { 413 - term.write(output.replace(/\n/g, "\r\n")); 414 - if (output && !output.endsWith("\n")) { 415 - term.write("\r\n"); 407 + try { 408 + const output: string | undefined = await callWorker( 409 + "run", 410 + trimmed, 411 + ); 412 + if (output) { 413 + term.write(output.replace(/\n/g, "\r\n")); 414 + if (output && !output.endsWith("\n")) { 415 + term.write("\r\n"); 416 + } 416 417 } 418 + 419 + // update history 420 + const idx = history.indexOf(trimmed); 421 + if (idx >= 0) history.splice(idx, 1); 422 + history.push(trimmed); 423 + historyIndex = history.length; 424 + } catch (error) { 425 + term.write(`${error}`.replace(/\n/g, "\r\n")); 417 426 } 418 427 419 - // Update cached PWD after command execution (cd, etc) 428 + // update pwd 420 429 cachedPwd = await callWorker("get_pwd"); 421 - 422 - // Update History 423 - const idx = history.indexOf(trimmed); 424 - if (idx >= 0) history.splice(idx, 1); 425 - history.push(trimmed); 426 - historyIndex = history.length; 427 430 } 428 431 } catch (err) { 429 - term.write(`\x1b[31mfatal: ${err}\x1b[0m\r\n`); 432 + term.write( 433 + `\x1b[31mfatal: ${err}\x1b[0m\r\n`.replace(/\n/g, "\r\n"), 434 + ); 430 435 } finally { 431 436 isRunningCommand = false; 432 437 } ··· 647 652 await readyPromise; 648 653 649 654 await callWorker("set-interrupt-buffer", interruptBuffer); 650 - await callWorker("run", "open welcome.txt"); 651 655 652 656 term.write(getPrompt()); 653 657
+3 -2
www/src/worker.ts
··· 11 11 12 12 // Initialize WASM 13 13 await init(); 14 - const error = init_engine(); 15 - if (error) { 14 + try { 15 + await init_engine(); 16 + } catch (error) { 16 17 console.error(error); 17 18 } 18 19