1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Turning failed parse regions into publishable LSP diagnostics.
use ;
use Diagnostic;
use crateDocument;
/// One parse-tree node offered to the user's diagnostic hook.
/// Options for [`diagnostics`].
/// Builds diagnostics from a settled (or cancelled, or round-capped) parse.
///
/// The tree is walked from the root; every failing node is offered to `hook`
/// (unless its ancestor already failed and `options.cascades` is `false`).
/// The hook turns a [`FailedNode`] into a `Diagnostic` — or `None` to stay
/// silent, e.g. for regions whose context type makes the failure expected.
///
/// Diagnostics returned with the default (zero) range get their range filled
/// in from the node's span, converted with the document's position encoding;
/// hooks that compute their own ranges keep them.
///
/// # Examples
///
/// ```
/// use increparse::{Engine, Outcome, Pass, Schedule, SerialExecutor, Span, Status};
/// use increparse_lsp::{diagnostics, DiagnosticsOptions, Document, PositionEncoding};
/// use lsp_types::{Diagnostic, DiagnosticSeverity, Position, Uri};
///
/// #[derive(Clone, Debug, PartialEq, Eq)]
/// enum Ctx { File, Bad }
///
/// struct Split;
/// impl Pass for Split {
/// type Ctx = Ctx;
/// fn parse(&self, source: &str, span: Span, ctx: &Ctx) -> Outcome<Ctx> {
/// match ctx {
/// Ctx::File if span.len() >= 2 => Outcome::Expand(vec![(
/// Span::new(span.start + 1, span.end, span.rev),
/// Ctx::Bad,
/// )]),
/// Ctx::File => Outcome::Done,
/// Ctx::Bad if source[span.to_range()].contains('!') => Outcome::Failed,
/// Ctx::Bad => Outcome::Done,
/// }
/// }
/// }
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut schedule = Schedule::new();
/// schedule.push(Split);
/// schedule.push(Split);
/// let engine = Engine::new(schedule);
///
/// let uri: Uri = "file:///x.txt".parse()?;
/// let mut doc = Document::open(uri, 1, "".into(), "ok!".into(), PositionEncoding::Utf16, Ctx::File);
/// doc.apply_changes(&engine, 1, &[], &SerialExecutor, &increparse::CancelToken::new());
///
/// let diags = diagnostics(&doc, DiagnosticsOptions::default(), |node| {
/// let _ = node.status;
/// Some(Diagnostic {
/// severity: Some(DiagnosticSeverity::ERROR),
/// message: "this region failed to parse".into(),
/// ..Diagnostic::default()
/// })
/// });
///
/// assert_eq!(diags.len(), 1);
/// assert_eq!(diags[0].range.start, Position { line: 0, character: 1 });
/// # Ok(())
/// # }
/// ```