1use nu_protocol::{
2 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 "decl_id" => Value::int(decl_id.get() as i64, span),
138 };
139
140 commands.push(Value::record(record, span))
141 }
142 }
143
144 sort_rows(&mut commands);
145
146 commands
147 }
148
149 fn collect_signatures(&self, signature: &Signature, span: Span) -> Value {
150 let mut sigs = signature
151 .input_output_types
152 .iter()
153 .map(|(input_type, output_type)| {
154 (
155 input_type.to_shape().to_string(),
156 Value::list(
157 self.collect_signature_entries(input_type, output_type, signature, span),
158 span,
159 ),
160 )
161 })
162 .collect::<Vec<(String, Value)>>();
163
164 if sigs.is_empty() {
169 let any_type = &Type::Any;
170 sigs.push((
171 any_type.to_shape().to_string(),
172 Value::list(
173 self.collect_signature_entries(any_type, any_type, signature, span),
174 span,
175 ),
176 ));
177 }
178 sigs.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
179 sigs.dedup_by(|(k1, _), (k2, _)| k1 == k2);
188 Value::record(sigs.into_iter().collect(), span)
189 }
190
191 fn collect_signature_entries(
192 &self,
193 input_type: &Type,
194 output_type: &Type,
195 signature: &Signature,
196 span: Span,
197 ) -> Vec<Value> {
198 let mut sig_records = vec![];
199
200 sig_records.push(Value::record(
202 record! {
203 "parameter_name" => Value::nothing(span),
204 "parameter_type" => Value::string("input", span),
205 "syntax_shape" => Value::string(input_type.to_shape().to_string(), span),
206 "is_optional" => Value::bool(false, span),
207 "short_flag" => Value::nothing(span),
208 "description" => Value::nothing(span),
209 "custom_completion" => Value::nothing(span),
210 "parameter_default" => Value::nothing(span),
211 },
212 span,
213 ));
214
215 for req in &signature.required_positional {
217 let custom =
218 extract_custom_completion_from_arg(self.engine_state, &req.custom_completion);
219
220 sig_records.push(Value::record(
221 record! {
222 "parameter_name" => Value::string(&req.name, span),
223 "parameter_type" => Value::string("positional", span),
224 "syntax_shape" => Value::string(req.shape.to_string(), span),
225 "is_optional" => Value::bool(false, span),
226 "short_flag" => Value::nothing(span),
227 "description" => Value::string(&req.desc, span),
228 "custom_completion" => Value::string(custom, span),
229 "parameter_default" => Value::nothing(span),
230 },
231 span,
232 ));
233 }
234
235 for opt in &signature.optional_positional {
237 let custom =
238 extract_custom_completion_from_arg(self.engine_state, &opt.custom_completion);
239 let default = if let Some(val) = &opt.default_value {
240 val.clone()
241 } else {
242 Value::nothing(span)
243 };
244
245 sig_records.push(Value::record(
246 record! {
247 "parameter_name" => Value::string(&opt.name, span),
248 "parameter_type" => Value::string("positional", span),
249 "syntax_shape" => Value::string(opt.shape.to_string(), span),
250 "is_optional" => Value::bool(true, span),
251 "short_flag" => Value::nothing(span),
252 "description" => Value::string(&opt.desc, span),
253 "custom_completion" => Value::string(custom, span),
254 "parameter_default" => default,
255 },
256 span,
257 ));
258 }
259
260 if let Some(rest) = &signature.rest_positional {
262 let name = if rest.name == "rest" { "" } else { &rest.name };
263 let custom =
264 extract_custom_completion_from_arg(self.engine_state, &rest.custom_completion);
265
266 sig_records.push(Value::record(
267 record! {
268 "parameter_name" => Value::string(name, span),
269 "parameter_type" => Value::string("rest", span),
270 "syntax_shape" => Value::string(rest.shape.to_string(), span),
271 "is_optional" => Value::bool(true, span),
272 "short_flag" => Value::nothing(span),
273 "description" => Value::string(&rest.desc, span),
274 "custom_completion" => Value::string(custom, span),
275 "parameter_default" => Value::nothing(span),
277 },
278 span,
279 ));
280 }
281
282 for named in &signature.named {
284 let flag_type;
285
286 if named.long == "help" {
288 continue;
289 }
290
291 let custom_completion_command_name: String =
292 extract_custom_completion_from_arg(self.engine_state, &named.custom_completion);
293 let shape = if let Some(arg) = &named.arg {
294 flag_type = Value::string("named", span);
295 Value::string(arg.to_string(), span)
296 } else {
297 flag_type = Value::string("switch", span);
298 Value::nothing(span)
299 };
300
301 let short_flag = if let Some(c) = named.short {
302 Value::string(c, span)
303 } else {
304 Value::nothing(span)
305 };
306
307 let default = if let Some(val) = &named.default_value {
308 val.clone()
309 } else {
310 Value::nothing(span)
311 };
312
313 sig_records.push(Value::record(
314 record! {
315 "parameter_name" => Value::string(&named.long, span),
316 "parameter_type" => flag_type,
317 "syntax_shape" => shape,
318 "is_optional" => Value::bool(!named.required, span),
319 "short_flag" => short_flag,
320 "description" => Value::string(&named.desc, span),
321 "custom_completion" => Value::string(custom_completion_command_name, span),
322 "parameter_default" => default,
323 },
324 span,
325 ));
326 }
327
328 sig_records.push(Value::record(
330 record! {
331 "parameter_name" => Value::nothing(span),
332 "parameter_type" => Value::string("output", span),
333 "syntax_shape" => Value::string(output_type.to_shape().to_string(), span),
334 "is_optional" => Value::bool(false, span),
335 "short_flag" => Value::nothing(span),
336 "description" => Value::nothing(span),
337 "custom_completion" => Value::nothing(span),
338 "parameter_default" => Value::nothing(span),
339 },
340 span,
341 ));
342
343 sig_records
344 }
345
346 pub fn collect_externs(&self, span: Span) -> Vec<Value> {
347 let mut externals = vec![];
348
349 for (command_name, decl_id) in &self.decls_map {
350 let decl = self.engine_state.get_decl(**decl_id);
351
352 if decl.is_known_external() {
353 let record = record! {
354 "name" => Value::string(String::from_utf8_lossy(command_name), span),
355 "description" => Value::string(decl.description(), span),
356 "decl_id" => Value::int(decl_id.get() as i64, span),
357 };
358
359 externals.push(Value::record(record, span))
360 }
361 }
362
363 sort_rows(&mut externals);
364 externals
365 }
366
367 pub fn collect_aliases(&self, span: Span) -> Vec<Value> {
368 let mut aliases = vec![];
369
370 for (decl_name, decl_id) in self.engine_state.get_decls_sorted(false) {
371 if self.visibility.is_decl_id_visible(&decl_id) {
372 let decl = self.engine_state.get_decl(decl_id);
373 if let Some(alias) = decl.as_alias() {
374 let aliased_decl_id = if let Expr::Call(wrapped_call) = &alias.wrapped_call.expr
375 {
376 Value::int(wrapped_call.decl_id.get() as i64, span)
377 } else {
378 Value::nothing(span)
379 };
380
381 let expansion = String::from_utf8_lossy(
382 self.engine_state.get_span_contents(alias.wrapped_call.span),
383 );
384
385 aliases.push(Value::record(
386 record! {
387 "name" => Value::string(String::from_utf8_lossy(&decl_name), span),
388 "expansion" => Value::string(expansion, span),
389 "description" => Value::string(alias.description(), span),
390 "decl_id" => Value::int(decl_id.get() as i64, span),
391 "aliased_decl_id" => aliased_decl_id,
392 },
393 span,
394 ));
395 }
396 }
397 }
398
399 sort_rows(&mut aliases);
400 aliases
402 }
403
404 fn collect_module(&self, module_name: &[u8], module_id: &ModuleId, span: Span) -> Value {
405 let module = self.engine_state.get_module(*module_id);
406
407 let all_decls = module.decls();
408
409 let mut export_commands: Vec<Value> = all_decls
410 .iter()
411 .filter_map(|(name_bytes, decl_id)| {
412 let decl = self.engine_state.get_decl(*decl_id);
413
414 if !decl.is_alias() && !decl.is_known_external() {
415 Some(Value::record(
416 record! {
417 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
418 "decl_id" => Value::int(decl_id.get() as i64, span),
419 },
420 span,
421 ))
422 } else {
423 None
424 }
425 })
426 .collect();
427
428 let mut export_aliases: 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() {
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_externs: 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_known_external() {
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_submodules: Vec<Value> = module
467 .submodules()
468 .iter()
469 .map(|(name_bytes, submodule_id)| self.collect_module(name_bytes, submodule_id, span))
470 .collect();
471
472 let mut export_consts: Vec<Value> = module
473 .consts()
474 .iter()
475 .map(|(name_bytes, var_id)| {
476 Value::record(
477 record! {
478 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
479 "type" => Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span),
480 "var_id" => Value::int(var_id.get() as i64, span),
481 },
482 span,
483 )
484 })
485 .collect();
486
487 sort_rows(&mut export_commands);
488 sort_rows(&mut export_aliases);
489 sort_rows(&mut export_externs);
490 sort_rows(&mut export_submodules);
491 sort_rows(&mut export_consts);
492
493 let (module_desc, module_extra_desc) = self
494 .engine_state
495 .build_module_desc(*module_id)
496 .unwrap_or_default();
497
498 Value::record(
499 record! {
500 "name" => Value::string(String::from_utf8_lossy(module_name), span),
501 "commands" => Value::list(export_commands, span),
502 "aliases" => Value::list(export_aliases, span),
503 "externs" => Value::list(export_externs, span),
504 "submodules" => Value::list(export_submodules, span),
505 "constants" => Value::list(export_consts, span),
506 "has_env_block" => Value::bool(module.env_block.is_some(), span),
507 "description" => Value::string(module_desc, span),
508 "extra_description" => Value::string(module_extra_desc, span),
509 "module_id" => Value::int(module_id.get() as i64, span),
510 "file" => Value::string(module.file.clone().map_or("unknown".to_string(), |(p, _)| p.path().to_string_lossy().to_string()), span),
511 },
512 span,
513 )
514 }
515
516 pub fn collect_modules(&self, span: Span) -> Vec<Value> {
517 let mut modules = vec![];
518
519 for (module_name, module_id) in &self.modules_map {
520 modules.push(self.collect_module(module_name, module_id, span));
521 }
522
523 modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
524 modules
525 }
526
527 pub fn collect_engine_state(&self, span: Span) -> Value {
528 let num_env_vars = self
529 .engine_state
530 .env_vars
531 .values()
532 .map(|overlay| overlay.len() as i64)
533 .sum();
534
535 Value::record(
536 record! {
537 "source_bytes" => Value::int(self.engine_state.next_span_start() as i64, span),
538 "num_vars" => Value::int(self.engine_state.num_vars() as i64, span),
539 "num_decls" => Value::int(self.engine_state.num_decls() as i64, span),
540 "num_blocks" => Value::int(self.engine_state.num_blocks() as i64, span),
541 "num_modules" => Value::int(self.engine_state.num_modules() as i64, span),
542 "num_env_vars" => Value::int(num_env_vars, span),
543 },
544 span,
545 )
546 }
547}
548
549fn extract_custom_completion_from_arg(
550 engine_state: &EngineState,
551 decl_id: &Option<DeclId>,
552) -> String {
553 if let Some(decl_id) = decl_id {
554 let custom_completion_command = engine_state.get_decl(*decl_id);
555 let custom_completion_command_name: &str = custom_completion_command.name();
556 custom_completion_command_name.to_string()
557 } else {
558 "".to_string()
559 }
560}
561
562fn sort_rows(decls: &mut [Value]) {
563 decls.sort_by(|a, b| match (a, b) {
564 (Value::Record { val: rec_a, .. }, Value::Record { val: rec_b, .. }) => {
565 match (rec_a.values().next(), rec_b.values().next()) {
568 (Some(val_a), Some(val_b)) => match (val_a, val_b) {
569 (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
570 str_a.cmp(str_b)
571 }
572 _ => Ordering::Equal,
573 },
574 _ => Ordering::Equal,
575 }
576 }
577 _ => Ordering::Equal,
578 });
579}