orgize/orgize-lsp/src/semantic_token.rs
2023-12-20 21:56:10 +08:00

123 lines
3.4 KiB
Rust

use orgize::{
export::{Container, Event, TraversalContext, Traverser},
rowan::{ast::AstNode, TextRange},
};
use tower_lsp::lsp_types::{Range, SemanticToken, SemanticTokenType};
use crate::org_document::OrgDocument;
/// Semantic token types that are used for highlighting
pub const LEGEND_TYPE: &[SemanticTokenType] = &[SemanticTokenType::COMMENT];
pub struct SemanticTokenTraverser<'a> {
pub doc: &'a OrgDocument,
pub range: Option<TextRange>,
pub tokens: Vec<SemanticToken>,
pub previous_line: u32,
pub previous_start: u32,
}
impl<'a> Traverser for SemanticTokenTraverser<'a> {
fn event(&mut self, event: Event, ctx: &mut TraversalContext) {
match event {
Event::Enter(Container::Comment(comment)) => {
let range = comment.syntax().text_range();
if self.contains_range(range) {
if let Some(token) = self.create_token(
comment.begin(),
comment.end(),
SemanticTokenType::COMMENT,
) {
self.tokens.push(token);
}
}
ctx.skip();
}
Event::Enter(Container::CommentBlock(comment)) => {
let range = comment.syntax().text_range();
if self.contains_range(range) {
if let Some(token) = self.create_token(
comment.begin(),
comment.end(),
SemanticTokenType::COMMENT,
) {
self.tokens.push(token);
}
}
ctx.skip();
}
_ => {}
}
}
}
impl<'a> SemanticTokenTraverser<'a> {
pub fn new(doc: &'a OrgDocument) -> Self {
SemanticTokenTraverser {
doc,
range: None,
previous_line: 0,
previous_start: 0,
tokens: vec![],
}
}
pub fn with_range(doc: &'a OrgDocument, range: Range) -> Self {
let start = doc.offset_of(range.start);
let end = doc.offset_of(range.end);
SemanticTokenTraverser {
doc,
range: Some(TextRange::new(start.into(), end.into())),
previous_line: 0,
previous_start: 0,
tokens: vec![],
}
}
fn contains_range(&self, range: TextRange) -> bool {
match self.range {
Some(r) => r.contains_range(range),
None => true,
}
}
fn create_token(
&mut self,
start: u32,
end: u32,
kind: SemanticTokenType,
) -> Option<SemanticToken> {
let length = end - start;
let token_type = LEGEND_TYPE.iter().position(|item| item == &kind)? as u32;
let line = self.doc.line_of(start);
let first = self.doc.line_of(line);
let start = self.doc.line_of(start) - first;
let delta_line = line - self.previous_line;
let delta_start = if delta_line == 0 {
start - self.previous_start
} else {
start
};
self.previous_line = line;
self.previous_start = start;
Some(SemanticToken {
delta_line,
delta_start,
length,
token_type,
token_modifiers_bitset: 0,
})
}
}