1use nu_protocol::{
2 CommandWideCompleter, DeclId, ModuleId, Signature, Span, Type, Value, VarId,
3 ast::Expr,
4 engine::{Command, EngineState, Stack, Visibility},
5 record,
6};
7use std::{cmp::Ordering, collections::HashMap};
8
9pub struct ScopeData<'e, 's> {
10 engine_state: &'e EngineState,
11 stack: &'s Stack,
12 vars_map: HashMap<&'e Vec<u8>, &'e VarId>,
13 decls_map: HashMap<&'e Vec<u8>, &'e DeclId>,
14 modules_map: HashMap<&'e Vec<u8>, &'e ModuleId>,
15 visibility: Visibility,
16}
17
18impl<'e, 's> ScopeData<'e, 's> {
19 pub fn new(engine_state: &'e EngineState, stack: &'s Stack) -> Self {
20 Self {
21 engine_state,
22 stack,
23 vars_map: HashMap::new(),
24 decls_map: HashMap::new(),
25 modules_map: HashMap::new(),
26 visibility: Visibility::new(),
27 }
28 }
29
30 pub fn populate_vars(&mut self) {
31 for overlay_frame in self.engine_state.active_overlays(&[]) {
32 self.vars_map.extend(&overlay_frame.vars);
33 }
34 }
35
36 pub fn populate_decls(&mut self) {
38 for overlay_frame in self.engine_state.active_overlays(&[]) {
39 self.decls_map.extend(&overlay_frame.decls);
40 self.visibility.merge_with(overlay_frame.visibility.clone());
41 }
42 }
43
44 pub fn populate_modules(&mut self) {
45 for overlay_frame in self.engine_state.active_overlays(&[]) {
46 self.modules_map.extend(&overlay_frame.modules);
47 }
48 }
49
50 pub fn collect_vars(&self, span: Span) -> Vec<Value> {
51 let mut vars = vec![];
52
53 for (var_name, var_id) in &self.vars_map {
54 let var_name = Value::string(String::from_utf8_lossy(var_name).to_string(), span);
55
56 let var = self.engine_state.get_var(**var_id);
57 let var_type = Value::string(var.ty.to_string(), span);
58 let is_const = Value::bool(var.const_val.is_some(), span);
59
60 let var_value_result = self.stack.get_var(**var_id, span);
61
62 if var_value_result.is_err() && var.const_val.is_none() {
65 continue;
66 }
67
68 let var_value = var_value_result
69 .ok()
70 .or(var.const_val.clone())
71 .unwrap_or(Value::nothing(span));
72
73 let var_id_val = Value::int(var_id.get() as i64, span);
74 let memory_size = Value::int(var_value.memory_size() as i64, span);
75
76 vars.push(Value::record(
77 record! {
78 "name" => var_name,
79 "type" => var_type,
80 "value" => var_value,
81 "is_const" => is_const,
82 "var_id" => var_id_val,
83 "mem_size" => memory_size,
84 },
85 span,
86 ));
87 }
88
89 sort_rows(&mut vars);
90 vars
91 }
92
93 pub fn collect_commands(&self, span: Span) -> Vec<Value> {
94 let mut commands = vec![];
95
96 for (command_name, decl_id) in &self.decls_map {
97 if self.visibility.is_decl_id_visible(decl_id)
98 && !self.engine_state.get_decl(**decl_id).is_alias()
99 {
100 let decl = self.engine_state.get_decl(**decl_id);
101 let signature = decl.signature();
102
103 let examples = decl
104 .examples()
105 .into_iter()
106 .map(|x| {
107 Value::record(
108 record! {
109 "description" => Value::string(x.description, span),
110 "example" => Value::string(x.example, span),
111 "result" => x.result.unwrap_or(Value::nothing(span)).with_span(span),
112 },
113 span,
114 )
115 })
116 .collect();
117
118 let attributes = decl
119 .attributes()
120 .into_iter()
121 .map(|(name, value)| {
122 Value::record(
123 record! {
124 "name" => Value::string(name, span),
125 "value" => value,
126 },
127 span,
128 )
129 })
130 .collect();
131
132 let record = record! {
133 "name" => Value::string(String::from_utf8_lossy(command_name), span),
134 "category" => Value::string(signature.category.to_string(), span),
135 "signatures" => self.collect_signatures(&signature, span),
136 "description" => Value::string(decl.description(), span),
137 "examples" => Value::list(examples, span),
138 "attributes" => Value::list(attributes, span),
139 "type" => Value::string(decl.command_type().to_string(), span),
140 "is_sub" => Value::bool(decl.is_sub(), span),
141 "is_const" => Value::bool(decl.is_const(), span),
142 "creates_scope" => Value::bool(signature.creates_scope, span),
143 "extra_description" => Value::string(decl.extra_description(), span),
144 "search_terms" => Value::string(decl.search_terms().join(", "), span),
145 "complete" => match signature.complete {
146 Some(CommandWideCompleter::Command(decl_id)) => Value::int(decl_id.get() as i64, span),
147 Some(CommandWideCompleter::External) => Value::string("external", span),
148 None => Value::nothing(span),
149 },
150 "decl_id" => Value::int(decl_id.get() as i64, span),
151 };
152
153 commands.push(Value::record(record, span))
154 }
155 }
156
157 sort_rows(&mut commands);
158
159 commands
160 }
161
162 fn collect_signatures(&self, signature: &Signature, span: Span) -> Value {
163 let mut sigs = signature
164 .input_output_types
165 .iter()
166 .map(|(input_type, output_type)| {
167 (
168 input_type.to_shape().to_string(),
169 Value::list(
170 self.collect_signature_entries(input_type, output_type, signature, span),
171 span,
172 ),
173 )
174 })
175 .collect::<Vec<(String, Value)>>();
176
177 if sigs.is_empty() {
182 let any_type = &Type::Any;
183 sigs.push((
184 any_type.to_shape().to_string(),
185 Value::list(
186 self.collect_signature_entries(any_type, any_type, signature, span),
187 span,
188 ),
189 ));
190 }
191 sigs.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
192 sigs.dedup_by(|(k1, _), (k2, _)| k1 == k2);
201 Value::record(sigs.into_iter().collect(), span)
202 }
203
204 fn collect_signature_entries(
205 &self,
206 input_type: &Type,
207 output_type: &Type,
208 signature: &Signature,
209 span: Span,
210 ) -> Vec<Value> {
211 let mut sig_records = vec![];
212
213 sig_records.push(Value::record(
215 record! {
216 "parameter_name" => Value::nothing(span),
217 "parameter_type" => Value::string("input", span),
218 "syntax_shape" => Value::string(input_type.to_shape().to_string(), span),
219 "is_optional" => Value::bool(false, span),
220 "short_flag" => Value::nothing(span),
221 "description" => Value::nothing(span),
222 "completion" => Value::nothing(span),
223 "parameter_default" => Value::nothing(span),
224 },
225 span,
226 ));
227
228 for req in &signature.required_positional {
230 let completion = req
231 .completion
232 .as_ref()
233 .map(|compl| compl.to_value(self.engine_state, span))
234 .unwrap_or(Value::nothing(span));
235
236 sig_records.push(Value::record(
237 record! {
238 "parameter_name" => Value::string(&req.name, span),
239 "parameter_type" => Value::string("positional", span),
240 "syntax_shape" => Value::string(req.shape.to_string(), span),
241 "is_optional" => Value::bool(false, span),
242 "short_flag" => Value::nothing(span),
243 "description" => Value::string(&req.desc, span),
244 "completion" => completion,
245 "parameter_default" => Value::nothing(span),
246 },
247 span,
248 ));
249 }
250
251 for opt in &signature.optional_positional {
253 let completion = opt
254 .completion
255 .as_ref()
256 .map(|compl| compl.to_value(self.engine_state, span))
257 .unwrap_or(Value::nothing(span));
258
259 let default = if let Some(val) = &opt.default_value {
260 val.clone()
261 } else {
262 Value::nothing(span)
263 };
264
265 sig_records.push(Value::record(
266 record! {
267 "parameter_name" => Value::string(&opt.name, span),
268 "parameter_type" => Value::string("positional", span),
269 "syntax_shape" => Value::string(opt.shape.to_string(), span),
270 "is_optional" => Value::bool(true, span),
271 "short_flag" => Value::nothing(span),
272 "description" => Value::string(&opt.desc, span),
273 "completion" => completion,
274 "parameter_default" => default,
275 },
276 span,
277 ));
278 }
279
280 if let Some(rest) = &signature.rest_positional {
282 let name = if rest.name == "rest" { "" } else { &rest.name };
283 let completion = rest
284 .completion
285 .as_ref()
286 .map(|compl| compl.to_value(self.engine_state, span))
287 .unwrap_or(Value::nothing(span));
288
289 sig_records.push(Value::record(
290 record! {
291 "parameter_name" => Value::string(name, span),
292 "parameter_type" => Value::string("rest", span),
293 "syntax_shape" => Value::string(rest.shape.to_string(), span),
294 "is_optional" => Value::bool(true, span),
295 "short_flag" => Value::nothing(span),
296 "description" => Value::string(&rest.desc, span),
297 "completion" => completion,
298 "parameter_default" => Value::nothing(span),
300 },
301 span,
302 ));
303 }
304
305 for named in &signature.named {
307 let flag_type;
308
309 if named.long == "help" {
311 continue;
312 }
313
314 let completion = named
315 .completion
316 .as_ref()
317 .map(|compl| compl.to_value(self.engine_state, span))
318 .unwrap_or(Value::nothing(span));
319
320 let shape = if let Some(arg) = &named.arg {
321 flag_type = Value::string("named", span);
322 Value::string(arg.to_string(), span)
323 } else {
324 flag_type = Value::string("switch", span);
325 Value::nothing(span)
326 };
327
328 let short_flag = if let Some(c) = named.short {
329 Value::string(c, span)
330 } else {
331 Value::nothing(span)
332 };
333
334 let default = if let Some(val) = &named.default_value {
335 val.clone()
336 } else {
337 Value::nothing(span)
338 };
339
340 sig_records.push(Value::record(
341 record! {
342 "parameter_name" => Value::string(&named.long, span),
343 "parameter_type" => flag_type,
344 "syntax_shape" => shape,
345 "is_optional" => Value::bool(!named.required, span),
346 "short_flag" => short_flag,
347 "description" => Value::string(&named.desc, span),
348 "completion" => completion,
349 "parameter_default" => default,
350 },
351 span,
352 ));
353 }
354
355 sig_records.push(Value::record(
357 record! {
358 "parameter_name" => Value::nothing(span),
359 "parameter_type" => Value::string("output", span),
360 "syntax_shape" => Value::string(output_type.to_shape().to_string(), span),
361 "is_optional" => Value::bool(false, span),
362 "short_flag" => Value::nothing(span),
363 "description" => Value::nothing(span),
364 "completion" => Value::nothing(span),
365 "parameter_default" => Value::nothing(span),
366 },
367 span,
368 ));
369
370 sig_records
371 }
372
373 pub fn collect_externs(&self, span: Span) -> Vec<Value> {
374 let mut externals = vec![];
375
376 for (command_name, decl_id) in &self.decls_map {
377 let decl = self.engine_state.get_decl(**decl_id);
378
379 if decl.is_known_external() {
380 let record = record! {
381 "name" => Value::string(String::from_utf8_lossy(command_name), span),
382 "description" => Value::string(decl.description(), span),
383 "decl_id" => Value::int(decl_id.get() as i64, span),
384 };
385
386 externals.push(Value::record(record, span))
387 }
388 }
389
390 sort_rows(&mut externals);
391 externals
392 }
393
394 pub fn collect_aliases(&self, span: Span) -> Vec<Value> {
395 let mut aliases = vec![];
396
397 for (decl_name, decl_id) in self.engine_state.get_decls_sorted(false) {
398 if self.visibility.is_decl_id_visible(&decl_id) {
399 let decl = self.engine_state.get_decl(decl_id);
400 if let Some(alias) = decl.as_alias() {
401 let aliased_decl_id = if let Expr::Call(wrapped_call) = &alias.wrapped_call.expr
402 {
403 Value::int(wrapped_call.decl_id.get() as i64, span)
404 } else {
405 Value::nothing(span)
406 };
407
408 let expansion = String::from_utf8_lossy(
409 self.engine_state.get_span_contents(alias.wrapped_call.span),
410 );
411
412 aliases.push(Value::record(
413 record! {
414 "name" => Value::string(String::from_utf8_lossy(&decl_name), span),
415 "expansion" => Value::string(expansion, span),
416 "description" => Value::string(alias.description(), span),
417 "decl_id" => Value::int(decl_id.get() as i64, span),
418 "aliased_decl_id" => aliased_decl_id,
419 },
420 span,
421 ));
422 }
423 }
424 }
425
426 sort_rows(&mut aliases);
427 aliases
429 }
430
431 fn collect_module(&self, module_name: &[u8], module_id: &ModuleId, span: Span) -> Value {
432 let module = self.engine_state.get_module(*module_id);
433
434 let all_decls = module.decls();
435
436 let mut export_commands: Vec<Value> = all_decls
437 .iter()
438 .filter_map(|(name_bytes, decl_id)| {
439 let decl = self.engine_state.get_decl(*decl_id);
440
441 if !decl.is_alias() && !decl.is_known_external() {
442 Some(Value::record(
443 record! {
444 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
445 "decl_id" => Value::int(decl_id.get() as i64, span),
446 },
447 span,
448 ))
449 } else {
450 None
451 }
452 })
453 .collect();
454
455 let mut export_aliases: Vec<Value> = all_decls
456 .iter()
457 .filter_map(|(name_bytes, decl_id)| {
458 let decl = self.engine_state.get_decl(*decl_id);
459
460 if decl.is_alias() {
461 Some(Value::record(
462 record! {
463 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
464 "decl_id" => Value::int(decl_id.get() as i64, span),
465 },
466 span,
467 ))
468 } else {
469 None
470 }
471 })
472 .collect();
473
474 let mut export_externs: Vec<Value> = all_decls
475 .iter()
476 .filter_map(|(name_bytes, decl_id)| {
477 let decl = self.engine_state.get_decl(*decl_id);
478
479 if decl.is_known_external() {
480 Some(Value::record(
481 record! {
482 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
483 "decl_id" => Value::int(decl_id.get() as i64, span),
484 },
485 span,
486 ))
487 } else {
488 None
489 }
490 })
491 .collect();
492
493 let mut export_submodules: Vec<Value> = module
494 .submodules()
495 .iter()
496 .map(|(name_bytes, submodule_id)| self.collect_module(name_bytes, submodule_id, span))
497 .collect();
498
499 let mut export_consts: Vec<Value> = module
500 .consts()
501 .iter()
502 .map(|(name_bytes, var_id)| {
503 Value::record(
504 record! {
505 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
506 "type" => Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span),
507 "var_id" => Value::int(var_id.get() as i64, span),
508 },
509 span,
510 )
511 })
512 .collect();
513
514 sort_rows(&mut export_commands);
515 sort_rows(&mut export_aliases);
516 sort_rows(&mut export_externs);
517 sort_rows(&mut export_submodules);
518 sort_rows(&mut export_consts);
519
520 let (module_desc, module_extra_desc) = self
521 .engine_state
522 .build_module_desc(*module_id)
523 .unwrap_or_default();
524
525 Value::record(
526 record! {
527 "name" => Value::string(String::from_utf8_lossy(module_name), span),
528 "commands" => Value::list(export_commands, span),
529 "aliases" => Value::list(export_aliases, span),
530 "externs" => Value::list(export_externs, span),
531 "submodules" => Value::list(export_submodules, span),
532 "constants" => Value::list(export_consts, span),
533 "has_env_block" => Value::bool(module.env_block.is_some(), span),
534 "description" => Value::string(module_desc, span),
535 "extra_description" => Value::string(module_extra_desc, span),
536 "module_id" => Value::int(module_id.get() as i64, span),
537 "file" => Value::string(module.file.clone().map_or("unknown".to_string(), |(p, _)| p.path().to_string_lossy().to_string()), span),
538 },
539 span,
540 )
541 }
542
543 pub fn collect_modules(&self, span: Span) -> Vec<Value> {
544 let mut modules = vec![];
545
546 for (module_name, module_id) in &self.modules_map {
547 modules.push(self.collect_module(module_name, module_id, span));
548 }
549
550 modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
551 modules
552 }
553
554 pub fn collect_engine_state(&self, span: Span) -> Value {
555 let num_env_vars = self
556 .engine_state
557 .env_vars
558 .values()
559 .map(|overlay| overlay.len() as i64)
560 .sum();
561
562 Value::record(
563 record! {
564 "source_bytes" => Value::int(self.engine_state.next_span_start() as i64, span),
565 "num_vars" => Value::int(self.engine_state.num_vars() as i64, span),
566 "num_decls" => Value::int(self.engine_state.num_decls() as i64, span),
567 "num_blocks" => Value::int(self.engine_state.num_blocks() as i64, span),
568 "num_modules" => Value::int(self.engine_state.num_modules() as i64, span),
569 "num_env_vars" => Value::int(num_env_vars, span),
570 },
571 span,
572 )
573 }
574}
575
576fn sort_rows(decls: &mut [Value]) {
577 decls.sort_by(|a, b| match (a, b) {
578 (Value::Record { val: rec_a, .. }, Value::Record { val: rec_b, .. }) => {
579 match (rec_a.values().next(), rec_b.values().next()) {
582 (Some(val_a), Some(val_b)) => match (val_a, val_b) {
583 (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
584 str_a.cmp(str_b)
585 }
586 _ => Ordering::Equal,
587 },
588 _ => Ordering::Equal,
589 }
590 }
591 _ => Ordering::Equal,
592 });
593}