1
+ use std:: unreachable;
2
+
1
3
use async_lsp:: lsp_types:: {
2
- Diagnostic , DiagnosticSeverity , Location , MarkedString , Position , PublishDiagnosticsParams ,
3
- Range , Url ,
4
+ Diagnostic , DiagnosticSeverity , DocumentSymbol , Location , MarkedString , Position ,
5
+ PublishDiagnosticsParams , Range , SymbolKind , Url ,
4
6
} ;
5
7
use tracing:: info;
6
8
use tree_sitter:: { Node , Tree , TreeCursor } ;
@@ -15,6 +17,35 @@ pub struct ParsedTree {
15
17
tree : Tree ,
16
18
}
17
19
20
+ #[ derive( Default ) ]
21
+ struct DocumentSymbolTreeBuilder {
22
+ // The stack are things we're still in the process of building/parsing.
23
+ stack : Vec < ( usize , DocumentSymbol ) > ,
24
+ // The found are things we've finished processing/parsing, at the top level of the stack.
25
+ found : Vec < DocumentSymbol > ,
26
+ }
27
+ impl DocumentSymbolTreeBuilder {
28
+ fn push ( & mut self , node : usize , symbol : DocumentSymbol ) {
29
+ self . stack . push ( ( node, symbol) ) ;
30
+ }
31
+
32
+ fn maybe_pop ( & mut self , node : usize ) {
33
+ let should_pop = self . stack . last ( ) . map_or ( false , |( n, _) | * n == node) ;
34
+ if should_pop {
35
+ let ( _, explored) = self . stack . pop ( ) . unwrap ( ) ;
36
+ if let Some ( ( _, parent) ) = self . stack . last_mut ( ) {
37
+ parent. children . as_mut ( ) . unwrap ( ) . push ( explored) ;
38
+ } else {
39
+ self . found . push ( explored) ;
40
+ }
41
+ }
42
+ }
43
+
44
+ fn build ( self ) -> Vec < DocumentSymbol > {
45
+ self . found
46
+ }
47
+ }
48
+
18
49
impl ProtoParser {
19
50
pub fn new ( ) -> Self {
20
51
let mut parser = tree_sitter:: Parser :: new ( ) ;
@@ -32,10 +63,7 @@ impl ProtoParser {
32
63
}
33
64
34
65
impl ParsedTree {
35
- fn walk_and_collect_kinds < ' a > (
36
- cursor : & mut TreeCursor < ' a > ,
37
- kinds : & [ & str ] ,
38
- ) -> Vec < Node < ' a > > {
66
+ fn walk_and_collect_kinds < ' a > ( cursor : & mut TreeCursor < ' a > , kinds : & [ & str ] ) -> Vec < Node < ' a > > {
39
67
let mut v = vec ! [ ] ;
40
68
41
69
loop {
@@ -76,12 +104,10 @@ impl ParsedTree {
76
104
}
77
105
}
78
106
79
- fn find_preceeding_comments ( & self , nid : usize , content : impl AsRef < [ u8 ] > ) -> Option < String > {
107
+ fn find_preceding_comments ( & self , nid : usize , content : impl AsRef < [ u8 ] > ) -> Option < String > {
80
108
let root = self . tree . root_node ( ) ;
81
109
let mut cursor = root. walk ( ) ;
82
110
83
- info ! ( "Looking for node with id: {nid}" ) ;
84
-
85
111
Self :: advance_cursor_to ( & mut cursor, nid) ;
86
112
if !cursor. goto_parent ( ) {
87
113
return None ;
@@ -134,6 +160,69 @@ impl ParsedTree {
134
160
Self :: walk_and_collect_kinds ( & mut cursor, kinds)
135
161
}
136
162
163
+ pub fn find_document_locations ( & self , content : impl AsRef < [ u8 ] > ) -> Vec < DocumentSymbol > {
164
+ let mut builder = DocumentSymbolTreeBuilder :: default ( ) ;
165
+ let content = content. as_ref ( ) ;
166
+
167
+ let mut cursor = self . tree . root_node ( ) . walk ( ) ;
168
+
169
+ self . find_document_locations_inner ( & mut builder, & mut cursor, content) ;
170
+
171
+ builder. build ( )
172
+ }
173
+
174
+ fn find_document_locations_inner (
175
+ & self ,
176
+ builder : & mut DocumentSymbolTreeBuilder ,
177
+ cursor : & ' _ mut TreeCursor ,
178
+ content : & [ u8 ] ,
179
+ ) {
180
+ let kinds = & [ "message_name" , "enum_name" ] ;
181
+ loop {
182
+ let node = cursor. node ( ) ;
183
+
184
+ if kinds. contains ( & node. kind ( ) ) {
185
+ let name = node. utf8_text ( content) . unwrap ( ) ;
186
+ let kind = match node. kind ( ) {
187
+ "message_name" => SymbolKind :: STRUCT ,
188
+ "enum_name" => SymbolKind :: ENUM ,
189
+ _ => unreachable ! ( "unsupported symbol kind" ) ,
190
+ } ;
191
+ let detail = self . find_preceding_comments ( node. id ( ) , content) ;
192
+ let message = node. parent ( ) . unwrap ( ) ;
193
+
194
+ let new_symbol = DocumentSymbol {
195
+ name : name. to_string ( ) ,
196
+ detail,
197
+ kind,
198
+ tags : None ,
199
+ deprecated : None ,
200
+ range : Range {
201
+ start : ts_to_lsp_position ( & message. start_position ( ) ) ,
202
+ end : ts_to_lsp_position ( & message. end_position ( ) ) ,
203
+ } ,
204
+ selection_range : Range {
205
+ start : ts_to_lsp_position ( & node. start_position ( ) ) ,
206
+ end : ts_to_lsp_position ( & node. end_position ( ) ) ,
207
+ } ,
208
+ children : Some ( vec ! [ ] ) ,
209
+ } ;
210
+
211
+ builder. push ( message. id ( ) , new_symbol) ;
212
+ }
213
+
214
+ if cursor. goto_first_child ( ) {
215
+ self . find_document_locations_inner ( builder, cursor, content) ;
216
+ builder. maybe_pop ( node. id ( ) ) ;
217
+ cursor. goto_parent ( ) ;
218
+ }
219
+
220
+ if !cursor. goto_next_sibling ( ) {
221
+ break ;
222
+ }
223
+ }
224
+ }
225
+
137
226
pub fn definition (
138
227
& self ,
139
228
pos : & Position ,
@@ -168,7 +257,7 @@ impl ParsedTree {
168
257
. find_childrens_by_kinds ( & [ "message_name" , "enum_name" , "service_name" , "rpc_name" ] )
169
258
. into_iter ( )
170
259
. filter ( |n| n. utf8_text ( content. as_ref ( ) ) . expect ( "utf-8 parse error" ) == text)
171
- . filter_map ( |n| self . find_preceeding_comments ( n. id ( ) , content. as_ref ( ) ) )
260
+ . filter_map ( |n| self . find_preceding_comments ( n. id ( ) , content. as_ref ( ) ) )
172
261
. map ( MarkedString :: String )
173
262
. collect ( ) ,
174
263
None => vec ! [ ] ,
@@ -200,7 +289,9 @@ impl ParsedTree {
200
289
201
290
#[ cfg( test) ]
202
291
mod test {
203
- use async_lsp:: lsp_types:: { DiagnosticSeverity , MarkedString , Position , Range , Url } ;
292
+ use async_lsp:: lsp_types:: {
293
+ DiagnosticSeverity , DocumentSymbol , MarkedString , Position , Range , SymbolKind , Url ,
294
+ } ;
204
295
205
296
use super :: ProtoParser ;
206
297
@@ -335,6 +426,147 @@ Author has a name and a country where they were born"#
335
426
) ;
336
427
}
337
428
429
+ #[ test]
430
+ fn test_document_symbols ( ) {
431
+ let contents = r#"syntax = "proto3";
432
+
433
+ package com.symbols;
434
+
435
+ // outer 1 comment
436
+ message Outer1 {
437
+ message Inner1 {
438
+ string name = 1;
439
+ };
440
+
441
+ Inner1 i = 1;
442
+ }
443
+
444
+ message Outer2 {
445
+ message Inner2 {
446
+ string name = 1;
447
+ };
448
+ // Inner 3 comment here
449
+ message Inner3 {
450
+ string name = 1;
451
+
452
+ enum X {
453
+ a = 1;
454
+ b = 2;
455
+ }
456
+ }
457
+ Inner1 i = 1;
458
+ Inner2 y = 2;
459
+ }
460
+
461
+ "# ;
462
+ let parsed = ProtoParser :: new ( ) . parse ( contents) ;
463
+ assert ! ( parsed. is_some( ) ) ;
464
+ let tree = parsed. unwrap ( ) ;
465
+ let res = tree. find_document_locations ( contents) ;
466
+
467
+ assert_eq ! ( res. len( ) , 2 ) ;
468
+ assert_eq ! (
469
+ res,
470
+ vec!(
471
+ DocumentSymbol {
472
+ name: "Outer1" . to_string( ) ,
473
+ detail: Some ( "outer 1 comment" . to_string( ) ) ,
474
+ kind: SymbolKind :: STRUCT ,
475
+ tags: None ,
476
+ range: Range {
477
+ start: Position :: new( 5 , 0 ) ,
478
+ end: Position :: new( 11 , 1 ) ,
479
+ } ,
480
+ selection_range: Range {
481
+ start: Position :: new( 5 , 8 ) ,
482
+ end: Position :: new( 5 , 14 ) ,
483
+ } ,
484
+ children: Some ( vec!( DocumentSymbol {
485
+ name: "Inner1" . to_string( ) ,
486
+ detail: None ,
487
+ kind: SymbolKind :: STRUCT ,
488
+ tags: None ,
489
+ deprecated: None ,
490
+ range: Range {
491
+ start: Position :: new( 6 , 4 ) ,
492
+ end: Position :: new( 8 , 5 ) ,
493
+ } ,
494
+ selection_range: Range {
495
+ start: Position :: new( 6 , 12 ) ,
496
+ end: Position :: new( 6 , 18 ) ,
497
+ } ,
498
+ children: Some ( vec!( ) ) ,
499
+ } , ) ) ,
500
+ deprecated: None ,
501
+ } ,
502
+ DocumentSymbol {
503
+ name: "Outer2" . to_string( ) ,
504
+ detail: None ,
505
+ kind: SymbolKind :: STRUCT ,
506
+ tags: None ,
507
+ range: Range {
508
+ start: Position :: new( 13 , 0 ) ,
509
+ end: Position :: new( 28 , 1 ) ,
510
+ } ,
511
+ selection_range: Range {
512
+ start: Position :: new( 13 , 8 ) ,
513
+ end: Position :: new( 13 , 14 ) ,
514
+ } ,
515
+ children: Some ( vec!(
516
+ DocumentSymbol {
517
+ name: "Inner2" . to_string( ) ,
518
+ detail: None ,
519
+ kind: SymbolKind :: STRUCT ,
520
+ tags: None ,
521
+ deprecated: None ,
522
+ range: Range {
523
+ start: Position :: new( 14 , 4 ) ,
524
+ end: Position :: new( 16 , 5 ) ,
525
+ } ,
526
+ selection_range: Range {
527
+ start: Position :: new( 14 , 12 ) ,
528
+ end: Position :: new( 14 , 18 ) ,
529
+ } ,
530
+ children: Some ( vec!( ) ) ,
531
+ } ,
532
+ DocumentSymbol {
533
+ name: "Inner3" . to_string( ) ,
534
+ detail: Some ( "Inner 3 comment here" . to_string( ) ) ,
535
+ kind: SymbolKind :: STRUCT ,
536
+ tags: None ,
537
+ deprecated: None ,
538
+ range: Range {
539
+ start: Position :: new( 18 , 4 ) ,
540
+ end: Position :: new( 25 , 5 ) ,
541
+ } ,
542
+ selection_range: Range {
543
+ start: Position :: new( 18 , 12 ) ,
544
+ end: Position :: new( 18 , 18 ) ,
545
+ } ,
546
+ children: Some ( vec!( DocumentSymbol {
547
+ name: "X" . to_string( ) ,
548
+ detail: None ,
549
+ kind: SymbolKind :: ENUM ,
550
+ tags: None ,
551
+ deprecated: None ,
552
+ range: Range {
553
+ start: Position :: new( 21 , 8 ) ,
554
+ end: Position :: new( 24 , 9 ) ,
555
+ } ,
556
+ selection_range: Range {
557
+ start: Position :: new( 21 , 13 ) ,
558
+ end: Position :: new( 21 , 14 ) ,
559
+ } ,
560
+ children: Some ( vec!( ) ) ,
561
+ } ) ) ,
562
+ }
563
+ ) ) ,
564
+ deprecated: None ,
565
+ } ,
566
+ )
567
+ ) ;
568
+ }
569
+
338
570
#[ test]
339
571
fn test_goto_definition ( ) {
340
572
let url = "file://foo/bar.proto" ;
0 commit comments