1use nu_protocol::{
2 DeclId, ModuleId, Signature, Span, SyntaxShape, 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)),
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 = extract_custom_completion_from_arg(self.engine_state, &req.shape);
218
219 sig_records.push(Value::record(
220 record! {
221 "parameter_name" => Value::string(&req.name, span),
222 "parameter_type" => Value::string("positional", span),
223 "syntax_shape" => Value::string(req.shape.to_string(), span),
224 "is_optional" => Value::bool(false, span),
225 "short_flag" => Value::nothing(span),
226 "description" => Value::string(&req.desc, span),
227 "custom_completion" => Value::string(custom, span),
228 "parameter_default" => Value::nothing(span),
229 },
230 span,
231 ));
232 }
233
234 for opt in &signature.optional_positional {
236 let custom = extract_custom_completion_from_arg(self.engine_state, &opt.shape);
237 let default = if let Some(val) = &opt.default_value {
238 val.clone()
239 } else {
240 Value::nothing(span)
241 };
242
243 sig_records.push(Value::record(
244 record! {
245 "parameter_name" => Value::string(&opt.name, span),
246 "parameter_type" => Value::string("positional", span),
247 "syntax_shape" => Value::string(opt.shape.to_string(), span),
248 "is_optional" => Value::bool(true, span),
249 "short_flag" => Value::nothing(span),
250 "description" => Value::string(&opt.desc, span),
251 "custom_completion" => Value::string(custom, span),
252 "parameter_default" => default,
253 },
254 span,
255 ));
256 }
257
258 if let Some(rest) = &signature.rest_positional {
260 let name = if rest.name == "rest" { "" } else { &rest.name };
261 let custom = extract_custom_completion_from_arg(self.engine_state, &rest.shape);
262
263 sig_records.push(Value::record(
264 record! {
265 "parameter_name" => Value::string(name, span),
266 "parameter_type" => Value::string("rest", span),
267 "syntax_shape" => Value::string(rest.shape.to_string(), span),
268 "is_optional" => Value::bool(true, span),
269 "short_flag" => Value::nothing(span),
270 "description" => Value::string(&rest.desc, span),
271 "custom_completion" => Value::string(custom, span),
272 "parameter_default" => Value::nothing(span),
274 },
275 span,
276 ));
277 }
278
279 for named in &signature.named {
281 let flag_type;
282
283 if named.long == "help" {
285 continue;
286 }
287
288 let mut custom_completion_command_name: String = "".to_string();
289 let shape = if let Some(arg) = &named.arg {
290 flag_type = Value::string("named", span);
291 custom_completion_command_name =
292 extract_custom_completion_from_arg(self.engine_state, arg);
293 Value::string(arg.to_string(), span)
294 } else {
295 flag_type = Value::string("switch", span);
296 Value::nothing(span)
297 };
298
299 let short_flag = if let Some(c) = named.short {
300 Value::string(c, span)
301 } else {
302 Value::nothing(span)
303 };
304
305 let default = if let Some(val) = &named.default_value {
306 val.clone()
307 } else {
308 Value::nothing(span)
309 };
310
311 sig_records.push(Value::record(
312 record! {
313 "parameter_name" => Value::string(&named.long, span),
314 "parameter_type" => flag_type,
315 "syntax_shape" => shape,
316 "is_optional" => Value::bool(!named.required, span),
317 "short_flag" => short_flag,
318 "description" => Value::string(&named.desc, span),
319 "custom_completion" => Value::string(custom_completion_command_name, span),
320 "parameter_default" => default,
321 },
322 span,
323 ));
324 }
325
326 sig_records.push(Value::record(
328 record! {
329 "parameter_name" => Value::nothing(span),
330 "parameter_type" => Value::string("output", span),
331 "syntax_shape" => Value::string(output_type.to_shape().to_string(), span),
332 "is_optional" => Value::bool(false, span),
333 "short_flag" => Value::nothing(span),
334 "description" => Value::nothing(span),
335 "custom_completion" => Value::nothing(span),
336 "parameter_default" => Value::nothing(span),
337 },
338 span,
339 ));
340
341 sig_records
342 }
343
344 pub fn collect_externs(&self, span: Span) -> Vec<Value> {
345 let mut externals = vec![];
346
347 for (command_name, decl_id) in &self.decls_map {
348 let decl = self.engine_state.get_decl(**decl_id);
349
350 if decl.is_known_external() {
351 let record = record! {
352 "name" => Value::string(String::from_utf8_lossy(command_name), span),
353 "description" => Value::string(decl.description(), span),
354 "decl_id" => Value::int(decl_id.get() as i64, span),
355 };
356
357 externals.push(Value::record(record, span))
358 }
359 }
360
361 sort_rows(&mut externals);
362 externals
363 }
364
365 pub fn collect_aliases(&self, span: Span) -> Vec<Value> {
366 let mut aliases = vec![];
367
368 for (decl_name, decl_id) in self.engine_state.get_decls_sorted(false) {
369 if self.visibility.is_decl_id_visible(&decl_id) {
370 let decl = self.engine_state.get_decl(decl_id);
371 if let Some(alias) = decl.as_alias() {
372 let aliased_decl_id = if let Expr::Call(wrapped_call) = &alias.wrapped_call.expr
373 {
374 Value::int(wrapped_call.decl_id.get() as i64, span)
375 } else {
376 Value::nothing(span)
377 };
378
379 let expansion = String::from_utf8_lossy(
380 self.engine_state.get_span_contents(alias.wrapped_call.span),
381 );
382
383 aliases.push(Value::record(
384 record! {
385 "name" => Value::string(String::from_utf8_lossy(&decl_name), span),
386 "expansion" => Value::string(expansion, span),
387 "description" => Value::string(alias.description(), span),
388 "decl_id" => Value::int(decl_id.get() as i64, span),
389 "aliased_decl_id" => aliased_decl_id,
390 },
391 span,
392 ));
393 }
394 }
395 }
396
397 sort_rows(&mut aliases);
398 aliases
400 }
401
402 fn collect_module(&self, module_name: &[u8], module_id: &ModuleId, span: Span) -> Value {
403 let module = self.engine_state.get_module(*module_id);
404
405 let all_decls = module.decls();
406
407 let mut export_commands: Vec<Value> = all_decls
408 .iter()
409 .filter_map(|(name_bytes, decl_id)| {
410 let decl = self.engine_state.get_decl(*decl_id);
411
412 if !decl.is_alias() && !decl.is_known_external() {
413 Some(Value::record(
414 record! {
415 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
416 "decl_id" => Value::int(decl_id.get() as i64, span),
417 },
418 span,
419 ))
420 } else {
421 None
422 }
423 })
424 .collect();
425
426 let mut export_aliases: Vec<Value> = all_decls
427 .iter()
428 .filter_map(|(name_bytes, decl_id)| {
429 let decl = self.engine_state.get_decl(*decl_id);
430
431 if decl.is_alias() {
432 Some(Value::record(
433 record! {
434 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
435 "decl_id" => Value::int(decl_id.get() as i64, span),
436 },
437 span,
438 ))
439 } else {
440 None
441 }
442 })
443 .collect();
444
445 let mut export_externs: Vec<Value> = all_decls
446 .iter()
447 .filter_map(|(name_bytes, decl_id)| {
448 let decl = self.engine_state.get_decl(*decl_id);
449
450 if decl.is_known_external() {
451 Some(Value::record(
452 record! {
453 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
454 "decl_id" => Value::int(decl_id.get() as i64, span),
455 },
456 span,
457 ))
458 } else {
459 None
460 }
461 })
462 .collect();
463
464 let mut export_submodules: Vec<Value> = module
465 .submodules()
466 .iter()
467 .map(|(name_bytes, submodule_id)| self.collect_module(name_bytes, submodule_id, span))
468 .collect();
469
470 let mut export_consts: Vec<Value> = module
471 .consts()
472 .iter()
473 .map(|(name_bytes, var_id)| {
474 Value::record(
475 record! {
476 "name" => Value::string(String::from_utf8_lossy(name_bytes), span),
477 "type" => Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span),
478 "var_id" => Value::int(var_id.get() as i64, span),
479 },
480 span,
481 )
482 })
483 .collect();
484
485 sort_rows(&mut export_commands);
486 sort_rows(&mut export_aliases);
487 sort_rows(&mut export_externs);
488 sort_rows(&mut export_submodules);
489 sort_rows(&mut export_consts);
490
491 let (module_desc, module_extra_desc) = self
492 .engine_state
493 .build_module_desc(*module_id)
494 .unwrap_or_default();
495
496 Value::record(
497 record! {
498 "name" => Value::string(String::from_utf8_lossy(module_name), span),
499 "commands" => Value::list(export_commands, span),
500 "aliases" => Value::list(export_aliases, span),
501 "externs" => Value::list(export_externs, span),
502 "submodules" => Value::list(export_submodules, span),
503 "constants" => Value::list(export_consts, span),
504 "has_env_block" => Value::bool(module.env_block.is_some(), span),
505 "description" => Value::string(module_desc, span),
506 "extra_description" => Value::string(module_extra_desc, span),
507 "module_id" => Value::int(module_id.get() as i64, span),
508 "file" => Value::string(module.file.clone().map_or("unknown".to_string(), |(p, _)| p.path().to_string_lossy().to_string()), span),
509 },
510 span,
511 )
512 }
513
514 pub fn collect_modules(&self, span: Span) -> Vec<Value> {
515 let mut modules = vec![];
516
517 for (module_name, module_id) in &self.modules_map {
518 modules.push(self.collect_module(module_name, module_id, span));
519 }
520
521 modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
522 modules
523 }
524
525 pub fn collect_engine_state(&self, span: Span) -> Value {
526 let num_env_vars = self
527 .engine_state
528 .env_vars
529 .values()
530 .map(|overlay| overlay.len() as i64)
531 .sum();
532
533 Value::record(
534 record! {
535 "source_bytes" => Value::int(self.engine_state.next_span_start() as i64, span),
536 "num_vars" => Value::int(self.engine_state.num_vars() as i64, span),
537 "num_decls" => Value::int(self.engine_state.num_decls() as i64, span),
538 "num_blocks" => Value::int(self.engine_state.num_blocks() as i64, span),
539 "num_modules" => Value::int(self.engine_state.num_modules() as i64, span),
540 "num_env_vars" => Value::int(num_env_vars, span),
541 },
542 span,
543 )
544 }
545}
546
547fn extract_custom_completion_from_arg(engine_state: &EngineState, shape: &SyntaxShape) -> String {
548 match shape {
549 SyntaxShape::CompleterWrapper(_, custom_completion_decl_id) => {
550 let custom_completion_command = engine_state.get_decl(*custom_completion_decl_id);
551 let custom_completion_command_name: &str = custom_completion_command.name();
552 custom_completion_command_name.to_string()
553 }
554 _ => "".to_string(),
555 }
556}
557
558fn sort_rows(decls: &mut [Value]) {
559 decls.sort_by(|a, b| match (a, b) {
560 (Value::Record { val: rec_a, .. }, Value::Record { val: rec_b, .. }) => {
561 match (rec_a.values().next(), rec_b.values().next()) {
564 (Some(val_a), Some(val_b)) => match (val_a, val_b) {
565 (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => {
566 str_a.cmp(str_b)
567 }
568 _ => Ordering::Equal,
569 },
570 _ => Ordering::Equal,
571 }
572 }
573 _ => Ordering::Equal,
574 });
575}