use crate::core::Span;
use crate::semantic::symbol_table::{Symbol, SymbolTable};
use crate::semantic::types::TokenType;
use crate::semantic::workspace::Workspace;
use crate::syntax::SyntaxFile;
#[derive(Debug, Clone, PartialEq)]
pub struct SemanticToken {
pub line: u32,
pub column: u32,
pub length: u32,
pub token_type: TokenType,
}
impl SemanticToken {
fn from_span(span: &Span, token_type: TokenType) -> Self {
let char_length = if span.start.line == span.end.line {
span.end.column.saturating_sub(span.start.column)
} else {
1
};
Self {
line: span.start.line as u32,
column: span.start.column as u32,
length: char_length as u32,
token_type,
}
}
}
pub struct SemanticTokenCollector;
impl SemanticTokenCollector {
pub fn collect_from_symbols(symbol_table: &SymbolTable, file_path: &str) -> Vec<SemanticToken> {
let mut tokens = Vec::new();
for symbol in symbol_table.get_symbols_for_file(file_path) {
if let Some(span) = symbol.span() {
let token_type = Self::map_symbol_to_token_type(symbol);
tokens.push(SemanticToken::from_span(&span, token_type));
}
if let Symbol::Alias {
target_span: Some(span),
..
} = symbol
{
tokens.push(SemanticToken::from_span(span, TokenType::Type));
}
if let Symbol::Import {
path_span: Some(span),
..
} = symbol
{
tokens.push(SemanticToken::from_span(span, TokenType::Namespace));
}
}
tokens.sort_by_key(|t| (t.line, t.column));
tokens
}
pub fn collect_from_workspace(
workspace: &Workspace<SyntaxFile>,
file_path: &str,
) -> Vec<SemanticToken> {
let mut tokens = Self::collect_from_symbols(workspace.symbol_table(), file_path);
let existing_positions: std::collections::HashSet<(u32, u32)> =
tokens.iter().map(|t| (t.line, t.column)).collect();
for ref_info in workspace
.reference_index()
.get_references_in_file(file_path)
{
let token_type = ref_info.token_type.unwrap_or(TokenType::Type);
let token = SemanticToken::from_span(&ref_info.span, token_type);
if !existing_positions.contains(&(token.line, token.column)) {
tokens.push(token);
}
}
tokens.sort_by_key(|t| (t.line, t.column));
tokens
}
fn map_symbol_to_token_type(symbol: &Symbol) -> TokenType {
match symbol {
Symbol::Package { .. } => TokenType::Namespace,
Symbol::Classifier { .. } => TokenType::Type,
Symbol::Usage { .. } | Symbol::Feature { .. } => TokenType::Property,
Symbol::Definition { .. } => TokenType::Type,
Symbol::Alias { .. } => TokenType::Variable,
Symbol::Import { .. } => TokenType::Namespace,
Symbol::Comment { .. } => TokenType::Comment,
}
}
}