1use crate::error::{IdeError, IdeResult};
7use crate::provider::IdeProvider;
8use crate::types::*;
9use async_trait::async_trait;
10use tracing::debug;
11
12#[derive(Debug, Clone)]
14pub struct CompletionRule {
15 pub pattern: String,
17 pub suggestions: Vec<CompletionItem>,
19}
20
21#[derive(Debug, Clone)]
23pub struct DiagnosticsRule {
24 pub pattern: String,
26 pub diagnostics: Vec<Diagnostic>,
28}
29
30#[derive(Debug, Clone)]
32pub struct HoverRule {
33 pub pattern: String,
35 pub hover: Hover,
37}
38
39#[derive(Debug, Clone)]
41pub struct DefinitionRule {
42 pub pattern: String,
44 pub location: Location,
46}
47
48pub struct ConfiguredRulesProvider {
50 language: String,
52 completion_rules: Vec<CompletionRule>,
54 diagnostics_rules: Vec<DiagnosticsRule>,
56 hover_rules: Vec<HoverRule>,
58 definition_rules: Vec<DefinitionRule>,
60}
61
62impl ConfiguredRulesProvider {
63 pub fn new(language: String) -> Self {
65 ConfiguredRulesProvider {
66 language,
67 completion_rules: Vec::new(),
68 diagnostics_rules: Vec::new(),
69 hover_rules: Vec::new(),
70 definition_rules: Vec::new(),
71 }
72 }
73
74 pub fn add_completion_rule(&mut self, rule: CompletionRule) {
76 self.completion_rules.push(rule);
77 }
78
79 pub fn add_diagnostics_rule(&mut self, rule: DiagnosticsRule) {
81 self.diagnostics_rules.push(rule);
82 }
83
84 pub fn add_hover_rule(&mut self, rule: HoverRule) {
86 self.hover_rules.push(rule);
87 }
88
89 pub fn add_definition_rule(&mut self, rule: DefinitionRule) {
91 self.definition_rules.push(rule);
92 }
93
94 pub async fn load_from_yaml(language: String, yaml_content: &str) -> IdeResult<Self> {
96 debug!("Loading configured rules from YAML for language: {}", language);
97
98 let mut provider = ConfiguredRulesProvider::new(language);
99
100 let config: serde_yaml::Value = serde_yaml::from_str(yaml_content).map_err(|e| {
102 IdeError::config_error(format!("Failed to parse YAML rules: {}", e))
103 })?;
104
105 if let Some(completions) = config.get("completions").and_then(|v| v.as_sequence()) {
107 for completion in completions {
108 if let Ok(rule) = Self::parse_completion_rule(completion) {
109 provider.add_completion_rule(rule);
110 }
111 }
112 }
113
114 if let Some(diagnostics) = config.get("diagnostics").and_then(|v| v.as_sequence()) {
116 for diagnostic in diagnostics {
117 if let Ok(rule) = Self::parse_diagnostics_rule(diagnostic) {
118 provider.add_diagnostics_rule(rule);
119 }
120 }
121 }
122
123 if let Some(hovers) = config.get("hovers").and_then(|v| v.as_sequence()) {
125 for hover in hovers {
126 if let Ok(rule) = Self::parse_hover_rule(hover) {
127 provider.add_hover_rule(rule);
128 }
129 }
130 }
131
132 if let Some(definitions) = config.get("definitions").and_then(|v| v.as_sequence()) {
134 for definition in definitions {
135 if let Ok(rule) = Self::parse_definition_rule(definition) {
136 provider.add_definition_rule(rule);
137 }
138 }
139 }
140
141 Ok(provider)
142 }
143
144 pub async fn load_from_json(language: String, json_content: &str) -> IdeResult<Self> {
146 debug!("Loading configured rules from JSON for language: {}", language);
147
148 let mut provider = ConfiguredRulesProvider::new(language);
149
150 let config: serde_json::Value = serde_json::from_str(json_content).map_err(|e| {
152 IdeError::config_error(format!("Failed to parse JSON rules: {}", e))
153 })?;
154
155 let yaml_config: serde_yaml::Value = serde_yaml::from_str(&serde_json::to_string(&config).unwrap()).map_err(|e| {
157 IdeError::config_error(format!("Failed to convert JSON to YAML: {}", e))
158 })?;
159
160 if let Some(completions) = yaml_config.get("completions").and_then(|v| v.as_sequence()) {
162 for completion in completions {
163 if let Ok(rule) = Self::parse_completion_rule(completion) {
164 provider.add_completion_rule(rule);
165 }
166 }
167 }
168
169 if let Some(diagnostics) = yaml_config.get("diagnostics").and_then(|v| v.as_sequence()) {
171 for diagnostic in diagnostics {
172 if let Ok(rule) = Self::parse_diagnostics_rule(diagnostic) {
173 provider.add_diagnostics_rule(rule);
174 }
175 }
176 }
177
178 if let Some(hovers) = yaml_config.get("hovers").and_then(|v| v.as_sequence()) {
180 for hover in hovers {
181 if let Ok(rule) = Self::parse_hover_rule(hover) {
182 provider.add_hover_rule(rule);
183 }
184 }
185 }
186
187 if let Some(definitions) = yaml_config.get("definitions").and_then(|v| v.as_sequence()) {
189 for definition in definitions {
190 if let Ok(rule) = Self::parse_definition_rule(definition) {
191 provider.add_definition_rule(rule);
192 }
193 }
194 }
195
196 Ok(provider)
197 }
198
199 fn parse_completion_rule(value: &serde_yaml::Value) -> IdeResult<CompletionRule> {
201 let pattern = value
202 .get("pattern")
203 .and_then(|v| v.as_str())
204 .ok_or_else(|| IdeError::config_error("Completion rule missing 'pattern' field"))?
205 .to_string();
206
207 let suggestions = value
208 .get("suggestions")
209 .and_then(|v| v.as_sequence())
210 .map(|seq| {
211 seq.iter()
212 .filter_map(|item| {
213 let label = item.get("label")?.as_str()?.to_string();
214 let kind = item
215 .get("kind")
216 .and_then(|k| k.as_str())
217 .and_then(Self::parse_completion_kind)
218 .unwrap_or(CompletionItemKind::Text);
219 let detail = item.get("detail").and_then(|d| d.as_str()).map(|s| s.to_string());
220 let documentation = item
221 .get("documentation")
222 .and_then(|d| d.as_str())
223 .map(|s| s.to_string());
224 let insert_text = item
225 .get("insertText")
226 .and_then(|t| t.as_str())
227 .unwrap_or(&label)
228 .to_string();
229
230 Some(CompletionItem {
231 label,
232 kind,
233 detail,
234 documentation,
235 insert_text,
236 })
237 })
238 .collect()
239 })
240 .unwrap_or_default();
241
242 Ok(CompletionRule { pattern, suggestions })
243 }
244
245 fn parse_diagnostics_rule(value: &serde_yaml::Value) -> IdeResult<DiagnosticsRule> {
247 let pattern = value
248 .get("pattern")
249 .and_then(|v| v.as_str())
250 .ok_or_else(|| IdeError::config_error("Diagnostics rule missing 'pattern' field"))?
251 .to_string();
252
253 let diagnostics = value
254 .get("diagnostics")
255 .and_then(|v| v.as_sequence())
256 .map(|seq| {
257 seq.iter()
258 .filter_map(|item| {
259 let message = item.get("message")?.as_str()?.to_string();
260 let severity = item
261 .get("severity")
262 .and_then(|s| s.as_str())
263 .and_then(Self::parse_severity)
264 .unwrap_or(DiagnosticSeverity::Information);
265 let source = item
266 .get("source")
267 .and_then(|s| s.as_str())
268 .unwrap_or("configured-rules")
269 .to_string();
270 let range = Range {
271 start: Position {
272 line: 0,
273 character: 0,
274 },
275 end: Position {
276 line: 0,
277 character: 0,
278 },
279 };
280
281 Some(Diagnostic {
282 range,
283 severity,
284 message,
285 source,
286 })
287 })
288 .collect()
289 })
290 .unwrap_or_default();
291
292 Ok(DiagnosticsRule { pattern, diagnostics })
293 }
294
295 fn parse_hover_rule(value: &serde_yaml::Value) -> IdeResult<HoverRule> {
297 let pattern = value
298 .get("pattern")
299 .and_then(|v| v.as_str())
300 .ok_or_else(|| IdeError::config_error("Hover rule missing 'pattern' field"))?
301 .to_string();
302
303 let contents = value
304 .get("contents")
305 .and_then(|v| v.as_str())
306 .ok_or_else(|| IdeError::config_error("Hover rule missing 'contents' field"))?
307 .to_string();
308
309 let hover = Hover {
310 contents,
311 range: None,
312 };
313
314 Ok(HoverRule { pattern, hover })
315 }
316
317 fn parse_definition_rule(value: &serde_yaml::Value) -> IdeResult<DefinitionRule> {
319 let pattern = value
320 .get("pattern")
321 .and_then(|v| v.as_str())
322 .ok_or_else(|| IdeError::config_error("Definition rule missing 'pattern' field"))?
323 .to_string();
324
325 let file_path = value
326 .get("file")
327 .and_then(|v| v.as_str())
328 .ok_or_else(|| IdeError::config_error("Definition rule missing 'file' field"))?
329 .to_string();
330
331 let line = value
332 .get("line")
333 .and_then(|v| v.as_u64())
334 .unwrap_or(0) as u32;
335
336 let character = value
337 .get("character")
338 .and_then(|v| v.as_u64())
339 .unwrap_or(0) as u32;
340
341 let location = Location {
342 file_path,
343 range: Range {
344 start: Position { line, character },
345 end: Position { line, character },
346 },
347 };
348
349 Ok(DefinitionRule { pattern, location })
350 }
351
352 fn parse_completion_kind(kind_str: &str) -> Option<CompletionItemKind> {
354 match kind_str.to_lowercase().as_str() {
355 "text" => Some(CompletionItemKind::Text),
356 "method" => Some(CompletionItemKind::Method),
357 "function" => Some(CompletionItemKind::Function),
358 "constructor" => Some(CompletionItemKind::Constructor),
359 "field" => Some(CompletionItemKind::Field),
360 "variable" => Some(CompletionItemKind::Variable),
361 "class" => Some(CompletionItemKind::Class),
362 "interface" => Some(CompletionItemKind::Interface),
363 "module" => Some(CompletionItemKind::Module),
364 "property" => Some(CompletionItemKind::Property),
365 "unit" => Some(CompletionItemKind::Unit),
366 "value" => Some(CompletionItemKind::Value),
367 "enum" => Some(CompletionItemKind::Enum),
368 "keyword" => Some(CompletionItemKind::Keyword),
369 "snippet" => Some(CompletionItemKind::Snippet),
370 "color" => Some(CompletionItemKind::Color),
371 "file" => Some(CompletionItemKind::File),
372 "reference" => Some(CompletionItemKind::Reference),
373 "folder" => Some(CompletionItemKind::Folder),
374 "enummember" => Some(CompletionItemKind::EnumMember),
375 "constant" => Some(CompletionItemKind::Constant),
376 "struct" => Some(CompletionItemKind::Struct),
377 "event" => Some(CompletionItemKind::Event),
378 "operator" => Some(CompletionItemKind::Operator),
379 "typeparameter" => Some(CompletionItemKind::TypeParameter),
380 _ => None,
381 }
382 }
383
384 fn parse_severity(severity_str: &str) -> Option<DiagnosticSeverity> {
386 match severity_str.to_lowercase().as_str() {
387 "error" => Some(DiagnosticSeverity::Error),
388 "warning" => Some(DiagnosticSeverity::Warning),
389 "information" | "info" => Some(DiagnosticSeverity::Information),
390 "hint" => Some(DiagnosticSeverity::Hint),
391 _ => None,
392 }
393 }
394
395 fn matches_pattern(&self, pattern: &str, context: &str) -> bool {
397 context.contains(pattern)
398 }
399}
400
401#[async_trait]
402impl IdeProvider for ConfiguredRulesProvider {
403 async fn get_completions(&self, params: &CompletionParams) -> IdeResult<Vec<CompletionItem>> {
404 debug!(
405 "Getting completions from configured rules for language: {}",
406 self.language
407 );
408
409 let mut completions = Vec::new();
410
411 for rule in &self.completion_rules {
412 if self.matches_pattern(&rule.pattern, ¶ms.context) {
413 completions.extend(rule.suggestions.clone());
414 }
415 }
416
417 Ok(completions)
418 }
419
420 async fn get_diagnostics(&self, params: &DiagnosticsParams) -> IdeResult<Vec<Diagnostic>> {
421 debug!(
422 "Getting diagnostics from configured rules for language: {}",
423 self.language
424 );
425
426 let mut diagnostics = Vec::new();
427
428 for rule in &self.diagnostics_rules {
429 if self.matches_pattern(&rule.pattern, ¶ms.source) {
430 diagnostics.extend(rule.diagnostics.clone());
431 }
432 }
433
434 Ok(diagnostics)
435 }
436
437 async fn get_hover(&self, _params: &HoverParams) -> IdeResult<Option<Hover>> {
438 debug!(
439 "Getting hover from configured rules for language: {}",
440 self.language
441 );
442
443 Ok(None)
446 }
447
448 async fn get_definition(&self, _params: &DefinitionParams) -> IdeResult<Option<Location>> {
449 debug!(
450 "Getting definition from configured rules for language: {}",
451 self.language
452 );
453
454 Ok(None)
457 }
458
459 fn is_available(&self, language: &str) -> bool {
460 language == self.language
461 }
462
463 fn name(&self) -> &str {
464 "configured-rules"
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn test_parse_completion_kind() {
474 assert_eq!(
475 ConfiguredRulesProvider::parse_completion_kind("function"),
476 Some(CompletionItemKind::Function)
477 );
478 assert_eq!(
479 ConfiguredRulesProvider::parse_completion_kind("class"),
480 Some(CompletionItemKind::Class)
481 );
482 assert_eq!(
483 ConfiguredRulesProvider::parse_completion_kind("unknown"),
484 None
485 );
486 }
487
488 #[test]
489 fn test_parse_severity() {
490 assert_eq!(
491 ConfiguredRulesProvider::parse_severity("error"),
492 Some(DiagnosticSeverity::Error)
493 );
494 assert_eq!(
495 ConfiguredRulesProvider::parse_severity("warning"),
496 Some(DiagnosticSeverity::Warning)
497 );
498 assert_eq!(
499 ConfiguredRulesProvider::parse_severity("unknown"),
500 None
501 );
502 }
503
504 #[test]
505 fn test_new_provider() {
506 let provider = ConfiguredRulesProvider::new("rust".to_string());
507 assert_eq!(provider.language, "rust");
508 assert!(provider.completion_rules.is_empty());
509 }
510
511 #[test]
512 fn test_add_completion_rule() {
513 let mut provider = ConfiguredRulesProvider::new("rust".to_string());
514 let rule = CompletionRule {
515 pattern: "fn ".to_string(),
516 suggestions: vec![CompletionItem {
517 label: "test".to_string(),
518 kind: CompletionItemKind::Function,
519 detail: None,
520 documentation: None,
521 insert_text: "test()".to_string(),
522 }],
523 };
524
525 provider.add_completion_rule(rule);
526 assert_eq!(provider.completion_rules.len(), 1);
527 }
528
529 #[test]
530 fn test_matches_pattern() {
531 let provider = ConfiguredRulesProvider::new("rust".to_string());
532 assert!(provider.matches_pattern("fn ", "fn test() {"));
533 assert!(!provider.matches_pattern("fn ", "let x = 5;"));
534 }
535
536 #[tokio::test]
537 async fn test_get_completions_with_matching_rule() {
538 let mut provider = ConfiguredRulesProvider::new("rust".to_string());
539 let rule = CompletionRule {
540 pattern: "fn ".to_string(),
541 suggestions: vec![CompletionItem {
542 label: "test".to_string(),
543 kind: CompletionItemKind::Function,
544 detail: None,
545 documentation: None,
546 insert_text: "test()".to_string(),
547 }],
548 };
549
550 provider.add_completion_rule(rule);
551
552 let params = CompletionParams {
553 language: "rust".to_string(),
554 file_path: "src/main.rs".to_string(),
555 position: Position {
556 line: 10,
557 character: 5,
558 },
559 context: "fn test".to_string(),
560 };
561
562 let result = provider.get_completions(¶ms).await;
563 assert!(result.is_ok());
564 assert_eq!(result.unwrap().len(), 1);
565 }
566
567 #[tokio::test]
568 async fn test_get_diagnostics_with_matching_rule() {
569 let mut provider = ConfiguredRulesProvider::new("rust".to_string());
570 let rule = DiagnosticsRule {
571 pattern: "unused".to_string(),
572 diagnostics: vec![Diagnostic {
573 range: Range {
574 start: Position {
575 line: 0,
576 character: 0,
577 },
578 end: Position {
579 line: 0,
580 character: 0,
581 },
582 },
583 severity: DiagnosticSeverity::Warning,
584 message: "unused variable".to_string(),
585 source: "configured-rules".to_string(),
586 }],
587 };
588
589 provider.add_diagnostics_rule(rule);
590
591 let params = DiagnosticsParams {
592 language: "rust".to_string(),
593 file_path: "src/main.rs".to_string(),
594 source: "let unused = 5;".to_string(),
595 };
596
597 let result = provider.get_diagnostics(¶ms).await;
598 assert!(result.is_ok());
599 assert_eq!(result.unwrap().len(), 1);
600 }
601
602 #[tokio::test]
603 async fn test_is_available() {
604 let provider = ConfiguredRulesProvider::new("rust".to_string());
605 assert!(provider.is_available("rust"));
606 assert!(!provider.is_available("typescript"));
607 }
608}