refactor(completion, parser): move custom_completion info from Expression to Signature (#15613)

Restricts custom completion from universal to internal arguments only.

Pros:
1. Less memory
2. More flexible for later customizations, e.g. #14923 

Cons:
1. limited customization capabilities, but at least covers all currently
existing features in nushell.

# Description

Mostly vibe coded by [Zed AI](https://zed.dev/ai) with a single prompt.
LGTM, but I'm not so sure @ysthakur 

# User-Facing Changes

Hopefully none.

# Tests + Formatting

+3

# After Submitting

---------

Co-authored-by: Yash Thakur <45539777+ysthakur@users.noreply.github.com>
This commit is contained in:
zc he
2025-07-25 02:21:58 +08:00
committed by GitHub
parent 1b01625e1e
commit 71baeff287
12 changed files with 194 additions and 144 deletions

View File

@ -648,14 +648,6 @@ impl HelpStyle {
}
}
/// Make syntax shape presentable by stripping custom completer info
fn document_shape(shape: &SyntaxShape) -> &SyntaxShape {
match shape {
SyntaxShape::CompleterWrapper(inner_shape, _) => inner_shape,
_ => shape,
}
}
#[derive(PartialEq)]
enum PositionalKind {
Required,
@ -686,15 +678,14 @@ fn write_positional(
long_desc,
"{help_subcolor_one}\"{}\" + {RESET}<{help_subcolor_two}{}{RESET}>",
String::from_utf8_lossy(kw),
document_shape(shape),
shape,
);
}
_ => {
let _ = write!(
long_desc,
"{help_subcolor_one}{}{RESET} <{help_subcolor_two}{}{RESET}>",
positional.name,
document_shape(&positional.shape),
positional.name, &positional.shape,
);
}
};
@ -767,11 +758,7 @@ where
}
// Type/Syntax shape info
if let Some(arg) = &flag.arg {
let _ = write!(
long_desc,
" <{help_subcolor_two}{}{RESET}>",
document_shape(arg)
);
let _ = write!(long_desc, " <{help_subcolor_two}{arg}{RESET}>");
}
if !flag.desc.is_empty() {
let _ = write!(

View File

@ -1,5 +1,5 @@
use nu_protocol::{
DeclId, ModuleId, Signature, Span, SyntaxShape, Type, Value, VarId,
DeclId, ModuleId, Signature, Span, Type, Value, VarId,
ast::Expr,
engine::{Command, EngineState, Stack, Visibility},
record,
@ -214,7 +214,8 @@ impl<'e, 's> ScopeData<'e, 's> {
// required_positional
for req in &signature.required_positional {
let custom = extract_custom_completion_from_arg(self.engine_state, &req.shape);
let custom =
extract_custom_completion_from_arg(self.engine_state, &req.custom_completion);
sig_records.push(Value::record(
record! {
@ -233,7 +234,8 @@ impl<'e, 's> ScopeData<'e, 's> {
// optional_positional
for opt in &signature.optional_positional {
let custom = extract_custom_completion_from_arg(self.engine_state, &opt.shape);
let custom =
extract_custom_completion_from_arg(self.engine_state, &opt.custom_completion);
let default = if let Some(val) = &opt.default_value {
val.clone()
} else {
@ -258,7 +260,8 @@ impl<'e, 's> ScopeData<'e, 's> {
// rest_positional
if let Some(rest) = &signature.rest_positional {
let name = if rest.name == "rest" { "" } else { &rest.name };
let custom = extract_custom_completion_from_arg(self.engine_state, &rest.shape);
let custom =
extract_custom_completion_from_arg(self.engine_state, &rest.custom_completion);
sig_records.push(Value::record(
record! {
@ -285,11 +288,10 @@ impl<'e, 's> ScopeData<'e, 's> {
continue;
}
let mut custom_completion_command_name: String = "".to_string();
let custom_completion_command_name: String =
extract_custom_completion_from_arg(self.engine_state, &named.custom_completion);
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);
@ -544,14 +546,16 @@ impl<'e, 's> ScopeData<'e, 's> {
}
}
fn extract_custom_completion_from_arg(engine_state: &EngineState, shape: &SyntaxShape) -> String {
match shape {
SyntaxShape::CompleterWrapper(_, 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(),
fn extract_custom_completion_from_arg(
engine_state: &EngineState,
decl_id: &Option<DeclId>,
) -> String {
if let Some(decl_id) = decl_id {
let custom_completion_command = engine_state.get_decl(*decl_id);
let custom_completion_command_name: &str = custom_completion_command.name();
custom_completion_command_name.to_string()
} else {
"".to_string()
}
}