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