use crate::prelude::*;
use biome_rowan::SyntaxKind;
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug, Eq, PartialEq)]
pub enum RecoveryError {
Eof,
AlreadyRecovered,
RecoveryDisabled,
}
impl Error for RecoveryError {}
impl Display for RecoveryError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RecoveryError::Eof => write!(f, "EOF"),
RecoveryError::AlreadyRecovered => write!(f, "already recovered"),
RecoveryError::RecoveryDisabled => write!(f, "recovery disabled"),
}
}
}
pub type RecoveryResult = Result<CompletedMarker, RecoveryError>;
pub struct ParseRecoveryTokenSet<K: SyntaxKind> {
node_kind: K,
recovery_set: TokenSet<K>,
line_break: bool,
}
impl<K: SyntaxKind> ParseRecoveryTokenSet<K> {
pub fn new(node_kind: K, recovery_set: TokenSet<K>) -> Self {
Self {
node_kind,
recovery_set,
line_break: false,
}
}
pub fn enable_recovery_on_line_break(mut self) -> Self {
self.line_break = true;
self
}
pub fn recover<P>(&self, p: &mut P) -> RecoveryResult
where
P: Parser<Kind = K>,
{
if p.at(P::Kind::EOF) {
return Err(RecoveryError::Eof);
}
if self.is_at_recovered(p) {
return Err(RecoveryError::AlreadyRecovered);
}
if p.is_speculative_parsing() {
return Err(RecoveryError::RecoveryDisabled);
}
let m = p.start();
while !(self.is_at_recovered(p) || p.at(P::Kind::EOF)) {
p.bump_any();
}
Ok(m.complete(p, self.node_kind))
}
#[inline]
fn is_at_recovered<P>(&self, p: &P) -> bool
where
P: Parser<Kind = K>,
{
p.at_ts(self.recovery_set) || (self.line_break && p.has_preceding_line_break())
}
}
pub trait ParseRecovery {
type Kind: SyntaxKind;
type Parser<'source>: Parser<Kind = Self::Kind>;
const RECOVERED_KIND: Self::Kind;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool;
fn recover(&self, p: &mut Self::Parser<'_>) -> RecoveryResult {
if p.at(Self::Kind::EOF) {
return Err(RecoveryError::Eof);
}
if self.is_at_recovered(p) {
return Err(RecoveryError::AlreadyRecovered);
}
if p.is_speculative_parsing() {
return Err(RecoveryError::RecoveryDisabled);
}
let m = p.start();
while !(self.is_at_recovered(p) || p.at(Self::Kind::EOF)) {
p.bump_any();
}
Ok(m.complete(p, Self::RECOVERED_KIND))
}
}