use ls_types::*;
use crate::state::{DocumentInfo, RuleInfo};
#[derive(Debug, Clone)]
pub struct LineIndex {
line_starts: Vec<usize>,
text_len: usize,
}
impl LineIndex {
pub fn new(text: &str) -> Self {
let mut line_starts = vec![0];
for (i, byte) in text.bytes().enumerate() {
if byte == b'\n' {
line_starts.push(i + 1);
}
}
Self {
line_starts,
text_len: text.len(),
}
}
pub fn offset_to_position(&self, offset: usize) -> Position {
let line = self.line_starts.partition_point(|&s| s <= offset).saturating_sub(1);
let col = offset.saturating_sub(self.line_starts[line]);
Position::new(line as u32, col as u32)
}
pub fn position_to_offset(&self, pos: Position) -> usize {
let line = pos.line as usize;
if line < self.line_starts.len() {
let line_start = self.line_starts[line];
let line_end = if line + 1 < self.line_starts.len() {
self.line_starts[line + 1].saturating_sub(1)
} else {
self.text_len
};
let requested = line_start + pos.character as usize;
requested.min(line_end)
} else {
panic!(
"position_to_offset received out-of-range line {} (line_count={})",
line,
self.line_starts.len()
);
}
}
pub fn span_to_range(&self, start: usize, end: usize) -> Range {
Range::new(self.offset_to_position(start), self.offset_to_position(end))
}
}
#[derive(Debug)]
pub enum SymbolAtOffset<'a> {
RuleDefinition(&'a RuleInfo),
RuleReference {
name: String,
span: (usize, usize),
},
}
pub fn symbol_at_offset<'a>(info: &'a DocumentInfo, offset: usize) -> Option<SymbolAtOffset<'a>> {
for rule in &info.rules {
if offset >= rule.name_span.0 && offset <= rule.name_span.1 {
return Some(SymbolAtOffset::RuleDefinition(rule));
}
}
for rule in &info.rules {
for refinfo in &rule.references {
if offset >= refinfo.span.0 && offset <= refinfo.span.1 {
return Some(SymbolAtOffset::RuleReference {
name: refinfo.name.clone(),
span: refinfo.span,
});
}
}
}
for rec in &info.recovers {
if offset >= rec.rule_name_span.0 && offset <= rec.rule_name_span.1 {
return Some(SymbolAtOffset::RuleReference {
name: rec.rule_name.clone(),
span: rec.rule_name_span,
});
}
}
for nc in &info.no_collapses {
if offset >= nc.rule_name_span.0 && offset <= nc.rule_name_span.1 {
return Some(SymbolAtOffset::RuleReference {
name: nc.rule_name.clone(),
span: nc.rule_name_span,
});
}
}
for pretty in &info.pretties {
if offset >= pretty.rule_name_span.0 && offset <= pretty.rule_name_span.1 {
return Some(SymbolAtOffset::RuleReference {
name: pretty.rule_name.clone(),
span: pretty.rule_name_span,
});
}
}
None
}