From ce6d3c6eb21107d42a9bf075e144400ad527a2ad Mon Sep 17 00:00:00 2001 From: Dan Davison <dandavison7@gmail.com> Date: Fri, 11 Nov 2022 17:20:28 -0500 Subject: [PATCH] Refactor creation of `$nu.scope` in eval.rs (#7104) The function was ~400 lines long and hence very hard to work with. --- crates/nu-engine/src/eval.rs | 445 +------------------------------ crates/nu-engine/src/lib.rs | 1 + crates/nu-engine/src/scope.rs | 482 ++++++++++++++++++++++++++++++++++ 3 files changed, 486 insertions(+), 442 deletions(-) create mode 100644 crates/nu-engine/src/scope.rs diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 43ec854e6b..1a023e67d6 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1,17 +1,15 @@ -use crate::{current_dir_str, get_full_help}; +use crate::{current_dir_str, get_full_help, scope::create_scope}; use nu_path::expand_path_with; use nu_protocol::{ ast::{ Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math, Operator, PathMember, }, - engine::{EngineState, Stack, Visibility}, + engine::{EngineState, Stack}, Config, HistoryFileFormat, IntoInterruptiblePipelineData, IntoPipelineData, ListStream, - PipelineData, Range, RawStream, ShellError, Span, Spanned, SyntaxShape, Unit, Value, VarId, - ENV_VARIABLE_ID, + PipelineData, Range, RawStream, ShellError, Span, Spanned, Unit, Value, VarId, ENV_VARIABLE_ID, }; use nu_utils::stdout_write_all_and_flush; -use std::cmp::Ordering; use std::collections::HashMap; use sysinfo::SystemExt; @@ -911,443 +909,6 @@ pub fn eval_subexpression( Ok(input) } -fn extract_custom_completion_from_arg(engine_state: &EngineState, shape: &SyntaxShape) -> String { - return match shape { - SyntaxShape::Custom(_, custom_completion_decl_id) => { - let custom_completion_command = engine_state.get_decl(*custom_completion_decl_id); - let custom_completion_command_name: &str = custom_completion_command.name(); - custom_completion_command_name.to_string() - } - _ => "".to_string(), - }; -} - -pub fn create_scope( - engine_state: &EngineState, - stack: &Stack, - span: Span, -) -> Result<Value, ShellError> { - let mut output_cols = vec![]; - let mut output_vals = vec![]; - - let mut vars = vec![]; - let mut commands = vec![]; - let mut aliases = vec![]; - let mut modules = vec![]; - - let mut vars_map = HashMap::new(); - let mut commands_map = HashMap::new(); - let mut aliases_map = HashMap::new(); - let mut modules_map = HashMap::new(); - let mut visibility = Visibility::new(); - - for overlay_frame in engine_state.active_overlays(&[]) { - vars_map.extend(&overlay_frame.vars); - commands_map.extend(&overlay_frame.decls); - aliases_map.extend(&overlay_frame.aliases); - modules_map.extend(&overlay_frame.modules); - - visibility.merge_with(overlay_frame.visibility.clone()); - } - - for var in vars_map { - let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span); - - let var_type = Value::string(engine_state.get_var(*var.1).ty.to_string(), span); - - let var_value = if let Ok(val) = stack.get_var(*var.1, span) { - val - } else { - Value::nothing(span) - }; - - vars.push(Value::Record { - cols: vec!["name".to_string(), "type".to_string(), "value".to_string()], - vals: vec![var_name, var_type, var_value], - span, - }) - } - - for ((command_name, _), decl_id) in commands_map { - if visibility.is_decl_id_visible(decl_id) { - let mut cols = vec![]; - let mut vals = vec![]; - - let mut module_commands = vec![]; - for module in &modules_map { - let module_name = String::from_utf8_lossy(module.0).to_string(); - let module_id = engine_state.find_module(module.0, &[]); - if let Some(module_id) = module_id { - let module = engine_state.get_module(module_id); - if module.has_decl(command_name) { - module_commands.push(module_name); - } - } - } - - cols.push("name".into()); - vals.push(Value::String { - val: String::from_utf8_lossy(command_name).to_string(), - span, - }); - - cols.push("module_name".into()); - vals.push(Value::string(module_commands.join(", "), span)); - - let decl = engine_state.get_decl(*decl_id); - let signature = decl.signature(); - - cols.push("category".to_string()); - vals.push(Value::String { - val: signature.category.to_string(), - span, - }); - - // signature - let mut sig_records = vec![]; - { - let sig_cols = vec![ - "command".to_string(), - "parameter_name".to_string(), - "parameter_type".to_string(), - "syntax_shape".to_string(), - "is_optional".to_string(), - "short_flag".to_string(), - "description".to_string(), - "custom_completion".to_string(), - ]; - - // required_positional - for req in signature.required_positional { - let sig_vals = vec![ - Value::string(&signature.name, span), - Value::string(req.name, span), - Value::string("positional", span), - Value::string(req.shape.to_string(), span), - Value::boolean(false, span), - Value::nothing(span), - Value::string(req.desc, span), - Value::string( - extract_custom_completion_from_arg(engine_state, &req.shape), - span, - ), - ]; - - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, - span, - }); - } - - // optional_positional - for opt in signature.optional_positional { - let sig_vals = vec![ - Value::string(&signature.name, span), - Value::string(opt.name, span), - Value::string("positional", span), - Value::string(opt.shape.to_string(), span), - Value::boolean(true, span), - Value::nothing(span), - Value::string(opt.desc, span), - Value::string( - extract_custom_completion_from_arg(engine_state, &opt.shape), - span, - ), - ]; - - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, - span, - }); - } - - { - // rest_positional - if let Some(rest) = signature.rest_positional { - let sig_vals = vec![ - Value::string(&signature.name, span), - Value::string(rest.name, span), - Value::string("rest", span), - Value::string(rest.shape.to_string(), span), - Value::boolean(true, span), - Value::nothing(span), - Value::string(rest.desc, span), - Value::string( - extract_custom_completion_from_arg(engine_state, &rest.shape), - span, - ), - ]; - - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, - span, - }); - } - } - - // named flags - for named in signature.named { - let flag_type; - - // Skip the help flag - if named.long == "help" { - continue; - } - - let mut custom_completion_command_name: String = "".to_string(); - let shape = if let Some(arg) = named.arg { - flag_type = Value::string("named", span); - custom_completion_command_name = - extract_custom_completion_from_arg(engine_state, &arg); - Value::string(arg.to_string(), span) - } else { - flag_type = Value::string("switch", span); - Value::nothing(span) - }; - - let short_flag = if let Some(c) = named.short { - Value::string(c, span) - } else { - Value::nothing(span) - }; - - let sig_vals = vec![ - Value::string(&signature.name, span), - Value::string(named.long, span), - flag_type, - shape, - Value::boolean(!named.required, span), - short_flag, - Value::string(named.desc, span), - Value::string(custom_completion_command_name, span), - ]; - - sig_records.push(Value::Record { - cols: sig_cols.clone(), - vals: sig_vals, - span, - }); - } - } - - cols.push("signature".to_string()); - vals.push(Value::List { - vals: sig_records, - span, - }); - - cols.push("usage".to_string()); - vals.push(Value::String { - val: decl.usage().into(), - span, - }); - - cols.push("examples".to_string()); - vals.push(Value::List { - vals: decl - .examples() - .into_iter() - .map(|x| Value::Record { - cols: vec!["description".into(), "example".into()], - vals: vec![ - Value::String { - val: x.description.to_string(), - span, - }, - Value::String { - val: x.example.to_string(), - span, - }, - ], - span, - }) - .collect(), - span, - }); - - cols.push("is_builtin".to_string()); - // we can only be a is_builtin or is_custom, not both - vals.push(Value::Bool { - val: !decl.is_custom_command(), - span, - }); - - cols.push("is_sub".to_string()); - vals.push(Value::Bool { - val: decl.is_sub(), - span, - }); - - cols.push("is_plugin".to_string()); - vals.push(Value::Bool { - val: decl.is_plugin().is_some(), - span, - }); - - cols.push("is_custom".to_string()); - vals.push(Value::Bool { - val: decl.is_custom_command(), - span, - }); - - cols.push("is_keyword".into()); - vals.push(Value::Bool { - val: decl.is_parser_keyword(), - span, - }); - - cols.push("is_extern".to_string()); - vals.push(Value::Bool { - val: decl.is_known_external(), - span, - }); - - cols.push("creates_scope".to_string()); - vals.push(Value::Bool { - val: signature.creates_scope, - span, - }); - - cols.push("extra_usage".to_string()); - vals.push(Value::String { - val: decl.extra_usage().into(), - span, - }); - - let search_terms = decl.search_terms(); - cols.push("search_terms".to_string()); - vals.push(if search_terms.is_empty() { - Value::nothing(span) - } else { - Value::String { - val: search_terms.join(", "), - span, - } - }); - - commands.push(Value::Record { cols, vals, span }) - } - } - - for (alias_name, alias_id) in aliases_map { - if visibility.is_alias_id_visible(alias_id) { - let alias = engine_state.get_alias(*alias_id); - let mut alias_text = String::new(); - for span in alias { - let contents = engine_state.get_span_contents(span); - if !alias_text.is_empty() { - alias_text.push(' '); - } - alias_text.push_str(&String::from_utf8_lossy(contents)); - } - aliases.push(( - Value::String { - val: String::from_utf8_lossy(alias_name).to_string(), - span, - }, - Value::string(alias_text, span), - )); - } - } - - for module in modules_map { - modules.push(Value::String { - val: String::from_utf8_lossy(module.0).to_string(), - span, - }); - } - - output_cols.push("vars".to_string()); - output_vals.push(Value::List { vals: vars, span }); - - commands.sort_by(|a, b| match (a, b) { - (Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => { - // Comparing the first value from the record - // It is expected that the first value is the name of the column - // The names of the commands should be a value string - match (rec_a.get(0), rec_b.get(0)) { - (Some(val_a), Some(val_b)) => match (val_a, val_b) { - (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => { - str_a.cmp(str_b) - } - _ => Ordering::Equal, - }, - _ => Ordering::Equal, - } - } - _ => Ordering::Equal, - }); - output_cols.push("commands".to_string()); - output_vals.push(Value::List { - vals: commands, - span, - }); - - aliases.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); - output_cols.push("aliases".to_string()); - output_vals.push(Value::List { - vals: aliases - .into_iter() - .map(|(alias, value)| Value::Record { - cols: vec!["alias".into(), "expansion".into()], - vals: vec![alias, value], - span, - }) - .collect(), - span, - }); - - modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); - output_cols.push("modules".to_string()); - output_vals.push(Value::List { - vals: modules, - span, - }); - - let engine_state_cols = vec![ - "source_bytes".to_string(), - "num_vars".to_string(), - "num_commands".to_string(), - "num_aliases".to_string(), - "num_blocks".to_string(), - "num_modules".to_string(), - "num_env_vars".to_string(), - ]; - - let engine_state_vals = vec![ - Value::int(engine_state.next_span_start() as i64, span), - Value::int(engine_state.num_vars() as i64, span), - Value::int(engine_state.num_decls() as i64, span), - Value::int(engine_state.num_aliases() as i64, span), - Value::int(engine_state.num_blocks() as i64, span), - Value::int(engine_state.num_modules() as i64, span), - Value::int( - engine_state - .env_vars - .values() - .map(|overlay| overlay.len() as i64) - .sum(), - span, - ), - ]; - - output_cols.push("engine_state".to_string()); - output_vals.push(Value::Record { - cols: engine_state_cols, - vals: engine_state_vals, - span, - }); - - Ok(Value::Record { - cols: output_cols, - vals: output_vals, - span, - }) -} - pub fn eval_variable( engine_state: &EngineState, stack: &Stack, diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index b1ca1a8807..0f6a8a15ce 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -4,6 +4,7 @@ pub mod documentation; pub mod env; mod eval; mod glob_from; +mod scope; pub use call_ext::CallExt; pub use column::get_columns; diff --git a/crates/nu-engine/src/scope.rs b/crates/nu-engine/src/scope.rs new file mode 100644 index 0000000000..c3c7dca960 --- /dev/null +++ b/crates/nu-engine/src/scope.rs @@ -0,0 +1,482 @@ +use nu_protocol::{ + engine::{EngineState, Stack, Visibility}, + ShellError, Signature, Span, SyntaxShape, Type, Value, +}; +use std::cmp::Ordering; +use std::collections::HashMap; + +pub fn create_scope( + engine_state: &EngineState, + stack: &Stack, + span: Span, +) -> Result<Value, ShellError> { + let mut scope_data = ScopeData::new(engine_state, stack); + + scope_data.populate_from_overlays(); + + let mut cols = vec![]; + let mut vals = vec![]; + + cols.push("vars".to_string()); + vals.push(Value::List { + vals: scope_data.collect_vars(span), + span, + }); + + cols.push("commands".to_string()); + vals.push(Value::List { + vals: scope_data.collect_commands(span), + span, + }); + + cols.push("aliases".to_string()); + vals.push(Value::List { + vals: scope_data + .collect_aliases(span) + .into_iter() + .map(|(alias, value)| Value::Record { + cols: vec!["alias".into(), "expansion".into()], + vals: vec![alias, value], + span, + }) + .collect(), + span, + }); + + cols.push("modules".to_string()); + vals.push(Value::List { + vals: scope_data.collect_modules(span), + span, + }); + + cols.push("engine_state".to_string()); + vals.push(scope_data.collect_engine_state(span)); + + Ok(Value::Record { cols, vals, span }) +} + +struct ScopeData<'e, 's> { + engine_state: &'e EngineState, + stack: &'s Stack, + vars_map: HashMap<&'e Vec<u8>, &'e usize>, + commands_map: HashMap<&'e (Vec<u8>, Type), &'e usize>, + aliases_map: HashMap<&'e Vec<u8>, &'e usize>, + modules_map: HashMap<&'e Vec<u8>, &'e usize>, + visibility: Visibility, +} + +impl<'e, 's> ScopeData<'e, 's> { + pub fn new(engine_state: &'e EngineState, stack: &'s Stack) -> Self { + Self { + engine_state, + stack, + vars_map: HashMap::new(), + commands_map: HashMap::new(), + aliases_map: HashMap::new(), + modules_map: HashMap::new(), + visibility: Visibility::new(), + } + } + + pub fn populate_from_overlays(&mut self) { + for overlay_frame in self.engine_state.active_overlays(&[]) { + self.vars_map.extend(&overlay_frame.vars); + self.commands_map.extend(&overlay_frame.decls); + self.aliases_map.extend(&overlay_frame.aliases); + self.modules_map.extend(&overlay_frame.modules); + self.visibility.merge_with(overlay_frame.visibility.clone()); + } + } + + pub fn collect_vars(&mut self, span: Span) -> Vec<Value> { + let mut vars = vec![]; + for var in &self.vars_map { + let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span); + + let var_type = Value::string(self.engine_state.get_var(**var.1).ty.to_string(), span); + + let var_value = if let Ok(val) = self.stack.get_var(**var.1, span) { + val + } else { + Value::nothing(span) + }; + + vars.push(Value::Record { + cols: vec!["name".to_string(), "type".to_string(), "value".to_string()], + vals: vec![var_name, var_type, var_value], + span, + }) + } + vars + } + + pub fn collect_commands(&mut self, span: Span) -> Vec<Value> { + let mut commands = vec![]; + for ((command_name, _), decl_id) in &self.commands_map { + if self.visibility.is_decl_id_visible(decl_id) { + let mut cols = vec![]; + let mut vals = vec![]; + + let mut module_commands = vec![]; + for module in &self.modules_map { + let module_name = String::from_utf8_lossy(module.0).to_string(); + let module_id = self.engine_state.find_module(module.0, &[]); + if let Some(module_id) = module_id { + let module = self.engine_state.get_module(module_id); + if module.has_decl(command_name) { + module_commands.push(module_name); + } + } + } + + cols.push("name".into()); + vals.push(Value::String { + val: String::from_utf8_lossy(command_name).to_string(), + span, + }); + + cols.push("module_name".into()); + vals.push(Value::string(module_commands.join(", "), span)); + + let decl = self.engine_state.get_decl(**decl_id); + let signature = decl.signature(); + + cols.push("category".to_string()); + vals.push(Value::String { + val: signature.category.to_string(), + span, + }); + + cols.push("signature".to_string()); + vals.push(Value::List { + vals: self.collect_signature_entries(&signature, span), + span, + }); + + cols.push("usage".to_string()); + vals.push(Value::String { + val: decl.usage().into(), + span, + }); + + cols.push("examples".to_string()); + vals.push(Value::List { + vals: decl + .examples() + .into_iter() + .map(|x| Value::Record { + cols: vec!["description".into(), "example".into()], + vals: vec![ + Value::String { + val: x.description.to_string(), + span, + }, + Value::String { + val: x.example.to_string(), + span, + }, + ], + span, + }) + .collect(), + span, + }); + + cols.push("is_builtin".to_string()); + // we can only be a is_builtin or is_custom, not both + vals.push(Value::Bool { + val: !decl.is_custom_command(), + span, + }); + + cols.push("is_sub".to_string()); + vals.push(Value::Bool { + val: decl.is_sub(), + span, + }); + + cols.push("is_plugin".to_string()); + vals.push(Value::Bool { + val: decl.is_plugin().is_some(), + span, + }); + + cols.push("is_custom".to_string()); + vals.push(Value::Bool { + val: decl.is_custom_command(), + span, + }); + + cols.push("is_keyword".into()); + vals.push(Value::Bool { + val: decl.is_parser_keyword(), + span, + }); + + cols.push("is_extern".to_string()); + vals.push(Value::Bool { + val: decl.is_known_external(), + span, + }); + + cols.push("creates_scope".to_string()); + vals.push(Value::Bool { + val: signature.creates_scope, + span, + }); + + cols.push("extra_usage".to_string()); + vals.push(Value::String { + val: decl.extra_usage().into(), + span, + }); + + let search_terms = decl.search_terms(); + cols.push("search_terms".to_string()); + vals.push(if search_terms.is_empty() { + Value::nothing(span) + } else { + Value::String { + val: search_terms.join(", "), + span, + } + }); + + commands.push(Value::Record { cols, vals, span }) + } + } + + commands.sort_by(|a, b| match (a, b) { + (Value::Record { vals: rec_a, .. }, Value::Record { vals: rec_b, .. }) => { + // Comparing the first value from the record + // It is expected that the first value is the name of the column + // The names of the commands should be a value string + match (rec_a.get(0), rec_b.get(0)) { + (Some(val_a), Some(val_b)) => match (val_a, val_b) { + (Value::String { val: str_a, .. }, Value::String { val: str_b, .. }) => { + str_a.cmp(str_b) + } + _ => Ordering::Equal, + }, + _ => Ordering::Equal, + } + } + _ => Ordering::Equal, + }); + commands + } + + fn collect_signature_entries(&self, signature: &Signature, span: Span) -> Vec<Value> { + let mut sig_records = vec![]; + + let sig_cols = vec![ + "command".to_string(), + "parameter_name".to_string(), + "parameter_type".to_string(), + "syntax_shape".to_string(), + "is_optional".to_string(), + "short_flag".to_string(), + "description".to_string(), + "custom_completion".to_string(), + ]; + + // required_positional + for req in &signature.required_positional { + let sig_vals = vec![ + Value::string(&signature.name, span), + Value::string(&req.name, span), + Value::string("positional", span), + Value::string(req.shape.to_string(), span), + Value::boolean(false, span), + Value::nothing(span), + Value::string(&req.desc, span), + Value::string( + extract_custom_completion_from_arg(self.engine_state, &req.shape), + span, + ), + ]; + + sig_records.push(Value::Record { + cols: sig_cols.clone(), + vals: sig_vals, + span, + }); + } + + // optional_positional + for opt in &signature.optional_positional { + let sig_vals = vec![ + Value::string(&signature.name, span), + Value::string(&opt.name, span), + Value::string("positional", span), + Value::string(opt.shape.to_string(), span), + Value::boolean(true, span), + Value::nothing(span), + Value::string(&opt.desc, span), + Value::string( + extract_custom_completion_from_arg(self.engine_state, &opt.shape), + span, + ), + ]; + + sig_records.push(Value::Record { + cols: sig_cols.clone(), + vals: sig_vals, + span, + }); + } + + // rest_positional + if let Some(rest) = &signature.rest_positional { + let sig_vals = vec![ + Value::string(&signature.name, span), + Value::string(&rest.name, span), + Value::string("rest", span), + Value::string(rest.shape.to_string(), span), + Value::boolean(true, span), + Value::nothing(span), + Value::string(&rest.desc, span), + Value::string( + extract_custom_completion_from_arg(self.engine_state, &rest.shape), + span, + ), + ]; + + sig_records.push(Value::Record { + cols: sig_cols.clone(), + vals: sig_vals, + span, + }); + } + + // named flags + for named in &signature.named { + let flag_type; + + // Skip the help flag + if named.long == "help" { + continue; + } + + let mut custom_completion_command_name: String = "".to_string(); + let shape = if let Some(arg) = &named.arg { + flag_type = Value::string("named", span); + custom_completion_command_name = + extract_custom_completion_from_arg(self.engine_state, arg); + Value::string(arg.to_string(), span) + } else { + flag_type = Value::string("switch", span); + Value::nothing(span) + }; + + let short_flag = if let Some(c) = named.short { + Value::string(c, span) + } else { + Value::nothing(span) + }; + + let sig_vals = vec![ + Value::string(&signature.name, span), + Value::string(&named.long, span), + flag_type, + shape, + Value::boolean(!named.required, span), + short_flag, + Value::string(&named.desc, span), + Value::string(custom_completion_command_name, span), + ]; + + sig_records.push(Value::Record { + cols: sig_cols.clone(), + vals: sig_vals, + span, + }); + } + sig_records + } + + pub fn collect_aliases(&mut self, span: Span) -> Vec<(Value, Value)> { + let mut aliases = vec![]; + for (alias_name, alias_id) in &self.aliases_map { + if self.visibility.is_alias_id_visible(alias_id) { + let alias = self.engine_state.get_alias(**alias_id); + let mut alias_text = String::new(); + for span in alias { + let contents = self.engine_state.get_span_contents(span); + if !alias_text.is_empty() { + alias_text.push(' '); + } + alias_text.push_str(&String::from_utf8_lossy(contents)); + } + aliases.push(( + Value::String { + val: String::from_utf8_lossy(alias_name).to_string(), + span, + }, + Value::string(alias_text, span), + )); + } + } + + aliases.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); + aliases + } + + pub fn collect_modules(&mut self, span: Span) -> Vec<Value> { + let mut modules = vec![]; + + for module in &self.modules_map { + modules.push(Value::String { + val: String::from_utf8_lossy(module.0).to_string(), + span, + }); + } + modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); + modules + } + + pub fn collect_engine_state(&mut self, span: Span) -> Value { + let engine_state_cols = vec![ + "source_bytes".to_string(), + "num_vars".to_string(), + "num_commands".to_string(), + "num_aliases".to_string(), + "num_blocks".to_string(), + "num_modules".to_string(), + "num_env_vars".to_string(), + ]; + + let engine_state_vals = vec![ + Value::int(self.engine_state.next_span_start() as i64, span), + Value::int(self.engine_state.num_vars() as i64, span), + Value::int(self.engine_state.num_decls() as i64, span), + Value::int(self.engine_state.num_aliases() as i64, span), + Value::int(self.engine_state.num_blocks() as i64, span), + Value::int(self.engine_state.num_modules() as i64, span), + Value::int( + self.engine_state + .env_vars + .values() + .map(|overlay| overlay.len() as i64) + .sum(), + span, + ), + ]; + Value::Record { + cols: engine_state_cols, + vals: engine_state_vals, + span, + } + } +} + +fn extract_custom_completion_from_arg(engine_state: &EngineState, shape: &SyntaxShape) -> String { + return match shape { + SyntaxShape::Custom(_, custom_completion_decl_id) => { + let custom_completion_command = engine_state.get_decl(*custom_completion_decl_id); + let custom_completion_command_name: &str = custom_completion_command.name(); + custom_completion_command_name.to_string() + } + _ => "".to_string(), + }; +}