From 845c930bd2c7fcd7f2e45186e46a85d47d28efc1 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:39:22 -0600 Subject: [PATCH 1/3] fix completion signature --- src/formatting.rs | 15 ++++++++++++++- tests/fixtures/expected/issue81.nu | 2 ++ tests/fixtures/input/issue81.nu | 2 ++ tests/ground_truth.rs | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/expected/issue81.nu create mode 100644 tests/fixtures/input/issue81.nu diff --git a/src/formatting.rs b/src/formatting.rs index 9ffcad4..1dcb7b2 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -12,7 +12,7 @@ use nu_protocol::{ Pattern, Pipeline, PipelineElement, PipelineRedirection, RecordItem, RedirectionTarget, }, engine::{EngineState, StateWorkingSet}, - Signature, Span, SyntaxShape, + Completion, Signature, Span, SyntaxShape, }; /// Commands that format their block arguments in a special way @@ -573,6 +573,15 @@ impl<'a> Formatter<'a> { } } + /// Write custom completion if present (e.g., @completion_name) + fn write_custom_completion(&mut self, completion: &Option) { + if let Some(Completion::Command(decl_id)) = completion { + let decl = self.working_set.get_decl(*decl_id); + self.write("@"); + self.write(decl.name()); + } + } + /// Format a signature (for def commands) fn format_signature(&mut self, sig: &Signature) { self.write("["); @@ -610,6 +619,7 @@ impl<'a> Formatter<'a> { if param.shape != SyntaxShape::Any { self.write(": "); self.write(&format!("{}", param.shape)); + self.write_custom_completion(¶m.completion); } } @@ -624,6 +634,7 @@ impl<'a> Formatter<'a> { if param.shape != SyntaxShape::Any { self.write(": "); self.write(&format!("{}", param.shape)); + self.write_custom_completion(¶m.completion); } if let Some(default) = ¶m.default_value { self.write(" = "); @@ -657,6 +668,7 @@ impl<'a> Formatter<'a> { if let Some(shape) = &flag.arg { self.write(": "); self.write(&format!("{}", shape)); + self.write_custom_completion(&flag.completion); } if let Some(default) = &flag.default_value { self.write(" = "); @@ -672,6 +684,7 @@ impl<'a> Formatter<'a> { if rest.shape != SyntaxShape::Any { self.write(": "); self.write(&format!("{}", rest.shape)); + self.write_custom_completion(&rest.completion); } } diff --git a/tests/fixtures/expected/issue81.nu b/tests/fixtures/expected/issue81.nu new file mode 100644 index 0000000..8810470 --- /dev/null +++ b/tests/fixtures/expected/issue81.nu @@ -0,0 +1,2 @@ +def f1 [] { } +def f2 [foo: int@f1 = 1] { } diff --git a/tests/fixtures/input/issue81.nu b/tests/fixtures/input/issue81.nu new file mode 100644 index 0000000..8810470 --- /dev/null +++ b/tests/fixtures/input/issue81.nu @@ -0,0 +1,2 @@ +def f1 [] { } +def f2 [foo: int@f1 = 1] { } diff --git a/tests/ground_truth.rs b/tests/ground_truth.rs index 9e4d8bb..0127965 100644 --- a/tests/ground_truth.rs +++ b/tests/ground_truth.rs @@ -743,6 +743,18 @@ fn issue76_test() { run_ground_truth_test(&test_binary, "issue76"); } +#[test] +fn issue81_test() { + let test_binary = get_test_binary(); + run_ground_truth_test(&test_binary, "issue81"); +} + +#[test] +fn idempotency_issue81_test() { + let test_binary = get_test_binary(); + run_idempotency_test(&test_binary, "issue81"); +} + #[test] fn ground_truth_inline_param_comment_issue77() { let test_binary = get_test_binary(); From 9be7207fc3b0161063b0125b3761e324daa391e1 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:55:59 -0600 Subject: [PATCH 2/3] clippy --- src/formatting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/formatting.rs b/src/formatting.rs index 2e3b121..a974d67 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -576,7 +576,7 @@ impl<'a> Formatter<'a> { } } - /// Write custom completion if present (e.g., @completion_name) + /// Write custom completion if present (e.g., @`completion_name`) fn write_custom_completion(&mut self, completion: &Option) { if let Some(Completion::Command(decl_id)) = completion { let decl = self.working_set.get_decl(*decl_id); From 347940c5e5c249e9a4d00ec959198a7d107a82da Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:30:34 -0500 Subject: [PATCH 3/3] add to completion formatting --- Cargo.lock | 1 + Cargo.toml | 1 + src/formatting.rs | 78 +++++++++++++++++++++--------- tests/fixtures/expected/issue81.nu | 3 ++ tests/fixtures/input/issue81.nu | 3 ++ 5 files changed, 63 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0faa4ab..8654149 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1301,6 +1301,7 @@ dependencies = [ "nu-cmd-lang", "nu-parser", "nu-protocol", + "nu-utils", "nuon", "rayon", "rstest", diff --git a/Cargo.toml b/Cargo.toml index 0f8fb66..88bfe7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ nu-ansi-term = "0.50.3" nu-cmd-lang = "0.110.0" nu-parser = "0.110.0" nu-protocol = "0.110.0" +nu-utils = "0.110.0" nuon = "0.110.0" rayon = "1.11.0" thiserror = "2.0.18" diff --git a/src/formatting.rs b/src/formatting.rs index a974d67..9a9f07e 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -15,6 +15,7 @@ use nu_protocol::{ engine::{EngineState, StateWorkingSet}, Completion, Signature, Span, SyntaxShape, }; +use nu_utils::NuCow; /// Commands that format their block arguments in a special way const BLOCK_COMMANDS: &[&str] = &["for", "while", "loop", "module"]; @@ -576,12 +577,57 @@ impl<'a> Formatter<'a> { } } - /// Write custom completion if present (e.g., @`completion_name`) + /// Write custom completion if present (e.g., @`completion_name` or @[list items]) fn write_custom_completion(&mut self, completion: &Option) { - if let Some(Completion::Command(decl_id)) = completion { - let decl = self.working_set.get_decl(*decl_id); - self.write("@"); - self.write(decl.name()); + match completion { + Some(Completion::Command(decl_id)) => { + let decl = self.working_set.get_decl(*decl_id); + let name = decl.name(); + self.write("@"); + // Quote command names that contain special characters (like spaces) + if name.contains(' ') + || name.contains('-') + || name.contains('[') + || name.contains(']') + { + self.write("\""); + self.write(name); + self.write("\""); + } else { + self.write(name); + } + } + Some(Completion::List(list)) => { + self.write("@["); + match list { + NuCow::Borrowed(items) => { + for (i, item) in items.iter().enumerate() { + if i > 0 { + self.write(" "); + } + self.write(item); + } + } + NuCow::Owned(items) => { + for (i, item) in items.iter().enumerate() { + if i > 0 { + self.write(" "); + } + self.write(item); + } + } + } + self.write("]"); + } + None => {} + } + } + + /// Write a `SyntaxShape` with any necessary prettifying + fn write_shape(&mut self, shape: &SyntaxShape) { + match shape { + SyntaxShape::Closure(Option::None) => self.write("closure"), + _ => self.write(&format!("{}", shape)), } } @@ -621,12 +667,7 @@ impl<'a> Formatter<'a> { self.write(¶m.name); if param.shape != SyntaxShape::Any { self.write(": "); - match ¶m.shape { - // Fixes an issue in which closure type hints were formatted as `closure()`. - // TODO: This feels hacky. Should this be addressed in the `nu-protocol` crate instead? - SyntaxShape::Closure(Option::None) => self.write("closure"), - _ => self.write(&format!("{}", param.shape)), - } + self.write_shape(¶m.shape); self.write_custom_completion(¶m.completion); } } @@ -641,10 +682,7 @@ impl<'a> Formatter<'a> { } if param.shape != SyntaxShape::Any { self.write(": "); - match ¶m.shape { - SyntaxShape::Closure(Option::None) => self.write("closure"), - _ => self.write(&format!("{}", param.shape)), - } + self.write_shape(¶m.shape); self.write_custom_completion(¶m.completion); } if let Some(default) = ¶m.default_value { @@ -678,10 +716,7 @@ impl<'a> Formatter<'a> { } if let Some(shape) = &flag.arg { self.write(": "); - match shape { - SyntaxShape::Closure(Option::None) => self.write("closure"), - _ => self.write(&format!("{}", shape)), - } + self.write_shape(shape); self.write_custom_completion(&flag.completion); } if let Some(default) = &flag.default_value { @@ -697,10 +732,7 @@ impl<'a> Formatter<'a> { self.write(&rest.name); if rest.shape != SyntaxShape::Any { self.write(": "); - match &rest.shape { - SyntaxShape::Closure(Option::None) => self.write("closure"), - _ => self.write(&format!("{}", rest.shape)), - } + self.write_shape(&rest.shape); self.write_custom_completion(&rest.completion); } } diff --git a/tests/fixtures/expected/issue81.nu b/tests/fixtures/expected/issue81.nu index 8810470..6738617 100644 --- a/tests/fixtures/expected/issue81.nu +++ b/tests/fixtures/expected/issue81.nu @@ -1,2 +1,5 @@ def f1 [] { } def f2 [foo: int@f1 = 1] { } +def f3 [x: string@[a b c]] { } +def "complete foo" [] { } +def test [x: int@"complete foo"] { } diff --git a/tests/fixtures/input/issue81.nu b/tests/fixtures/input/issue81.nu index 8810470..6738617 100644 --- a/tests/fixtures/input/issue81.nu +++ b/tests/fixtures/input/issue81.nu @@ -1,2 +1,5 @@ def f1 [] { } def f2 [foo: int@f1 = 1] { } +def f3 [x: string@[a b c]] { } +def "complete foo" [] { } +def test [x: int@"complete foo"] { }