pub fn offset_to_line_col(source: &str, offset: u32) -> (u32, u32) {
let offset = (offset as usize).min(source.len());
let before = &source[..offset];
let line = before.chars().filter(|&c| c == '\n').count() as u32 + 1;
let col = match before.rfind('\n') {
Some(newline_pos) => {
(offset - newline_pos - 1) as u32 + 1
}
None => {
offset as u32 + 1
}
};
(line, col)
}
#[allow(dead_code)] pub fn count_lines_in_range(source: &str, start: u32, end: u32) -> usize {
let start = (start as usize).min(source.len());
let end = (end as usize).min(source.len());
if start >= end {
return 1;
}
source[start..end].chars().filter(|&c| c == '\n').count() + 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_offset_to_line_col_first_line() {
let src = "let x = 1;";
assert_eq!(offset_to_line_col(src, 0), (1, 1));
assert_eq!(offset_to_line_col(src, 4), (1, 5));
}
#[test]
fn test_offset_to_line_col_second_line() {
let src = "let x = 1;\nlet y = 2;";
assert_eq!(offset_to_line_col(src, 11), (2, 1));
assert_eq!(offset_to_line_col(src, 15), (2, 5));
}
#[test]
fn test_count_lines_single_line() {
let src = "const x = 1;";
assert_eq!(count_lines_in_range(src, 0, src.len() as u32), 1);
}
#[test]
fn test_count_lines_multi_line() {
let src = "line1\nline2\nline3";
assert_eq!(count_lines_in_range(src, 0, src.len() as u32), 3);
}
}