@@ -3,142 +3,204 @@ use lsp_types::*;
33use vhdl_lang:: ast:: ExternalObjectClass ;
44use vhdl_lang:: { AnyEntKind , Concurrent , Object , Overloaded , Type } ;
55
6- // Semantic token type indices — order must match TOKEN_TYPES
7- const VARIABLE : u32 = 0 ;
8- const PARAMETER : u32 = 1 ;
9- const PROPERTY : u32 = 2 ;
10- const ENUM_MEMBER : u32 = 3 ;
11- const FUNCTION : u32 = 4 ;
12- const TYPE : u32 = 5 ;
13- const CLASS : u32 = 6 ;
14- const NAMESPACE : u32 = 7 ;
15- const STRUCT : u32 = 8 ;
16- const ENUM : u32 = 9 ;
6+ /// Generates token type index constants and the TOKEN_TYPES legend array
7+ /// from a single declaration, keeping the two in sync automatically.
8+ macro_rules! define_token_types {
9+ ( $( ( $const: ident = $lsp_type: expr) ) ,+ $( , ) ? ) => {
10+ define_token_types!( @consts 0 , $( $const, ) +) ;
11+
12+ pub const TOKEN_TYPES : & [ SemanticTokenType ] = & [
13+ $( $lsp_type, ) +
14+ ] ;
15+ } ;
16+
17+ // Base case
18+ ( @consts $idx: expr, ) => { } ;
19+ // Recursive case: assign current index, increment for the rest
20+ ( @consts $idx: expr, $const: ident, $( $rest: ident, ) * ) => {
21+ const $const: u32 = $idx;
22+ define_token_types!( @consts ( $idx + 1 ) , $( $rest, ) * ) ;
23+ } ;
24+ }
25+
26+ define_token_types ! {
27+ ( VARIABLE = SemanticTokenType :: VARIABLE ) , // signals, variables, constants, files
28+ ( PARAMETER = SemanticTokenType :: PARAMETER ) , // subprogram parameters
29+ ( PROPERTY = SemanticTokenType :: PROPERTY ) , // attributes, record fields
30+ ( ENUM_MEMBER = SemanticTokenType :: ENUM_MEMBER ) , // enum literals
31+ ( FUNCTION = SemanticTokenType :: FUNCTION ) , // functions, procedures
32+ ( TYPE = SemanticTokenType :: TYPE ) , // types (general)
33+ ( CLASS = SemanticTokenType :: CLASS ) , // protected types, components
34+ ( NAMESPACE = SemanticTokenType :: NAMESPACE ) , // libraries, design units, labels
35+ ( STRUCT = SemanticTokenType :: STRUCT ) , // record types
36+ ( ENUM = SemanticTokenType :: ENUM ) , // enum types
37+ }
1738
1839// Semantic token modifier bits
1940const MOD_READONLY : u32 = 1 << 0 ;
2041
21- pub const TOKEN_TYPES : & [ SemanticTokenType ] = & [
22- SemanticTokenType :: VARIABLE , // 0: signals, variables, constants, files
23- SemanticTokenType :: PARAMETER , // 1: subprogram parameters
24- SemanticTokenType :: PROPERTY , // 2: attributes, record fields
25- SemanticTokenType :: ENUM_MEMBER , // 3: enum literals
26- SemanticTokenType :: FUNCTION , // 4: functions, procedures
27- SemanticTokenType :: TYPE , // 5: types (general)
28- SemanticTokenType :: CLASS , // 6: protected types, components
29- SemanticTokenType :: NAMESPACE , // 7: libraries, design units, labels
30- SemanticTokenType :: STRUCT , // 8: record types
31- SemanticTokenType :: ENUM , // 9: enum types
32- ] ;
33-
3442pub const TOKEN_MODIFIERS : & [ SemanticTokenModifier ] = & [
3543 SemanticTokenModifier :: READONLY , // bit 0: constants, generics
3644] ;
3745
38- fn object_token ( obj : & Object ) -> ( u32 , u32 ) {
46+ /// Classification of a VHDL entity into an LSP semantic token.
47+ struct TokenClassification {
48+ token_type : u32 ,
49+ modifiers : u32 ,
50+ }
51+
52+ /// A resolved semantic token ready for caching and encoding.
53+ pub ( crate ) struct CachedToken {
54+ pub range : vhdl_lang:: Range ,
55+ pub token_type : u32 ,
56+ pub modifiers : u32 ,
57+ }
58+
59+ fn object_token ( obj : & Object ) -> TokenClassification {
3960 if obj. is_param ( ) {
40- return ( PARAMETER , 0 ) ;
61+ return TokenClassification {
62+ token_type : PARAMETER ,
63+ modifiers : 0 ,
64+ } ;
4165 }
4266 if obj. is_generic ( ) || obj. is_constant ( ) {
43- return ( VARIABLE , MOD_READONLY ) ;
67+ return TokenClassification {
68+ token_type : VARIABLE ,
69+ modifiers : MOD_READONLY ,
70+ } ;
71+ }
72+ TokenClassification {
73+ token_type : VARIABLE ,
74+ modifiers : 0 ,
4475 }
45- ( VARIABLE , 0 )
4676}
4777
48- fn overloaded_token ( o : & Overloaded ) -> ( u32 , u32 ) {
78+ fn overloaded_token ( o : & Overloaded ) -> TokenClassification {
4979 match o {
50- Overloaded :: EnumLiteral ( _) => ( ENUM_MEMBER , 0 ) ,
80+ Overloaded :: EnumLiteral ( _) => TokenClassification {
81+ token_type : ENUM_MEMBER ,
82+ modifiers : 0 ,
83+ } ,
5184 Overloaded :: Alias ( inner) => overloaded_token ( inner. kind ( ) ) ,
52- _ => ( FUNCTION , 0 ) ,
85+ _ => TokenClassification {
86+ token_type : FUNCTION ,
87+ modifiers : 0 ,
88+ } ,
5389 }
5490}
5591
56- fn type_token ( t : & Type ) -> ( u32 , u32 ) {
92+ fn type_token ( t : & Type ) -> TokenClassification {
5793 match t {
58- Type :: Enum ( _) => ( ENUM , 0 ) ,
59- Type :: Record ( _) => ( STRUCT , 0 ) ,
60- Type :: Protected ( ..) => ( CLASS , 0 ) ,
94+ Type :: Enum ( _) => TokenClassification {
95+ token_type : ENUM ,
96+ modifiers : 0 ,
97+ } ,
98+ Type :: Record ( _) => TokenClassification {
99+ token_type : STRUCT ,
100+ modifiers : 0 ,
101+ } ,
102+ Type :: Protected ( ..) => TokenClassification {
103+ token_type : CLASS ,
104+ modifiers : 0 ,
105+ } ,
61106 Type :: Subtype ( sub) => type_token ( sub. type_mark ( ) . kind ( ) ) ,
62107 Type :: Alias ( t) => type_token ( t. kind ( ) ) ,
63- _ => ( TYPE , 0 ) ,
108+ _ => TokenClassification {
109+ token_type : TYPE ,
110+ modifiers : 0 ,
111+ } ,
64112 }
65113}
66114
67- fn to_semantic_token ( kind : & AnyEntKind ) -> Option < ( u32 , u32 ) > {
115+ fn classify ( kind : & AnyEntKind ) -> Option < TokenClassification > {
68116 let result = match kind {
69117 AnyEntKind :: Object ( obj) => object_token ( obj) ,
70118 AnyEntKind :: DeferredConstant ( _)
71119 | AnyEntKind :: LoopParameter ( _)
72- | AnyEntKind :: PhysicalLiteral ( _) => ( VARIABLE , MOD_READONLY ) ,
120+ | AnyEntKind :: PhysicalLiteral ( _) => TokenClassification {
121+ token_type : VARIABLE ,
122+ modifiers : MOD_READONLY ,
123+ } ,
73124 AnyEntKind :: Overloaded ( o) => overloaded_token ( o) ,
74125 AnyEntKind :: Type ( t) => type_token ( t) ,
75- AnyEntKind :: Component ( _) => ( CLASS , 0 ) ,
76- AnyEntKind :: Attribute ( _) | AnyEntKind :: ElementDeclaration ( _) => ( PROPERTY , 0 ) ,
77- AnyEntKind :: Library | AnyEntKind :: Design ( _) => ( NAMESPACE , 0 ) ,
78- AnyEntKind :: View ( _) => ( TYPE , 0 ) ,
79- AnyEntKind :: File ( _) | AnyEntKind :: InterfaceFile ( _) => ( VARIABLE , 0 ) ,
126+ AnyEntKind :: Component ( _) => TokenClassification {
127+ token_type : CLASS ,
128+ modifiers : 0 ,
129+ } ,
130+ AnyEntKind :: Attribute ( _) | AnyEntKind :: ElementDeclaration ( _) => TokenClassification {
131+ token_type : PROPERTY ,
132+ modifiers : 0 ,
133+ } ,
134+ AnyEntKind :: Library | AnyEntKind :: Design ( _) => TokenClassification {
135+ token_type : NAMESPACE ,
136+ modifiers : 0 ,
137+ } ,
138+ AnyEntKind :: View ( _) => TokenClassification {
139+ token_type : TYPE ,
140+ modifiers : 0 ,
141+ } ,
142+ AnyEntKind :: File ( _) | AnyEntKind :: InterfaceFile ( _) => TokenClassification {
143+ token_type : VARIABLE ,
144+ modifiers : 0 ,
145+ } ,
80146 AnyEntKind :: ObjectAlias { base_object, .. } => object_token ( base_object. object ( ) ) ,
81147 AnyEntKind :: ExternalAlias { class, .. } => match class {
82- ExternalObjectClass :: Constant => ( VARIABLE , MOD_READONLY ) ,
83- _ => ( VARIABLE , 0 ) ,
148+ ExternalObjectClass :: Constant => TokenClassification {
149+ token_type : VARIABLE ,
150+ modifiers : MOD_READONLY ,
151+ } ,
152+ _ => TokenClassification {
153+ token_type : VARIABLE ,
154+ modifiers : 0 ,
155+ } ,
156+ } ,
157+ AnyEntKind :: Concurrent ( Some ( Concurrent :: Instance ) , _) => TokenClassification {
158+ token_type : CLASS ,
159+ modifiers : 0 ,
84160 } ,
85- AnyEntKind :: Concurrent ( Some ( Concurrent :: Instance ) , _) => ( CLASS , 0 ) ,
86161 AnyEntKind :: Concurrent ( ..) | AnyEntKind :: Sequential ( ..) => return None ,
87162 } ;
88163 Some ( result)
89164}
90165
91- /// Check if a token overlaps the filter range by line.
92- /// Character-level precision is not needed as clients request full-line ranges.
93- fn in_range ( token_range : & vhdl_lang:: Range , filter : & vhdl_lang:: Range ) -> bool {
94- token_range. start . line <= filter. end . line && token_range. end . line >= filter. start . line
95- }
96-
97166/// Map and sort raw tokens from the AST walk into cacheable form.
98167fn map_and_sort (
99- raw_tokens : Vec < ( vhdl_lang:: SrcPos , vhdl_lang:: EntRef < ' _ > ) > ,
100- ) -> Vec < ( vhdl_lang:: Range , u32 , u32 ) > {
101- let mut tokens: Vec < _ > = raw_tokens
168+ mut raw_tokens : Vec < ( vhdl_lang:: SrcPos , vhdl_lang:: EntRef < ' _ > ) > ,
169+ ) -> Vec < CachedToken > {
170+ raw_tokens. sort_by ( |( pos_a, _) , ( pos_b, _) | pos_a. cmp ( pos_b) ) ;
171+
172+ raw_tokens
102173 . into_iter ( )
103174 . filter_map ( |( pos, ent) | {
104- let ( token_type, token_modifiers) = to_semantic_token ( ent. kind ( ) ) ?;
105- let range = pos. range ( ) ;
106- Some ( ( range, token_type, token_modifiers) )
175+ let cls = classify ( ent. kind ( ) ) ?;
176+ Some ( CachedToken {
177+ range : pos. range ( ) ,
178+ token_type : cls. token_type ,
179+ modifiers : cls. modifiers ,
180+ } )
107181 } )
108- . collect ( ) ;
109-
110- tokens. sort_by ( |a, b| {
111- a. 0 . start
112- . line
113- . cmp ( & b. 0 . start . line )
114- . then ( a. 0 . start . character . cmp ( & b. 0 . start . character ) )
115- } ) ;
116-
117- tokens
182+ . collect ( )
118183}
119184
120185/// Delta-encode sorted tokens, optionally filtering to a range.
121- fn encode (
122- tokens : & [ ( vhdl_lang:: Range , u32 , u32 ) ] ,
123- range_filter : Option < & vhdl_lang:: Range > ,
124- ) -> Vec < SemanticToken > {
186+ fn encode ( tokens : & [ CachedToken ] , range_filter : Option < & vhdl_lang:: Range > ) -> Vec < SemanticToken > {
125187 let mut semantic_tokens = Vec :: with_capacity ( tokens. len ( ) ) ;
126188 let mut prev_line = 0u32 ;
127189 let mut prev_start = 0u32 ;
128190
129- for ( range , token_type , token_modifiers ) in tokens {
191+ for token in tokens {
130192 if let Some ( filter) = range_filter {
131- if !in_range ( range, filter) {
193+ if !token . range . overlaps_lines ( filter) {
132194 continue ;
133195 }
134196 }
135197
136- let line = range. start . line ;
137- let start = range. start . character ;
138- if range. start . line != range. end . line {
198+ let line = token . range . start . line ;
199+ let start = token . range . start . character ;
200+ if token . range . start . line != token . range . end . line {
139201 continue ; // Skip multi-line tokens; identifiers never span lines
140202 }
141- let length = range. end . character - range. start . character ;
203+ let length = token . range . end . character - token . range . start . character ;
142204
143205 let delta_line = line - prev_line;
144206 let delta_start = if delta_line == 0 {
@@ -151,8 +213,8 @@ fn encode(
151213 delta_line,
152214 delta_start,
153215 length,
154- token_type : * token_type,
155- token_modifiers_bitset : * token_modifiers ,
216+ token_type : token . token_type ,
217+ token_modifiers_bitset : token . modifiers ,
156218 } ) ;
157219
158220 prev_line = line;
@@ -164,10 +226,10 @@ fn encode(
164226
165227impl VHDLServer {
166228 /// Get or compute the cached semantic tokens for a file.
167- fn cached_semantic_tokens ( & mut self , uri : & Url ) -> Option < & [ ( vhdl_lang :: Range , u32 , u32 ) ] > {
229+ fn cached_semantic_tokens ( & mut self , uri : & Url ) -> Option < & [ CachedToken ] > {
168230 if !self . semantic_token_cache . contains_key ( uri) {
169231 let source = self . project . get_source ( & uri_to_file_name ( uri) ) ?;
170- let raw_tokens = self . project . semantic_tokens ( & source) ;
232+ let raw_tokens = self . project . find_all_entity_references ( & source) ;
171233 let tokens = map_and_sort ( raw_tokens) ;
172234 self . semantic_token_cache . insert ( uri. clone ( ) , tokens) ;
173235 }
0 commit comments