1#![warn(missing_docs)]
2use std::time::Duration;
25
26#[derive(Debug, Clone)]
31pub struct LspLimits {
32 pub workspace_symbol_cap: usize,
37
38 pub references_cap: usize,
40
41 pub completion_cap: usize,
43
44 pub document_symbol_cap: usize,
46
47 pub code_lens_cap: usize,
49
50 pub diagnostics_per_file_cap: usize,
52
53 pub inlay_hints_cap: usize,
55
56 pub ast_cache_max_entries: usize,
61
62 pub ast_cache_ttl_secs: u64,
64
65 pub symbol_cache_max_entries: usize,
67
68 pub max_indexed_files: usize,
73
74 pub max_symbols_per_file: usize,
76
77 pub max_total_symbols: usize,
79
80 pub parse_storm_threshold: usize,
82
83 pub max_file_size_bytes: usize,
88
89 pub workspace_scan_deadline: Duration,
94
95 pub file_index_deadline: Duration,
97
98 pub reference_search_deadline: Duration,
100
101 pub regex_scan_deadline: Duration,
103
104 pub fs_operation_deadline: Duration,
106
107 pub semantic_tokens_deadline: Duration,
109
110 pub code_lens_resolve_deadline: Duration,
112
113 pub completion_deadline: Duration,
115
116 pub return_partial_on_timeout: bool,
121
122 pub include_open_docs_when_degraded: bool,
124}
125
126impl Default for LspLimits {
127 fn default() -> Self {
128 Self {
129 workspace_symbol_cap: 200,
131 references_cap: 500,
132 completion_cap: 100,
133 document_symbol_cap: 500,
134 code_lens_cap: 100,
135 diagnostics_per_file_cap: 200,
136 inlay_hints_cap: 500,
137
138 ast_cache_max_entries: 100,
140 ast_cache_ttl_secs: 300,
141 symbol_cache_max_entries: 1000,
142
143 max_indexed_files: 10_000,
145 max_symbols_per_file: 5_000,
146 max_total_symbols: 500_000,
147 parse_storm_threshold: 10,
148 max_file_size_bytes: 1_024 * 1_024, workspace_scan_deadline: Duration::from_secs(30),
152 file_index_deadline: Duration::from_secs(5),
153 reference_search_deadline: Duration::from_secs(2),
154 regex_scan_deadline: Duration::from_secs(1),
155 fs_operation_deadline: Duration::from_millis(500),
156 semantic_tokens_deadline: Duration::from_secs(2),
157 code_lens_resolve_deadline: Duration::from_secs(1),
158 completion_deadline: Duration::from_millis(500),
159
160 return_partial_on_timeout: true,
162 include_open_docs_when_degraded: true,
163 }
164 }
165}
166
167impl LspLimits {
168 pub fn large_workspace() -> Self {
170 Self {
171 max_indexed_files: 50_000,
172 max_total_symbols: 2_000_000,
173 workspace_scan_deadline: Duration::from_secs(120),
174 ..Default::default()
175 }
176 }
177
178 pub fn constrained() -> Self {
180 Self {
181 ast_cache_max_entries: 50,
182 max_indexed_files: 5_000,
183 max_total_symbols: 100_000,
184 workspace_scan_deadline: Duration::from_secs(15),
185 reference_search_deadline: Duration::from_secs(1),
186 ..Default::default()
187 }
188 }
189
190 pub fn update_from_value(&mut self, settings: &serde_json::Value) {
194 if let Some(limits) = settings.get("limits") {
195 if let Some(v) = limits.get("workspaceSymbolCap").and_then(|v| v.as_u64()) {
197 self.workspace_symbol_cap = v as usize;
198 }
199 if let Some(v) = limits.get("referencesCap").and_then(|v| v.as_u64()) {
200 self.references_cap = v as usize;
201 }
202 if let Some(v) = limits.get("completionCap").and_then(|v| v.as_u64()) {
203 self.completion_cap = v as usize;
204 }
205
206 if let Some(v) = limits.get("astCacheMaxEntries").and_then(|v| v.as_u64()) {
208 self.ast_cache_max_entries = v as usize;
209 }
210
211 if let Some(v) = limits.get("maxIndexedFiles").and_then(|v| v.as_u64()) {
213 self.max_indexed_files = v as usize;
214 }
215 if let Some(v) = limits.get("maxTotalSymbols").and_then(|v| v.as_u64()) {
216 self.max_total_symbols = v as usize;
217 }
218
219 if let Some(v) = limits.get("maxFileSizeBytes").and_then(|v| v.as_u64()) {
221 self.max_file_size_bytes = v as usize;
222 }
223
224 if let Some(v) = limits.get("workspaceScanDeadlineMs").and_then(|v| v.as_u64()) {
226 self.workspace_scan_deadline = Duration::from_millis(v);
227 }
228 if let Some(v) = limits.get("referenceSearchDeadlineMs").and_then(|v| v.as_u64()) {
229 self.reference_search_deadline = Duration::from_millis(v);
230 }
231 }
232 }
233}
234
235pub static LSP_LIMITS: std::sync::LazyLock<std::sync::RwLock<LspLimits>> =
240 std::sync::LazyLock::new(|| std::sync::RwLock::new(LspLimits::default()));
241
242#[inline]
244pub fn workspace_symbol_cap() -> usize {
245 LSP_LIMITS.read().map(|l| l.workspace_symbol_cap).unwrap_or(200)
246}
247
248#[inline]
250pub fn references_cap() -> usize {
251 LSP_LIMITS.read().map(|l| l.references_cap).unwrap_or(500)
252}
253
254#[inline]
256pub fn completion_cap() -> usize {
257 LSP_LIMITS.read().map(|l| l.completion_cap).unwrap_or(100)
258}
259
260#[inline]
262pub fn reference_search_deadline() -> Duration {
263 LSP_LIMITS.read().map(|l| l.reference_search_deadline).unwrap_or(Duration::from_secs(2))
264}
265
266#[inline]
268pub fn regex_scan_deadline() -> Duration {
269 LSP_LIMITS.read().map(|l| l.regex_scan_deadline).unwrap_or(Duration::from_secs(1))
270}
271
272#[inline]
274pub fn code_lens_cap() -> usize {
275 LSP_LIMITS.read().map(|l| l.code_lens_cap).unwrap_or(100)
276}
277
278#[inline]
280pub fn document_symbol_cap() -> usize {
281 LSP_LIMITS.read().map(|l| l.document_symbol_cap).unwrap_or(500)
282}
283
284#[inline]
286pub fn semantic_tokens_deadline() -> Duration {
287 LSP_LIMITS.read().map(|l| l.semantic_tokens_deadline).unwrap_or(Duration::from_secs(2))
288}
289
290#[inline]
292pub fn code_lens_resolve_deadline() -> Duration {
293 LSP_LIMITS.read().map(|l| l.code_lens_resolve_deadline).unwrap_or(Duration::from_secs(1))
294}
295
296#[inline]
298pub fn completion_deadline() -> Duration {
299 LSP_LIMITS.read().map(|l| l.completion_deadline).unwrap_or(Duration::from_millis(500))
300}
301
302#[inline]
304pub fn inlay_hints_cap() -> usize {
305 LSP_LIMITS.read().map(|l| l.inlay_hints_cap).unwrap_or(500)
306}
307
308#[inline]
310pub fn diagnostics_per_file_cap() -> usize {
311 LSP_LIMITS.read().map(|l| l.diagnostics_per_file_cap).unwrap_or(200)
312}
313
314#[inline]
316pub fn max_file_size_bytes() -> usize {
317 LSP_LIMITS.read().map(|l| l.max_file_size_bytes).unwrap_or(1_024 * 1_024)
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn test_default_limits() {
326 let limits = LspLimits::default();
327 assert_eq!(limits.workspace_symbol_cap, 200);
328 assert_eq!(limits.references_cap, 500);
329 assert_eq!(limits.max_indexed_files, 10_000);
330 assert_eq!(limits.max_file_size_bytes, 1_024 * 1_024);
331 }
332
333 #[test]
334 fn test_large_workspace_limits() {
335 let limits = LspLimits::large_workspace();
336 assert_eq!(limits.max_indexed_files, 50_000);
337 assert_eq!(limits.max_total_symbols, 2_000_000);
338 }
339
340 #[test]
341 fn test_constrained_limits() {
342 let limits = LspLimits::constrained();
343 assert_eq!(limits.max_indexed_files, 5_000);
344 assert_eq!(limits.ast_cache_max_entries, 50);
345 }
346
347 #[test]
348 fn test_update_from_value() {
349 let mut limits = LspLimits::default();
350 let settings = serde_json::json!({
351 "limits": {
352 "workspaceSymbolCap": 300,
353 "maxIndexedFiles": 20000
354 }
355 });
356 limits.update_from_value(&settings);
357 assert_eq!(limits.workspace_symbol_cap, 300);
358 assert_eq!(limits.max_indexed_files, 20_000);
359 }
360}