1use crate::{
2 BlockId, Category, Config, DeclId, FileId, GetSpan, Handlers, HistoryConfig, JobId, Module,
3 ModuleId, OverlayId, ShellError, SignalAction, Signals, Signature, Span, SpanId, Type, Value,
4 VarId, VirtualPathId,
5 ast::Block,
6 debugger::{Debugger, NoopDebugger},
7 engine::{
8 CachedFile, Command, CommandType, DEFAULT_OVERLAY_NAME, EnvVars, OverlayFrame, ScopeFrame,
9 Stack, StateDelta, Variable, Visibility,
10 description::{Doccomments, build_desc},
11 },
12 eval_const::create_nu_constant,
13 report_error::ReportLog,
14 shell_error::io::IoError,
15};
16use fancy_regex::Regex;
17use lru::LruCache;
18use nu_path::AbsolutePathBuf;
19use nu_utils::IgnoreCaseExt;
20use std::{
21 collections::HashMap,
22 num::NonZeroUsize,
23 path::PathBuf,
24 sync::{
25 Arc, Mutex, MutexGuard, PoisonError,
26 atomic::{AtomicBool, AtomicU32, Ordering},
27 mpsc::Sender,
28 mpsc::channel,
29 },
30};
31
32type PoisonDebuggerError<'a> = PoisonError<MutexGuard<'a, Box<dyn Debugger>>>;
33
34#[cfg(feature = "plugin")]
35use crate::{PluginRegistryFile, PluginRegistryItem, RegisteredPlugin};
36
37use super::{CurrentJob, Jobs, Mail, Mailbox, ThreadJob};
38
39#[derive(Clone, Debug)]
40pub enum VirtualPath {
41 File(FileId),
42 Dir(Vec<VirtualPathId>),
43}
44
45pub struct ReplState {
46 pub buffer: String,
47 pub cursor_pos: usize,
49}
50
51pub struct IsDebugging(AtomicBool);
52
53impl IsDebugging {
54 pub fn new(val: bool) -> Self {
55 IsDebugging(AtomicBool::new(val))
56 }
57}
58
59impl Clone for IsDebugging {
60 fn clone(&self) -> Self {
61 IsDebugging(AtomicBool::new(self.0.load(Ordering::Relaxed)))
62 }
63}
64
65#[derive(Clone)]
83pub struct EngineState {
84 files: Vec<CachedFile>,
85 pub(super) virtual_paths: Vec<(String, VirtualPath)>,
86 vars: Vec<Variable>,
87 decls: Arc<Vec<Box<dyn Command + 'static>>>,
88 pub(super) blocks: Arc<Vec<Arc<Block>>>,
92 pub(super) modules: Arc<Vec<Arc<Module>>>,
93 pub spans: Vec<Span>,
94 doccomments: Doccomments,
95 pub scope: ScopeFrame,
96 signals: Signals,
97 pub signal_handlers: Option<Handlers>,
98 pub env_vars: Arc<EnvVars>,
99 pub previous_env_vars: Arc<HashMap<String, Value>>,
100 pub config: Arc<Config>,
101 pub pipeline_externals_state: Arc<(AtomicU32, AtomicU32)>,
102 pub repl_state: Arc<Mutex<ReplState>>,
103 pub table_decl_id: Option<DeclId>,
104 #[cfg(feature = "plugin")]
105 pub plugin_path: Option<PathBuf>,
106 #[cfg(feature = "plugin")]
107 plugins: Vec<Arc<dyn RegisteredPlugin>>,
108 config_path: HashMap<String, PathBuf>,
109 pub history_enabled: bool,
110 pub history_session_id: i64,
111 pub file: Option<PathBuf>,
113 pub regex_cache: Arc<Mutex<LruCache<String, Regex>>>,
114 pub is_interactive: bool,
115 pub is_login: bool,
116 startup_time: i64,
117 is_debugging: IsDebugging,
118 pub debugger: Arc<Mutex<Box<dyn Debugger>>>,
119 pub report_log: Arc<Mutex<ReportLog>>,
120
121 pub jobs: Arc<Mutex<Jobs>>,
122
123 pub current_job: CurrentJob,
125
126 pub root_job_sender: Sender<Mail>,
127
128 pub exit_warning_given: Arc<AtomicBool>,
135}
136
137const REGEX_CACHE_SIZE: usize = 100; pub const NU_VARIABLE_ID: VarId = VarId::new(0);
141pub const IN_VARIABLE_ID: VarId = VarId::new(1);
142pub const ENV_VARIABLE_ID: VarId = VarId::new(2);
143pub const UNKNOWN_SPAN_ID: SpanId = SpanId::new(0);
147
148impl EngineState {
149 pub fn new() -> Self {
150 let (send, recv) = channel::<Mail>();
151
152 Self {
153 files: vec![],
154 virtual_paths: vec![],
155 vars: vec![
156 Variable::new(Span::new(0, 0), Type::Any, false),
157 Variable::new(Span::new(0, 0), Type::Any, false),
158 Variable::new(Span::new(0, 0), Type::Any, false),
159 Variable::new(Span::new(0, 0), Type::Any, false),
160 Variable::new(Span::new(0, 0), Type::Any, false),
161 ],
162 decls: Arc::new(vec![]),
163 blocks: Arc::new(vec![]),
164 modules: Arc::new(vec![Arc::new(Module::new(
165 DEFAULT_OVERLAY_NAME.as_bytes().to_vec(),
166 ))]),
167 spans: vec![Span::unknown()],
168 doccomments: Doccomments::new(),
169 scope: ScopeFrame::with_empty_overlay(
171 DEFAULT_OVERLAY_NAME.as_bytes().to_vec(),
172 ModuleId::new(0),
173 false,
174 ),
175 signal_handlers: None,
176 signals: Signals::empty(),
177 env_vars: Arc::new(
178 [(DEFAULT_OVERLAY_NAME.to_string(), HashMap::new())]
179 .into_iter()
180 .collect(),
181 ),
182 previous_env_vars: Arc::new(HashMap::new()),
183 config: Arc::new(Config::default()),
184 pipeline_externals_state: Arc::new((AtomicU32::new(0), AtomicU32::new(0))),
185 repl_state: Arc::new(Mutex::new(ReplState {
186 buffer: "".to_string(),
187 cursor_pos: 0,
188 })),
189 table_decl_id: None,
190 #[cfg(feature = "plugin")]
191 plugin_path: None,
192 #[cfg(feature = "plugin")]
193 plugins: vec![],
194 config_path: HashMap::new(),
195 history_enabled: true,
196 history_session_id: 0,
197 file: None,
198 regex_cache: Arc::new(Mutex::new(LruCache::new(
199 NonZeroUsize::new(REGEX_CACHE_SIZE).expect("tried to create cache of size zero"),
200 ))),
201 is_interactive: false,
202 is_login: false,
203 startup_time: -1,
204 is_debugging: IsDebugging::new(false),
205 debugger: Arc::new(Mutex::new(Box::new(NoopDebugger))),
206 report_log: Arc::default(),
207 jobs: Arc::new(Mutex::new(Jobs::default())),
208 current_job: CurrentJob {
209 id: JobId::new(0),
210 background_thread_job: None,
211 mailbox: Arc::new(Mutex::new(Mailbox::new(recv))),
212 },
213 root_job_sender: send,
214 exit_warning_given: Arc::new(AtomicBool::new(false)),
215 }
216 }
217
218 pub fn signals(&self) -> &Signals {
219 &self.signals
220 }
221
222 pub fn reset_signals(&mut self) {
223 self.signals.reset();
224 if let Some(ref handlers) = self.signal_handlers {
225 handlers.run(SignalAction::Reset);
226 }
227 }
228
229 pub fn set_signals(&mut self, signals: Signals) {
230 self.signals = signals;
231 }
232
233 pub fn merge_delta(&mut self, mut delta: StateDelta) -> Result<(), ShellError> {
241 self.files.extend(delta.files);
243 self.virtual_paths.extend(delta.virtual_paths);
244 self.vars.extend(delta.vars);
245 self.spans.extend(delta.spans);
246 self.doccomments.merge_with(delta.doccomments);
247
248 if !delta.decls.is_empty() {
250 Arc::make_mut(&mut self.decls).extend(delta.decls);
251 }
252 if !delta.blocks.is_empty() {
253 Arc::make_mut(&mut self.blocks).extend(delta.blocks);
254 }
255 if !delta.modules.is_empty() {
256 Arc::make_mut(&mut self.modules).extend(delta.modules);
257 }
258
259 let first = delta.scope.remove(0);
260
261 for (delta_name, delta_overlay) in first.clone().overlays {
262 if let Some((_, existing_overlay)) = self
263 .scope
264 .overlays
265 .iter_mut()
266 .find(|(name, _)| name == &delta_name)
267 {
268 for item in delta_overlay.decls.into_iter() {
270 existing_overlay.decls.insert(item.0, item.1);
271 }
272 for item in delta_overlay.vars.into_iter() {
273 existing_overlay.vars.insert(item.0, item.1);
274 }
275 for item in delta_overlay.modules.into_iter() {
276 existing_overlay.modules.insert(item.0, item.1);
277 }
278
279 existing_overlay
280 .visibility
281 .merge_with(delta_overlay.visibility);
282 } else {
283 self.scope.overlays.push((delta_name, delta_overlay));
285 }
286 }
287
288 let mut activated_ids = self.translate_overlay_ids(&first);
289
290 let mut removed_ids = vec![];
291
292 for name in &first.removed_overlays {
293 if let Some(overlay_id) = self.find_overlay(name) {
294 removed_ids.push(overlay_id);
295 }
296 }
297
298 self.scope
300 .active_overlays
301 .retain(|id| !removed_ids.contains(id));
302
303 self.scope
305 .active_overlays
306 .retain(|id| !activated_ids.contains(id));
307 self.scope.active_overlays.append(&mut activated_ids);
308
309 #[cfg(feature = "plugin")]
310 if !delta.plugins.is_empty() {
311 for plugin in std::mem::take(&mut delta.plugins) {
312 if let Some(handlers) = &self.signal_handlers {
314 plugin.clone().configure_signal_handler(handlers)?;
315 }
316
317 if let Some(existing) = self
319 .plugins
320 .iter_mut()
321 .find(|p| p.identity().name() == plugin.identity().name())
322 {
323 existing.stop()?;
325 *existing = plugin;
326 } else {
327 self.plugins.push(plugin);
328 }
329 }
330 }
331
332 #[cfg(feature = "plugin")]
333 if !delta.plugin_registry_items.is_empty() {
334 if self.plugin_path.is_some() {
336 self.update_plugin_file(std::mem::take(&mut delta.plugin_registry_items))?;
337 }
338 }
339
340 Ok(())
341 }
342
343 pub fn merge_env(&mut self, stack: &mut Stack) -> Result<(), ShellError> {
345 for mut scope in stack.env_vars.drain(..) {
346 for (overlay_name, mut env) in Arc::make_mut(&mut scope).drain() {
347 if let Some(env_vars) = Arc::make_mut(&mut self.env_vars).get_mut(&overlay_name) {
348 env_vars.extend(env.drain());
350 } else {
351 Arc::make_mut(&mut self.env_vars).insert(overlay_name, env);
353 }
354 }
355 }
356
357 let cwd = self.cwd(Some(stack))?;
358 std::env::set_current_dir(cwd).map_err(|err| {
359 IoError::new_internal(err, "Could not set current dir", crate::location!())
360 })?;
361
362 if let Some(config) = stack.config.take() {
363 self.config = config;
365
366 #[cfg(feature = "plugin")]
368 self.update_plugin_gc_configs(&self.config.plugin_gc);
369 }
370
371 Ok(())
372 }
373
374 pub fn active_overlay_ids<'a, 'b>(
375 &'b self,
376 removed_overlays: &'a [Vec<u8>],
377 ) -> impl DoubleEndedIterator<Item = &'b OverlayId> + 'a
378 where
379 'b: 'a,
380 {
381 self.scope.active_overlays.iter().filter(|id| {
382 !removed_overlays
383 .iter()
384 .any(|name| name == self.get_overlay_name(**id))
385 })
386 }
387
388 pub fn active_overlays<'a, 'b>(
389 &'b self,
390 removed_overlays: &'a [Vec<u8>],
391 ) -> impl DoubleEndedIterator<Item = &'b OverlayFrame> + 'a
392 where
393 'b: 'a,
394 {
395 self.active_overlay_ids(removed_overlays)
396 .map(|id| self.get_overlay(*id))
397 }
398
399 pub fn active_overlay_names<'a, 'b>(
400 &'b self,
401 removed_overlays: &'a [Vec<u8>],
402 ) -> impl DoubleEndedIterator<Item = &'b [u8]> + 'a
403 where
404 'b: 'a,
405 {
406 self.active_overlay_ids(removed_overlays)
407 .map(|id| self.get_overlay_name(*id))
408 }
409
410 fn translate_overlay_ids(&self, other: &ScopeFrame) -> Vec<OverlayId> {
412 let other_names = other.active_overlays.iter().map(|other_id| {
413 &other
414 .overlays
415 .get(other_id.get())
416 .expect("internal error: missing overlay")
417 .0
418 });
419
420 other_names
421 .map(|other_name| {
422 self.find_overlay(other_name)
423 .expect("internal error: missing overlay")
424 })
425 .collect()
426 }
427
428 pub fn last_overlay_name(&self, removed_overlays: &[Vec<u8>]) -> &[u8] {
429 self.active_overlay_names(removed_overlays)
430 .last()
431 .expect("internal error: no active overlays")
432 }
433
434 pub fn last_overlay(&self, removed_overlays: &[Vec<u8>]) -> &OverlayFrame {
435 self.active_overlay_ids(removed_overlays)
436 .last()
437 .map(|id| self.get_overlay(*id))
438 .expect("internal error: no active overlays")
439 }
440
441 pub fn get_overlay_name(&self, overlay_id: OverlayId) -> &[u8] {
442 &self
443 .scope
444 .overlays
445 .get(overlay_id.get())
446 .expect("internal error: missing overlay")
447 .0
448 }
449
450 pub fn get_overlay(&self, overlay_id: OverlayId) -> &OverlayFrame {
451 &self
452 .scope
453 .overlays
454 .get(overlay_id.get())
455 .expect("internal error: missing overlay")
456 .1
457 }
458
459 pub fn render_env_vars(&self) -> HashMap<&str, &Value> {
460 let mut result: HashMap<&str, &Value> = HashMap::new();
461
462 for overlay_name in self.active_overlay_names(&[]) {
463 let name = String::from_utf8_lossy(overlay_name);
464 if let Some(env_vars) = self.env_vars.get(name.as_ref()) {
465 result.extend(env_vars.iter().map(|(k, v)| (k.as_str(), v)));
466 }
467 }
468
469 result
470 }
471
472 pub fn add_env_var(&mut self, name: String, val: Value) {
473 let overlay_name = String::from_utf8_lossy(self.last_overlay_name(&[])).to_string();
474
475 if let Some(env_vars) = Arc::make_mut(&mut self.env_vars).get_mut(&overlay_name) {
476 env_vars.insert(name, val);
477 } else {
478 Arc::make_mut(&mut self.env_vars)
479 .insert(overlay_name, [(name, val)].into_iter().collect());
480 }
481 }
482
483 pub fn get_env_var(&self, name: &str) -> Option<&Value> {
484 for overlay_id in self.scope.active_overlays.iter().rev() {
485 let overlay_name = String::from_utf8_lossy(self.get_overlay_name(*overlay_id));
486 if let Some(env_vars) = self.env_vars.get(overlay_name.as_ref()) {
487 if let Some(val) = env_vars.get(name) {
488 return Some(val);
489 }
490 }
491 }
492
493 None
494 }
495
496 pub fn get_env_var_insensitive(&self, name: &str) -> Option<(&String, &Value)> {
501 for overlay_id in self.scope.active_overlays.iter().rev() {
502 let overlay_name = String::from_utf8_lossy(self.get_overlay_name(*overlay_id));
503 if let Some(env_vars) = self.env_vars.get(overlay_name.as_ref()) {
504 if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
505 return Some((v.0, v.1));
506 }
507 }
508 }
509
510 None
511 }
512
513 #[cfg(feature = "plugin")]
514 pub fn plugins(&self) -> &[Arc<dyn RegisteredPlugin>] {
515 &self.plugins
516 }
517
518 #[cfg(feature = "plugin")]
519 fn update_plugin_file(&self, updated_items: Vec<PluginRegistryItem>) -> Result<(), ShellError> {
520 use std::fs::File;
522
523 let plugin_path = self
524 .plugin_path
525 .as_ref()
526 .ok_or_else(|| ShellError::GenericError {
527 error: "Plugin file path not set".into(),
528 msg: "".into(),
529 span: None,
530 help: Some("you may be running nu with --no-config-file".into()),
531 inner: vec![],
532 })?;
533
534 let mut contents = match File::open(plugin_path.as_path()) {
536 Ok(mut plugin_file) => PluginRegistryFile::read_from(&mut plugin_file, None),
537 Err(err) => {
538 if err.kind() == std::io::ErrorKind::NotFound {
539 Ok(PluginRegistryFile::default())
540 } else {
541 Err(ShellError::Io(IoError::new_internal_with_path(
542 err,
543 "Failed to open plugin file",
544 crate::location!(),
545 PathBuf::from(plugin_path),
546 )))
547 }
548 }
549 }?;
550
551 for item in updated_items {
553 contents.upsert_plugin(item);
554 }
555
556 let plugin_file = File::create(plugin_path.as_path()).map_err(|err| {
558 IoError::new_internal_with_path(
559 err,
560 "Failed to write plugin file",
561 crate::location!(),
562 PathBuf::from(plugin_path),
563 )
564 })?;
565
566 contents.write_to(plugin_file, None)
567 }
568
569 #[cfg(feature = "plugin")]
571 fn update_plugin_gc_configs(&self, plugin_gc: &crate::PluginGcConfigs) {
572 for plugin in &self.plugins {
573 plugin.set_gc_config(plugin_gc.get(plugin.identity().name()));
574 }
575 }
576
577 pub fn num_files(&self) -> usize {
578 self.files.len()
579 }
580
581 pub fn num_virtual_paths(&self) -> usize {
582 self.virtual_paths.len()
583 }
584
585 pub fn num_vars(&self) -> usize {
586 self.vars.len()
587 }
588
589 pub fn num_decls(&self) -> usize {
590 self.decls.len()
591 }
592
593 pub fn num_blocks(&self) -> usize {
594 self.blocks.len()
595 }
596
597 pub fn num_modules(&self) -> usize {
598 self.modules.len()
599 }
600
601 pub fn num_spans(&self) -> usize {
602 self.spans.len()
603 }
604 pub fn print_vars(&self) {
605 for var in self.vars.iter().enumerate() {
606 println!("var{}: {:?}", var.0, var.1);
607 }
608 }
609
610 pub fn print_decls(&self) {
611 for decl in self.decls.iter().enumerate() {
612 println!("decl{}: {:?}", decl.0, decl.1.signature());
613 }
614 }
615
616 pub fn print_blocks(&self) {
617 for block in self.blocks.iter().enumerate() {
618 println!("block{}: {:?}", block.0, block.1);
619 }
620 }
621
622 pub fn print_contents(&self) {
623 for cached_file in self.files.iter() {
624 let string = String::from_utf8_lossy(&cached_file.content);
625 println!("{string}");
626 }
627 }
628
629 pub fn find_decl(&self, name: &[u8], removed_overlays: &[Vec<u8>]) -> Option<DeclId> {
633 let mut visibility: Visibility = Visibility::new();
634
635 for overlay_frame in self.active_overlays(removed_overlays).rev() {
636 visibility.append(&overlay_frame.visibility);
637
638 if let Some(decl_id) = overlay_frame.get_decl(name) {
639 if visibility.is_decl_id_visible(&decl_id) {
640 return Some(decl_id);
641 }
642 }
643 }
644
645 None
646 }
647
648 pub fn find_decl_name(&self, decl_id: DeclId, removed_overlays: &[Vec<u8>]) -> Option<&[u8]> {
652 let mut visibility: Visibility = Visibility::new();
653
654 for overlay_frame in self.active_overlays(removed_overlays).rev() {
655 visibility.append(&overlay_frame.visibility);
656
657 if visibility.is_decl_id_visible(&decl_id) {
658 for (name, id) in overlay_frame.decls.iter() {
659 if id == &decl_id {
660 return Some(name);
661 }
662 }
663 }
664 }
665
666 None
667 }
668
669 pub fn find_overlay(&self, name: &[u8]) -> Option<OverlayId> {
673 self.scope.find_overlay(name)
674 }
675
676 pub fn find_active_overlay(&self, name: &[u8]) -> Option<OverlayId> {
680 self.scope.find_active_overlay(name)
681 }
682
683 pub fn find_module(&self, name: &[u8], removed_overlays: &[Vec<u8>]) -> Option<ModuleId> {
687 for overlay_frame in self.active_overlays(removed_overlays).rev() {
688 if let Some(module_id) = overlay_frame.modules.get(name) {
689 return Some(*module_id);
690 }
691 }
692
693 None
694 }
695
696 pub fn get_module_comments(&self, module_id: ModuleId) -> Option<&[Span]> {
697 self.doccomments.get_module_comments(module_id)
698 }
699
700 #[cfg(feature = "plugin")]
701 pub fn plugin_decls(&self) -> impl Iterator<Item = &Box<dyn Command + 'static>> {
702 let mut unique_plugin_decls = HashMap::new();
703
704 for decl in self.decls.iter().filter(|d| d.is_plugin()) {
706 unique_plugin_decls.insert(decl.name(), decl);
707 }
708
709 let mut plugin_decls: Vec<(&str, &Box<dyn Command>)> =
710 unique_plugin_decls.into_iter().collect();
711
712 plugin_decls.sort_by(|a, b| a.0.cmp(b.0));
714 plugin_decls.into_iter().map(|(_, decl)| decl)
715 }
716
717 pub fn which_module_has_decl(
718 &self,
719 decl_name: &[u8],
720 removed_overlays: &[Vec<u8>],
721 ) -> Option<&[u8]> {
722 for overlay_frame in self.active_overlays(removed_overlays).rev() {
723 for (module_name, module_id) in overlay_frame.modules.iter() {
724 let module = self.get_module(*module_id);
725 if module.has_decl(decl_name) {
726 return Some(module_name);
727 }
728 }
729 }
730
731 None
732 }
733
734 pub fn find_commands_by_predicate(
735 &self,
736 mut predicate: impl FnMut(&[u8]) -> bool,
737 ignore_deprecated: bool,
738 ) -> Vec<(DeclId, Vec<u8>, Option<String>, CommandType)> {
739 let mut output = vec![];
740
741 for overlay_frame in self.active_overlays(&[]).rev() {
742 for (name, decl_id) in &overlay_frame.decls {
743 if overlay_frame.visibility.is_decl_id_visible(decl_id) && predicate(name) {
744 let command = self.get_decl(*decl_id);
745 if ignore_deprecated && command.signature().category == Category::Removed {
746 continue;
747 }
748 output.push((
749 *decl_id,
750 name.clone(),
751 Some(command.description().to_string()),
752 command.command_type(),
753 ));
754 }
755 }
756 }
757
758 output
759 }
760
761 pub fn get_span_contents(&self, span: Span) -> &[u8] {
762 for file in &self.files {
763 if file.covered_span.contains_span(span) {
764 return &file.content
765 [(span.start - file.covered_span.start)..(span.end - file.covered_span.start)];
766 }
767 }
768 &[0u8; 0]
769 }
770
771 pub fn span_match_prefix(&self, span: Span, prefix: &[u8]) -> Option<(Span, Span)> {
774 let contents = self.get_span_contents(span);
775
776 if contents.starts_with(prefix) {
777 span.split_at(prefix.len())
778 } else {
779 None
780 }
781 }
782
783 pub fn span_match_postfix(&self, span: Span, prefix: &[u8]) -> Option<(Span, Span)> {
786 let contents = self.get_span_contents(span);
787
788 if contents.ends_with(prefix) {
789 span.split_at(span.len() - prefix.len())
790 } else {
791 None
792 }
793 }
794
795 pub fn get_config(&self) -> &Arc<Config> {
800 &self.config
801 }
802
803 pub fn set_config(&mut self, conf: impl Into<Arc<Config>>) {
804 let conf = conf.into();
805
806 #[cfg(feature = "plugin")]
807 if conf.plugin_gc != self.config.plugin_gc {
808 self.update_plugin_gc_configs(&conf.plugin_gc);
810 }
811
812 self.config = conf;
813 }
814
815 pub fn get_plugin_config(&self, plugin: &str) -> Option<&Value> {
820 self.config.plugins.get(plugin)
821 }
822
823 pub fn history_config(&self) -> Option<HistoryConfig> {
825 self.history_enabled.then(|| self.config.history)
826 }
827
828 pub fn get_var(&self, var_id: VarId) -> &Variable {
829 self.vars
830 .get(var_id.get())
831 .expect("internal error: missing variable")
832 }
833
834 pub fn get_constant(&self, var_id: VarId) -> Option<&Value> {
835 let var = self.get_var(var_id);
836 var.const_val.as_ref()
837 }
838
839 pub fn generate_nu_constant(&mut self) {
840 self.vars[NU_VARIABLE_ID.get()].const_val = Some(create_nu_constant(self, Span::unknown()));
841 }
842
843 pub fn get_decl(&self, decl_id: DeclId) -> &dyn Command {
844 self.decls
845 .get(decl_id.get())
846 .expect("internal error: missing declaration")
847 .as_ref()
848 }
849
850 pub fn get_decls_sorted(&self, include_hidden: bool) -> Vec<(Vec<u8>, DeclId)> {
852 let mut decls_map = HashMap::new();
853
854 for overlay_frame in self.active_overlays(&[]) {
855 let new_decls = if include_hidden {
856 overlay_frame.decls.clone()
857 } else {
858 overlay_frame
859 .decls
860 .clone()
861 .into_iter()
862 .filter(|(_, id)| overlay_frame.visibility.is_decl_id_visible(id))
863 .collect()
864 };
865
866 decls_map.extend(new_decls);
867 }
868
869 let mut decls: Vec<(Vec<u8>, DeclId)> = decls_map.into_iter().collect();
870
871 decls.sort_by(|a, b| a.0.cmp(&b.0));
872 decls
873 }
874
875 pub fn get_signature(&self, decl: &dyn Command) -> Signature {
876 if let Some(block_id) = decl.block_id() {
877 *self.blocks[block_id.get()].signature.clone()
878 } else {
879 decl.signature()
880 }
881 }
882
883 pub fn get_signatures_and_declids(&self, include_hidden: bool) -> Vec<(Signature, DeclId)> {
885 self.get_decls_sorted(include_hidden)
886 .into_iter()
887 .map(|(_, id)| {
888 let decl = self.get_decl(id);
889
890 (self.get_signature(decl).update_from_command(decl), id)
891 })
892 .collect()
893 }
894
895 pub fn get_block(&self, block_id: BlockId) -> &Arc<Block> {
896 self.blocks
897 .get(block_id.get())
898 .expect("internal error: missing block")
899 }
900
901 pub fn try_get_block(&self, block_id: BlockId) -> Option<&Arc<Block>> {
907 self.blocks.get(block_id.get())
908 }
909
910 pub fn get_module(&self, module_id: ModuleId) -> &Module {
911 self.modules
912 .get(module_id.get())
913 .expect("internal error: missing module")
914 }
915
916 pub fn get_virtual_path(&self, virtual_path_id: VirtualPathId) -> &(String, VirtualPath) {
917 self.virtual_paths
918 .get(virtual_path_id.get())
919 .expect("internal error: missing virtual path")
920 }
921
922 pub fn next_span_start(&self) -> usize {
923 if let Some(cached_file) = self.files.last() {
924 cached_file.covered_span.end
925 } else {
926 0
927 }
928 }
929
930 pub fn files(
931 &self,
932 ) -> impl DoubleEndedIterator<Item = &CachedFile> + ExactSizeIterator<Item = &CachedFile> {
933 self.files.iter()
934 }
935
936 pub fn add_file(&mut self, filename: Arc<str>, content: Arc<[u8]>) -> FileId {
937 let next_span_start = self.next_span_start();
938 let next_span_end = next_span_start + content.len();
939
940 let covered_span = Span::new(next_span_start, next_span_end);
941
942 self.files.push(CachedFile {
943 name: filename,
944 content,
945 covered_span,
946 });
947
948 FileId::new(self.num_files() - 1)
949 }
950
951 pub fn set_config_path(&mut self, key: &str, val: PathBuf) {
952 self.config_path.insert(key.to_string(), val);
953 }
954
955 pub fn get_config_path(&self, key: &str) -> Option<&PathBuf> {
956 self.config_path.get(key)
957 }
958
959 pub fn build_desc(&self, spans: &[Span]) -> (String, String) {
960 let comment_lines: Vec<&[u8]> = spans
961 .iter()
962 .map(|span| self.get_span_contents(*span))
963 .collect();
964 build_desc(&comment_lines)
965 }
966
967 pub fn build_module_desc(&self, module_id: ModuleId) -> Option<(String, String)> {
968 self.get_module_comments(module_id)
969 .map(|comment_spans| self.build_desc(comment_spans))
970 }
971
972 #[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
976 pub fn current_work_dir(&self) -> String {
977 self.cwd(None)
978 .map(|path| path.to_string_lossy().to_string())
979 .unwrap_or_default()
980 }
981
982 pub fn cwd(&self, stack: Option<&Stack>) -> Result<AbsolutePathBuf, ShellError> {
989 fn error(msg: &str, cwd: impl AsRef<nu_path::Path>) -> ShellError {
991 ShellError::GenericError {
992 error: msg.into(),
993 msg: format!("$env.PWD = {}", cwd.as_ref().display()),
994 span: None,
995 help: Some("Use `cd` to reset $env.PWD into a good state".into()),
996 inner: vec![],
997 }
998 }
999
1000 let pwd = if let Some(stack) = stack {
1002 stack.get_env_var(self, "PWD")
1003 } else {
1004 self.get_env_var("PWD")
1005 };
1006
1007 let pwd = pwd.ok_or_else(|| error("$env.PWD not found", ""))?;
1008
1009 if let Ok(pwd) = pwd.as_str() {
1010 let path = AbsolutePathBuf::try_from(pwd)
1011 .map_err(|_| error("$env.PWD is not an absolute path", pwd))?;
1012
1013 if path.parent().is_some() && nu_path::has_trailing_slash(path.as_ref()) {
1016 Err(error("$env.PWD contains trailing slashes", &path))
1017 } else if !path.exists() {
1018 Err(error("$env.PWD points to a non-existent directory", &path))
1019 } else if !path.is_dir() {
1020 Err(error("$env.PWD points to a non-directory", &path))
1021 } else {
1022 Ok(path)
1023 }
1024 } else {
1025 Err(error("$env.PWD is not a string", format!("{pwd:?}")))
1026 }
1027 }
1028
1029 pub fn cwd_as_string(&self, stack: Option<&Stack>) -> Result<String, ShellError> {
1031 let cwd = self.cwd(stack)?;
1032 cwd.into_os_string()
1033 .into_string()
1034 .map_err(|err| ShellError::NonUtf8Custom {
1035 msg: format!("The current working directory is not a valid utf-8 string: {err:?}"),
1036 span: Span::unknown(),
1037 })
1038 }
1039
1040 pub fn get_file_contents(&self) -> &[CachedFile] {
1042 &self.files
1043 }
1044
1045 pub fn get_startup_time(&self) -> i64 {
1046 self.startup_time
1047 }
1048
1049 pub fn set_startup_time(&mut self, startup_time: i64) {
1050 self.startup_time = startup_time;
1051 }
1052
1053 pub fn activate_debugger(
1054 &self,
1055 debugger: Box<dyn Debugger>,
1056 ) -> Result<(), PoisonDebuggerError> {
1057 let mut locked_debugger = self.debugger.lock()?;
1058 *locked_debugger = debugger;
1059 locked_debugger.activate();
1060 self.is_debugging.0.store(true, Ordering::Relaxed);
1061 Ok(())
1062 }
1063
1064 pub fn deactivate_debugger(&self) -> Result<Box<dyn Debugger>, PoisonDebuggerError> {
1065 let mut locked_debugger = self.debugger.lock()?;
1066 locked_debugger.deactivate();
1067 let ret = std::mem::replace(&mut *locked_debugger, Box::new(NoopDebugger));
1068 self.is_debugging.0.store(false, Ordering::Relaxed);
1069 Ok(ret)
1070 }
1071
1072 pub fn is_debugging(&self) -> bool {
1073 self.is_debugging.0.load(Ordering::Relaxed)
1074 }
1075
1076 pub fn recover_from_panic(&mut self) {
1077 if Mutex::is_poisoned(&self.repl_state) {
1078 self.repl_state = Arc::new(Mutex::new(ReplState {
1079 buffer: "".to_string(),
1080 cursor_pos: 0,
1081 }));
1082 }
1083 if Mutex::is_poisoned(&self.jobs) {
1084 self.jobs = Arc::new(Mutex::new(Jobs::default()));
1085 }
1086 if Mutex::is_poisoned(&self.regex_cache) {
1087 self.regex_cache = Arc::new(Mutex::new(LruCache::new(
1088 NonZeroUsize::new(REGEX_CACHE_SIZE).expect("tried to create cache of size zero"),
1089 )));
1090 }
1091 }
1092
1093 pub fn add_span(&mut self, span: Span) -> SpanId {
1095 self.spans.push(span);
1096 SpanId::new(self.num_spans() - 1)
1097 }
1098
1099 pub fn find_span_id(&self, span: Span) -> Option<SpanId> {
1101 self.spans
1102 .iter()
1103 .position(|sp| sp == &span)
1104 .map(SpanId::new)
1105 }
1106
1107 pub fn is_background_job(&self) -> bool {
1109 self.current_job.background_thread_job.is_some()
1110 }
1111
1112 pub fn current_thread_job(&self) -> Option<&ThreadJob> {
1114 self.current_job.background_thread_job.as_ref()
1115 }
1116}
1117
1118impl GetSpan for &EngineState {
1119 fn get_span(&self, span_id: SpanId) -> Span {
1121 *self
1122 .spans
1123 .get(span_id.get())
1124 .expect("internal error: missing span")
1125 }
1126}
1127
1128impl Default for EngineState {
1129 fn default() -> Self {
1130 Self::new()
1131 }
1132}
1133
1134#[cfg(test)]
1135mod engine_state_tests {
1136 use crate::engine::StateWorkingSet;
1137 use std::str::{Utf8Error, from_utf8};
1138
1139 use super::*;
1140
1141 #[test]
1142 fn add_file_gives_id() {
1143 let engine_state = EngineState::new();
1144 let mut engine_state = StateWorkingSet::new(&engine_state);
1145 let id = engine_state.add_file("test.nu".into(), &[]);
1146
1147 assert_eq!(id, FileId::new(0));
1148 }
1149
1150 #[test]
1151 fn add_file_gives_id_including_parent() {
1152 let mut engine_state = EngineState::new();
1153 let parent_id = engine_state.add_file("test.nu".into(), Arc::new([]));
1154
1155 let mut working_set = StateWorkingSet::new(&engine_state);
1156 let working_set_id = working_set.add_file("child.nu".into(), &[]);
1157
1158 assert_eq!(parent_id, FileId::new(0));
1159 assert_eq!(working_set_id, FileId::new(1));
1160 }
1161
1162 #[test]
1163 fn merge_states() -> Result<(), ShellError> {
1164 let mut engine_state = EngineState::new();
1165 engine_state.add_file("test.nu".into(), Arc::new([]));
1166
1167 let delta = {
1168 let mut working_set = StateWorkingSet::new(&engine_state);
1169 let _ = working_set.add_file("child.nu".into(), &[]);
1170 working_set.render()
1171 };
1172
1173 engine_state.merge_delta(delta)?;
1174
1175 assert_eq!(engine_state.num_files(), 2);
1176 assert_eq!(&*engine_state.files[0].name, "test.nu");
1177 assert_eq!(&*engine_state.files[1].name, "child.nu");
1178
1179 Ok(())
1180 }
1181
1182 #[test]
1183 fn list_variables() -> Result<(), Utf8Error> {
1184 let varname = "something";
1185 let varname_with_sigil = "$".to_owned() + varname;
1186 let engine_state = EngineState::new();
1187 let mut working_set = StateWorkingSet::new(&engine_state);
1188 working_set.add_variable(
1189 varname.as_bytes().into(),
1190 Span { start: 0, end: 1 },
1191 Type::Int,
1192 false,
1193 );
1194 let variables = working_set
1195 .list_variables()
1196 .into_iter()
1197 .map(from_utf8)
1198 .collect::<Result<Vec<&str>, Utf8Error>>()?;
1199 assert_eq!(variables, vec![varname_with_sigil]);
1200 Ok(())
1201 }
1202
1203 #[test]
1204 fn get_plugin_config() {
1205 let mut engine_state = EngineState::new();
1206
1207 assert!(
1208 engine_state.get_plugin_config("example").is_none(),
1209 "Unexpected plugin configuration"
1210 );
1211
1212 let mut plugins = HashMap::new();
1213 plugins.insert("example".into(), Value::string("value", Span::test_data()));
1214
1215 let mut config = Config::clone(engine_state.get_config());
1216 config.plugins = plugins;
1217
1218 engine_state.set_config(config);
1219
1220 assert!(
1221 engine_state.get_plugin_config("example").is_some(),
1222 "Plugin configuration not found"
1223 );
1224 }
1225}
1226
1227#[cfg(test)]
1228mod test_cwd {
1229 use crate::{
1244 Value,
1245 engine::{EngineState, Stack},
1246 };
1247 use nu_path::{AbsolutePath, Path, assert_path_eq};
1248 use tempfile::{NamedTempFile, TempDir};
1249
1250 #[cfg(any(unix, windows))]
1252 fn symlink(
1253 original: impl AsRef<AbsolutePath>,
1254 link: impl AsRef<AbsolutePath>,
1255 ) -> std::io::Result<()> {
1256 let original = original.as_ref();
1257 let link = link.as_ref();
1258
1259 #[cfg(unix)]
1260 {
1261 std::os::unix::fs::symlink(original, link)
1262 }
1263 #[cfg(windows)]
1264 {
1265 if original.is_dir() {
1266 std::os::windows::fs::symlink_dir(original, link)
1267 } else {
1268 std::os::windows::fs::symlink_file(original, link)
1269 }
1270 }
1271 }
1272
1273 fn engine_state_with_pwd(path: impl AsRef<Path>) -> EngineState {
1275 let mut engine_state = EngineState::new();
1276 engine_state.add_env_var(
1277 "PWD".into(),
1278 Value::test_string(path.as_ref().to_str().unwrap()),
1279 );
1280 engine_state
1281 }
1282
1283 fn stack_with_pwd(path: impl AsRef<Path>) -> Stack {
1285 let mut stack = Stack::new();
1286 stack.add_env_var(
1287 "PWD".into(),
1288 Value::test_string(path.as_ref().to_str().unwrap()),
1289 );
1290 stack
1291 }
1292
1293 #[test]
1294 fn pwd_not_set() {
1295 let engine_state = EngineState::new();
1296 engine_state.cwd(None).unwrap_err();
1297 }
1298
1299 #[test]
1300 fn pwd_is_empty_string() {
1301 let engine_state = engine_state_with_pwd("");
1302 engine_state.cwd(None).unwrap_err();
1303 }
1304
1305 #[test]
1306 fn pwd_is_non_string_value() {
1307 let mut engine_state = EngineState::new();
1308 engine_state.add_env_var("PWD".into(), Value::test_glob("*"));
1309 engine_state.cwd(None).unwrap_err();
1310 }
1311
1312 #[test]
1313 fn pwd_is_relative_path() {
1314 let engine_state = engine_state_with_pwd("./foo");
1315
1316 engine_state.cwd(None).unwrap_err();
1317 }
1318
1319 #[test]
1320 fn pwd_has_trailing_slash() {
1321 let dir = TempDir::new().unwrap();
1322 let engine_state = engine_state_with_pwd(dir.path().join(""));
1323
1324 engine_state.cwd(None).unwrap_err();
1325 }
1326
1327 #[test]
1328 fn pwd_points_to_root() {
1329 #[cfg(windows)]
1330 let root = Path::new(r"C:\");
1331 #[cfg(not(windows))]
1332 let root = Path::new("/");
1333
1334 let engine_state = engine_state_with_pwd(root);
1335 let cwd = engine_state.cwd(None).unwrap();
1336 assert_path_eq!(cwd, root);
1337 }
1338
1339 #[test]
1340 fn pwd_points_to_normal_file() {
1341 let file = NamedTempFile::new().unwrap();
1342 let engine_state = engine_state_with_pwd(file.path());
1343
1344 engine_state.cwd(None).unwrap_err();
1345 }
1346
1347 #[test]
1348 fn pwd_points_to_normal_directory() {
1349 let dir = TempDir::new().unwrap();
1350 let engine_state = engine_state_with_pwd(dir.path());
1351
1352 let cwd = engine_state.cwd(None).unwrap();
1353 assert_path_eq!(cwd, dir.path());
1354 }
1355
1356 #[test]
1357 fn pwd_points_to_symlink_to_file() {
1358 let file = NamedTempFile::new().unwrap();
1359 let temp_file = AbsolutePath::try_new(file.path()).unwrap();
1360 let dir = TempDir::new().unwrap();
1361 let temp = AbsolutePath::try_new(dir.path()).unwrap();
1362
1363 let link = temp.join("link");
1364 symlink(temp_file, &link).unwrap();
1365 let engine_state = engine_state_with_pwd(&link);
1366
1367 engine_state.cwd(None).unwrap_err();
1368 }
1369
1370 #[test]
1371 fn pwd_points_to_symlink_to_directory() {
1372 let dir = TempDir::new().unwrap();
1373 let temp = AbsolutePath::try_new(dir.path()).unwrap();
1374
1375 let link = temp.join("link");
1376 symlink(temp, &link).unwrap();
1377 let engine_state = engine_state_with_pwd(&link);
1378
1379 let cwd = engine_state.cwd(None).unwrap();
1380 assert_path_eq!(cwd, link);
1381 }
1382
1383 #[test]
1384 fn pwd_points_to_broken_symlink() {
1385 let dir = TempDir::new().unwrap();
1386 let temp = AbsolutePath::try_new(dir.path()).unwrap();
1387 let other_dir = TempDir::new().unwrap();
1388 let other_temp = AbsolutePath::try_new(other_dir.path()).unwrap();
1389
1390 let link = temp.join("link");
1391 symlink(other_temp, &link).unwrap();
1392 let engine_state = engine_state_with_pwd(&link);
1393
1394 drop(other_dir);
1395 engine_state.cwd(None).unwrap_err();
1396 }
1397
1398 #[test]
1399 fn pwd_points_to_nonexistent_entity() {
1400 let engine_state = engine_state_with_pwd(TempDir::new().unwrap().path());
1401
1402 engine_state.cwd(None).unwrap_err();
1403 }
1404
1405 #[test]
1406 fn stack_pwd_not_set() {
1407 let dir = TempDir::new().unwrap();
1408 let engine_state = engine_state_with_pwd(dir.path());
1409 let stack = Stack::new();
1410
1411 let cwd = engine_state.cwd(Some(&stack)).unwrap();
1412 assert_eq!(cwd, dir.path());
1413 }
1414
1415 #[test]
1416 fn stack_pwd_is_empty_string() {
1417 let dir = TempDir::new().unwrap();
1418 let engine_state = engine_state_with_pwd(dir.path());
1419 let stack = stack_with_pwd("");
1420
1421 engine_state.cwd(Some(&stack)).unwrap_err();
1422 }
1423
1424 #[test]
1425 fn stack_pwd_points_to_normal_directory() {
1426 let dir1 = TempDir::new().unwrap();
1427 let dir2 = TempDir::new().unwrap();
1428 let engine_state = engine_state_with_pwd(dir1.path());
1429 let stack = stack_with_pwd(dir2.path());
1430
1431 let cwd = engine_state.cwd(Some(&stack)).unwrap();
1432 assert_path_eq!(cwd, dir2.path());
1433 }
1434
1435 #[test]
1436 fn stack_pwd_points_to_normal_directory_with_symlink_components() {
1437 let dir = TempDir::new().unwrap();
1438 let temp = AbsolutePath::try_new(dir.path()).unwrap();
1439
1440 let link = temp.join("link");
1442 symlink(temp, &link).unwrap();
1443 let foo = link.join("foo");
1444 std::fs::create_dir(temp.join("foo")).unwrap();
1445 let engine_state = EngineState::new();
1446 let stack = stack_with_pwd(&foo);
1447
1448 let cwd = engine_state.cwd(Some(&stack)).unwrap();
1449 assert_path_eq!(cwd, foo);
1450 }
1451}