use std::ops::Deref;
use ahash::RandomState;
use lady_deirdre::{
analysis::{
AbstractTask,
AnalysisTask,
DocumentReadGuard,
SemanticAccess,
TaskHandle,
TriggerHandle,
},
arena::{Id, Identifiable},
lexis::ToSpan,
sync::Shared,
syntax::SyntaxTree,
};
use crate::{
analysis::{
symbols::{LookupOptions, ModuleSymbol, SymbolsLookup},
DiagnosticsDepth,
ModuleDiagnostics,
ModuleError,
ModuleResult,
ModuleResultEx,
ModuleText,
},
interpret::ScriptFn,
report::system_panic,
runtime::{PackageMeta, ScriptOrigin},
syntax::{PolyRefOrigin, ScriptNode, SpanBounds},
};
pub struct ModuleReadGuard<'a, H: TaskHandle = TriggerHandle> {
pub(super) id: Id,
pub(super) package: &'static PackageMeta,
pub(super) task: AnalysisTask<'a, ScriptNode, H, RandomState>,
}
impl<'a, H: TaskHandle> Identifiable for ModuleReadGuard<'a, H> {
#[inline(always)]
fn id(&self) -> Id {
self.id
}
}
impl<'a, H: TaskHandle> ModuleRead<H> for ModuleReadGuard<'a, H> {
#[inline(always)]
fn package(&self) -> &'static PackageMeta {
self.package
}
}
impl<'a, H: TaskHandle> ModuleReadSealed<H> for ModuleReadGuard<'a, H> {
type Task = AnalysisTask<'a, ScriptNode, H, RandomState>;
#[inline(always)]
fn task(&self) -> &Self::Task {
&self.task
}
}
pub trait ModuleRead<H: TaskHandle>: Identifiable + ModuleReadSealed<H> {
fn package(&self) -> &'static PackageMeta;
#[inline(always)]
fn is_interrupted(&self) -> bool {
self.task().handle().is_triggered()
}
fn text(&self) -> ModuleText {
ModuleText {
package: self.package(),
doc_read: self.read_doc(),
}
}
fn diagnostics(&self, depth: DiagnosticsDepth) -> ModuleResult<ModuleDiagnostics> {
let doc_read = self.read_doc();
let ScriptNode::Root { semantics, .. } = doc_read.deref().root() else {
system_panic!("Incorrect root variant.");
};
let id = self.id();
let root_semantics = semantics.get().into_module_result(id)?;
match depth {
1 => {
let (revision, snapshot) = root_semantics
.diagnostics_cross_1
.snapshot(self.task())
.into_module_result(id)?;
Ok(ModuleDiagnostics {
id,
issues: snapshot.issues.clone(),
depth,
revision,
})
}
2 => {
let (revision, snapshot) = root_semantics
.diagnostics_cross_2
.snapshot(self.task())
.into_module_result(id)?;
Ok(ModuleDiagnostics {
id,
issues: snapshot.issues.clone(),
depth,
revision,
})
}
3 => {
let (revision, snapshot) = root_semantics
.diagnostics_cross_3
.snapshot(self.task())
.into_module_result(id)?;
Ok(ModuleDiagnostics {
id,
issues: snapshot.issues.clone(),
depth,
revision,
})
}
_ => Ok(ModuleDiagnostics {
id,
issues: Shared::default(),
depth,
revision: 0,
}),
}
}
fn symbols(
&self,
span: impl ToSpan,
options: LookupOptions,
) -> ModuleResult<Vec<ModuleSymbol>> {
let doc_read = self.read_doc();
let span = {
let doc_read = self.read_doc();
match span.to_site_span(doc_read.deref()) {
Some(span) => span,
None => return Err(ModuleError::Cursor(self.id())),
}
};
Ok(SymbolsLookup::lookup(doc_read.deref(), span, options))
}
fn content_origin(&self) -> ScriptOrigin {
let doc_read = self.read_doc();
let ScriptNode::Root { statements, .. } = doc_read.root() else {
system_panic!("Incorrect root variant.");
};
let (Some(first), Some(last)) = (statements.first(), statements.last()) else {
return ScriptOrigin::eoi(self.id());
};
let mut start = first.script_origin(doc_read.deref(), SpanBounds::Header);
let end = last.script_origin(doc_read.deref(), SpanBounds::Footer);
start.union(&end);
start
}
fn compile(&self) -> ModuleResult<ScriptFn> {
let task = self.task();
let doc_read = self.read_doc();
ScriptFn::compile(task, doc_read.deref(), &doc_read.root_node_ref())
.into_module_result(self.id())
}
}
pub trait ModuleReadSealed<H: TaskHandle>: Identifiable {
type Task: SemanticAccess<ScriptNode, H, RandomState>;
fn task(&self) -> &Self::Task;
#[track_caller]
#[inline(always)]
fn read_doc(&self) -> DocumentReadGuard<ScriptNode, RandomState> {
let id = self.id();
match self.task().read_doc(id) {
Ok(doc_read) => doc_read,
Err(error) => system_panic!("Analysis internal error. {error}",),
}
}
}