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