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 WorkspaceSymbols,
207}
208
209impl RequestKind {
210 pub fn label(self) -> &'static str {
211 match self {
212 Self::Goto => "goto definition",
213 Self::Hover => "hover",
214 Self::SwitchHeader => "switch source/header",
215 Self::Locations(kind) => kind.label(),
216 Self::Format => "format",
217 Self::Rename => "rename",
218 Self::CodeAction => "code action",
219 Self::DocumentSymbols => "document symbols",
220 Self::WorkspaceSymbols => "workspace symbols",
221 }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
228pub enum RequestRefusal {
229 NotOpen,
231 StaleRevision,
233 Unsupported,
235 NotReady,
237 IdentityExhausted,
239 Overloaded,
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
245#[serde(transparent)]
246pub struct ServerColumn(usize);
247impl ServerColumn {
248 pub const fn new(value: usize) -> Self {
249 Self(value)
250 }
251 pub const fn get(self) -> usize {
252 self.0
253 }
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
257pub struct ServerPosition {
258 pub line: LineIndex,
259 pub column: ServerColumn,
260}
261
262#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
266pub struct ProtoSymbol {
267 pub name: String,
268 pub container: String,
269 pub kind: String,
271 pub location: ServerLocation,
272}
273
274#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
275pub struct ServerLocation {
276 pub doc: ResourceLocation,
279 pub position: ServerPosition,
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
286pub struct ServerEdit {
287 pub start: ServerPosition,
288 pub end: ServerPosition,
289 pub new_text: String,
290}
291
292#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
298pub struct ProtoAction {
299 pub title: String,
300 pub edits: Option<Vec<(ResourceLocation, Vec<ServerEdit>)>>,
301 pub has_external_command: bool,
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
305pub struct ReplyContext {
306 pub stamp: RequestStamp,
307 pub encoding: PositionEncoding,
308 pub kind: RequestKind,
309}
310
311#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
315pub struct RequestInput {
316 pub document: DocumentId,
317 pub revision: BufferRevision,
318 #[serde(with = "strop_core::path_serde")]
319 pub path: PathBuf,
320 pub line: LineIndex,
321 pub byte_col: ByteColumn,
322 pub line_text: crate::FrozenLine,
323 pub kind: RequestKind,
324 #[serde(default)]
327 pub rename_to: Option<String>,
328}
329
330#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
332pub struct PendingRequest {
333 pub stamp: RequestStamp,
334 pub input: RequestInput,
335 #[serde(default)]
341 pub tab_width: Option<usize>,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
345pub struct DiagnosticContext {
346 pub server: ServerId,
347 pub document: DocumentId,
348 pub revision: BufferRevision,
351 pub encoding: PositionEncoding,
352 pub version: Option<WireVersion>,
353}
354
355#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
356pub enum LspEvent {
357 Diagnostics {
358 context: DiagnosticContext,
359 doc: ResourceLocation,
362 diags: Vec<Diag>,
363 },
364 Ready {
365 server: ServerId,
366 name: String,
367 },
368 Failed {
369 server: ServerId,
370 name: String,
371 hint: String,
372 },
373 ServerMessage {
377 server: ServerId,
378 name: String,
379 text: String,
380 },
381 HoverText {
382 context: ReplyContext,
383 text: String,
384 },
385 GotoLocation {
386 context: ReplyContext,
387 location: ServerLocation,
388 },
389 Locations {
390 context: ReplyContext,
391 kind: LocKind,
392 items: Vec<ServerLocation>,
393 },
394 Edits {
397 context: ReplyContext,
398 edits: Vec<ServerEdit>,
399 },
400 WorkspaceEdits {
403 context: ReplyContext,
404 edits: Vec<(ResourceLocation, Vec<ServerEdit>)>,
405 },
406 ActionList {
409 context: ReplyContext,
410 actions: Vec<ProtoAction>,
411 },
412 Symbols {
415 context: ReplyContext,
416 symbols: Vec<ProtoSymbol>,
417 },
418 WorkspaceSymbols {
421 server: ServerId,
422 generation: u64,
423 symbols: Vec<ProtoSymbol>,
424 },
425 WorkspaceSymbolsFailed {
428 server: ServerId,
429 generation: u64,
430 reason: String,
431 },
432 Note {
434 context: ReplyContext,
435 text: String,
436 },
437}