1use nu_protocol::{
29 CommandWideCompleter, DeclId, ModuleId, Signature, Span, Type, Value, VarId,
30 ast::Expr,
31 engine::{Command, CommandType, EngineState, ScopeBindings, Stack, Visibility},
32 record,
33};
34use std::{cmp::Ordering, collections::HashMap};
35
36pub struct ScopeData<'e, 's> {
41 engine_state: &'e EngineState,
42 stack: &'s Stack,
43 decls_map: HashMap<Vec<u8>, DeclId>,
44 modules_map: HashMap<Vec<u8>, ModuleId>,
45 visibility: Visibility,
46}
47
48impl<'e, 's> ScopeData<'e, 's> {
49 pub fn new(engine_state: &'e EngineState, stack: &'s Stack) -> Self {
50 Self {
51 engine_state,
52 stack,
53 decls_map: HashMap::new(),
54 modules_map: HashMap::new(),
55 visibility: Visibility::new(),
56 }
57 }
58
59 pub fn populate_vars(&mut self) {}
62
63 pub fn populate_decls(&mut self) {
65 let bindings = self.collect_local_and_global_bindings();
66 self.decls_map = bindings.decls;
67 self.visibility = bindings.visibility;
68 }
69
70 pub fn populate_modules(&mut self) {
71 let bindings = self.collect_local_and_global_bindings();
72 self.modules_map = bindings.modules;
73 }
74
75 fn collect_local_and_global_bindings(&self) -> ScopeBindings {
77 let mut bindings = ScopeBindings::default();
78 for overlay_frame in self.engine_state.active_overlays(&[]) {
79 bindings.extend_from_overlay(overlay_frame);
80 }
81 for local in &self.stack.active_scope_bindings {
82 bindings.extend_from_bindings(local);
83 }
84 if let Some(pc) = self.stack.ir_instruction_index {
85 for region in &self.stack.ir_scope_regions {
87 if region.contains(pc) {
88 bindings.extend_from_bindings(®ion.bindings);
89 }
90 }
91 }
92 bindings
93 }
94
95 pub fn collect_vars(&self, span: Span) -> Vec<Value> {
103 let mut name_to_id: HashMap<Vec<u8>, VarId> = HashMap::new();
104
105 for overlay_frame in self.engine_state.active_overlays(&[]) {
106 for (name, var_id) in &overlay_frame.vars {
107 name_to_id.insert(name.clone(), *var_id);
108 }
109 }
110
111 for var_id in stack_var_ids(self.stack) {
113 if let Some(name) = &self.engine_state.get_var(var_id).name {
114 name_to_id.insert(name.clone(), var_id);
115 }
116 }
117
118 let mut vars = vec![];
119
120 for (var_name, var_id) in &name_to_id {
121 if is_unlet(self.stack, *var_id) {
122 continue;
123 }
124
125 let var = self.engine_state.get_var(*var_id);
126 let var_type = Value::string(var.ty.to_string(), span);
127 let is_const = Value::bool(var.const_val.is_some(), span);
128
129 let var_value_result = self.stack.get_var(*var_id, span);
130
131 if var_value_result.is_err() && var.const_val.is_none() {
134 let in_permanent_overlay = self
137 .engine_state
138 .active_overlays(&[])
139 .any(|overlay| overlay.vars.values().any(|id| *id == *var_id));
140 if !in_permanent_overlay {
141 continue;
142 }
143 }
144
145 let var_value = var_value_result
146 .ok()
147 .or(var.const_val.clone())
148 .unwrap_or(Value::nothing(span));
149
150 let var_id_val = Value::int(var_id.get() as i64, span);
151 let memory_size = Value::int(var_value.memory_size() as i64, span);
152
153 vars.push(Value::record(
154 record! {
155 "name" => Value::string(String::from_utf8_lossy(var_name).to_string(), span),
156 "type" => var_type,
157 "value" => var_value,
158 "is_const" => is_const,
159 "var_id" => var_id_val,
160 "mem_size" => memory_size,
161 },
162 span,
163 ));
164 }
165
166 sort_rows(&mut vars);
167 vars
168 }
169
170 pub fn collect_commands(&self, span: Span) -> Vec<Value> {
171 let mut commands = vec![];
172
173 for (command_name, decl_id) in &self.decls_map {
174 if self.visibility.is_decl_id_visible(decl_id)
175 && !self.engine_state.get_decl(*decl_id).is_alias()
176 {
177 let command_name = String::from_utf8_lossy(command_name);
178 let decl = self.engine_state.get_decl(*decl_id);
179 let signature = decl.signature();
180
181 let examples = decl
182 .examples()
183 .into_iter()
184 .map(|x| {
185 Value::record(
186 record! {
187 "description" => Value::string(x.description, span),
188 "example" => Value::string(x.example, span),
189 "result" => x.result.unwrap_or(Value::nothing(span)).with_span(span),
190 },
191 span,
192 )
193 })
194 .collect();
195
196 let attributes = decl
197 .attributes()
198 .into_iter()
199 .map(|(name, value)| {
200 Value::record(
201 record! {
202 "name" => Value::string(name, span),
203 "value" => value,
204 },
205 span,
206 )
207 })
208 .collect();
209
210 let deprecations = decl
211 .deprecation_info()
212 .into_iter()
213 .map(|entry| entry.into_value(&command_name, span))
214 .collect();
215
216 let record = record! {
217 "name" => Value::string(command_name, span),
218 "category" => Value::string(signature.category.to_string(), span),
219 "signatures" => self.collect_signatures(&signature, span),
220 "description" => Value::string(decl.description(), span),
221 "examples" => Value::list(examples, span),
222 "attributes" => Value::list(attributes, span),
223 "type" => Value::string(decl.command_type().to_string(), span),
224 "is_sub" => Value::bool(decl.is_sub(), span),
225 "is_const" => Value::bool(decl.is_const(), span),
226 "creates_scope" => Value::bool(signature.creates_scope, span),
227 "extra_description" => Value::string(decl.extra_description(), span),
228 "search_terms" => Value::string(decl.search_terms().join(", "), span),
229 "complete" => match signature.complete {
230 Some(CommandWideCompleter::Command(decl_id)) => Value::int(decl_id.get() as i64, span),
231 Some(CommandWideCompleter::External) => Value::string("external", span),
232 None => Value::nothing(span),
233 },
234 "deprecation_info" => Value::list(deprecations, span),
235 "decl_id" => Value::int(decl_id.get() as i64, span),
236 };
237
238 commands.push(Value::record(record, span))
239 }
240 }
241
242 sort_rows(&mut commands);
243
244 commands
245 }
246
247 fn collect_signatures(&self, signature: &Signature, span: Span) -> Value {
248 let mut sigs = signature
249 .input_output_types
250 .iter()
251 .map(|(input_type, output_type)| {
252 (
253 input_type.to_shape().to_string(),
254 Value::list(
255 self.collect_signature_entries(input_type, output_type, signature, span),
256 span,
257 ),
258 )
259 })
260 .collect::<Vec<(String, Value)>>();
261
262 if sigs.is_empty() {
267 let any_type = &Type::Any;
268 sigs.push((
269 any_type.to_shape().to_string(),
270 Value::list(
271 self.collect_signature_entries(any_type, any_type, signature, span),
272 span,
273 ),
274 ));
275 }
276 sigs.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
277 sigs.dedup_by(|(k1, _), (k2, _)| k1 == k2);
286 Value::record(sigs.into_iter().collect(), span)
287 }
288
289 fn collect_signature_entries(
290 &self,
291 input_type: &Type,
292 output_type: &Type,
293 signature: &Signature,
294 span: Span,
295 ) -> Vec<Value> {
296 let mut sig_records = vec![];
297
298 sig_records.push(Value::record(
300 record! {
301 "parameter_name" => Value::nothing(span),
302 "parameter_type" => Value::string("input", span),
303 "syntax_shape" => Value::string(input_type.to_shape().to_string(), span),
304 "is_optional" => Value::bool(false, span),
305 "short_flag" => Value::nothing(span),
306 "description" => Value::nothing(span),
307 "completion" => Value::nothing(span),
308 "parameter_default" => Value::nothing(span),
309 },
310 span,
311 ));
312
313 for req in &signature.required_positional {
315 let completion = req
316 .completion
317 .as_ref()
318 .map(|compl| compl.to_value(self.engine_state, span))
319 .unwrap_or(Value::nothing(span));
320
321 sig_records.push(Value::record(
322 record! {
323 "parameter_name" => Value::string(&req.name, span),
324 "parameter_type" => Value::string("positional", span),
325 "syntax_shape" => Value::string(req.shape.to_string(), span),
326 "is_optional" => Value::bool(false, span),
327 "short_flag" => Value::nothing(span),
328 "description" => Value::string(&req.desc, span),
329 "completion" => completion,
330 "parameter_default" => Value::nothing(span),
331 },
332 span,
333 ));
334 }
335
336 for opt in &signature.optional_positional {
338 let completion = opt
339 .completion
340 .as_ref()
341 .map(|compl| compl.to_value(self.engine_state, span))
342 .unwrap_or(Value::nothing(span));
343
344 let default = if let Some(val) = &opt.default_value {
345 val.clone()
346 } else {
347 Value::nothing(span)
348 };
349
350 sig_records.push(Value::record(
351 record! {
352 "parameter_name" => Value::string(&opt.name, span),
353 "parameter_type" => Value::string("positional", span),
354 "syntax_shape" => Value::string(opt.shape.to_string(), span),
355 "is_optional" => Value::bool(true, span),
356 "short_flag" => Value::nothing(span),
357 "description" => Value::string(&opt.desc, span),
358 "completion" => completion,
359 "parameter_default" => default,
360 },
361 span,
362 ));
363 }
364
365 if let Some(rest) = &signature.rest_positional {
367 let name = if rest.name == "rest" { "" } else { &rest.name };
368 let completion = rest
369 .completion
370 .as_ref()
371 .map(|compl| compl.to_value(self.engine_state, span))
372 .unwrap_or(Value::nothing(span));
373
374 sig_records.push(Value::record(
375 record! {
376 "parameter_name" => Value::string(name, span),
377 "parameter_type" => Value::string("rest", span),
378 "syntax_shape" => Value::string(rest.shape.to_string(), span),
379 "is_optional" => Value::bool(true, span),
380 "short_flag" => Value::nothing(span),
381 "description" => Value::string(&rest.desc, span),
382 "completion" => completion,
383 "parameter_default" => Value::nothing(span),
385 },
386 span,
387 ));
388 }
389
390 for named in &signature.named {
392 let flag_type;
393
394 if named.long == "help" {
396 continue;
397 }
398
399 let completion = named
400 .completion
401 .as_ref()
402 .map(|compl| compl.to_value(self.engine_state, span))
403 .unwrap_or(Value::nothing(span));
404
405 let shape = if let Some(arg) = &named.arg {
406 flag_type = Value::string("named", span);
407 Value::string(arg.to_string(), span)
408 } else {
409 flag_type = Value::string("switch", span);
410 Value::nothing(span)
411 };
412
413 let short_flag = if let Some(c) = named.short {
414 Value::string(c, span)
415 } else {
416 Value::nothing(span)
417 };
418
419 let default = if let Some(val) = &named.default_value {
420 val.clone()
421 } else {
422 Value::nothing(span)
423 };
424
425 sig_records.push(Value::record(
426 record! {
427 "parameter_name" => Value::string(&named.long, span),
428 "parameter_type" => flag_type,
429 "syntax_shape" => shape,
430 "is_optional" => Value::bool(!named.required, span),
431 "short_flag" => short_flag,
432 "description" => Value::string(&named.desc, span),
433 "completion" => completion,
434 "parameter_default" => default,
435 },
436 span,
437 ));
438 }
439
440 sig_records.push(Value::record(
442 record! {
443 "parameter_name" => Value::nothing(span),
444 "parameter_type" => Value::string("output", span),
445 "syntax_shape" => Value::string(output_type.to_shape().to_string(), span),
446 "is_optional" => Value::bool(false, span),
447 "short_flag" => Value::nothing(span),
448 "description" => Value::nothing(span),
449 "completion" => Value::nothing(span),
450 "parameter_default" => Value::nothing(span),
451 },
452 span,
453 ));
454
455 sig_records
456 }
457
458 pub fn collect_externs(&self, span: Span) -> Vec<Value> {
459 let mut externals = vec![];
460
461 for (command_name, decl_id) in &self.decls_map {
462 let decl = self.engine_state.get_decl(*decl_id);
463
464 if decl.is_known_external() {
465 let record = record! {
466 "name" => Value::string(String::from_utf8_lossy(command_name), span),
467 "description" => Value::string(decl.description(), span),
468 "decl_id" => Value::int(decl_id.get() as i64, span),
469 };
470
471 externals.push(Value::record(record, span))
472 }
473 }
474
475 sort_rows(&mut externals);
476 externals
477 }
478
479 pub fn collect_aliases(&self, span: Span) -> Vec<Value> {
480 let mut aliases = vec![];
481
482 for (decl_name, decl_id) in &self.decls_map {
483 if self.visibility.is_decl_id_visible(decl_id) {
484 let decl = self.engine_state.get_decl(*decl_id);
485 if let Some(alias) = decl.as_alias() {
486 let aliased_decl_id = if let Expr::Call(wrapped_call) = &alias.wrapped_call.expr
487 {
488 Value::int(wrapped_call.decl_id.get() as i64, span)
489 } else {
490 Value::nothing(span)
491 };
492
493 let expansion = String::from_utf8_lossy(
494 self.engine_state.get_span_contents(alias.wrapped_call.span),
495 );
496
497 aliases.push(Value::record(
498 record! {
499 "name" => Value::string(String::from_utf8_lossy(decl_name), span),
500 "expansion" => Value::string(expansion, span),
501 "description" => Value::string(alias.description(), span),
502 "decl_id" => Value::int(decl_id.get() as i64, span),
503 "aliased_decl_id" => aliased_decl_id,
504 },
505 span,
506 ));
507 }
508 }
509 }
510
511 sort_rows(&mut aliases);
512 aliases
513 }
514
515 fn collect_module(&self, module_name: &[u8], module_id: &ModuleId, span: Span) -> Value {
516 let module = self.engine_state.get_module(*module_id);
517
518 let all_decls = module.decls();
519
520 let mut export_commands: Vec<Value> = all_decls
521 .iter()
522 .filter_map(|(name_bytes, decl_id)| {
523 let decl = self.engine_state.get_decl(*decl_id);
524
525 if !decl.is_alias() && !decl.is_known_external() {
526 Some(Value::record(
527 record! {
528 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
529 "decl_id" => Value::int(decl_id.get() as i64, span),
530 },
531 span,
532 ))
533 } else {
534 None
535 }
536 })
537 .collect();
538
539 let mut export_aliases: Vec<Value> = all_decls
540 .iter()
541 .filter_map(|(name_bytes, decl_id)| {
542 let decl = self.engine_state.get_decl(*decl_id);
543
544 if decl.is_alias() {
545 Some(Value::record(
546 record! {
547 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
548 "decl_id" => Value::int(decl_id.get() as i64, span),
549 },
550 span,
551 ))
552 } else {
553 None
554 }
555 })
556 .collect();
557
558 let mut export_externs: Vec<Value> = all_decls
559 .iter()
560 .filter_map(|(name_bytes, decl_id)| {
561 let decl = self.engine_state.get_decl(*decl_id);
562
563 if decl.is_known_external() {
564 Some(Value::record(
565 record! {
566 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
567 "decl_id" => Value::int(decl_id.get() as i64, span),
568 },
569 span,
570 ))
571 } else {
572 None
573 }
574 })
575 .collect();
576
577 let mut export_submodules: Vec<Value> = module
578 .submodules()
579 .iter()
580 .map(|(name_bytes, submodule_id)| self.collect_module(name_bytes, submodule_id, span))
581 .collect();
582
583 let mut export_consts: Vec<Value> = module
584 .consts()
585 .iter()
586 .map(|(name_bytes, var_id)| {
587 Value::record(
588 record! {
589 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
590 "type" => Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span),
591 "var_id" => Value::int(var_id.get() as i64, span),
592 },
593 span,
594 )
595 })
596 .collect();
597
598 sort_rows(&mut export_commands);
599 sort_rows(&mut export_aliases);
600 sort_rows(&mut export_externs);
601 sort_rows(&mut export_submodules);
602 sort_rows(&mut export_consts);
603
604 let (module_desc, module_extra_desc) = self
605 .engine_state
606 .build_module_desc(*module_id)
607 .unwrap_or_default();
608
609 Value::record(
610 record! {
611 "name" => Value::string(String::from_utf8_lossy(module_name), span),
612 "commands" => Value::list(export_commands, span),
613 "aliases" => Value::list(export_aliases, span),
614 "externs" => Value::list(export_externs, span),
615 "submodules" => Value::list(export_submodules, span),
616 "constants" => Value::list(export_consts, span),
617 "has_env_block" => Value::bool(module.env_block.is_some(), span),
618 "description" => Value::string(module_desc, span),
619 "extra_description" => Value::string(module_extra_desc, span),
620 "module_id" => Value::int(module_id.get() as i64, span),
621 "file" => Value::string(module.file.clone().map_or("unknown".to_string(), |(p, _)| p.path().to_string_lossy().to_string()), span),
622 },
623 span,
624 )
625 }
626
627 pub fn collect_modules(&self, span: Span) -> Vec<Value> {
628 let mut modules = vec![];
629
630 for (module_name, module_id) in &self.modules_map {
631 modules.push(self.collect_module(module_name, module_id, span));
632 }
633
634 modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
635 modules
636 }
637
638 pub fn collect_engine_state(&self, span: Span) -> Value {
639 let num_env_vars = self
640 .engine_state
641 .env_vars
642 .values()
643 .map(|overlay| overlay.len() as i64)
644 .sum();
645
646 let config = self.stack.get_config(self.engine_state);
647 let last_result = Value::record(
648 record! {
649 "name" => Value::string(
650 format!("${}", nu_protocol::LAST_RESULT_VAR_NAME),
651 span,
652 ),
653 "size_limit" => Value::filesize(config.max_last_result_size, span),
654 "memory_size" => Value::filesize(
655 nu_protocol::Filesize::new(self.stack.last_result_memory_size() as i64),
656 span,
657 ),
658 "truncated" => Value::bool(self.stack.last_result_was_truncated(), span),
659 "has_metadata" => Value::bool(
660 self.stack.last_result_metadata().is_some(),
661 span,
662 ),
663 },
664 span,
665 );
666
667 Value::record(
668 record! {
669 "source_bytes" => Value::int(self.engine_state.next_span_start() as i64, span),
670 "num_vars" => Value::int(self.engine_state.num_vars() as i64, span),
671 "num_decls" => Value::int(self.engine_state.num_decls() as i64, span),
672 "num_blocks" => Value::int(self.engine_state.num_blocks() as i64, span),
673 "num_modules" => Value::int(self.engine_state.num_modules() as i64, span),
674 "num_env_vars" => Value::int(num_env_vars, span),
675 "last_result" => last_result,
676 },
677 span,
678 )
679 }
680}
681
682fn stack_var_ids(stack: &Stack) -> Vec<VarId> {
688 let mut ids = Vec::new();
689 collect_stack_var_ids(stack, &mut ids);
690 ids
691}
692
693fn collect_stack_var_ids(stack: &Stack, ids: &mut Vec<VarId>) {
694 if let Some(parent) = &stack.parent_stack {
695 collect_stack_var_ids(parent, ids);
696 ids.retain(|id| !stack.parent_deletions.contains(id));
697 }
698 for (var_id, _) in &stack.vars {
700 ids.push(*var_id);
701 }
702}
703
704fn is_unlet(stack: &Stack, var_id: VarId) -> bool {
706 let mut current = Some(stack);
707 while let Some(s) = current {
708 if s.deletions.contains(&var_id) || s.parent_deletions.contains(&var_id) {
709 return true;
710 }
711 current = s.parent_stack.as_deref();
712 }
713 false
714}
715
716fn sort_rows(decls: &mut [Value]) {
717 decls.sort_by(|a, b| match (a, b) {
718 (Value::Record { val: rec_a, .. }, Value::Record { val: rec_b, .. }) => {
719 match (rec_a.values().next(), rec_b.values().next()) {
722 (Some(val_a), Some(val_b)) => match (val_a, val_b) {
723 (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
724 str_a.cmp(str_b)
725 }
726 _ => Ordering::Equal,
727 },
728 _ => Ordering::Equal,
729 }
730 }
731 _ => Ordering::Equal,
732 });
733}
734
735pub fn find_builtin_decl(engine_state: &EngineState, name: &str) -> Option<DeclId> {
741 for idx in (0..engine_state.num_decls()).rev() {
742 let decl_id = DeclId::new(idx);
743 let decl = engine_state.get_decl(decl_id);
744 if decl.command_type() == CommandType::Builtin && decl.name() == name {
745 return Some(decl_id);
746 }
747 }
748 None
749}