nml/src/lsp/semantic.rs

243 lines
5.8 KiB
Rust
Raw Normal View History

2024-10-18 12:43:51 +02:00
use std::cell::RefCell;
use std::ops::Range;
use std::rc::Rc;
2024-07-21 15:56:56 +02:00
2024-10-16 23:42:49 +02:00
use tower_lsp::lsp_types::SemanticToken;
2024-10-18 12:43:51 +02:00
use tower_lsp::lsp_types::SemanticTokenModifier;
2024-10-16 23:42:49 +02:00
use tower_lsp::lsp_types::SemanticTokenType;
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
use crate::parser::source::LineCursor;
use crate::parser::source::Source;
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
pub const TOKEN_TYPE: &[SemanticTokenType] = &[
SemanticTokenType::NAMESPACE,
SemanticTokenType::TYPE,
SemanticTokenType::CLASS,
SemanticTokenType::ENUM,
SemanticTokenType::INTERFACE,
SemanticTokenType::STRUCT,
SemanticTokenType::TYPE_PARAMETER,
SemanticTokenType::PARAMETER,
SemanticTokenType::VARIABLE,
SemanticTokenType::PROPERTY,
SemanticTokenType::ENUM_MEMBER,
SemanticTokenType::EVENT,
SemanticTokenType::FUNCTION,
SemanticTokenType::METHOD,
SemanticTokenType::MACRO,
SemanticTokenType::KEYWORD,
SemanticTokenType::MODIFIER,
SemanticTokenType::COMMENT,
SemanticTokenType::STRING,
SemanticTokenType::NUMBER,
SemanticTokenType::REGEXP,
SemanticTokenType::OPERATOR,
SemanticTokenType::DECORATOR,
];
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
pub const TOKEN_MODIFIERS: &[SemanticTokenModifier] = &[
SemanticTokenModifier::DECLARATION,
SemanticTokenModifier::DEFINITION,
SemanticTokenModifier::READONLY,
SemanticTokenModifier::STATIC,
SemanticTokenModifier::DEPRECATED,
SemanticTokenModifier::ABSTRACT,
SemanticTokenModifier::ASYNC,
SemanticTokenModifier::MODIFICATION,
SemanticTokenModifier::DOCUMENTATION,
SemanticTokenModifier::DEFAULT_LIBRARY,
];
2024-10-16 23:42:49 +02:00
2024-10-18 12:43:51 +02:00
fn token_index(name: &str) -> u32
{
TOKEN_TYPE.iter()
.enumerate()
.find(|(_, token)| token.as_str() == name)
.map(|(index, _)| index as u32)
.unwrap_or(0)
}
fn modifier_index(name: &str) -> u32
{
TOKEN_MODIFIERS.iter()
.enumerate()
.find(|(_, token)| token.as_str() == name)
.map(|(index, _)| index as u32)
.unwrap_or(0)
}
macro_rules! token {
($key:expr) => {
{
(token_index($key), 0)
}
};
($key:expr, $($mods:tt)*) => {
{
let mut bitset : u32 = 0;
$(
bitset |= 1 << modifier_index($mods);
)*
(token_index($key), bitset)
}
};
2024-10-16 23:42:49 +02:00
}
2024-10-18 12:43:51 +02:00
#[derive(Debug)]
pub struct Tokens
{
pub section_heading: (u32, u32),
pub section_reference: (u32, u32),
pub section_kind: (u32, u32),
pub section_name: (u32, u32),
}
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
impl Tokens
{
pub fn new() -> Self
{
Self {
section_heading : token!("number"),
section_reference : token!("enum", "async"),
section_kind : token!("enum"),
section_name : token!("string"),
}
2024-10-16 23:42:49 +02:00
}
2024-10-18 12:43:51 +02:00
}
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
/// Semantics for a buffer
#[derive(Debug)]
pub struct Semantics {
/// The tokens
pub token: Tokens,
/// The current cursor
cursor: RefCell<LineCursor>,
/// Semantic tokens
pub tokens: RefCell<Vec<SemanticToken>>,
2024-07-21 15:56:56 +02:00
}
2024-10-18 12:43:51 +02:00
impl Semantics {
pub fn new(source: Rc<dyn Source>) -> Semantics {
Self {
token: Tokens::new(),
cursor: RefCell::new(LineCursor::new(source)),
tokens: RefCell::new(vec![]),
}
}
2024-07-21 15:56:56 +02:00
2024-10-18 12:43:51 +02:00
pub fn add(
&self,
source: Rc<dyn Source>,
range: Range<usize>,
token: (u32, u32)
) {
let mut tokens = self.tokens.borrow_mut();
let mut cursor = self.cursor.borrow_mut();
let mut current = cursor.clone();
cursor.move_to(range.start);
while cursor.pos != range.end {
let end = source.content()[cursor.pos..]
.find('\n')
.unwrap_or(source.content().len() - cursor.pos);
let len = usize::min(range.end - cursor.pos, end);
2024-10-18 14:04:15 +02:00
let clen = source.content()[cursor.pos..cursor.pos+len]
.chars()
.fold(0, |clen, _| clen + 1);
2024-10-18 12:43:51 +02:00
let delta_line = cursor.line - current.line;
let delta_start = if delta_line == 0 {
if let Some(last) = tokens.last() {
cursor.line_pos - current.line_pos + last.length as usize
} else {
cursor.line_pos - current.line_pos
}
} else {
cursor.line_pos
};
//eprintln!("CURRENT={:#?}, CURS={:#?}", current, cursor);
tokens.push(SemanticToken {
delta_line: delta_line as u32,
delta_start: delta_start as u32,
2024-10-18 14:04:15 +02:00
length: clen as u32,
2024-10-18 12:43:51 +02:00
token_type: token.0,
token_modifiers_bitset: token.1
});
current = cursor.clone();
let pos = cursor.pos;
cursor.move_to(pos + len);
}
}
2024-07-21 15:56:56 +02:00
}
2024-10-18 14:04:15 +02:00
#[cfg(test)]
pub mod tests {
#[macro_export]
macro_rules! validate_semantics {
($state:expr, $source:expr, $idx:expr,) => {};
($state:expr, $source:expr, $idx:expr, $token_name:ident { $($field:ident == $value:expr),* }; $($tail:tt)*) => {{
let token = $state.shared.semantics
.as_ref()
.unwrap()
.borrow()
.get(&($source as Rc<dyn Source>))
.unwrap()
.tokens
.borrow()
[$idx];
let token_type = $state.shared.semantics
.as_ref()
.unwrap()
.borrow()
.get(&($source as Rc<dyn Source>))
.unwrap()
.token
.$token_name;
let found_token = (token.token_type, token.token_modifiers_bitset);
assert!(found_token == token_type, "Invalid token at index {}, expected {}{token_type:#?}, got: {found_token:#?}",
$idx, stringify!($token_name));
$(
let val = &token.$field;
assert!(*val == $value, "Invalid field {} at index {}, expected {:#?}, found {:#?}",
stringify!($field),
$idx,
$value,
val);
)*
validate_semantics!($state, $source, ($idx+1), $($tail)*);
}};
($state:expr, $source:expr, $idx:expr, $token_name:ident; $($tail:tt)*) => {{
let token = $state.shared.semantics
.as_ref()
.unwrap()
.borrow()
.get(&($source as Rc<dyn Source>))
.unwrap()
.tokens
.borrow()
[$idx];
let token_type = $state.shared.semantics
.as_ref()
.unwrap()
.borrow()
.get(&($source as Rc<dyn Source>))
.unwrap()
.token
.$token_name;
let found_token = (token.token_type, token.token_modifiers_bitset);
assert!(found_token == token_type, "Invalid token at index {}, expected {}{token_type:#?}, got: {found_token:#?}",
$idx, stringify!($token_name));
validate_semantics!($state, $source, ($idx+1), $($tail)*);
}};
}
}