use base64;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use crate::VersionedTextDocumentIdentifier;
#[derive(Debug, Eq, PartialEq, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SemanticHighlightingClientCapability {
pub semantic_highlighting: bool,
}
#[derive(Debug, Eq, PartialEq, Default, Deserialize, Serialize, Clone)]
pub struct SemanticHighlightingServerCapability {
#[serde(skip_serializing_if = "Option::is_none")]
pub scopes: Option<Vec<Vec<String>>>,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct SemanticHighlightingToken {
pub character: u32,
pub length: u16,
pub scope: u16,
}
impl SemanticHighlightingToken {
fn deserialize_tokens<'de, D>(
deserializer: D,
) -> Result<Option<Vec<SemanticHighlightingToken>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let opt_s = Option::<String>::deserialize(deserializer)?;
if let Some(s) = opt_s {
let bytes = base64::decode_config(s.as_str(), base64::STANDARD)
.map_err(|_| serde::de::Error::custom("Error parsing base64 string"))?;
let mut res = Vec::new();
for chunk in bytes.chunks_exact(8) {
res.push(SemanticHighlightingToken {
character: u32::from_be_bytes(<[u8; 4]>::try_from(&chunk[0..4]).unwrap()),
length: u16::from_be_bytes(<[u8; 2]>::try_from(&chunk[4..6]).unwrap()),
scope: u16::from_be_bytes(<[u8; 2]>::try_from(&chunk[6..8]).unwrap()),
});
}
Result::Ok(Some(res))
} else {
Result::Ok(None)
}
}
fn serialize_tokens<S>(
tokens: &Option<Vec<SemanticHighlightingToken>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if let Some(tokens) = tokens {
let mut bytes = vec![];
for token in tokens {
bytes.extend_from_slice(&token.character.to_be_bytes());
bytes.extend_from_slice(&token.length.to_be_bytes());
bytes.extend_from_slice(&token.scope.to_be_bytes());
}
serializer.collect_str(&base64::display::Base64Display::with_config(
&bytes,
base64::STANDARD,
))
} else {
serializer.serialize_none()
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
pub struct SemanticHighlightingInformation {
pub line: i32,
#[serde(
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "SemanticHighlightingToken::deserialize_tokens",
serialize_with = "SemanticHighlightingToken::serialize_tokens"
)]
pub tokens: Option<Vec<SemanticHighlightingToken>>,
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SemanticHighlightingParams {
pub text_document: VersionedTextDocumentIdentifier,
pub lines: Vec<SemanticHighlightingInformation>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::test_serialization;
#[test]
fn test_semantic_highlighting_information_serialization() {
test_serialization(
&SemanticHighlightingInformation {
line: 10,
tokens: Some(vec![
SemanticHighlightingToken {
character: 0x00000001,
length: 0x0002,
scope: 0x0003,
},
SemanticHighlightingToken {
character: 0x00112222,
length: 0x0FF0,
scope: 0x0202,
},
]),
},
r#"{"line":10,"tokens":"AAAAAQACAAMAESIiD/ACAg=="}"#,
);
test_serialization(
&SemanticHighlightingInformation {
line: 22,
tokens: None,
},
r#"{"line":22}"#,
);
}
}