nushell on your web browser
nushell wasm terminal
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 9f22274ad1600ad1a5d5f38d30e83b601ee89b9c 231 lines 6.3 kB view raw
1use futures::stream::AbortHandle; 2use nu_protocol::Signal; 3use rust_embed::RustEmbed; 4use std::{ 5 collections::HashMap, 6 sync::{ 7 Arc, Mutex, OnceLock, RwLock, 8 atomic::{AtomicUsize, Ordering}, 9 }, 10 time::{Duration, SystemTime, UNIX_EPOCH}, 11}; 12use vfs::{EmbeddedFS, OverlayFS, VfsPath}; 13use wasm_bindgen::prelude::*; 14 15use crate::memory_fs::MemoryFS; 16 17static ROOT: OnceLock<Arc<VfsPath>> = OnceLock::new(); 18 19fn init_vfs() -> Arc<VfsPath> { 20 let memory_fs = VfsPath::new(MemoryFS::new()); 21 let embedded_fs = VfsPath::new(EmbeddedFS::<EmbeddedFiles>::new()); 22 let overlaid_fs = VfsPath::new(OverlayFS::new(&[memory_fs, embedded_fs])); 23 Arc::new(overlaid_fs) 24} 25 26pub fn get_vfs() -> Arc<VfsPath> { 27 ROOT.get_or_init(init_vfs).clone() 28} 29 30#[derive(RustEmbed, Debug)] 31#[folder = "embedded/"] 32#[exclude = ".gitkeep"] 33pub struct EmbeddedFiles; 34 35static PWD: OnceLock<RwLock<Arc<VfsPath>>> = OnceLock::new(); 36 37pub fn get_pwd() -> Arc<VfsPath> { 38 PWD.get_or_init(|| RwLock::new(get_vfs())) 39 .read() 40 .unwrap() 41 .clone() 42} 43 44pub fn set_pwd(path: Arc<VfsPath>) { 45 *PWD.get_or_init(|| RwLock::new(get_vfs())).write().unwrap() = path; 46} 47 48pub struct TaskInfo { 49 pub id: usize, 50 pub description: String, 51 pub handle: AbortHandle, 52} 53 54pub struct CallbackWrapper(js_sys::Function); 55unsafe impl Send for CallbackWrapper {} 56unsafe impl Sync for CallbackWrapper {} 57 58static NEXT_TASK_ID: AtomicUsize = AtomicUsize::new(1); 59static TASK_REGISTRY: OnceLock<Mutex<HashMap<usize, TaskInfo>>> = OnceLock::new(); 60static TASK_CALLBACK: OnceLock<Mutex<Option<CallbackWrapper>>> = OnceLock::new(); 61 62pub fn get_registry() -> &'static Mutex<HashMap<usize, TaskInfo>> { 63 TASK_REGISTRY.get_or_init(|| Mutex::new(HashMap::new())) 64} 65 66#[wasm_bindgen] 67pub fn register_task_count_callback(f: js_sys::Function) { 68 let _ = TASK_CALLBACK.get_or_init(|| Mutex::new(None)); 69 if let Ok(mut guard) = TASK_CALLBACK.get().unwrap().lock() { 70 *guard = Some(CallbackWrapper(f)); 71 } 72} 73 74pub fn notify_task_count() { 75 if let Some(mutex) = TASK_CALLBACK.get() { 76 if let Ok(guard) = mutex.lock() { 77 if let Some(cb) = guard.as_ref() { 78 let count = if let Ok(reg) = get_registry().lock() { 79 reg.len() 80 } else { 81 0 82 }; 83 let this = JsValue::NULL; 84 let arg = JsValue::from(count as u32); 85 let _ = cb.0.call1(&this, &arg); 86 } 87 } 88 } 89} 90 91pub fn register_task(description: String, handle: AbortHandle) -> usize { 92 let id = NEXT_TASK_ID.fetch_add(1, Ordering::Relaxed); 93 if let Ok(mut reg) = get_registry().lock() { 94 reg.insert( 95 id, 96 TaskInfo { 97 id, 98 description, 99 handle, 100 }, 101 ); 102 } 103 notify_task_count(); 104 id 105} 106 107pub fn remove_task(id: usize) { 108 if let Ok(mut reg) = get_registry().lock() { 109 reg.remove(&id); 110 } 111 notify_task_count(); 112} 113 114pub fn get_all_tasks() -> Vec<(usize, String)> { 115 if let Ok(reg) = get_registry().lock() { 116 reg.values() 117 .map(|t| (t.id, t.description.clone())) 118 .collect() 119 } else { 120 vec![] 121 } 122} 123 124pub fn kill_task_by_id(id: usize) -> bool { 125 if let Ok(mut reg) = get_registry().lock() { 126 if let Some(task) = reg.remove(&id) { 127 task.handle.abort(); 128 drop(reg); 129 notify_task_count(); 130 return true; 131 } 132 } 133 false 134} 135 136// static PENDING_DELTAS: OnceLock<Mutex<Vec<StateDelta>>> = OnceLock::new(); 137 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// } 144 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// } 155 156pub static CONSOLE_CALLBACK: OnceLock<Mutex<Option<CallbackWrapper>>> = OnceLock::new(); 157 158#[wasm_bindgen] 159pub fn register_console_callback(f: js_sys::Function) { 160 let _ = CONSOLE_CALLBACK.get_or_init(|| Mutex::new(None)); 161 if let Ok(mut guard) = CONSOLE_CALLBACK.get().unwrap().lock() { 162 *guard = Some(CallbackWrapper(f)); 163 } 164} 165 166pub fn print_to_console(msg: &str, is_cmd: bool) { 167 if let Some(mutex) = CONSOLE_CALLBACK.get() { 168 if let Ok(guard) = mutex.lock() { 169 if let Some(cb) = guard.as_ref() { 170 let this = JsValue::NULL; 171 let arg = JsValue::from_str(msg); 172 let arg2 = JsValue::from_bool(is_cmd); 173 let _ = cb.0.call2(&this, &arg, &arg2); 174 } 175 } 176 } 177} 178 179pub fn current_time() -> Option<SystemTime> { 180 UNIX_EPOCH.checked_add(Duration::from_millis(js_sys::Date::now() as u64)) 181} 182 183use js_sys::Int32Array; 184use std::cell::RefCell; 185 186// We use thread_local storage because the Wasm worker is single-threaded. 187// This holds the reference to the SharedArrayBuffer view passed from JS. 188thread_local! { 189 pub static INTERRUPT_BUFFER: RefCell<Option<Int32Array>> = RefCell::new(None); 190} 191 192#[wasm_bindgen] 193pub fn set_interrupt_buffer(buffer: Int32Array) { 194 INTERRUPT_BUFFER.with(|b| { 195 *b.borrow_mut() = Some(buffer); 196 }); 197} 198 199pub fn check_interrupt() -> bool { 200 INTERRUPT_BUFFER.with(|b| { 201 if let Some(buffer) = b.borrow().as_ref() { 202 match js_sys::Atomics::load(buffer, 0) { 203 Ok(1) => true, 204 _ => false, 205 } 206 } else { 207 false 208 } 209 }) 210} 211 212pub fn set_interrupt(value: bool) { 213 INTERRUPT_BUFFER.with(|b| { 214 if let Some(buffer) = b.borrow().as_ref() { 215 let _ = js_sys::Atomics::store(buffer, 0, value as i32); 216 } 217 }); 218} 219 220pub struct InterruptBool; 221 222impl Signal for InterruptBool { 223 #[inline] 224 fn get(&self) -> bool { 225 check_interrupt() 226 } 227 #[inline] 228 fn set(&self, value: bool) { 229 set_interrupt(value); 230 } 231}