use crate::{SourceMap, Token};
const FORWARD_SCAN: usize = 8;
#[derive(Debug, Clone)]
pub struct LookupCursor<'a> {
lines: Vec<&'a [Token]>,
prev: Option<(u32, usize)>,
}
impl<'a> LookupCursor<'a> {
pub(crate) fn new(map: &'a SourceMap<'_>) -> Self {
Self { lines: map.generate_lookup_table(), prev: None }
}
#[inline]
pub fn lookup_token(&mut self, line: u32, col: u32) -> Option<Token> {
let (tokens, next) = self.first_at_or_after(line, col)?;
let index = if tokens.get(next).is_some_and(|token| token.dst_col == col) {
next
} else {
next.checked_sub(1)?
};
self.remember(line, tokens, index)
}
#[inline]
pub fn lookup_token_approx(&mut self, line: u32, col: u32) -> Option<Token> {
let (tokens, next) = self.first_at_or_after(line, col)?;
let index = if tokens.get(next).is_some_and(|token| token.dst_col == col) {
next
} else {
next.saturating_sub(1)
};
self.remember(line, tokens, index)
}
#[inline]
fn first_at_or_after(&self, line: u32, col: u32) -> Option<(&'a [Token], usize)> {
let tokens = *self.lines.get(line as usize)?;
let mut next = match self.prev {
Some((prev_line, index))
if prev_line == line && (index == 0 || tokens[index - 1].dst_col < col) =>
{
index
}
_ => 0,
};
let end = tokens.len().min(next + FORWARD_SCAN);
while next < end && tokens[next].dst_col < col {
next += 1;
}
if next == end {
next += tokens[next..].partition_point(|token| token.dst_col < col);
}
Some((tokens, next))
}
#[inline]
fn remember(&mut self, line: u32, tokens: &[Token], index: usize) -> Option<Token> {
let token = *tokens.get(index)?;
self.prev = Some((line, index));
Some(token)
}
}