pub fn visual_col_to_char_col(line: &str, visual_col: usize, tab_width: usize) -> usize {
let tab_w = if tab_width == 0 { 1 } else { tab_width };
let mut visual = 0usize;
for (i, ch) in line.chars().enumerate() {
if visual >= visual_col {
return i;
}
let advance = if ch == '\t' {
tab_w - (visual % tab_w)
} else {
1
};
if ch == '\t' && visual + advance > visual_col {
return i;
}
visual += advance;
}
line.chars().count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_exact_visual_col() {
assert_eq!(visual_col_to_char_col("hello", 0, 4), 0);
assert_eq!(visual_col_to_char_col("hello", 1, 4), 1);
assert_eq!(visual_col_to_char_col("hello", 3, 4), 3);
assert_eq!(visual_col_to_char_col("hello", 4, 4), 4);
}
#[test]
fn tab_expansion_click_inside_run_lands_on_tab_char() {
let line = "x\tyz";
assert_eq!(visual_col_to_char_col(line, 1, 4), 1); assert_eq!(visual_col_to_char_col(line, 2, 4), 1); assert_eq!(visual_col_to_char_col(line, 3, 4), 1); assert_eq!(visual_col_to_char_col(line, 4, 4), 2); assert_eq!(visual_col_to_char_col(line, 5, 4), 3); }
#[test]
fn tab_at_column_boundary() {
let line = "abcd\tefg";
assert_eq!(visual_col_to_char_col(line, 4, 4), 4); assert_eq!(visual_col_to_char_col(line, 5, 4), 4); assert_eq!(visual_col_to_char_col(line, 7, 4), 4); assert_eq!(visual_col_to_char_col(line, 8, 4), 5); }
#[test]
fn past_eol_clamps_to_char_count() {
assert_eq!(visual_col_to_char_col("hi", 99, 4), 2);
assert_eq!(visual_col_to_char_col("x", 100, 4), 1);
}
#[test]
fn empty_line_always_zero() {
assert_eq!(visual_col_to_char_col("", 0, 4), 0);
assert_eq!(visual_col_to_char_col("", 5, 4), 0);
}
#[test]
fn multibyte_single_cell_chars() {
let line = "αβγδε"; assert_eq!(visual_col_to_char_col(line, 0, 4), 0);
assert_eq!(visual_col_to_char_col(line, 2, 4), 2);
assert_eq!(visual_col_to_char_col(line, 4, 4), 4);
assert_eq!(visual_col_to_char_col(line, 5, 4), 5); }
#[test]
fn tab_width_one_treats_tab_as_single_cell() {
let line = "a\tb";
assert_eq!(visual_col_to_char_col(line, 0, 1), 0); assert_eq!(visual_col_to_char_col(line, 1, 1), 1); assert_eq!(visual_col_to_char_col(line, 2, 1), 2); }
#[test]
fn tab_width_zero_treated_as_one() {
let line = "a\tb";
assert_eq!(visual_col_to_char_col(line, 0, 0), 0);
assert_eq!(visual_col_to_char_col(line, 1, 0), 1);
assert_eq!(visual_col_to_char_col(line, 2, 0), 2);
}
#[test]
fn leading_tab_then_text() {
let line = "\thello";
assert_eq!(visual_col_to_char_col(line, 0, 4), 0); assert_eq!(visual_col_to_char_col(line, 3, 4), 0); assert_eq!(visual_col_to_char_col(line, 4, 4), 1); assert_eq!(visual_col_to_char_col(line, 8, 4), 5); }
}