Skip to main content

rantlr_core/
fix_hint.rs

1//! Structured auto-fix payloads for the linter / playground / LSP.
2
3use serde::Serialize;
4
5use crate::span::Span;
6
7/// A concrete rewrite the UI can preview and apply via Monaco `executeEdits`.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
9#[serde(rename_all = "camelCase")]
10pub struct FixHint {
11    pub title: String,
12    /// Human explanation in plain “dev-speak”.
13    pub explanation: String,
14    /// Current source slice that will be replaced.
15    pub before: String,
16    /// Suggested replacement source (same as [`Self::suggestion`]).
17    pub after: String,
18    /// Exact string to splice over `[start, end)` — preferred by Smart Fix Pro.
19    pub suggestion: String,
20    /// Inclusive-exclusive UTF-8 byte range into the grammar source.
21    pub start: u32,
22    pub end: u32,
23    /// 1-based line/column range covering the same bytes (for Monaco).
24    pub start_line: u32,
25    pub start_col: u32,
26    pub end_line: u32,
27    pub end_col: u32,
28}
29
30impl FixHint {
31    pub fn new(
32        title: impl Into<String>,
33        explanation: impl Into<String>,
34        before: impl Into<String>,
35        after: impl Into<String>,
36        span: Span,
37        source: &str,
38    ) -> Self {
39        let after = after.into();
40        let (start_line, start_col) = span.line_col(source);
41        let end_off = span.end.as_usize().min(source.len());
42        let (end_line, end_col) = Span::from_offsets(end_off, end_off).line_col(source);
43        Self {
44            title: title.into(),
45            explanation: explanation.into(),
46            before: before.into(),
47            suggestion: after.clone(),
48            after,
49            start: span.start.0,
50            end: span.end.0,
51            start_line: start_line as u32,
52            start_col: start_col as u32,
53            end_line: end_line as u32,
54            end_col: end_col.max(1) as u32,
55        }
56    }
57}