1use std::ops::Range;
2
3use bevy_ecs::prelude::*;
4use derive_more::{AsMut, AsRef, Deref, DerefMut};
5use tracing::{debug, instrument};
6
7use crate::{components::*, prelude::*};
8
9#[derive(Component, Debug)]
11pub struct TokenComponent {
12 pub range: crate::lsp_types::Range,
13 pub text: String,
14 pub source_span: Range<usize>,
15 pub is_error: bool,
17}
18
19#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
21pub struct CstTokens(pub Vec<Spanned<rowan::SyntaxKind>>);
22
23#[instrument(skip(query, commands))]
24pub fn get_current_cst_token(
25 query: Query<(Entity, &CstTokens, &PositionComponent, &RopeC, &Source)>,
26 mut commands: Commands,
27) {
28 for (entity, cst_tokens, position, rope, source) in &query {
29 commands.entity(entity).remove::<TokenComponent>();
30 let Some(offset) = position_to_offset(position.0, &rope.0) else {
31 debug!("Couldn't transform to an offset ({:?})", position.0);
32 continue;
33 };
34
35 let token = cst_tokens
36 .0
37 .iter()
38 .filter(|x| x.span().contains(&offset))
39 .min_by_key(|x| x.span().end - x.span().start);
40
41 let Some(token) = token else {
42 let closest = cst_tokens.0.iter().min_by_key(|x| {
43 let d_start = offset.abs_diff(x.span().start);
44 let d_end = offset.abs_diff(x.span().end);
45 d_start.min(d_end)
46 });
47 let Some(token) = closest else {
48 debug!("No CST tokens found");
49 continue;
50 };
51 let span = token.span().clone();
52 if span.start >= source.0.len() || span.end > source.0.len() {
53 continue;
54 }
55 if offset > span.end {
59 let empty_span = offset..offset;
60 let Some(range) = range_to_range(&empty_span, &rope.0) else {
61 debug!("Failed to transform offset to range");
62 continue;
63 };
64 debug!("Cursor in whitespace after token, inserting empty TokenComponent");
65 commands.entity(entity).insert(TokenComponent {
66 text: String::new(),
67 range,
68 source_span: empty_span,
69 is_error: false,
70 });
71 continue;
72 }
73 let text = source.0[span.clone()].to_string();
74 let Some(range) = range_to_range(&span, &rope.0) else {
75 debug!("Failed to transform span to range");
76 continue;
77 };
78 debug!("Closest CST token {:?} '{}'", token.value(), text);
79 commands.entity(entity).insert(TokenComponent {
80 text,
81 range,
82 source_span: span,
83 is_error: false,
84 });
85 continue;
86 };
87
88 let span = token.span().clone();
89 if span.start >= source.0.len() || span.end > source.0.len() {
90 continue;
91 }
92 let text = source.0[span.clone()].to_string();
93 let Some(range) = range_to_range(&span, &rope.0) else {
94 debug!("Failed to transform span to range");
95 continue;
96 };
97 debug!("CST token at cursor {:?} '{}'", token.value(), text);
98 commands.entity(entity).insert(TokenComponent {
99 text,
100 range,
101 source_span: span,
102 is_error: false,
103 });
104 }
105}
106
107pub mod semantic_token {
108 use crate::lsp_types::SemanticTokenType as STT;
109 pub const BOOLEAN: STT = STT::new("boolean");
110 pub const LANG_TAG: STT = STT::new("langTag");
111}