+2
-1
.gitignore
+2
-1
.gitignore
+52
-53
src/cmd/source.rs
+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
+1
-1
src/default_context.rs
+31
-13
src/lib.rs
+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
+4
-1
www/src/worker.ts