From 5189e9486c4d18dca894fd245bcccf3b4e6b798b Mon Sep 17 00:00:00 2001 From: blindfs Date: Fri, 11 Apr 2025 08:11:57 +0800 Subject: [PATCH] fix(completion): quoted cell path completion --- .../src/completions/cell_path_completions.rs | 30 +++++++++++++------ crates/nu-cli/tests/completions/mod.rs | 27 +++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/crates/nu-cli/src/completions/cell_path_completions.rs b/crates/nu-cli/src/completions/cell_path_completions.rs index 665558190f..1191380eb4 100644 --- a/crates/nu-cli/src/completions/cell_path_completions.rs +++ b/crates/nu-cli/src/completions/cell_path_completions.rs @@ -24,7 +24,10 @@ fn prefix_from_path_member(member: &PathMember, pos: usize) -> (String, Span) { .get(..pos + 1 - start) .map(str::to_string) .unwrap_or(prefix_str); - (prefix_str, Span::new(start, pos + 1)) + // strip wrapping quotes + let quotations = ['"', '\'', '`']; + let prefix_str = prefix_str.strip_prefix(quotations).unwrap_or(&prefix_str); + (prefix_str.to_string(), Span::new(start, pos + 1)) } impl Completer for CellPathCompletion<'_> { @@ -108,14 +111,23 @@ fn get_suggestions_by_value( value: &Value, current_span: reedline::Span, ) -> Vec { - let to_suggestion = |s: String, v: Option<&Value>| SemanticSuggestion { - suggestion: Suggestion { - value: s, - span: current_span, - description: v.map(|v| v.get_type().to_string()), - ..Suggestion::default() - }, - kind: Some(SuggestionKind::CellPath), + let to_suggestion = |s: String, v: Option<&Value>| { + // Check if the string needs quoting (has spaces or punctuation) + let value = if s.contains(|c: char| c.is_whitespace() || c.is_ascii_punctuation()) { + format!("{:?}", s) + } else { + s + }; + + SemanticSuggestion { + suggestion: Suggestion { + value, + span: current_span, + description: v.map(|v| v.get_type().to_string()), + ..Suggestion::default() + }, + kind: Some(SuggestionKind::CellPath), + } }; match value { Value::Record { val, .. } => val diff --git a/crates/nu-cli/tests/completions/mod.rs b/crates/nu-cli/tests/completions/mod.rs index 9792f2b19b..5c6a10059a 100644 --- a/crates/nu-cli/tests/completions/mod.rs +++ b/crates/nu-cli/tests/completions/mod.rs @@ -2007,6 +2007,33 @@ fn table_cell_path_completions() { match_suggestions(&expected, &suggestions); } +#[test] +fn quoted_cell_path_completions() { + let (_, _, mut engine, mut stack) = new_engine(); + let command = r#"let foo = {'foo bar':1 'foo\\"bar"': 1 '.': 1 '|': 1}"#; + assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok()); + let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); + + let expected: Vec<_> = vec![ + "\".\"", + "\"foo bar\"", + "\"foo\\\\\\\\\\\"bar\\\"\"", + "\"|\"", + ]; + let completion_str = "$foo."; + let suggestions = completer.complete(completion_str, completion_str.len()); + match_suggestions(&expected, &suggestions); + + let expected: Vec<_> = vec!["\"foo bar\"", "\"foo\\\\\\\\\\\"bar\\\"\""]; + let completion_str = "$foo.`foo"; + let suggestions = completer.complete(completion_str, completion_str.len()); + match_suggestions(&expected, &suggestions); + + let completion_str = "$foo.foo"; + let suggestions = completer.complete(completion_str, completion_str.len()); + match_suggestions(&expected, &suggestions); +} + #[test] fn alias_of_command_and_flags() { let (_, _, mut engine, mut stack) = new_engine();