use std::{collections::HashMap, io, path::Path};
use crate::{
definitions::{
Definition, constructor::ConstructorDefinition, enumeration::EnumDefinition,
error::ErrorDefinition, event::EventDefinition, modifier::ModifierDefinition,
structure::StructDefinition,
},
error::{ErrorKind, Result},
prelude::OrPanic as _,
textindex::{TextIndex, TextRange, compute_indices},
};
#[cfg_attr(docsrs, doc(cfg(feature = "slang")))]
#[cfg(feature = "slang")]
pub mod slang;
#[cfg_attr(docsrs, doc(cfg(feature = "solar")))]
#[cfg(feature = "solar")]
pub mod solar;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct DocumentId(u64);
impl DocumentId {
#[must_use]
pub fn new() -> Self {
DocumentId(fastrand::u64(..))
}
}
#[derive(Debug)]
pub struct ParsedDocument {
pub id: DocumentId,
pub definitions: Vec<Definition>,
}
pub trait Parse: Clone {
fn parse_document(
&mut self,
input: impl io::Read,
path: Option<impl AsRef<Path>>,
keep_contents: bool,
) -> Result<ParsedDocument>;
fn get_sources(self) -> Result<HashMap<DocumentId, String>>;
}
fn gather_offsets(definitions: &[Definition]) -> Vec<usize> {
fn register_span(offsets: &mut Vec<usize>, span: &TextRange) {
offsets.push(span.start.utf8);
offsets.push(span.end.utf8);
}
let mut offsets = Vec::with_capacity(definitions.len() * 16); for def in definitions {
def.span().inspect(|s| register_span(&mut offsets, s));
match def {
Definition::Constructor(ConstructorDefinition { params, .. })
| Definition::Error(ErrorDefinition { params, .. })
| Definition::Event(EventDefinition { params, .. })
| Definition::Modifier(ModifierDefinition { params, .. })
| Definition::Enumeration(EnumDefinition {
members: params, ..
})
| Definition::Struct(StructDefinition {
members: params, ..
}) => {
for p in params {
register_span(&mut offsets, &p.span);
}
}
Definition::Function(d) => {
d.params
.iter()
.for_each(|i| register_span(&mut offsets, &i.span));
d.returns
.iter()
.for_each(|i| register_span(&mut offsets, &i.span));
}
Definition::NatspecParsingError(ErrorKind::NatspecParsingError { span, .. }) => {
register_span(&mut offsets, span);
}
Definition::Contract(_)
| Definition::Interface(_)
| Definition::Library(_)
| Definition::Variable(_)
| Definition::NatspecParsingError(_) => {}
}
}
offsets.sort_unstable();
offsets
}
fn populate(text_indices: &[TextIndex], definitions: &mut Vec<Definition>) {
fn populate_span(indices: &[TextIndex], start_idx: usize, span: &mut TextRange) -> usize {
let idx;
(idx, span.start) = indices
.iter()
.enumerate()
.skip(start_idx)
.find_map(|(i, ti)| (ti.utf8 >= span.start.utf8).then_some((i, *ti)))
.or_panic("utf8 start offset should be present in cache");
span.end = *indices
.iter()
.skip(idx + 1)
.find(|ti| ti.utf8 >= span.end.utf8)
.or_panic("utf8 end offset should be present in cache");
idx + 1
}
let mut idx = 0;
for def in definitions {
if let Some(span) = def.span_mut() {
idx = populate_span(text_indices, idx, span);
}
match def {
Definition::Constructor(ConstructorDefinition { params, .. })
| Definition::Error(ErrorDefinition { params, .. })
| Definition::Event(EventDefinition { params, .. })
| Definition::Modifier(ModifierDefinition { params, .. })
| Definition::Enumeration(EnumDefinition {
members: params, ..
})
| Definition::Struct(StructDefinition {
members: params, ..
}) => {
for p in params {
idx = populate_span(text_indices, idx, &mut p.span);
}
}
Definition::Function(d) => {
for p in &mut d.params {
idx = populate_span(text_indices, idx, &mut p.span);
}
for p in &mut d.returns {
idx = populate_span(text_indices, idx, &mut p.span);
}
}
Definition::NatspecParsingError(ErrorKind::NatspecParsingError { span, .. }) => {
idx = populate_span(text_indices, idx, span);
}
Definition::Contract(_)
| Definition::Interface(_)
| Definition::Library(_)
| Definition::Variable(_)
| Definition::NatspecParsingError(_) => {}
}
}
}
pub fn complete_text_ranges(source: &str, definitions: &mut Vec<Definition>) {
let offsets = gather_offsets(definitions);
if offsets.is_empty() {
return;
}
let text_indices = compute_indices(source, &offsets);
populate(&text_indices, definitions);
}