Repository: helix-editor/helix
File: helix-term/src/commands.rs (Rust)

Code before:
        Some(prev_index) => get_successive_chars(prev_index, contents)
            .map(|(c1, c2)| !char_is_word(c1) && char_is_word(c2))
            .unwrap_or(false),
        // we are at the beginning of the file
        None => contents.get_char(index).map(char_is_word).unwrap_or(false),
    }
}

fn is_eow(index: usize, contents: RopeSlice) -> bool {
    match index.checked_sub(1) {
        Some(prev_index) => {
            // we are at the end of the file
            if index == contents.len_chars() {
                contents
                    .get_char(prev_index)
                    .map(char_is_word)
                    .unwrap_or(false)
            } else {
                get_successive_chars(prev_index, contents)
                    .map(|(c1, c2)| char_is_word(c1) && !char_is_word(c2))
                    .unwrap_or(false)
            }
        }
        None => false,
    }
}

fn get_successive_chars(start: usize, contents: RopeSlice) -> Option<(char, char)> {
    let mut it = contents.chars_at(start);
    let c1 = it.next()?;
    let c2 = it.next()?;
    Some((c1, c2))
}

fn make_search_word_bounded(cx: &mut Context) {
    // Defaults to the active search register instead `/` to be more ergonomic assuming most people
    // would use this command following `search_selection`. This avoids selecting the register
    // twice.
    let register = cx
        .register
        .unwrap_or(cx.editor.registers.last_search_register);
    let regex = match cx.editor.registers.first(register, cx.editor) {
        Some(regex) => regex,
        None => return,
    };
    let start_anchored = regex.starts_with("\\b");
    let end_anchored = regex.ends_with("\\b");

    if start_anchored && end_anchored {
        return;
    }

Review comment (suggestion):
These can be moved into the body of `search_selection_impl`. I don't think we will need them elsewhere so I don't think we need to move them somewhere generic like `helix-stdx/src/rope.rs`

Code after:
            let word = regex::escape(&selection.fragment(text));
            format!("{}{}{}", prefix, word, suffix)
        })
        .collect::<HashSet<_>>() // Collect into hashset to deduplicate identical regexes
        .into_iter()
        .collect::<Vec<_>>()
        .join("|");

    let msg = format!("register '{}' set to '{}'", register, &regex);
    match cx.editor.registers.push(register, regex) {
        Ok(_) => {
            cx.editor.registers.last_search_register = register;
            cx.editor.set_status(msg)
        }
        Err(err) => cx.editor.set_error(err.to_string()),
    }
}

fn make_search_word_bounded(cx: &mut Context) {
    // Defaults to the active search register instead `/` to be more ergonomic assuming most people
    // would use this command following `search_selection`. This avoids selecting the register
    // twice.
    let register = cx
        .register
        .unwrap_or(cx.editor.registers.last_search_register);
    let regex = match cx.editor.registers.first(register, cx.editor) {
        Some(regex) => regex,
        None => return,
    };
    let start_anchored = regex.starts_with("\\b");
    let end_anchored = regex.ends_with("\\b");

    if start_anchored && end_anchored {
        return;
    }

    let mut new_regex = String::with_capacity(
        regex.len() + if start_anchored { 0 } else { 2 } + if end_anchored { 0 } else { 2 },
    );

    if !start_anchored {
        new_regex.push_str("\\b");
    }
    new_regex.push_str(&regex);
    if !end_anchored {
        new_regex.push_str("\\b");
    }

    let msg = format!("register '{}' set to '{}'", register, &new_regex);
    match cx.editor.registers.push(register, new_regex) {
        Ok(_) => {