1use crate::types::{DocumentUri, Uri};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
13pub struct Position {
14 pub line: u32,
16 pub character: u32,
20}
21
22impl Position {
23 pub fn new(line: u32, character: u32) -> Self {
25 Self { line, character }
26 }
27
28 pub fn start() -> Self {
30 Self::new(0, 0)
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub struct Range {
37 pub start: Position,
39 pub end: Position,
41}
42
43impl Range {
44 pub fn new(start: Position, end: Position) -> Self {
46 Self { start, end }
47 }
48
49 pub fn from_coords(start_line: u32, start_char: u32, end_line: u32, end_char: u32) -> Self {
51 Self::new(
52 Position::new(start_line, start_char),
53 Position::new(end_line, end_char),
54 )
55 }
56
57 pub fn single_char(position: Position) -> Self {
59 Self::new(
60 position,
61 Position::new(position.line, position.character + 1),
62 )
63 }
64
65 pub fn contains(&self, position: Position) -> bool {
67 position >= self.start && position < self.end
68 }
69
70 pub fn is_empty(&self) -> bool {
72 self.start == self.end
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
78pub struct Location {
79 pub uri: DocumentUri,
81 pub range: Range,
83}
84
85impl Location {
86 pub fn new(uri: impl Into<DocumentUri>, range: Range) -> Self {
88 Self {
89 uri: uri.into(),
90 range,
91 }
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
97pub struct LocationLink {
98 #[serde(skip_serializing_if = "Option::is_none")]
102 pub origin_selection_range: Option<Range>,
103
104 pub target_uri: DocumentUri,
106
107 pub target_range: Range,
111
112 pub target_selection_range: Range,
115}
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
119pub struct Diagnostic {
120 pub range: Range,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
126 pub severity: Option<DiagnosticSeverity>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub code: Option<DiagnosticCode>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub code_description: Option<CodeDescription>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub source: Option<String>,
139
140 pub message: String,
142
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub tags: Option<Vec<DiagnosticTag>>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub related_information: Option<Vec<DiagnosticRelatedInformation>>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
154 pub data: Option<serde_json::Value>,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159#[repr(u8)]
160pub enum DiagnosticSeverity {
161 Error = 1,
163 Warning = 2,
165 Information = 3,
167 Hint = 4,
169}
170
171impl Serialize for DiagnosticSeverity {
172 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
173 where
174 S: serde::Serializer,
175 {
176 serializer.serialize_u8(*self as u8)
177 }
178}
179
180impl<'de> Deserialize<'de> for DiagnosticSeverity {
181 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182 where
183 D: serde::Deserializer<'de>,
184 {
185 let value = u8::deserialize(deserializer)?;
186 match value {
187 1 => Ok(DiagnosticSeverity::Error),
188 2 => Ok(DiagnosticSeverity::Warning),
189 3 => Ok(DiagnosticSeverity::Information),
190 4 => Ok(DiagnosticSeverity::Hint),
191 _ => Err(serde::de::Error::custom(format!(
192 "Invalid diagnostic severity: {}",
193 value
194 ))),
195 }
196 }
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
201#[serde(untagged)]
202pub enum DiagnosticCode {
203 Number(i32),
204 String(String),
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
209pub struct CodeDescription {
210 pub href: Uri,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
216#[repr(u8)]
217pub enum DiagnosticTag {
218 Unnecessary = 1,
220 Deprecated = 2,
222}
223
224impl Serialize for DiagnosticTag {
225 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
226 where
227 S: serde::Serializer,
228 {
229 serializer.serialize_u8(*self as u8)
230 }
231}
232
233impl<'de> Deserialize<'de> for DiagnosticTag {
234 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
235 where
236 D: serde::Deserializer<'de>,
237 {
238 let value = u8::deserialize(deserializer)?;
239 match value {
240 1 => Ok(DiagnosticTag::Unnecessary),
241 2 => Ok(DiagnosticTag::Deprecated),
242 _ => Err(serde::de::Error::custom(format!(
243 "Invalid diagnostic tag: {}",
244 value
245 ))),
246 }
247 }
248}
249
250#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
252pub struct DiagnosticRelatedInformation {
253 pub location: Location,
255 pub message: String,
257}
258
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct Command {
262 pub title: String,
264 pub command: String,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub arguments: Option<Vec<serde_json::Value>>,
269}
270
271#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
273pub struct TextEdit {
274 pub range: Range,
276 pub new_text: String,
278}
279
280impl TextEdit {
281 pub fn new(range: Range, new_text: impl Into<String>) -> Self {
283 Self {
284 range,
285 new_text: new_text.into(),
286 }
287 }
288
289 pub fn insert(position: Position, text: impl Into<String>) -> Self {
291 Self::new(Range::new(position, position), text)
292 }
293
294 pub fn delete(range: Range) -> Self {
296 Self::new(range, "")
297 }
298
299 pub fn replace(range: Range, new_text: impl Into<String>) -> Self {
301 Self::new(range, new_text)
302 }
303}
304
305#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
307pub struct ChangeAnnotation {
308 pub label: String,
310
311 #[serde(skip_serializing_if = "Option::is_none")]
313 pub needs_confirmation: Option<bool>,
314
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub description: Option<String>,
318}
319
320pub type ChangeAnnotationIdentifier = String;
322
323#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
325pub struct AnnotatedTextEdit {
326 pub range: Range,
328 pub new_text: String,
330 pub annotation_id: ChangeAnnotationIdentifier,
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
336pub struct TextDocumentEdit {
337 pub text_document: OptionalVersionedTextDocumentIdentifier,
339 pub edits: Vec<OneOf<TextEdit, AnnotatedTextEdit>>,
341}
342
343#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
345#[serde(tag = "kind")]
346pub enum ResourceOperation {
347 #[serde(rename = "create")]
348 Create(CreateFile),
349 #[serde(rename = "rename")]
350 Rename(RenameFile),
351 #[serde(rename = "delete")]
352 Delete(DeleteFile),
353}
354
355#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
357pub struct CreateFile {
358 pub kind: String, pub uri: DocumentUri,
362 #[serde(skip_serializing_if = "Option::is_none")]
364 pub options: Option<CreateFileOptions>,
365 #[serde(skip_serializing_if = "Option::is_none")]
367 pub annotation_id: Option<ChangeAnnotationIdentifier>,
368}
369
370#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
372pub struct CreateFileOptions {
373 #[serde(skip_serializing_if = "Option::is_none")]
375 pub overwrite: Option<bool>,
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub ignore_if_exists: Option<bool>,
379}
380
381#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
383pub struct RenameFile {
384 pub kind: String, pub old_uri: DocumentUri,
388 pub new_uri: DocumentUri,
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub options: Option<RenameFileOptions>,
393 #[serde(skip_serializing_if = "Option::is_none")]
395 pub annotation_id: Option<ChangeAnnotationIdentifier>,
396}
397
398#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
400pub struct RenameFileOptions {
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub overwrite: Option<bool>,
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub ignore_if_exists: Option<bool>,
407}
408
409#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
411pub struct DeleteFile {
412 pub kind: String, pub uri: DocumentUri,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub options: Option<DeleteFileOptions>,
419 #[serde(skip_serializing_if = "Option::is_none")]
421 pub annotation_id: Option<ChangeAnnotationIdentifier>,
422}
423
424#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
426pub struct DeleteFileOptions {
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub recursive: Option<bool>,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub ignore_if_not_exists: Option<bool>,
433}
434
435#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
437pub struct WorkspaceEdit {
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub changes: Option<HashMap<DocumentUri, Vec<TextEdit>>>,
441
442 #[serde(skip_serializing_if = "Option::is_none")]
446 pub document_changes: Option<Vec<DocumentChange>>,
447
448 #[serde(skip_serializing_if = "Option::is_none")]
451 pub change_annotations: Option<HashMap<ChangeAnnotationIdentifier, ChangeAnnotation>>,
452}
453
454#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
456#[serde(untagged)]
457pub enum DocumentChange {
458 TextDocumentEdit(TextDocumentEdit),
459 ResourceOperation(ResourceOperation),
460}
461
462#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
464pub struct TextDocumentIdentifier {
465 pub uri: DocumentUri,
467}
468
469impl TextDocumentIdentifier {
470 pub fn new(uri: impl Into<DocumentUri>) -> Self {
472 Self { uri: uri.into() }
473 }
474}
475
476#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
478pub struct VersionedTextDocumentIdentifier {
479 pub uri: DocumentUri,
481 pub version: i32,
483}
484
485impl VersionedTextDocumentIdentifier {
486 pub fn new(uri: impl Into<DocumentUri>, version: i32) -> Self {
488 Self {
489 uri: uri.into(),
490 version,
491 }
492 }
493}
494
495#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
497pub struct OptionalVersionedTextDocumentIdentifier {
498 pub uri: DocumentUri,
500 pub version: Option<i32>,
506}
507
508impl OptionalVersionedTextDocumentIdentifier {
509 pub fn new(uri: impl Into<DocumentUri>, version: Option<i32>) -> Self {
511 Self {
512 uri: uri.into(),
513 version,
514 }
515 }
516}
517
518#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
520#[serde(untagged)]
521pub enum OneOf<A, B> {
522 Left(A),
523 Right(B),
524}
525
526impl<A, B> OneOf<A, B> {
527 pub fn is_left(&self) -> bool {
528 matches!(self, OneOf::Left(_))
529 }
530
531 pub fn is_right(&self) -> bool {
532 matches!(self, OneOf::Right(_))
533 }
534
535 pub fn left(self) -> Option<A> {
536 match self {
537 OneOf::Left(a) => Some(a),
538 OneOf::Right(_) => None,
539 }
540 }
541
542 pub fn right(self) -> Option<B> {
543 match self {
544 OneOf::Right(b) => Some(b),
545 OneOf::Left(_) => None,
546 }
547 }
548}
549
550impl<A> From<A> for OneOf<A, ()> {
551 fn from(a: A) -> Self {
552 OneOf::Left(a)
553 }
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559
560 #[test]
561 fn test_position_ordering() {
562 let pos1 = Position::new(1, 5);
563 let pos2 = Position::new(1, 10);
564 let pos3 = Position::new(2, 0);
565
566 assert!(pos1 < pos2);
567 assert!(pos2 < pos3);
568 assert!(pos1 < pos3);
569 }
570
571 #[test]
572 fn test_range_contains() {
573 let range = Range::new(Position::new(1, 5), Position::new(1, 10));
574
575 assert!(range.contains(Position::new(1, 7)));
576 assert!(!range.contains(Position::new(1, 4)));
577 assert!(!range.contains(Position::new(1, 10))); assert!(!range.contains(Position::new(2, 0)));
579 }
580
581 #[test]
582 fn test_range_is_empty() {
583 let pos = Position::new(1, 5);
584 let empty_range = Range::new(pos, pos);
585 let non_empty_range = Range::new(pos, Position::new(1, 6));
586
587 assert!(empty_range.is_empty());
588 assert!(!non_empty_range.is_empty());
589 }
590
591 #[test]
592 fn test_text_edit_operations() {
593 let pos = Position::new(1, 5);
594 let range = Range::new(pos, Position::new(1, 10));
595
596 let insert = TextEdit::insert(pos, "text");
597 assert_eq!(insert.range.start, insert.range.end);
598
599 let delete = TextEdit::delete(range);
600 assert_eq!(delete.new_text, "");
601
602 let replace = TextEdit::replace(range, "new");
603 assert_eq!(replace.new_text, "new");
604 assert_eq!(replace.range, range);
605 }
606
607 #[test]
608 fn test_diagnostic_severity_values() {
609 assert_eq!(DiagnosticSeverity::Error as u8, 1);
611 assert_eq!(DiagnosticSeverity::Warning as u8, 2);
612 assert_eq!(DiagnosticSeverity::Information as u8, 3);
613 assert_eq!(DiagnosticSeverity::Hint as u8, 4);
614 }
615
616 #[test]
617 fn test_one_of_type() {
618 let left: OneOf<i32, String> = OneOf::Left(42);
619 let right: OneOf<i32, String> = OneOf::Right("test".to_string());
620
621 assert!(left.is_left());
622 assert!(!left.is_right());
623 assert!(!right.is_left());
624 assert!(right.is_right());
625
626 assert_eq!(left.left(), Some(42));
627 assert_eq!(right.right(), Some("test".to_string()));
628 }
629}