Repository: nushell/nushell
File: crates/nu-cli/src/repl.rs (Rust)

Code before:
fn escape_special_vscode_bytes(input: &str) -> String {
    input
        .chars()
        .flat_map(|c| {
            let mut buf = [0; 4]; // Buffer to hold UTF-8 bytes of the character
            let c_bytes = c.encode_utf8(&mut buf); // Get UTF-8 bytes for the character

            if c_bytes.len() == 1 {
                let byte = c_bytes.as_bytes()[0];

                match byte {
                    // Escape bytes below 0x20
                    b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
                    // Escape semicolon as \x3B
                    b';' => "\\x3B".to_string().into_bytes(),
                    // Escape backslash as \\
                    b'\\' => "\\\\".to_string().into_bytes(),
                    // Otherwise, return the character unchanged
                    _ => vec![byte],
                }
            } else {
                // Escape all multi-byte characters (i.e., those above 0x7F)
                c_bytes
                    .bytes()
                    .flat_map(|b| format!("\\x{:02X}", b).into_bytes())
                    .collect()
            }
        })
        .map(|b| b as char) // Convert escaped bytes back to chars
        .collect() // Collect into final String
}

fn get_line_editor(engine_state: &mut EngineState, use_color: bool) -> Result<Reedline> {
    let mut start_time = std::time::Instant::now();
    let mut line_editor = Reedline::create();

    // Now that reedline is created, get the history session id and store it in engine_state
    store_history_id_in_engine(engine_state, &line_editor);
    perf!("setup reedline", start_time, use_color);

    if let Some(history) = engine_state.history_config() {
        start_time = std::time::Instant::now();

        line_editor = setup_history(engine_state, line_editor, history)?;

        perf!("setup history", start_time, use_color);
    }
    Ok(line_editor)
}

struct LoopContext<'a> {

Review comment (security):
According to the spec they don't have to be escaped. Not sure if VS Code will reinterpret a multibyte `\xEB\xAB` as utf-8 or expects there to only find lone ASCII `\x7F`

Code after:
fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
    let bytes = input
        .chars()
        .flat_map(|c| {
            let mut buf = [0; 4]; // Buffer to hold UTF-8 bytes of the character
            let c_bytes = c.encode_utf8(&mut buf); // Get UTF-8 bytes for the character

            if c_bytes.len() == 1 {
                let byte = c_bytes.as_bytes()[0];

                match byte {
                    // Escape bytes below 0x20
                    b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
                    // Escape semicolon as \x3B
                    b';' => "\\x3B".to_string().into_bytes(),
                    // Escape backslash as \\
                    b'\\' => "\\\\".to_string().into_bytes(),
                    // Otherwise, return the character unchanged
                    _ => vec![byte],
                }
            } else {
                // pass through multi-byte characters unchanged
                c_bytes.bytes().collect()
            }
        })
        .collect();

    String::from_utf8(bytes).map_err(|err| ShellError::CantConvert {
        to_type: "string".to_string(),
        from_type: "bytes".to_string(),
        span: Span::unknown(),
        help: Some(format!(
            "Error {err}, Unable to convert {input} to escaped bytes"
        )),
    })
}

fn get_line_editor(engine_state: &mut EngineState, use_color: bool) -> Result<Reedline> {
    let mut start_time = std::time::Instant::now();
    let mut line_editor = Reedline::create();

    // Now that reedline is created, get the history session id and store it in engine_state
    store_history_id_in_engine(engine_state, &line_editor);
    perf!("setup reedline", start_time, use_color);

    if let Some(history) = engine_state.history_config() {
        start_time = std::time::Instant::now();

        line_editor = setup_history(engine_state, line_editor, history)?;

        perf!("setup history", start_time, use_color);