pub type PascalCaseBuf = [u8; 32];
pub const fn fmt_pascal_case_const(name: &str) -> (PascalCaseBuf, usize) {
let mut buf = [0; 32];
let mut buf_i = 0;
let mut name_i = 0;
let name = name.as_bytes();
while name_i < name.len() {
let first = name[name_i];
name_i += 1;
buf[buf_i] = first;
buf_i += 1;
while name_i < name.len() {
let rest = name[name_i];
name_i += 1;
if rest == b'_' {
break;
}
buf[buf_i] = rest.to_ascii_lowercase();
buf_i += 1;
}
}
(buf, buf_i)
}
pub fn fmt_pascal_case(
f: &mut std::fmt::Formatter<'_>,
name: &str,
) -> std::fmt::Result {
for word in name.split('_') {
let mut chars = word.chars();
if let Some(first) = chars.next() {
write!(f, "{first}")?;
}
for rest in chars {
write!(f, "{}", rest.to_lowercase())?;
}
}
Ok(())
}
macro_rules! lsp_enum {
(
impl $typ: ident {
$(
$(#[$attr:meta])*
const $name:ident = $value:expr;
)*
}
) => {
impl $typ {
$(
$(#[$attr])*
pub const $name: $typ = $typ($value);
)*
}
impl std::fmt::Debug for $typ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
$(
Self::$name => crate::protocol::macros::fmt_pascal_case(f, stringify!($name)),
)*
_ => write!(f, "{}({})", stringify!($typ), self.0),
}
}
}
impl std::fmt::Display for $typ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
$(
Self::$name => write!(f, "{}", $value),
)*
_ => write!(f, "{}", self.0),
}
}
}
impl std::convert::TryFrom<&str> for $typ {
type Error = &'static str;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match () {
$(
_ if {
const X: (crate::protocol::macros::PascalCaseBuf, usize) = crate::protocol::macros::fmt_pascal_case_const(stringify!($name));
let (buf, len) = X;
&buf[..len] == value.as_bytes()
} => Ok(Self::$name),
)*
_ => Err("unknown enum variant"),
}
}
}
}
}
pub(crate) use lsp_enum;
macro_rules! match_notification {
(
match $method:ident on $server_field:expr => {
$(
@ $notif_method:literal => $notif_rust_method:ident,
)*
}
) => {
paste::paste! {
match $method {
$(
| $notif_method => {
let server = $server_field.clone();
let handler: NotificationHandlerFn<P, T> =
Box::new(move |_method, params, mut ctx, mut writer| {
let server = server.clone();
{
use opentelemetry::trace::FutureExt as _;
otel::span!(concat!("lsp.", $notif_method), in |cx| {
async move {
let params = match params {
| Some(p) => match serde_json::from_value(p) {
| Ok(params) => params,
| Err(_e) => {
return writer;
},
},
| None => {
return writer;
},
};
let mut ctx_ref = crate::database::PartitionWriteContextRef::new(&mut writer);
server.$notif_rust_method(params, &mut ctx, &mut ctx_ref).await;
writer
}
.with_context(cx)
.boxed()
})
}
});
(handler, T::[<$notif_rust_method:snake:upper _LANE>])
},
)*
| _ => {
#[allow(unused)]
let handler: NotificationHandlerFn<P, T> =
Box::new(move |method, _, _, writer| {
async move {
writer
}
.boxed()
});
(handler, crate::scheduler::lanes::DEFAULT_LANE)
},
}
}
};
}
macro_rules! match_request {
(
match $method:ident on $server_field:expr => {
$(
$req_method:literal => $req_rust_method:ident,
)*
}
) => {
paste::paste! {
match $method {
$(
| $req_method => Self::route(
$server_field.clone(),
|server, params, mut ctx, mut writer| {
use opentelemetry::trace::FutureExt as _;
otel::span!(concat!("lsp.", $req_method), in |cx| {
async move {
let mut ctx_ref = crate::database::PartitionWriteContextRef::new(&mut writer);
let result = server.$req_rust_method(params, &mut ctx, &mut ctx_ref).await;
(result, writer)
}
.with_context(cx)
})
},
T::[<$req_rust_method:snake:upper _LANE>],
),
)*
| _ => {
let handler: RequestHandlerFn<P, T> =
Box::new(move |method, id, _, _, writer| {
async move {
let mut error = jsonrpc::Error::method_not_found();
error.data = Some(LSPAny::from(method));
let response = Response::from_error(id, error);
(Some(response), writer)
}
.boxed()
});
(handler, crate::scheduler::lanes::DEFAULT_LANE)
},
}
}
};
}
pub(crate) use {
match_notification,
match_request,
};