From b821b149871f109ff7faf1ea3e2faa9b80471ee9 Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 9 Sep 2021 21:06:55 +1200 Subject: [PATCH 1/4] Add simple completions support --- src/main.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 60da80eb6..09b78404c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ +use std::{arch::x86_64::_CMP_EQ_OQ, cell::RefCell, rc::Rc}; + use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter}; use nu_command::create_default_context; use nu_engine::eval_block; -use nu_parser::parse; +use nu_parser::{flatten_block, parse}; use nu_protocol::{ engine::{EngineState, EvaluationContext, StateWorkingSet}, Value, }; +use reedline::{Completer, DefaultCompletionActionHandler}; #[cfg(test)] mod tests; @@ -53,6 +56,10 @@ fn main() -> std::io::Result<()> { } else { use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; + let completer = EQCompleter { + engine_state: engine_state.clone(), + }; + let mut line_editor = Reedline::create()? .with_history(Box::new(FileBackedHistory::with_file( 1000, @@ -60,7 +67,10 @@ fn main() -> std::io::Result<()> { )?))? .with_highlighter(Box::new(NuHighlighter { engine_state: engine_state.clone(), - })); + })) + .with_completion_action_handler(Box::new( + DefaultCompletionActionHandler::default().with_completer(Box::new(completer)), + )); let prompt = DefaultPrompt::new(1); let mut current_line = 1; @@ -143,3 +153,38 @@ fn main() -> std::io::Result<()> { Ok(()) } } + +struct EQCompleter { + engine_state: Rc>, +} + +impl Completer for EQCompleter { + fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> { + let engine_state = self.engine_state.borrow(); + let mut working_set = StateWorkingSet::new(&*engine_state); + let offset = working_set.next_span_start(); + let pos = offset + pos; + let (output, err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); + + let flattened = flatten_block(&working_set, &output); + + for flat in flattened { + if pos >= flat.0.start && pos <= flat.0.end { + match flat.1 { + nu_parser::FlatShape::External | nu_parser::FlatShape::InternalCall => { + return vec![( + reedline::Span { + start: flat.0.start - offset, + end: flat.0.end - offset, + }, + "hello".into(), + )] + } + _ => {} + } + } + } + + vec![] + } +} From bb6781a3b10d4bae65bdf8758a6ce1a4f284abbb Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 10 Sep 2021 09:47:20 +1200 Subject: [PATCH 2/4] Add row conditions --- crates/nu-command/src/default_context.rs | 6 +- crates/nu-command/src/if_.rs | 2 +- crates/nu-command/src/lib.rs | 1 + crates/nu-command/src/where_.rs | 92 ++++++++++++++++++++++++ crates/nu-engine/src/eval.rs | 1 + crates/nu-parser/src/flatten.rs | 1 + crates/nu-parser/src/parser.rs | 89 ++++++++++++++++++----- crates/nu-protocol/src/ast/expr.rs | 1 + crates/nu-protocol/src/value/mod.rs | 4 ++ src/main.rs | 4 +- src/tests.rs | 16 +++++ 11 files changed, 195 insertions(+), 22 deletions(-) create mode 100644 crates/nu-command/src/where_.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index fbaaaf537..c3f41be6f 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -5,7 +5,9 @@ use nu_protocol::{ Signature, SyntaxShape, }; -use crate::{Alias, Benchmark, BuildString, Def, Do, Each, For, If, Length, Let, LetEnv}; +use crate::{ + where_::Where, Alias, Benchmark, BuildString, Def, Do, Each, For, If, Length, Let, LetEnv, +}; pub fn create_default_context() -> Rc> { let engine_state = Rc::new(RefCell::new(EngineState::new())); @@ -33,6 +35,8 @@ pub fn create_default_context() -> Rc> { working_set.add_decl(Box::new(Each)); + working_set.add_decl(Box::new(Where)); + working_set.add_decl(Box::new(Do)); working_set.add_decl(Box::new(Benchmark)); diff --git a/crates/nu-command/src/if_.rs b/crates/nu-command/src/if_.rs index bb785c4c2..2b8df4867 100644 --- a/crates/nu-command/src/if_.rs +++ b/crates/nu-command/src/if_.rs @@ -11,7 +11,7 @@ impl Command for If { } fn usage(&self) -> &str { - "Create a variable and give it a value." + "Conditionally run a block." } fn signature(&self) -> nu_protocol::Signature { diff --git a/crates/nu-command/src/lib.rs b/crates/nu-command/src/lib.rs index 3e7d99e7d..e9c29e17b 100644 --- a/crates/nu-command/src/lib.rs +++ b/crates/nu-command/src/lib.rs @@ -10,6 +10,7 @@ mod if_; mod length; mod let_; mod let_env; +mod where_; pub use alias::Alias; pub use benchmark::Benchmark; diff --git a/crates/nu-command/src/where_.rs b/crates/nu-command/src/where_.rs new file mode 100644 index 000000000..b876277bb --- /dev/null +++ b/crates/nu-command/src/where_.rs @@ -0,0 +1,92 @@ +use nu_engine::eval_expression; +use nu_protocol::ast::{Call, Expr, Expression}; +use nu_protocol::engine::{Command, EvaluationContext}; +use nu_protocol::{IntoValueStream, ShellError, Signature, SyntaxShape, Value}; + +pub struct Where; + +impl Command for Where { + fn name(&self) -> &str { + "where" + } + + fn usage(&self) -> &str { + "Filter values based on a condition." + } + + fn signature(&self) -> nu_protocol::Signature { + Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition") + } + + fn run( + &self, + context: &EvaluationContext, + call: &Call, + input: Value, + ) -> Result { + let cond = call.positional[0].clone(); + + let context = context.enter_scope(); + + let (var_id, cond) = match cond { + Expression { + expr: Expr::RowCondition(var_id, expr), + .. + } => (var_id, expr), + _ => return Err(ShellError::InternalError("Expected row condition".into())), + }; + + match input { + Value::Stream { stream, span } => { + let output_stream = stream + .filter(move |value| { + context.add_var(var_id, value.clone()); + + let result = eval_expression(&context, &cond); + + match result { + Ok(result) => result.is_true(), + _ => false, + } + }) + .into_value_stream(); + + Ok(Value::Stream { + stream: output_stream, + span, + }) + } + Value::List { vals, span } => { + let output_stream = vals + .into_iter() + .filter(move |value| { + context.add_var(var_id, value.clone()); + + let result = eval_expression(&context, &cond); + + match result { + Ok(result) => result.is_true(), + _ => false, + } + }) + .into_value_stream(); + + Ok(Value::Stream { + stream: output_stream, + span, + }) + } + x => { + context.add_var(var_id, x.clone()); + + let result = eval_expression(&context, &cond)?; + + if result.is_true() { + Ok(x) + } else { + Ok(Value::Nothing { span: call.head }) + } + } + } + } +} diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index d04c39348..b3da15c87 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -135,6 +135,7 @@ pub fn eval_expression( value.follow_cell_path(&column_path.tail) } + Expr::RowCondition(_, expr) => eval_expression(context, expr), Expr::Call(call) => eval_call(context, call, Value::nothing()), Expr::ExternalCall(_, _) => Err(ShellError::ExternalNotSupported(expr.span)), Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }), diff --git a/crates/nu-parser/src/flatten.rs b/crates/nu-parser/src/flatten.rs index 7439da618..1f9279454 100644 --- a/crates/nu-parser/src/flatten.rs +++ b/crates/nu-parser/src/flatten.rs @@ -114,6 +114,7 @@ pub fn flatten_expression( Expr::String(_) => { vec![(expr.span, FlatShape::String)] } + Expr::RowCondition(_, expr) => flatten_expression(working_set, expr), Expr::Subexpression(block_id) => { flatten_block(working_set, working_set.get_block(*block_id)) } diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 57506b7fd..0bea2fe27 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -851,7 +851,7 @@ pub(crate) fn parse_dollar_expr( } else if let (expr, None) = parse_range(working_set, span) { (expr, None) } else { - parse_full_column_path(working_set, span) + parse_full_column_path(working_set, None, span) } } @@ -922,7 +922,7 @@ pub fn parse_string_interpolation( end: b + 1, }; - let (expr, err) = parse_full_column_path(working_set, span); + let (expr, err) = parse_full_column_path(working_set, None, span); error = error.or(err); output.push(expr); } @@ -957,7 +957,7 @@ pub fn parse_string_interpolation( end, }; - let (expr, err) = parse_full_column_path(working_set, span); + let (expr, err) = parse_full_column_path(working_set, None, span); error = error.or(err); output.push(expr); } @@ -1047,6 +1047,7 @@ pub fn parse_variable_expr( pub fn parse_full_column_path( working_set: &mut StateWorkingSet, + implicit_head: Option, span: Span, ) -> (Expression, Option) { // FIXME: assume for now a paren expr, but needs more @@ -1057,10 +1058,10 @@ pub fn parse_full_column_path( let (tokens, err) = lex(source, span.start, &[b'\n'], &[b'.']); error = error.or(err); - let mut tokens = tokens.into_iter(); - if let Some(head) = tokens.next() { + let mut tokens = tokens.into_iter().peekable(); + if let Some(head) = tokens.peek() { let bytes = working_set.get_span_contents(head.span); - let head = if bytes.starts_with(b"(") { + let (head, mut expect_dot) = if bytes.starts_with(b"(") { let mut start = head.span.start; let mut end = head.span.end; @@ -1085,27 +1086,42 @@ pub fn parse_full_column_path( let source = working_set.get_span_contents(span); - let (tokens, err) = lex(source, span.start, &[b'\n'], &[]); + let (output, err) = lex(source, span.start, &[b'\n'], &[]); error = error.or(err); - let (output, err) = lite_parse(&tokens); + let (output, err) = lite_parse(&output); error = error.or(err); let (output, err) = parse_block(working_set, &output, true); error = error.or(err); let block_id = working_set.add_block(output); + tokens.next(); - Expression { - expr: Expr::Subexpression(block_id), - span, - ty: Type::Unknown, // FIXME - } + ( + Expression { + expr: Expr::Subexpression(block_id), + span, + ty: Type::Unknown, // FIXME + }, + true, + ) } else if bytes.starts_with(b"$") { let (out, err) = parse_variable_expr(working_set, head.span); error = error.or(err); - out + tokens.next(); + + (out, true) + } else if let Some(var_id) = implicit_head { + ( + Expression { + expr: Expr::Var(var_id), + span: Span::unknown(), + ty: Type::Unknown, + }, + false, + ) } else { return ( garbage(span), @@ -1119,7 +1135,6 @@ pub fn parse_full_column_path( let mut tail = vec![]; - let mut expect_dot = true; for path_element in tokens { let bytes = working_set.get_span_contents(path_element.span); @@ -1293,11 +1308,40 @@ pub fn parse_var_with_opt_type( ) } } + +pub fn expand_to_cell_path( + working_set: &mut StateWorkingSet, + expression: &mut Expression, + var_id: VarId, +) { + if let Expression { + expr: Expr::String(_), + span, + .. + } = expression + { + // Re-parse the string as if it were a cell-path + let (new_expression, _err) = parse_full_column_path(working_set, Some(var_id), *span); + + *expression = new_expression; + } +} + pub fn parse_row_condition( working_set: &mut StateWorkingSet, spans: &[Span], ) -> (Expression, Option) { - parse_math_expression(working_set, spans) + let var_id = working_set.add_variable(b"$it".to_vec(), Type::Unknown); + let (expression, err) = parse_math_expression(working_set, spans, Some(var_id)); + let span = span(spans); + ( + Expression { + ty: Type::Bool, + span, + expr: Expr::RowCondition(var_id, Box::new(expression)), + }, + err, + ) } pub fn parse_signature( @@ -1995,7 +2039,7 @@ pub fn parse_value( if let (expr, None) = parse_range(working_set, span) { return (expr, None); } else { - return parse_full_column_path(working_set, span); + return parse_full_column_path(working_set, None, span); } } else if bytes.starts_with(b"{") { if matches!(shape, SyntaxShape::Block) || matches!(shape, SyntaxShape::Any) { @@ -2142,6 +2186,7 @@ pub fn parse_operator( pub fn parse_math_expression( working_set: &mut StateWorkingSet, spans: &[Span], + lhs_row_var_id: Option, ) -> (Expression, Option) { // As the expr_stack grows, we increase the required precedence to grow larger // If, at any time, the operator we're looking at is the same or lower precedence @@ -2200,6 +2245,10 @@ pub fn parse_math_expression( .pop() .expect("internal error: expression stack empty"); + if let Some(row_var_id) = lhs_row_var_id { + expand_to_cell_path(working_set, &mut lhs, row_var_id); + } + let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); error = error.or(err); @@ -2230,6 +2279,10 @@ pub fn parse_math_expression( .pop() .expect("internal error: expression stack empty"); + if let Some(row_var_id) = lhs_row_var_id { + expand_to_cell_path(working_set, &mut lhs, row_var_id); + } + let (result_ty, err) = math_result_type(working_set, &mut lhs, &mut op, &mut rhs); error = error.or(err); @@ -2256,7 +2309,7 @@ pub fn parse_expression( match bytes[0] { b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' | b'(' | b'{' - | b'[' | b'$' | b'"' | b'\'' | b'-' => parse_math_expression(working_set, spans), + | b'[' | b'$' | b'"' | b'\'' | b'-' => parse_math_expression(working_set, spans, None), _ => parse_call(working_set, spans, true), } } diff --git a/crates/nu-protocol/src/ast/expr.rs b/crates/nu-protocol/src/ast/expr.rs index ac29cd24b..5a873631e 100644 --- a/crates/nu-protocol/src/ast/expr.rs +++ b/crates/nu-protocol/src/ast/expr.rs @@ -15,6 +15,7 @@ pub enum Expr { Call(Box), ExternalCall(Vec, Vec>), Operator(Operator), + RowCondition(VarId, Box), BinaryOp(Box, Box, Box), //lhs, op, rhs Subexpression(BlockId), Block(BlockId), diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index d02f1ce2d..23c1b89a9 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -277,6 +277,10 @@ impl Value { Ok(current) } + + pub fn is_true(&self) -> bool { + matches!(self, Value::Bool { val: true, .. }) + } } impl PartialEq for Value { diff --git a/src/main.rs b/src/main.rs index 09b78404c..a8015245c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{arch::x86_64::_CMP_EQ_OQ, cell::RefCell, rc::Rc}; +use std::{cell::RefCell, rc::Rc}; use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter}; use nu_command::create_default_context; @@ -164,7 +164,7 @@ impl Completer for EQCompleter { let mut working_set = StateWorkingSet::new(&*engine_state); let offset = working_set.next_span_start(); let pos = offset + pos; - let (output, err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); + let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); let flattened = flatten_block(&working_set, &output); diff --git a/src/tests.rs b/src/tests.rs index d5cb6d3f0..df619f2b9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -292,3 +292,19 @@ fn row_iteration() -> TestResult { fn record_iteration() -> TestResult { run_test("([[name, level]; [aa, 100], [bb, 200]] | each { $it | each { |x| if $x.column == \"level\" { $x.value + 100 } else { $x.value } } }).level", "[200, 300]") } + +#[test] +fn row_condition1() -> TestResult { + run_test( + "([[name, size]; [a, 1], [b, 2], [c, 3]] | where size < 3).name", + "[a, b]", + ) +} + +#[test] +fn row_condition2() -> TestResult { + run_test( + "[[name, size]; [a, 1], [b, 2], [c, 3]] | where $it.size > 2 | length", + "1", + ) +} From f7333ebe588dee1d7ee7d80b1bb057151f3af037 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 10 Sep 2021 09:47:57 +1200 Subject: [PATCH 3/4] Check box --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index d6ba87f44..f0112bfb5 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,7 @@ - [x] Column path - [x] ...rest without calling it rest - [x] Iteration (`each`) over tables -- [ ] Row conditions +- [x] Row conditions - [ ] Value serialization - [ ] Handling rows with missing columns during a cell path - [ ] Error shortcircuit (stopping on first error) From abda6f148c4e1924b7e477f371a221da0884fdc6 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 10 Sep 2021 10:09:40 +1200 Subject: [PATCH 4/4] Finish up completions --- TODO.md | 1 + crates/nu-cli/src/completions.rs | 54 +++++++++++++++++++ crates/nu-cli/src/lib.rs | 2 + crates/nu-protocol/src/engine/engine_state.rs | 36 +++++++++++++ src/main.rs | 47 ++-------------- 5 files changed, 97 insertions(+), 43 deletions(-) create mode 100644 crates/nu-cli/src/completions.rs diff --git a/TODO.md b/TODO.md index f0112bfb5..92049f2ac 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,7 @@ - [x] ...rest without calling it rest - [x] Iteration (`each`) over tables - [x] Row conditions +- [x] Simple completions - [ ] Value serialization - [ ] Handling rows with missing columns during a cell path - [ ] Error shortcircuit (stopping on first error) diff --git a/crates/nu-cli/src/completions.rs b/crates/nu-cli/src/completions.rs new file mode 100644 index 000000000..45b9b17bd --- /dev/null +++ b/crates/nu-cli/src/completions.rs @@ -0,0 +1,54 @@ +use std::{cell::RefCell, rc::Rc}; + +use nu_parser::{flatten_block, parse}; +use nu_protocol::engine::{EngineState, StateWorkingSet}; +use reedline::Completer; + +pub struct NuCompleter { + engine_state: Rc>, +} + +impl NuCompleter { + pub fn new(engine_state: Rc>) -> Self { + Self { engine_state } + } +} + +impl Completer for NuCompleter { + fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> { + let engine_state = self.engine_state.borrow(); + let mut working_set = StateWorkingSet::new(&*engine_state); + let offset = working_set.next_span_start(); + let pos = offset + pos; + let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); + + let flattened = flatten_block(&working_set, &output); + + for flat in flattened { + if pos >= flat.0.start && pos <= flat.0.end { + match flat.1 { + nu_parser::FlatShape::External | nu_parser::FlatShape::InternalCall => { + let prefix = working_set.get_span_contents(flat.0); + let results = working_set.find_commands_by_prefix(prefix); + + return results + .into_iter() + .map(move |x| { + ( + reedline::Span { + start: flat.0.start - offset, + end: flat.0.end - offset, + }, + String::from_utf8_lossy(&x).to_string(), + ) + }) + .collect(); + } + _ => {} + } + } + } + + vec![] + } +} diff --git a/crates/nu-cli/src/lib.rs b/crates/nu-cli/src/lib.rs index a8c602012..111a74c96 100644 --- a/crates/nu-cli/src/lib.rs +++ b/crates/nu-cli/src/lib.rs @@ -1,5 +1,7 @@ +mod completions; mod errors; mod syntax_highlight; +pub use completions::NuCompleter; pub use errors::{report_parsing_error, report_shell_error}; pub use syntax_highlight::NuHighlighter; diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 80d437a57..a9c307fc4 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -123,6 +123,24 @@ impl EngineState { None } + pub fn find_commands_by_prefix(&self, name: &[u8]) -> Vec> { + let mut output = vec![]; + + for scope in self.scope.iter().rev() { + for decl in &scope.decls { + if decl.0.starts_with(name) { + output.push(decl.0.clone()); + } + } + } + + output + } + + pub fn get_span_contents(&self, span: Span) -> &[u8] { + &self.file_contents[span.start..span.end] + } + pub fn get_var(&self, var_id: VarId) -> &Type { self.vars .get(var_id) @@ -496,6 +514,24 @@ impl<'a> StateWorkingSet<'a> { } } + pub fn find_commands_by_prefix(&self, name: &[u8]) -> Vec> { + let mut output = vec![]; + + for scope in self.delta.scope.iter().rev() { + for decl in &scope.decls { + if decl.0.starts_with(name) { + output.push(decl.0.clone()); + } + } + } + + let mut permanent = self.permanent_state.find_commands_by_prefix(name); + + output.append(&mut permanent); + + output + } + pub fn get_block(&self, block_id: BlockId) -> &Block { let num_permanent_blocks = self.permanent_state.num_blocks(); if block_id < num_permanent_blocks { diff --git a/src/main.rs b/src/main.rs index a8015245c..32e0fb4c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,12 @@ -use std::{cell::RefCell, rc::Rc}; - -use nu_cli::{report_parsing_error, report_shell_error, NuHighlighter}; +use nu_cli::{report_parsing_error, report_shell_error, NuCompleter, NuHighlighter}; use nu_command::create_default_context; use nu_engine::eval_block; -use nu_parser::{flatten_block, parse}; +use nu_parser::parse; use nu_protocol::{ engine::{EngineState, EvaluationContext, StateWorkingSet}, Value, }; -use reedline::{Completer, DefaultCompletionActionHandler}; +use reedline::DefaultCompletionActionHandler; #[cfg(test)] mod tests; @@ -56,9 +54,7 @@ fn main() -> std::io::Result<()> { } else { use reedline::{DefaultPrompt, FileBackedHistory, Reedline, Signal}; - let completer = EQCompleter { - engine_state: engine_state.clone(), - }; + let completer = NuCompleter::new(engine_state.clone()); let mut line_editor = Reedline::create()? .with_history(Box::new(FileBackedHistory::with_file( @@ -153,38 +149,3 @@ fn main() -> std::io::Result<()> { Ok(()) } } - -struct EQCompleter { - engine_state: Rc>, -} - -impl Completer for EQCompleter { - fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> { - let engine_state = self.engine_state.borrow(); - let mut working_set = StateWorkingSet::new(&*engine_state); - let offset = working_set.next_span_start(); - let pos = offset + pos; - let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false); - - let flattened = flatten_block(&working_set, &output); - - for flat in flattened { - if pos >= flat.0.start && pos <= flat.0.end { - match flat.1 { - nu_parser::FlatShape::External | nu_parser::FlatShape::InternalCall => { - return vec![( - reedline::Span { - start: flat.0.start - offset, - end: flat.0.end - offset, - }, - "hello".into(), - )] - } - _ => {} - } - } - } - - vec![] - } -}