nushell on your web browser
nushell wasm terminal
2
fork

Configure Feed

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

use async locks for later i guess?

ptr.pet 6c915e4a bba4e7b5

verified
+184 -108
+42
Cargo.lock
··· 200 200 ] 201 201 202 202 [[package]] 203 + name = "async-lock" 204 + version = "3.4.1" 205 + source = "registry+https://github.com/rust-lang/crates.io-index" 206 + checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" 207 + dependencies = [ 208 + "event-listener", 209 + "event-listener-strategy", 210 + "pin-project-lite", 211 + ] 212 + 213 + [[package]] 203 214 name = "atoi_simd" 204 215 version = "0.16.1" 205 216 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 712 723 version = "0.4.31" 713 724 source = "registry+https://github.com/rust-lang/crates.io-index" 714 725 checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 726 + 727 + [[package]] 728 + name = "concurrent-queue" 729 + version = "2.5.0" 730 + source = "registry+https://github.com/rust-lang/crates.io-index" 731 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 732 + dependencies = [ 733 + "crossbeam-utils", 734 + ] 715 735 716 736 [[package]] 717 737 name = "console" ··· 1302 1322 ] 1303 1323 1304 1324 [[package]] 1325 + name = "event-listener" 1326 + version = "5.4.1" 1327 + source = "registry+https://github.com/rust-lang/crates.io-index" 1328 + checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 1329 + dependencies = [ 1330 + "concurrent-queue", 1331 + "parking", 1332 + "pin-project-lite", 1333 + ] 1334 + 1335 + [[package]] 1336 + name = "event-listener-strategy" 1337 + version = "0.5.4" 1338 + source = "registry+https://github.com/rust-lang/crates.io-index" 1339 + checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" 1340 + dependencies = [ 1341 + "event-listener", 1342 + "pin-project-lite", 1343 + ] 1344 + 1345 + [[package]] 1305 1346 name = "fancy-regex" 1306 1347 version = "0.16.2" 1307 1348 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1330 1371 dependencies = [ 1331 1372 "ansi_term", 1332 1373 "anyhow", 1374 + "async-lock", 1333 1375 "console_error_panic_hook", 1334 1376 "futures", 1335 1377 "getrandom 0.3.4",
+1
Cargo.toml
··· 37 37 38 38 scc = "3" 39 39 rapidhash = { version = "4", features = ["unsafe"] } 40 + async-lock = "3.4.1" 40 41 41 42 [patch.crates-io] 42 43 vfs = { git = "https://github.com/landaire/rust-vfs", branch = "fix/wasm" }
+23 -17
src/completion.rs
··· 1 + use futures::FutureExt; 2 + use js_sys::Promise; 3 + use wasm_bindgen_futures::future_to_promise; 4 + 1 5 use super::*; 2 6 3 7 #[derive(Debug, Serialize)] ··· 28 32 } 29 33 30 34 #[wasm_bindgen] 31 - pub fn completion(input: &str, js_cursor_pos: usize) -> String { 32 - let engine_guard = ENGINE_STATE 33 - .get() 34 - .unwrap() 35 - .lock() 36 - .expect("engine state initialized"); 35 + pub fn completion(input: String, js_cursor_pos: usize) -> Promise { 36 + future_to_promise(completion_impl(input, js_cursor_pos).map(|s| Ok(JsValue::from_str(&s)))) 37 + } 38 + 39 + pub async fn completion_impl(input: String, js_cursor_pos: usize) -> String { 40 + let engine_guard = read_engine_state().await; 37 41 let root = get_pwd(); 38 42 39 43 // Map UTF-16 cursor position (from JS) to Byte index (for Rust) ··· 43 47 .nth(js_cursor_pos) 44 48 .unwrap_or(input.len()); 45 49 46 - // 1. Parse & Flatten 47 - let mut working_set = StateWorkingSet::new(&engine_guard); 48 - // CRITICAL: Capture the start offset so we can normalize spans later 49 - let global_offset = working_set.next_span_start(); 50 - let block = parse(&mut working_set, None, input.as_bytes(), false); 51 - let shapes = flatten_block(&working_set, &block); 50 + let (working_set, shapes, global_offset) = { 51 + let mut working_set = StateWorkingSet::new(&engine_guard); 52 + let global_offset = working_set.next_span_start(); 53 + let block = parse(&mut working_set, None, input.as_bytes(), false); 54 + let shapes = flatten_block(&working_set, &block); 55 + (working_set, shapes, global_offset) 56 + }; 52 57 53 58 // 2. Identify context 54 59 let mut is_command_pos = false; ··· 92 97 } 93 98 } 94 99 100 + let cmds = 101 + working_set.find_commands_by_predicate(|value| value.starts_with(prefix.as_bytes()), true); 102 + drop(working_set); 103 + drop(engine_guard); 104 + 95 105 // Fallback to Lexer if in whitespace 96 106 if !found_shape { 97 107 let (tokens, _err) = lex(input.as_bytes(), 0, &[], &[], true); ··· 146 156 }); 147 157 }; 148 158 149 - let working_set = StateWorkingSet::new(&engine_guard); 150 - 151 159 if is_command_pos { 152 - for (_, name, desc, _) in working_set 153 - .find_commands_by_predicate(|value| value.starts_with(prefix.as_bytes()), true) 154 - { 160 + for (_, name, desc, _) in cmds { 155 161 add_cmd_suggestion(name, desc); 156 162 } 157 163 } else {
+15 -15
src/highlight.rs
··· 1 + use futures::FutureExt; 2 + use js_sys::Promise; 3 + use wasm_bindgen_futures::future_to_promise; 4 + 1 5 use super::*; 2 6 3 7 #[wasm_bindgen] 4 - pub fn highlight(input: &str) -> String { 5 - let engine_guard = ENGINE_STATE 6 - .get() 7 - .unwrap() 8 - .lock() 9 - .expect("engine state initialized"); 10 - let mut working_set = StateWorkingSet::new(&engine_guard); 11 - 12 - // Capture the global offset before parsing, as parse will generate spans relative to this 13 - let global_offset = working_set.next_span_start(); 14 - 15 - // Parse the input block using Nushell's full parser 16 - let block = parse(&mut working_set, None, input.as_bytes(), false); 8 + pub fn highlight(input: String) -> Promise { 9 + future_to_promise(highlight_impl(input).map(|s| Ok(JsValue::from_str(&s)))) 10 + } 17 11 18 - // Flatten the block to get shapes (semantic tokens) 19 - let shapes = flatten_block(&working_set, &block); 12 + pub async fn highlight_impl(input: String) -> String { 13 + let (global_offset, shapes) = { 14 + let engine_guard = read_engine_state().await; 15 + let mut working_set = StateWorkingSet::new(&engine_guard); 16 + let offset = working_set.next_span_start(); 17 + let block = parse(&mut working_set, None, input.as_bytes(), false); 18 + (offset, flatten_block(&working_set, &block)) 19 + }; 20 20 21 21 let mut output = String::new(); 22 22 let mut last_end = 0;
+98 -72
src/lib.rs
··· 1 + use async_lock::{RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard}; 2 + use futures::FutureExt; 1 3 use jacquard::chrono; 4 + use js_sys::Promise; 2 5 use miette::Report; 3 6 use nu_cmd_base::hook::eval_hook; 4 7 use nu_cmd_extra::add_extra_command_context; ··· 7 10 use nu_parser::{FlatShape, TokenContents, flatten_block, lex, parse}; 8 11 use nu_protocol::{ 9 12 Config, ListStream, PipelineData, Signals, Span, 10 - engine::{Call, EngineState, Stack, StateWorkingSet}, 13 + engine::{EngineState, Stack, StateWorkingSet}, 11 14 }; 12 15 use serde::Serialize; 13 16 use std::{ 14 17 io::Cursor, 15 - path::PathBuf, 16 - sync::{Arc, Mutex, OnceLock}, 18 + sync::{Arc, OnceLock}, 17 19 time::UNIX_EPOCH, 18 20 }; 19 21 use vfs::VfsError; 20 22 use wasm_bindgen::prelude::*; 23 + use wasm_bindgen_futures::future_to_promise; 21 24 22 25 pub mod cmd; 23 26 pub mod completion; ··· 67 70 let _ = print_to_console(&msg, false); 68 71 } 69 72 70 - static ENGINE_STATE: OnceLock<Mutex<EngineState>> = OnceLock::new(); 71 - static STACK: OnceLock<Mutex<Stack>> = OnceLock::new(); 73 + static ENGINE_STATE: OnceLock<RwLock<EngineState>> = OnceLock::new(); 74 + #[inline] 75 + async fn read_engine_state() -> RwLockReadGuard<'static, EngineState> { 76 + ENGINE_STATE.get().unwrap().read().await 77 + } 78 + #[inline] 79 + async fn write_engine_state() -> RwLockWriteGuard<'static, EngineState> { 80 + ENGINE_STATE.get().unwrap().write().await 81 + } 82 + 83 + static STACK: OnceLock<RwLock<Stack>> = OnceLock::new(); 84 + #[inline] 85 + async fn read_stack() -> RwLockReadGuard<'static, Stack> { 86 + STACK.get().unwrap().read().await 87 + } 88 + #[inline] 89 + async fn write_stack() -> RwLockWriteGuard<'static, Stack> { 90 + STACK.get().unwrap().write().await 91 + } 72 92 73 93 fn init_engine_internal() -> Result<(), Report> { 74 94 let mut engine_state = create_default_context(); ··· 146 166 engine_state.set_signals(Signals::new(Arc::new(InterruptBool))); 147 167 148 168 ENGINE_STATE 149 - .set(Mutex::new(engine_state)) 169 + .set(RwLock::new(engine_state)) 150 170 .map_err(|_| miette::miette!("ENGINE_STATE was already set!?"))?; 151 171 STACK 152 - .set(Mutex::new(Stack::new())) 172 + .set(RwLock::new(Stack::new())) 153 173 .map_err(|_| miette::miette!("STACK was already set!?"))?; 154 174 155 175 // web_sys::console::log_1(&"Hello, World!".into()); ··· 163 183 init_engine_internal().map_or_else(|err| format!("error: {err}"), |_| String::new()) 164 184 } 165 185 166 - fn run_command_internal( 167 - engine_state: &mut EngineState, 168 - stack: &mut Stack, 169 - input: &str, 170 - ) -> Result<(), CommandError> { 171 - // apply any pending deltas from previous commands (like `source`) 172 - apply_pending_deltas(engine_state).map_err(|e| CommandError { 173 - error: Report::new(e), 174 - start_offset: 0, 175 - })?; 176 - let pwd = get_pwd_string().into_value(Span::unknown()); 177 - engine_state.add_env_var("PWD".to_string(), pwd.clone()); 186 + async fn run_command_internal(input: &str) -> Result<(), CommandError> { 187 + let mut engine_state = ENGINE_STATE.get().unwrap().upgradable_read().await; 188 + let (mut working_set, signals, config) = { 189 + let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 190 + apply_pending_deltas(&mut write_engine_state).map_err(|e| CommandError { 191 + error: Report::new(e), 192 + start_offset: 0, 193 + })?; 194 + write_engine_state.add_env_var( 195 + "PWD".to_string(), 196 + get_pwd_string().into_value(Span::unknown()), 197 + ); 198 + engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 178 199 179 - let mut working_set = StateWorkingSet::new(engine_state); 200 + ( 201 + StateWorkingSet::new(&engine_state), 202 + engine_state.signals().clone(), 203 + engine_state.config.clone(), 204 + ) 205 + }; 180 206 let start_offset = working_set.next_span_start(); 181 207 let block = parse(&mut working_set, Some("entry"), input.as_bytes(), false); 182 208 ··· 197 223 start_offset, 198 224 }); 199 225 } 226 + let delta = working_set.delta; 200 227 201 - engine_state 202 - .merge_delta(working_set.delta) 203 - .map_err(cmd_err)?; 204 - let result = eval_block::<nu_protocol::debugger::WithoutDebug>( 205 - engine_state, 206 - stack, 207 - &block, 208 - PipelineData::Empty, 209 - ); 228 + let result = { 229 + let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 230 + let mut stack = write_stack().await; 231 + write_engine_state.merge_delta(delta).map_err(cmd_err)?; 232 + let res = eval_block::<nu_protocol::debugger::WithoutDebug>( 233 + &mut write_engine_state, 234 + &mut stack, 235 + &block, 236 + PipelineData::Empty, 237 + ); 238 + engine_state = RwLockWriteGuard::downgrade_to_upgradable(write_engine_state); 239 + res 240 + }; 210 241 211 242 let pipeline_data = result.map_err(cmd_err)?.body; 212 - let signals = engine_state.signals().clone(); 213 243 214 244 // this is annoying but we have to collect here so we can uncover errors 215 245 // before passing the data off to Table, because otherwise Table ··· 250 280 x => x, 251 281 }; 252 282 253 - let conf = stack.get_config(engine_state); 254 - let hook = conf.hooks.display_output.as_ref(); 255 - let res = eval_hook( 256 - engine_state, 257 - stack, 258 - Some(pipeline_data), 259 - vec![], 260 - hook.unwrap(), 261 - "display_output", 262 - ) 263 - .map_err(cmd_err)?; 283 + let res = { 284 + let mut write_engine_state = RwLockUpgradableReadGuard::upgrade(engine_state).await; 285 + let mut stack = write_stack().await; 286 + eval_hook( 287 + &mut write_engine_state, 288 + &mut stack, 289 + Some(pipeline_data), 290 + vec![], 291 + config.hooks.display_output.as_ref().unwrap(), 292 + "display_output", 293 + ) 294 + .map_err(cmd_err)? 295 + }; 264 296 265 297 match res { 266 298 PipelineData::Empty => {} 267 299 PipelineData::Value(v, _) => { 268 - print_to_console(&v.to_expanded_string("\n", &engine_state.config), true) 269 - .map_err(cmd_err)?; 300 + print_to_console(&v.to_expanded_string("\n", &config), true).map_err(cmd_err)?; 270 301 } 271 302 PipelineData::ByteStream(s, _) => { 272 303 for line in s.lines().into_iter().flatten() { ··· 279 310 let out = item 280 311 .unwrap_error() 281 312 .map_err(cmd_err)? 282 - .to_expanded_string("\n", &engine_state.config); 313 + .to_expanded_string("\n", &config); 283 314 print_to_console(&out, true).map_err(cmd_err)?; 284 315 } 285 316 } ··· 289 320 } 290 321 291 322 #[wasm_bindgen] 292 - pub fn run_command(input: &str) -> Option<String> { 293 - let (mut engine_guard, mut stack_guard) = ( 294 - ENGINE_STATE 295 - .get() 296 - .unwrap() 297 - .lock() 298 - .expect("engine state initialized"), 299 - STACK.get().unwrap().lock().expect("stack initialized"), 300 - ); 323 + pub fn run_command(input: String) -> Promise { 324 + set_interrupt(false); 301 325 302 - set_interrupt(false); 303 - match run_command_internal(&mut engine_guard, &mut stack_guard, input) { 304 - Ok(_) => None, 305 - Err(cmd_err) => Some(format_error( 306 - cmd_err.error, 307 - input.to_owned(), 308 - cmd_err.start_offset, 309 - )), 310 - } 326 + future_to_promise(async move { 327 + run_command_internal(&input) 328 + .map(|res| { 329 + res.map_or_else( 330 + |cmd_err| { 331 + Some(format_error( 332 + cmd_err.error, 333 + input.to_owned(), 334 + cmd_err.start_offset, 335 + )) 336 + }, 337 + |_| None, 338 + ) 339 + }) 340 + .map(|res| { 341 + Ok(res 342 + .map(|s| JsValue::from_str(&s)) 343 + .unwrap_or_else(JsValue::null)) 344 + }) 345 + .await 346 + }) 311 347 } 312 348 313 349 #[wasm_bindgen] ··· 320 356 } 321 357 pwd.as_str().to_string() 322 358 } 323 - 324 - pub fn get_pwd_buf() -> PathBuf { 325 - // web_sys::console::log_1(&"before pwd".into()); 326 - let pwd = get_pwd(); 327 - // web_sys::console::log_1(&"after pwd".into()); 328 - if pwd.is_root() { 329 - return PathBuf::new().join("/"); 330 - } 331 - pwd.as_str().into() 332 - }
+1
www/src/main.ts
··· 174 174 output += currentLine; 175 175 } 176 176 } catch (e) { 177 + console.error(`cant highlight: ${e}`); 177 178 output += currentLine; 178 179 } 179 180
+4 -4
www/src/worker.ts
··· 23 23 }); 24 24 25 25 // Handle messages from Main Thread 26 - self.onmessage = (e) => { 26 + self.onmessage = async (e) => { 27 27 const { id, type, payload } = e.data; 28 28 29 29 // console.log("Received message:", id, type, payload); ··· 35 35 set_interrupt_buffer(payload); 36 36 break; 37 37 case "run": 38 - result = run_command(payload); 38 + result = await run_command(payload); 39 39 break; 40 40 case "completion": 41 - result = completion(payload.line, payload.cursor); 41 + result = await completion(payload.line, payload.cursor); 42 42 break; 43 43 case "highlight": 44 - result = highlight(payload); 44 + result = await highlight(payload); 45 45 break; 46 46 case "get_pwd": 47 47 result = get_pwd_string();