use super::{
render::{merge_spans, render, Span},
terms::Terms,
HighlightOptions,
};
use crate::{AnalysisError, AnalysisResult, CompiledAnalyzer};
use uqa_core::memory::{Budgeted, BudgetedVec, MemoryBudget};
pub fn highlight_compiled(
text: &str,
query_inputs: &[String],
analyzer: &CompiledAnalyzer,
opts: &HighlightOptions,
) -> AnalysisResult<String> {
Ok(highlight_compiled_budgeted(
text,
query_inputs,
analyzer,
opts,
&MemoryBudget::new(usize::MAX),
|| Ok(()),
)?
.into_parts()
.0)
}
pub fn highlight_compiled_budgeted(
text: &str,
query_inputs: &[String],
analyzer: &CompiledAnalyzer,
opts: &HighlightOptions,
budget: &MemoryBudget,
mut poll: impl FnMut() -> AnalysisResult<()>,
) -> AnalysisResult<Budgeted<String>> {
poll()?;
if text.is_empty() || query_inputs.is_empty() {
return crate::allocation::copy_text(text, budget, &mut poll);
}
let mut terms = Terms::new(budget);
for query in query_inputs {
poll()?;
terms.append(
analyzer.analyze_tokens_budgeted(query, budget, &mut poll)?,
false,
&mut poll,
)?;
}
if terms.is_empty() {
return crate::allocation::copy_text(text, budget, &mut poll);
}
terms.sort_unique(&mut poll)?;
let source = analyzer.analyze_tokens_budgeted(text, budget, &mut poll)?;
let mut spans = BudgetedVec::new(budget);
for token in source.tokens() {
poll()?;
if !terms.contains(token.term(), &mut poll)? {
continue;
}
let range = &token
.offsets()
.ok_or(AnalysisError::MissingTokenOffsets)?
.utf8;
if range.start > range.end {
return Err(AnalysisError::InvalidTextSpan {
start: range.start,
end: range.end,
});
}
for offset in [range.start, range.end] {
if !text.is_char_boundary(offset) {
return Err(AnalysisError::InvalidTextOffset {
coordinate: "UTF-8",
offset,
length: text.len(),
});
}
}
if !range.is_empty() {
spans.push(Span::new(range.start, range.end))?;
}
}
drop(source);
drop(terms);
merge_spans(&mut spans, &mut poll)?;
render(text, spans, opts, budget, &mut poll)
}