Skip to main content

merman_editor_core/
diagnostics.rs

1use crate::types::Range;
2use merman_analysis::{
3    AnalysisDiagnostic, AnalysisPayload, DiagnosticCategory, DiagnosticFix, DiagnosticSeverity,
4    DiagnosticSpan,
5};
6use serde::{Deserialize, Serialize};
7
8pub fn analysis_payload_to_diagnostics(payload: &AnalysisPayload) -> Vec<EditorDiagnostic> {
9    payload
10        .diagnostics
11        .iter()
12        .map(analysis_diagnostic_to_editor)
13        .collect()
14}
15
16pub fn analysis_diagnostic_to_editor(diagnostic: &AnalysisDiagnostic) -> EditorDiagnostic {
17    EditorDiagnostic {
18        range: diagnostic
19            .span
20            .as_ref()
21            .map(range_from_span)
22            .unwrap_or_default(),
23        severity: diagnostic.severity,
24        code: diagnostic.id.clone(),
25        source: "merman".to_string(),
26        message: diagnostic.message.clone(),
27        related: diagnostic
28            .related
29            .iter()
30            .filter_map(|related| {
31                Some(EditorDiagnosticRelated {
32                    message: related.message.clone(),
33                    range: related.span.as_ref().map(range_from_span)?,
34                })
35            })
36            .collect(),
37        data: diagnostic_data(diagnostic),
38    }
39}
40
41pub fn diagnostic_data(diagnostic: &AnalysisDiagnostic) -> Option<DiagnosticCodeActionData> {
42    Some(DiagnosticCodeActionData {
43        id: diagnostic.id.clone(),
44        code: diagnostic.code,
45        code_name: diagnostic.code_name.clone(),
46        category: diagnostic.category,
47        diagram_type: diagnostic.diagram_type.clone(),
48        help: diagnostic.help.clone(),
49        fixes: diagnostic.fixes.clone(),
50    })
51}
52
53fn range_from_span(span: &DiagnosticSpan) -> Range {
54    Range::new(
55        crate::types::Position::new(span.lsp_range.start.line, span.lsp_range.start.character),
56        crate::types::Position::new(span.lsp_range.end.line, span.lsp_range.end.character),
57    )
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct EditorDiagnostic {
62    pub range: Range,
63    pub severity: DiagnosticSeverity,
64    pub code: String,
65    pub source: String,
66    pub message: String,
67    pub related: Vec<EditorDiagnosticRelated>,
68    pub data: Option<DiagnosticCodeActionData>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct EditorDiagnosticRelated {
73    pub message: String,
74    pub range: Range,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub struct DiagnosticCodeActionData {
80    pub id: String,
81    #[serde(default, skip_serializing_if = "Option::is_none")]
82    pub code: Option<i32>,
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub code_name: Option<String>,
85    #[serde(default = "default_diagnostic_category")]
86    pub category: DiagnosticCategory,
87    #[serde(default, skip_serializing_if = "Option::is_none")]
88    pub diagram_type: Option<String>,
89    #[serde(default, skip_serializing_if = "Option::is_none")]
90    pub help: Option<String>,
91    #[serde(default, skip_serializing_if = "Vec::is_empty")]
92    pub fixes: Vec<DiagnosticFix>,
93}
94
95fn default_diagnostic_category() -> DiagnosticCategory {
96    DiagnosticCategory::Internal
97}