use rustledger_core::Directive;
use rustledger_parser::ParseResult;
use super::helpers::{extract_accounts, extract_currencies, extract_payees};
#[derive(Debug, Clone)]
pub struct EditorCache {
pub accounts: Vec<String>,
pub currencies: Vec<String>,
pub payees: Vec<String>,
pub line_index: LineIndex,
}
impl EditorCache {
pub fn new(source: &str, parse_result: &ParseResult) -> Self {
Self {
accounts: extract_accounts(parse_result),
currencies: extract_currencies(parse_result),
payees: extract_payees(parse_result),
line_index: LineIndex::new(source),
}
}
pub fn from_directives(directives: &[Directive]) -> Self {
Self {
accounts: rustledger_core::extract_accounts(directives),
currencies: rustledger_core::extract_currencies(directives),
payees: rustledger_core::extract_payees(directives),
line_index: LineIndex::empty(),
}
}
}
#[derive(Debug, Clone)]
pub struct LineIndex {
line_starts: Vec<usize>,
len: usize,
}
impl LineIndex {
pub fn empty() -> Self {
Self {
line_starts: vec![0],
len: 0,
}
}
pub fn new(source: &str) -> Self {
let mut line_starts = vec![0];
for (i, ch) in source.char_indices() {
if ch == '\n' {
line_starts.push(i + 1);
}
}
Self {
line_starts,
len: source.len(),
}
}
pub fn offset_to_position(&self, offset: usize) -> (u32, u32) {
let offset = offset.min(self.len);
let line = match self.line_starts.binary_search(&offset) {
Ok(line) => line,
Err(line) => line.saturating_sub(1),
};
let line_start = self.line_starts[line];
let col = offset - line_start;
(line as u32, col as u32)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_line_index_empty_source() {
let index = LineIndex::new("");
let (line, col) = index.offset_to_position(0);
assert_eq!(line, 0);
assert_eq!(col, 0);
}
#[test]
fn test_line_index_single_line() {
let source = "hello world";
let index = LineIndex::new(source);
let (line, col) = index.offset_to_position(0);
assert_eq!(line, 0);
assert_eq!(col, 0);
let (line, col) = index.offset_to_position(6);
assert_eq!(line, 0);
assert_eq!(col, 6);
}
#[test]
fn test_line_index_multiple_lines() {
let source = "line1\nline2\nline3";
let index = LineIndex::new(source);
let (line, col) = index.offset_to_position(0);
assert_eq!(line, 0);
assert_eq!(col, 0);
let (line, col) = index.offset_to_position(6);
assert_eq!(line, 1);
assert_eq!(col, 0);
let (line, col) = index.offset_to_position(12);
assert_eq!(line, 2);
assert_eq!(col, 0);
let (line, col) = index.offset_to_position(15);
assert_eq!(line, 2);
assert_eq!(col, 3);
}
#[test]
fn test_line_index_beyond_length() {
let source = "hello";
let index = LineIndex::new(source);
let (line, col) = index.offset_to_position(1000);
assert_eq!(line, 0);
assert_eq!(col, 5);
}
}