My program failed and the tester's error message is not clear. Quite confusing considering information available in the instruction

I’m stuck on Stage #EP2.

I’ve tried testing locally with this registered specification script:

#!/usr/bin/env python3
print("add")
print("commit")
print("branch")

print("cat-file")

print("remote")
print("rebase")
print("reset")

print("stash")
print("status")
# this file is in /tmp/sc

and ran this command complete -C /tmp/sc git to store the script path in a Hashmap.
Things seem to work as I see my output:

   Compiling codecrafters-shell v0.1.0 (/home/skhuong/rnd/simsh)
    Finished `release` profile [optimized] target(s) in 1.14s
$ complete -C /tmp/sc git
$ complete -p git
complete -C '/tmp/sc' git
$ git re
rebase  remote  reset
$ git sta
stash   status
$ git sta

However, it couldn’t pass the test and what’s weird to me is the error message:

[tester::#EP2] Expected argv[3] to be "git", found ""

Based on previous stage, argv[3] supposed to be the prefix—not the command—ain’t it?

Here are my logs:

[compile]    Compiling codecrafters-shell v0.1.0 (/app)
[compile]     Finished `release` profile [optimized] target(s) in 2.31s
[compile] Moved ./.codecrafters/run.sh → ./your_program.sh
[compile] Compilation successful.
Debug = true
[tester::#EP2] Running tests for Stage #EP2 (Programmable Completion - Multiple completer candidates)
[tester::#EP2] Running ./your_program.sh
[your-program] $ complete -C /tmp/dog/multiCandidateEnvCompleter git
[tester::#EP2] ✓ Registered command-based completion
[tester::#EP2] Typed "git sta"
[your-program] $ git sta
[tester::#EP2] ✓ Prompt line matches "$ git sta"
[tester::#EP2] Pressed "<TAB>" (expecting bell to ring)
[tester::#EP2] ✓ Received bell
[tester::#EP2] Pressed "<TAB>" (expecting "stash  status" as completion options)
[tester::#EP2] Didn't find expected line.
[tester::#EP2] Expected: "stash  status"
[tester::#EP2] Received: "" (no line received)
[tester::#EP2] Errors from the completer script:
[tester::#EP2] ✓ Arguments are of length 4 (including program name)
[tester::#EP2] ✓ Expected value of argv[1] found
[tester::#EP2] ✓ Expected value of argv[2] found
[tester::#EP2] Expected argv[3] to be "git", found ""
[tester::#EP2] ✓ Expected value of environment variable COMP_LINE found
[tester::#EP2] ✓ Expected value of environment variable COMP_POINT found
[tester::#EP2] Test failed

And here’s a snippet of my code:

use rustyline::completion::Pair;
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::process::{Command, Stdio};

use crate::parser;

pub fn get_completions_from_registered_spec(
    line: &str,
    pos: usize,
    path: &String,
) -> Option<(usize, Vec<Pair>)> {
    let args = parser::command_input_parser(line).0;

    let command = args[0].clone();
    let mut subcommand = String::new();
    let mut prefix = String::new();
    if args.len() > 3 {
        if let Some(arg) = args.last() {
            prefix = arg.clone();
            subcommand = args[args.len() - 2].clone();
        }
    } else if args.len() == 3 {
        subcommand = args[1].clone();
        prefix = args[2].clone();
    } else if args.len() == 2 {
        prefix = args[1].clone();
    }

    let is_line_ends_with_space = line.ends_with(" ");
    let mut vars = HashMap::<String, String>::new();
    vars.insert("COMP_LINE".to_string(), line.to_string());
    vars.insert("COMP_POINT".to_string(), pos.to_string());

    let mut child = Command::new(path)
        .envs(vars)
        .args(vec![command, prefix.clone(), subcommand])
        .stdout(Stdio::piped())
        .spawn()
        .expect("Failed to execute completion script");

    let mut completions: Vec<String> = vec![];

    if let Some(stdout) = child.stdout.take() {
        let reader = BufReader::new(stdout);
        for line_from_reader in reader.lines() {
            if let Ok(completion) = line_from_reader
                && completion.starts_with(prefix.as_str())
            {
                completions.push(completion);
            }
        }
    }

    let pos = pos
        - if is_line_ends_with_space {
            0
        } else {
            prefix.len()
        };

    if completions.len() > 0 {
        return Some((
            pos,
            completions
                .iter()
                .map(|completion| Pair {
                    display: completion.clone(),
                    replacement: format!("{} ", completion.clone()),
                })
                .collect(),
        ));
    }

    None
}

Could anyone please hlep?
Here is my repo if you need to check other file: simsh/src/completion/get_completions_from_registered_spec.rs at main · skuong/simsh · GitHub

Thanks in advance

Hey @skuong, could you double-check if your implementation satisfies this requirement:

Perfect!

I was confused by this in #ZI0:

  1. argv[3] — The word immediately before the word being completed. If there’s no preceding word, pass an empty string.

But It’s working now.

Thank you so much :star_struck: