Repository: nushell/nushell
File: crates/nu-cli/tests/completions/mod.rs (Rust)

Code before:
    let command = r#"
        def comp [] {
            [$"\u{1b}[35mfoo", $"\u{1b}[31mbar", $"\u{1b}[34mbaz"]
        }
        def my-command [arg: string@comp] {}"#;
    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 completion_str = "my-command ";
    let suggestions = completer.complete(completion_str, completion_str.len());
    match_suggestions(&vec!["bar", "baz", "foo"], &suggestions);
}

#[test]
fn custom_completions_strip_ansi_from_record_values() {
    let (_, _, mut engine, mut stack) = new_engine();
    // Record-style completions with ANSI codes in the value field should be stripped
    let command = r#"
        def comp [] {
            {
                completions: [
                    { value: $"\u{1b}[35mmagenta_dir", description: "a zoxide dir" },
                    { value: "plain_dir", description: "a normal dir" },
                ],
                options: { sort: false }
            }
        }
        def my-command [arg: string@comp] {}"#;
    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 completion_str = "my-command ";
    let suggestions = completer.complete(completion_str, completion_str.len());
    // ANSI codes should be stripped, leaving clean values
    assert_eq!(suggestions[0].value, "magenta_dir");
    assert_eq!(suggestions[1].value, "plain_dir");
}

#[rstest]
/// Fallback to file completions if custom completer returns null
#[case::fallback(r#"
    def comp [] { null }
    def my-command [arg: string@comp] {}"#,
    "my-command test", None,
    vec![folder("test_a"), file("test_a_symlink"), folder("test_b")],
    4
)]
/// Custom function arguments mixed with subcommands
#[case::arguments_and_subcommands(r#"
    def foo [i: directory] {}
    def "foo test bar" [] {}"#,

Review comment (suggestion):
You can just have a list of records since your changes don't affect how the `{completions:...,options:...}` record is handled. Nor do you need a description, since we're not checking those later anyway.
```suggestion
            [{ value: $"\u{1b}[35mmagenta_dir" }, { value: "plain_dir" }]
```

Also, I don't think the inclusion of `"plain_dir"` is necessary, but it's not a big deal

Code after:
        def comp [] {
            [$"\u{1b}[35mfoo", $"\u{1b}[31mbar", $"\u{1b}]8;;http://example.com\u{7}baz\u{1b}]8;;\u{7}"]
        }
        def my-command [arg: string@comp] {}"#;
    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 completion_str = "my-command ";
    let suggestions = completer.complete(completion_str, completion_str.len());
    match_suggestions(&vec!["bar", "baz", "foo"], &suggestions);
}

#[test]
fn custom_completions_strip_ansi_from_record_values() {
    let (_, _, mut engine, mut stack) = new_engine();
    let command = r#"
        def comp [] {
            [{ value: $"\u{1b}[35mmagenta_dir" }, { value: "plain_dir" }]
        }
        def my-command [arg: string@comp] {}"#;
    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 completion_str = "my-command ";
    let suggestions = completer.complete(completion_str, completion_str.len());
    match_suggestions(&vec!["magenta_dir", "plain_dir"], &suggestions);
}

#[rstest]
/// Fallback to file completions if custom completer returns null
#[case::fallback(r#"
    def comp [] { null }
    def my-command [arg: string@comp] {}"#,
    "my-command test", None,
    vec![folder("test_a"), file("test_a_symlink"), folder("test_b")],
    4
)]
/// Custom function arguments mixed with subcommands
#[case::arguments_and_subcommands(r#"
    def foo [i: directory] {}
    def "foo test bar" [] {}"#,
    "foo test", None,
    vec![folder("test_a"), file("test_a_symlink"), folder("test_b"), "foo test bar".into()],
    8
)]
/// If argument type is something like int/string, complete only subcommands
#[case::arguments_vs_subcommands(r#"
    def foo [i: string] {}
    def "foo test bar" [] {}"#,
    "foo test", None,
    vec!["foo test bar".into()],