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