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