1use std::path::PathBuf;
3use strop_core::id::{BufferRevision, ByteColumn, DocumentId, LineIndex};
4use strop_workspace::ResourceLocation;
5
6#[derive(
9 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
10)]
11pub enum Severity {
12 Error,
13 Warning,
14 Information,
15 Hint,
16}
17
18impl Severity {
19 pub const fn code(self) -> u8 {
21 match self {
22 Self::Error => 1,
23 Self::Warning => 2,
24 Self::Information => 3,
25 Self::Hint => 4,
26 }
27 }
28
29 pub const fn char(self) -> char {
31 match self {
32 Self::Error => 'E',
33 Self::Warning => 'W',
34 Self::Information => 'I',
35 Self::Hint => 'H',
36 }
37 }
38}
39
40#[derive(
44 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
45)]
46#[serde(transparent)]
47pub struct WireVersion(i32);
48
49impl WireVersion {
50 pub const fn new(value: i32) -> Self {
51 Self(value)
52 }
53 pub const fn get(self) -> i32 {
54 self.0
55 }
56 pub(crate) fn next(self) -> Option<Self> {
57 self.0.checked_add(1).map(Self)
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
64pub struct Diag {
65 pub line: LineIndex,
66 pub col: ServerColumn,
67 pub end_line: LineIndex,
68 pub end_col: ServerColumn,
69 pub severity: Severity,
70 pub message: String,
71}
72
73impl Diag {
74 pub fn resolve(self, encoding: PositionEncoding, buffer: &strop_core::Buffer) -> ResolvedDiag {
78 let last = buffer.len_lines().saturating_sub(1);
79 let line = LineIndex::new(self.line.get().min(last));
80 let end_line = LineIndex::new(self.end_line.get().min(last));
81 let start_text = buffer.line_text(line);
82 let end_text = if end_line == line {
83 start_text.clone()
84 } else {
85 buffer.line_text(end_line)
86 };
87 ResolvedDiag {
88 line,
89 col: to_byte_col(&start_text, self.col, encoding),
90 end_line,
91 end_col: to_byte_col(&end_text, self.end_col, encoding),
92 severity: self.severity,
93 message: self.message,
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
101pub struct ResolvedDiag {
102 pub line: LineIndex,
103 pub col: ByteColumn,
104 pub end_line: LineIndex,
105 pub end_col: ByteColumn,
106 pub severity: Severity,
107 pub message: String,
108}
109
110impl ResolvedDiag {
111 pub fn severity_char(&self) -> char {
112 self.severity.char()
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
117#[serde(transparent)]
118pub struct ServerId(u64);
119impl ServerId {
120 pub const fn new(value: u64) -> Self {
121 Self(value)
122 }
123 pub const fn get(self) -> u64 {
124 self.0
125 }
126 pub(crate) fn allocate() -> Self {
127 static NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
128 match NEXT.fetch_update(
129 std::sync::atomic::Ordering::Relaxed,
130 std::sync::atomic::Ordering::Relaxed,
131 |n| n.checked_add(1),
132 ) {
133 Ok(value) => Self(value),
134 Err(_) => panic!("LSP server identity exhausted"),
135 }
136 }
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
140#[serde(transparent)]
141pub struct RequestId(u64);
142impl RequestId {
143 pub const fn new(value: u64) -> Self {
144 Self(value)
145 }
146 pub const fn get(self) -> u64 {
147 self.0
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
152pub struct RequestStamp {
153 pub request: RequestId,
154 pub server: ServerId,
155 pub document: DocumentId,
156 pub revision: BufferRevision,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
160pub enum PositionEncoding {
161 Utf8,
162 Utf16,
163}
164
165pub fn to_server_col(line: &str, byte_col: ByteColumn, enc: PositionEncoding) -> ServerColumn {
167 crate::to_server_col_slice(line.into(), byte_col, enc)
168}
169
170pub fn to_byte_col(line: &str, server_col: ServerColumn, enc: PositionEncoding) -> ByteColumn {
172 crate::to_byte_col_slice(line.into(), server_col, enc)
173}
174
175#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
176pub enum LocKind {
177 References,
178 Implementation,
179 TypeDefinition,
180 Declaration,
181}
182impl LocKind {
183 pub fn label(self) -> &'static str {
184 match self {
185 Self::References => "references",
186 Self::Implementation => "implementation",
187 Self::TypeDefinition => "type definition",
188 Self::Declaration => "declaration",
189 }
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
194pub enum RequestKind {
195 Goto,
196 Hover,
197 SwitchHeader,
198 Locations(LocKind),
199 Format,
200 Rename,
201 CodeAction,
202 DocumentSymbols,
204}
205
206impl RequestKind {
207 pub fn label(self) -> &'static str {
208 match self {
209 Self::Goto => "goto definition",
210 Self::Hover => "hover",
211 Self::SwitchHeader => "switch source/header",
212 Self::Locations(kind) => kind.label(),
213 Self::Format => "format",
214 Self::Rename => "rename",
215 Self::CodeAction => "code action",
216 Self::DocumentSymbols => "document symbols",
217 }
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
224pub enum RequestRefusal {
225 NotOpen,
227 StaleRevision,
229 Unsupported,
231 IdentityExhausted,
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
236#[serde(transparent)]
237pub struct ServerColumn(usize);
238impl ServerColumn {
239 pub const fn new(value: usize) -> Self {
240 Self(value)
241 }
242 pub const fn get(self) -> usize {
243 self.0
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
248pub struct ServerPosition {
249 pub line: LineIndex,
250 pub column: ServerColumn,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
257pub struct ProtoSymbol {
258 pub name: String,
259 pub container: String,
260 pub kind: String,
262 pub location: ServerLocation,
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
266pub struct ServerLocation {
267 pub doc: ResourceLocation,
270 pub position: ServerPosition,
271}
272
273#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
277pub struct ServerEdit {
278 pub start: ServerPosition,
279 pub end: ServerPosition,
280 pub new_text: String,
281}
282
283#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
289pub struct ProtoAction {
290 pub title: String,
291 pub edits: Option<Vec<(ResourceLocation, Vec<ServerEdit>)>>,
292 pub has_external_command: bool,
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
296pub struct ReplyContext {
297 pub stamp: RequestStamp,
298 pub encoding: PositionEncoding,
299 pub kind: RequestKind,
300}
301
302#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
306pub struct RequestInput {
307 pub document: DocumentId,
308 pub revision: BufferRevision,
309 #[serde(with = "strop_core::path_serde")]
310 pub path: PathBuf,
311 pub line: LineIndex,
312 pub byte_col: ByteColumn,
313 pub line_text: crate::FrozenLine,
314 pub kind: RequestKind,
315 #[serde(default)]
318 pub rename_to: Option<String>,
319}
320
321#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
323pub struct PendingRequest {
324 pub stamp: RequestStamp,
325 pub input: RequestInput,
326 #[serde(default)]
332 pub tab_width: Option<usize>,
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
336pub struct DiagnosticContext {
337 pub server: ServerId,
338 pub document: DocumentId,
339 pub revision: BufferRevision,
342 pub encoding: PositionEncoding,
343 pub version: Option<WireVersion>,
344}
345
346#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
347pub enum LspEvent {
348 Diagnostics {
349 context: DiagnosticContext,
350 doc: ResourceLocation,
353 diags: Vec<Diag>,
354 },
355 Ready {
356 server: ServerId,
357 name: String,
358 },
359 Failed {
360 server: ServerId,
361 name: String,
362 hint: String,
363 },
364 ServerMessage {
368 server: ServerId,
369 name: String,
370 text: String,
371 },
372 HoverText {
373 context: ReplyContext,
374 text: String,
375 },
376 GotoLocation {
377 context: ReplyContext,
378 location: ServerLocation,
379 },
380 Locations {
381 context: ReplyContext,
382 kind: LocKind,
383 items: Vec<ServerLocation>,
384 },
385 Edits {
388 context: ReplyContext,
389 edits: Vec<ServerEdit>,
390 },
391 WorkspaceEdits {
394 context: ReplyContext,
395 edits: Vec<(ResourceLocation, Vec<ServerEdit>)>,
396 },
397 ActionList {
400 context: ReplyContext,
401 actions: Vec<ProtoAction>,
402 },
403 Symbols {
406 context: ReplyContext,
407 symbols: Vec<ProtoSymbol>,
408 },
409 Note {
411 context: ReplyContext,
412 text: String,
413 },
414}