Skip to main content

hammer/
diagnose.rs

1//! The `diagnose` export: Hammer sends paragraphs of a scene, and the plugin replies with the issues
2//! in them, which the editor underlines. A check's manifest `scope` says which paragraphs: `paragraph`,
3//! the default, sends only those whose text the check has not seen; `scene` sends every paragraph of
4//! the scene each time, blank ones included, for issues that depend on other paragraphs.
5
6use serde::{Deserialize, Serialize};
7use serde_json::{Map, Value};
8
9#[derive(Deserialize)]
10pub struct Request {
11    /// Which of the manifest's `[[diagnostics]]` checks to run.
12    pub diagnostics: String,
13    /// Plain text, without markdown.
14    pub paragraphs: Vec<String>,
15    /// The project's BCP 47 tag, or none.
16    pub language: Option<String>,
17    /// The name of the project the text belongs to, for operations such as `entry.list`.
18    pub project: String,
19    /// The plugin's declared settings, every key present.
20    #[serde(default)]
21    pub settings: Map<String, Value>,
22    /// The UI's BCP 47 tag, for messages in the user's language; not [Request::language], the text's.
23    #[serde(default)]
24    pub locale: Option<String>,
25}
26
27#[derive(Serialize, Default)]
28pub struct Reply {
29    pub diagnostics: Vec<Diagnostic>,
30}
31
32/// An issue in one paragraph, from `start` to `end` as UTF-8 byte offsets into it.
33#[derive(Serialize)]
34pub struct Diagnostic {
35    /// Its index in [Request::paragraphs].
36    pub paragraph: usize,
37    pub start: usize,
38    pub end: usize,
39    pub message: String,
40    /// Offered to the user.
41    pub fixes: Vec<Fix>,
42    #[serde(skip_serializing_if = "Severity::is_error")]
43    pub severity: Severity,
44}
45
46#[derive(Serialize, Clone, Copy, PartialEq, Eq, Default, Debug)]
47#[serde(rename_all = "lowercase")]
48pub enum Severity {
49    /// A mistake: a wavy underline.
50    #[default]
51    Error,
52    /// Something the writer may want to change: a quieter, dotted underline.
53    Suggestion,
54}
55
56impl Severity {
57    fn is_error(&self) -> bool {
58        *self == Severity::Error
59    }
60}
61
62/// A replacement for a [Diagnostic]'s range, shown to the user as itself, or as a label where the
63/// replacement alone would not say what it does, such as removing a word.
64#[derive(Serialize)]
65#[serde(untagged)]
66pub enum Fix {
67    Replace(String),
68    Labelled { replacement: String, label: String },
69}