use crate::ast::{Declaration, FieldType, Schema};
use crate::span::Span;
use crate::token::{Token, TokenKind};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SemanticKind {
ModelRef,
EnumRef,
CompositeTypeRef,
}
#[derive(Debug, Clone)]
pub struct SemanticToken {
pub span: Span,
pub kind: SemanticKind,
}
pub fn semantic_tokens(ast: &Schema, tokens: &[Token]) -> Vec<SemanticToken> {
let model_names: HashSet<&str> = ast.models().map(|m| m.name.value.as_str()).collect();
let enum_names: HashSet<&str> = ast.enums().map(|e| e.name.value.as_str()).collect();
let type_names: HashSet<&str> = ast.types().map(|t| t.name.value.as_str()).collect();
let mut result = Vec::new();
for decl in &ast.declarations {
if let Declaration::Model(model) = decl {
for field in &model.fields {
if let FieldType::UserType(type_name) = &field.field_type {
let kind = if type_names.contains(type_name.as_str()) {
SemanticKind::CompositeTypeRef
} else if model_names.contains(type_name.as_str()) {
SemanticKind::ModelRef
} else if enum_names.contains(type_name.as_str()) {
SemanticKind::EnumRef
} else {
continue;
};
if let Some(tok) = tokens.iter().find(|t| {
matches!(&t.kind, TokenKind::Ident(name) if name == type_name)
&& t.span.start >= field.span.start
&& t.span.end <= field.span.end
&& t.span.start != field.name.span.start
}) {
result.push(SemanticToken {
span: tok.span,
kind,
});
}
}
}
}
}
result.sort_by_key(|t| t.span.start);
result
}