use std::path::PathBuf;
pub struct DocumentState {
pub text: String,
pub version: i32,
pub line_offsets: Vec<usize>,
}
pub struct WorkspaceState {
pub root: PathBuf,
pub config_path: Option<PathBuf>,
}
impl DocumentState {
pub fn new(text: String, version: i32) -> Self {
let line_offsets = build_line_offsets(&text);
Self {
text,
version,
line_offsets,
}
}
}
pub fn build_line_offsets(source: &str) -> Vec<usize> {
let mut offsets = vec![0usize];
for (i, b) in source.bytes().enumerate() {
if b == b'\n' {
offsets.push(i + 1);
}
}
offsets
}
pub fn utf16_char_to_byte_offset(line: &str, utf16_col: u32) -> usize {
let mut utf16_count = 0u32;
let mut byte_offset = 0usize;
for ch in line.chars() {
if utf16_count >= utf16_col {
break;
}
utf16_count += ch.len_utf16() as u32;
byte_offset += ch.len_utf8();
}
byte_offset
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_line_offsets_ascii() {
let source = "fn foo() {}\nfn bar() {}\n";
let offsets = build_line_offsets(source);
assert_eq!(offsets[0], 0);
assert_eq!(offsets[1], 12); assert_eq!(offsets[2], 24);
}
#[test]
fn test_build_line_offsets_empty() {
let offsets = build_line_offsets("");
assert_eq!(offsets, vec![0]);
}
#[test]
fn test_utf16_roundtrip_ascii() {
let line = "let x = 42;";
for col in 0..=(line.len() as u32) {
let byte = utf16_char_to_byte_offset(line, col);
assert_eq!(byte, col as usize, "col={col}");
}
}
#[test]
fn test_utf16_roundtrip_multibyte() {
let line = "a€b𝄞c";
assert_eq!(utf16_char_to_byte_offset(line, 0), 0); assert_eq!(utf16_char_to_byte_offset(line, 1), 1); assert_eq!(utf16_char_to_byte_offset(line, 2), 4); assert_eq!(utf16_char_to_byte_offset(line, 3), 5); assert_eq!(utf16_char_to_byte_offset(line, 5), 9); }
#[test]
fn test_document_state_construction() {
let text = "line one\nline two\n".to_string();
let doc = DocumentState::new(text.clone(), 1);
assert_eq!(doc.version, 1);
assert_eq!(doc.text, text);
assert_eq!(doc.line_offsets[0], 0);
assert_eq!(doc.line_offsets[1], 9); }
}