1
- use std:: { collections:: HashMap , fs:: read_to_string} ;
1
+ use std:: {
2
+ collections:: HashMap ,
3
+ fs:: read_to_string,
4
+ sync:: { mpsc:: Sender , Arc , Mutex , MutexGuard , RwLock , RwLockWriteGuard } ,
5
+ thread,
6
+ } ;
2
7
use tracing:: { error, info} ;
3
8
4
9
use async_lsp:: lsp_types:: {
5
- CompletionItem , CompletionItemKind , PublishDiagnosticsParams , Url , WorkspaceFolder ,
10
+ CompletionItem , CompletionItemKind , ProgressParamsValue , PublishDiagnosticsParams , Url ,
11
+ WorkDoneProgress , WorkDoneProgressBegin , WorkDoneProgressEnd , WorkDoneProgressReport ,
12
+ WorkspaceFolder ,
6
13
} ;
7
14
use tree_sitter:: Node ;
8
15
use walkdir:: WalkDir ;
@@ -13,79 +20,146 @@ use crate::{
13
20
} ;
14
21
15
22
pub struct ProtoLanguageState {
16
- documents : HashMap < Url , String > ,
17
- pub trees : HashMap < Url , ParsedTree > ,
18
- parser : ProtoParser ,
23
+ documents : Arc < RwLock < HashMap < Url , String > > > ,
24
+ trees : Arc < RwLock < HashMap < Url , ParsedTree > > > ,
25
+ parser : Arc < Mutex < ProtoParser > > ,
19
26
}
20
27
21
28
impl ProtoLanguageState {
22
29
pub fn new ( ) -> Self {
23
30
ProtoLanguageState {
24
31
documents : Default :: default ( ) ,
25
32
trees : Default :: default ( ) ,
26
- parser : ProtoParser :: new ( ) ,
33
+ parser : Arc :: new ( Mutex :: new ( ProtoParser :: new ( ) ) ) ,
27
34
}
28
35
}
29
36
30
- pub fn get_content ( & self , uri : & Url ) -> & str {
37
+ pub fn get_content ( & self , uri : & Url ) -> String {
31
38
self . documents
39
+ . read ( )
40
+ . expect ( "poison" )
32
41
. get ( uri)
33
- . map ( |s| s. as_str ( ) )
42
+ . map ( |s| s. to_string ( ) )
34
43
. unwrap_or_default ( )
35
44
}
36
45
37
- pub fn get_tree ( & self , uri : & Url ) -> Option < & ParsedTree > {
38
- self . trees . get ( uri)
46
+ pub fn get_tree ( & self , uri : & Url ) -> Option < ParsedTree > {
47
+ self . trees . read ( ) . expect ( "poison" ) . get ( uri) . cloned ( )
39
48
}
40
49
41
- pub fn get_trees_for_package ( & self , package : & str ) -> Vec < & ParsedTree > {
50
+ pub fn get_trees_for_package ( & self , package : & str ) -> Vec < ParsedTree > {
42
51
self . trees
52
+ . read ( )
53
+ . expect ( "poison" )
43
54
. values ( )
44
55
. filter ( |tree| {
45
56
let content = self . get_content ( & tree. uri ) ;
46
57
tree. get_package_name ( content. as_bytes ( ) )
47
58
. unwrap_or_default ( )
48
59
== package
49
60
} )
61
+ . map ( ToOwned :: to_owned)
50
62
. collect ( )
51
63
}
52
64
53
- pub fn upsert_content ( & mut self , uri : & Url , content : String ) -> bool {
54
- if let Some ( parsed) = self . parser . parse ( uri. clone ( ) , content. as_bytes ( ) ) {
55
- self . trees . insert ( uri. clone ( ) , parsed) ;
56
- self . documents . insert ( uri. clone ( ) , content) ;
65
+ fn upsert_content_impl (
66
+ mut parser : MutexGuard < ProtoParser > ,
67
+ uri : & Url ,
68
+ content : String ,
69
+ mut docs : RwLockWriteGuard < HashMap < Url , String > > ,
70
+ mut trees : RwLockWriteGuard < HashMap < Url , ParsedTree > > ,
71
+ ) -> bool {
72
+ if let Some ( parsed) = parser. parse ( uri. clone ( ) , content. as_bytes ( ) ) {
73
+ trees. insert ( uri. clone ( ) , parsed) ;
74
+ docs. insert ( uri. clone ( ) , content) ;
57
75
true
58
76
} else {
59
- error ! ( uri=%uri, "failed to parse content" ) ;
60
77
false
61
78
}
62
79
}
63
80
64
- pub fn add_workspace_folder ( & mut self , workspace : WorkspaceFolder ) {
65
- for entry in WalkDir :: new ( workspace. uri . path ( ) )
66
- . into_iter ( )
67
- . filter_map ( |e| e. ok ( ) )
68
- {
69
- let path = entry. path ( ) ;
70
- if path. is_absolute ( ) && path. is_file ( ) {
71
- let Some ( ext) = path. extension ( ) else {
72
- continue ;
73
- } ;
74
-
75
- let Ok ( content) = read_to_string ( path) else {
76
- continue ;
77
- } ;
78
-
79
- let Ok ( uri) = Url :: from_file_path ( path) else {
80
- continue ;
81
- } ;
82
-
83
- if ext == "proto" {
84
- let r = self . upsert_content ( & uri, content) ;
85
- info ! ( "workspace parse file: {}, result: {}" , path. display( ) , r) ;
81
+ pub fn upsert_content ( & mut self , uri : & Url , content : String ) -> bool {
82
+ let parser = self . parser . lock ( ) . expect ( "poison" ) ;
83
+ let tree = self . trees . write ( ) . expect ( "poison" ) ;
84
+ let docs = self . documents . write ( ) . expect ( "poison" ) ;
85
+ Self :: upsert_content_impl ( parser, uri, content, docs, tree)
86
+ }
87
+
88
+ pub fn add_workspace_folder_async (
89
+ & mut self ,
90
+ workspace : WorkspaceFolder ,
91
+ tx : Sender < ProgressParamsValue > ,
92
+ ) {
93
+ let parser = self . parser . clone ( ) ;
94
+ let tree = self . trees . clone ( ) ;
95
+ let docs = self . documents . clone ( ) ;
96
+
97
+ let begin = ProgressParamsValue :: WorkDone ( WorkDoneProgress :: Begin ( WorkDoneProgressBegin {
98
+ title : String :: from ( "indexing" ) ,
99
+ cancellable : Some ( false ) ,
100
+ percentage : Some ( 0 ) ,
101
+ ..Default :: default ( )
102
+ } ) ) ;
103
+
104
+ if let Err ( e) = tx. send ( begin) {
105
+ error ! ( error=%e, "failed to send work begin progress" ) ;
106
+ }
107
+
108
+ thread:: spawn ( move || {
109
+ let files: Vec < _ > = WalkDir :: new ( workspace. uri . path ( ) )
110
+ . into_iter ( )
111
+ . filter_map ( |e| e. ok ( ) )
112
+ . filter ( |e| e. path ( ) . extension ( ) . is_some ( ) )
113
+ . filter ( |e| e. path ( ) . extension ( ) . unwrap ( ) == "proto" )
114
+ . collect ( ) ;
115
+
116
+ let total_files = files. len ( ) ;
117
+ let mut current = 0 ;
118
+
119
+ for file in files. into_iter ( ) {
120
+ let path = file. path ( ) ;
121
+ if path. is_absolute ( ) && path. is_file ( ) {
122
+ let Ok ( content) = read_to_string ( path) else {
123
+ continue ;
124
+ } ;
125
+
126
+ let Ok ( uri) = Url :: from_file_path ( path) else {
127
+ continue ;
128
+ } ;
129
+
130
+ Self :: upsert_content_impl (
131
+ parser. lock ( ) . expect ( "poison" ) ,
132
+ & uri,
133
+ content,
134
+ docs. write ( ) . expect ( "poison" ) ,
135
+ tree. write ( ) . expect ( "poison" ) ,
136
+ ) ;
137
+
138
+ current += 1 ;
139
+
140
+ let report = ProgressParamsValue :: WorkDone ( WorkDoneProgress :: Report (
141
+ WorkDoneProgressReport {
142
+ cancellable : Some ( false ) ,
143
+ message : Some ( path. display ( ) . to_string ( ) ) ,
144
+ percentage : Some ( ( current * 100 / total_files) as u32 ) ,
145
+ } ,
146
+ ) ) ;
147
+
148
+ if let Err ( e) = tx. send ( report) {
149
+ error ! ( error=%e, "failed to send work report progress" ) ;
150
+ }
86
151
}
87
152
}
88
- }
153
+ let report =
154
+ ProgressParamsValue :: WorkDone ( WorkDoneProgress :: End ( WorkDoneProgressEnd {
155
+ message : Some ( String :: from ( "completed" ) ) ,
156
+ } ) ) ;
157
+
158
+ info ! ( len = total_files, "workspace file parsing completed" ) ;
159
+ if let Err ( e) = tx. send ( report) {
160
+ error ! ( error=%e, "failed to send work completed result" ) ;
161
+ }
162
+ } ) ;
89
163
}
90
164
91
165
pub fn upsert_file ( & mut self , uri : & Url , content : String ) -> Option < PublishDiagnosticsParams > {
@@ -96,20 +170,26 @@ impl ProtoLanguageState {
96
170
97
171
pub fn delete_file ( & mut self , uri : & Url ) {
98
172
info ! ( uri=%uri, "deleting file" ) ;
99
- self . documents . remove ( uri) ;
100
- self . trees . remove ( uri) ;
173
+ self . documents . write ( ) . expect ( "poison" ) . remove ( uri) ;
174
+ self . trees . write ( ) . expect ( "poison" ) . remove ( uri) ;
101
175
}
102
176
103
177
pub fn rename_file ( & mut self , new_uri : & Url , old_uri : & Url ) {
104
178
info ! ( new_uri=%new_uri, old_uri=%new_uri, "renaming file" ) ;
105
179
106
- if let Some ( v) = self . documents . remove ( old_uri) {
107
- self . documents . insert ( new_uri. clone ( ) , v) ;
180
+ if let Some ( v) = self . documents . write ( ) . expect ( "poison" ) . remove ( old_uri) {
181
+ self . documents
182
+ . write ( )
183
+ . expect ( "poison" )
184
+ . insert ( new_uri. clone ( ) , v) ;
108
185
}
109
186
110
- if let Some ( mut v) = self . trees . remove ( old_uri) {
187
+ if let Some ( mut v) = self . trees . write ( ) . expect ( "poison" ) . remove ( old_uri) {
111
188
v. uri = new_uri. clone ( ) ;
112
- self . trees . insert ( new_uri. clone ( ) , v) ;
189
+ self . trees
190
+ . write ( )
191
+ . expect ( "poison" )
192
+ . insert ( new_uri. clone ( ) , v) ;
113
193
}
114
194
}
115
195
0 commit comments