use crate::wire::{
CodeAction, Completion, Diagnostic, Format, FormatCtx, Hover, LabelCtx, LexAnnotationOut,
RenderOut, WireNode,
};
pub trait LexHandler: Send + Sync {
fn on_label(&self, _ctx: &LabelCtx) {}
fn on_validate(&self, _ctx: &LabelCtx) -> Result<Vec<Diagnostic>, HandlerError> {
Ok(Vec::new())
}
fn on_resolve(&self, _ctx: &LabelCtx) -> Result<Option<WireNode>, HandlerError> {
Ok(None)
}
fn on_ir_build(&self, _ctx: &LabelCtx) -> Result<Option<WireNode>, HandlerError> {
Ok(None)
}
fn on_render(&self, _ctx: &LabelCtx, _fmt: Format) -> Result<Option<RenderOut>, HandlerError> {
Ok(None)
}
fn on_hover(&self, _ctx: &LabelCtx) -> Result<Option<Hover>, HandlerError> {
Ok(None)
}
fn on_completion(&self, _ctx: &LabelCtx) -> Result<Vec<Completion>, HandlerError> {
Ok(Vec::new())
}
fn on_code_action(&self, _ctx: &LabelCtx) -> Result<Vec<CodeAction>, HandlerError> {
Ok(Vec::new())
}
fn on_format(&self, _ctx: &FormatCtx) -> Result<Option<LexAnnotationOut>, HandlerError> {
Ok(None)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum HandlerError {
Internal { message: String },
Unsupported { detail: String },
Custom {
code: i32,
message: String,
data: Option<serde_json::Value>,
},
}
impl HandlerError {
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal {
message: message.into(),
}
}
pub fn unsupported(detail: impl Into<String>) -> Self {
Self::Unsupported {
detail: detail.into(),
}
}
}
impl std::fmt::Display for HandlerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
HandlerError::Internal { message } => {
write!(f, "handler internal error: {message}")
}
HandlerError::Unsupported { detail } => {
write!(f, "handler does not support: {detail}")
}
HandlerError::Custom { code, message, .. } => {
write!(f, "handler error {code}: {message}")
}
}
}
}
impl std::error::Error for HandlerError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::{LabelCtx, NodeRef, Position, Range};
struct NoOp;
impl LexHandler for NoOp {}
fn ctx() -> LabelCtx {
LabelCtx {
label: "test.label".into(),
params: serde_json::json!({}),
body: crate::wire::AnnotationBody::None,
node: NodeRef {
kind: "annotation".into(),
range: Range {
start: Position(0, 0),
end: Position(0, 0),
},
origin: None,
},
}
}
#[test]
fn noop_handler_returns_defaults() {
let h = NoOp;
let c = ctx();
h.on_label(&c);
assert!(h.on_validate(&c).unwrap().is_empty());
assert!(h.on_resolve(&c).unwrap().is_none());
assert!(h.on_ir_build(&c).unwrap().is_none());
assert!(h.on_render(&c, Format::Html).unwrap().is_none());
assert!(h.on_hover(&c).unwrap().is_none());
assert!(h.on_completion(&c).unwrap().is_empty());
assert!(h.on_code_action(&c).unwrap().is_empty());
let format_ctx = crate::wire::FormatCtx {
label: "test.label".into(),
params: vec![],
node: WireNode::Paragraph {
range: Range {
start: Position(0, 0),
end: Position(0, 0),
},
origin: None,
inlines: vec![],
},
format_options: None,
};
assert!(h.on_format(&format_ctx).unwrap().is_none());
}
#[test]
fn handler_error_display() {
assert_eq!(
HandlerError::internal("boom").to_string(),
"handler internal error: boom"
);
assert_eq!(
HandlerError::unsupported("png").to_string(),
"handler does not support: png"
);
assert_eq!(
HandlerError::Custom {
code: -32001,
message: "custom".into(),
data: None,
}
.to_string(),
"handler error -32001: custom"
);
}
}