Skip to main content

Grove/API/
Types.rs

1//! Common API Types Module
2//!
3//! Defines common types used throughout the VS Code API facade.
4
5use serde::{Deserialize, Serialize};
6
7/// Position in a text document
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9pub struct Position {
10	/// Line position in a document (0-based)
11	pub line:u32,
12
13	/// Character offset on a line in a document (0-based)
14	pub character:u32,
15}
16
17impl Position {
18	/// Create a new position
19	pub fn new(line:u32, character:u32) -> Self { Self { line, character } }
20
21	/// Position at line 0, character 0
22	pub fn zero() -> Self { Self { line:0, character:0 } }
23
24	/// Create a position from a line and column (1-based to 0-based)
25	pub fn from_line_column(line:u32, column:u32) -> Self {
26		Self { line:line.saturating_sub(1), character:column.saturating_sub(1) }
27	}
28}
29
30impl Default for Position {
31	fn default() -> Self { Self::zero() }
32}
33
34/// A range in a text document
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub struct Range {
37	/// The range's start position
38	pub start:Position,
39
40	/// The range's end position
41	pub end:Position,
42}
43
44impl Range {
45	/// Create a new range
46	pub fn new(start:Position, end:Position) -> Self { Self { start, end } }
47
48	/// Create a range covering a single position
49	pub fn empty(position:Position) -> Self { Self { start:position, end:position } }
50
51	/// Check if the position is in this range
52	pub fn contains(&self, position:Position) -> bool { position >= self.start && position <= self.end }
53}
54
55impl Default for Range {
56	fn default() -> Self { Self::empty(Position::zero()) }
57}
58
59/// Represents a location inside a resource
60#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub struct Location {
62	/// The resource identifier of this location
63	pub uri:String,
64
65	/// The document range
66	pub range:Range,
67}
68
69impl Location {
70	/// Create a new location
71	pub fn new(uri:String, range:Range) -> Self { Self { uri, range } }
72}
73
74/// Represents a diagnostic message
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct Diagnostic {
77	/// The range at which the message applies
78	pub range:Range,
79
80	/// The diagnostic's severity
81	pub severity:Option<DiagnosticSeverity>,
82
83	/// The diagnostic's code
84	pub code:Option<DiagnosticCode>,
85
86	/// A human-readable string describing the source of this diagnostic
87	pub source:Option<String>,
88
89	/// The diagnostic's message
90	pub message:String,
91
92	/// Additional metadata about the diagnostic
93	pub tags:Option<Vec<DiagnosticTag>>,
94
95	/// Related diagnostic information
96	pub related_information:Option<Vec<DiagnosticRelatedInformation>>,
97}
98
99impl Diagnostic {
100	/// Create a new diagnostic
101	pub fn new(range:Range, message:String) -> Self {
102		Self {
103			range,
104
105			severity:None,
106
107			code:None,
108
109			source:None,
110
111			message,
112
113			tags:None,
114
115			related_information:None,
116		}
117	}
118}
119
120/// The severity of a diagnostic
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
122pub enum DiagnosticSeverity {
123	/// Not an error, but something to be aware of
124	Hint = 3,
125
126	/// Informational
127	Information = 2,
128
129	/// Something to warn about
130	Warning = 1,
131
132	/// An error
133	Error = 0,
134}
135
136/// Diagnostic code
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[serde(untagged)]
139pub enum DiagnosticCode {
140	/// Numeric code
141	Number(i64),
142
143	/// String code
144	String(String),
145}
146
147/// Diagnostic tags
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
149pub enum DiagnosticTag {
150	/// Unused or unnecessary code
151	Unnecessary = 1,
152
153	/// Deprecated code
154	Deprecated = 2,
155}
156
157/// Related diagnostic information
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct DiagnosticRelatedInformation {
160	/// The location of this related diagnostic information
161	pub location:Location,
162
163	/// The message of this related diagnostic information
164	pub message:String,
165}
166
167/// Represents a text change
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct TextEdit {
170	/// The range of the text document to be manipulated
171	pub range:Range,
172
173	/// The new text for the provided range
174	pub new_text:String,
175}
176
177impl TextEdit {
178	/// Create a new text edit
179	pub fn new(range:Range, new_text:String) -> Self { Self { range, new_text } }
180
181	/// Delete the text at the given range
182	pub fn delete(range:Range) -> Self { Self { range, new_text:String::new() } }
183
184	/// Insert the given text at the given position
185	pub fn insert(position:Position, new_text:String) -> Self { Self { range:Range::empty(position), new_text } }
186
187	/// Replace the text at the given range with the given text
188	pub fn replace(range:Range, new_text:String) -> Self { Self { range, new_text } }
189}
190
191/// Workspace edit
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct WorkspaceEdit {
194	/// Holds changes to existing resources
195	pub changes:Option<std::collections::HashMap<String, Vec<TextEdit>>>,
196}
197
198impl WorkspaceEdit {
199	/// Create a new workspace edit
200	pub fn new() -> Self { Self { changes:Some(std::collections::HashMap::new()) } }
201}
202
203impl Default for WorkspaceEdit {
204	fn default() -> Self { Self::new() }
205}
206
207/// Completion item
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct CompletionItem {
210	/// The label of this completion item
211	pub label:String,
212
213	/// The kind of this completion item
214	pub kind:Option<CompletionItemKind>,
215
216	/// A human-readable string with additional information
217	pub detail:Option<String>,
218
219	/// A human-readable string that represents a doc-comment
220	pub documentation:Option<CompletionItemDocumentation>,
221
222	/// Select this item when showing
223	#[serde(rename = "preselect")]
224	pub preselect:Option<bool>,
225
226	/// A string that should be used when comparing this item with other items
227	pub sort_text:Option<String>,
228
229	/// A string that should be used when filtering items
230	pub filter_text:Option<String>,
231}
232
233impl CompletionItem {
234	/// Create a new completion item
235	pub fn new(label:String) -> Self {
236		Self {
237			label,
238
239			kind:None,
240
241			detail:None,
242
243			documentation:None,
244
245			preselect:None,
246
247			sort_text:None,
248
249			filter_text:None,
250		}
251	}
252}
253
254/// Completion item kind
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
256pub enum CompletionItemKind {
257	/// Text completion
258	Text = 1,
259
260	/// Method completion
261	Method = 2,
262
263	/// Function completion
264	Function = 3,
265
266	/// Constructor completion
267	Constructor = 4,
268
269	/// Field completion
270	Field = 5,
271
272	/// Variable completion
273	Variable = 6,
274
275	/// Class completion
276	Class = 7,
277
278	/// Interface completion
279	Interface = 8,
280
281	/// Module completion
282	Module = 9,
283
284	/// Property completion
285	Property = 10,
286
287	/// Unit completion
288	Unit = 11,
289
290	/// Value completion
291	Value = 12,
292
293	/// Enum completion
294	Enum = 13,
295
296	/// Keyword completion
297	Keyword = 14,
298
299	/// Snippet completion
300	Snippet = 15,
301
302	/// Color completion
303	Color = 16,
304
305	/// File completion
306	File = 17,
307
308	/// Reference completion
309	Reference = 18,
310
311	/// Folder completion
312	Folder = 19,
313
314	/// Enum member completion
315	EnumMember = 20,
316
317	/// Constant completion
318	Constant = 21,
319
320	/// Struct completion
321	Struct = 22,
322
323	/// Event completion
324	Event = 23,
325
326	/// Operator completion
327	Operator = 24,
328
329	/// Type parameter completion
330	TypeParameter = 25,
331}
332
333/// Completion item documentation
334#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(untagged)]
336pub enum CompletionItemDocumentation {
337	/// Markdown string
338	String(String),
339
340	/// Value object
341	Value(CompletionItemDocumentationValue),
342}
343
344/// Completion item documentation value object
345#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct CompletionItemDocumentationValue {
347	/// The kind of documentation
348	pub kind:String,
349
350	/// The documentation content
351	pub value:String,
352}
353
354/// Partial result token
355#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct PartialResultParams {
357	/// An optional token that a server can use to report partial results
358	#[serde(rename = "partialResultToken")]
359	pub partial_result_token:Option<String>,
360}
361
362/// Work done progress params
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct WorkDoneProgressParams {
365	/// An optional token that a server can use to report work done progress
366	#[serde(rename = "workDoneToken")]
367	pub work_done_token:Option<String>,
368}
369
370/// Text document identifier
371#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
372pub struct TextDocumentIdentifier {
373	/// The text document's uri
374	pub uri:String,
375}
376
377impl TextDocumentIdentifier {
378	/// Create a new text document identifier
379	pub fn new(uri:String) -> Self { Self { uri } }
380}
381
382/// Versioned text document identifier
383#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
384pub struct VersionedTextDocumentIdentifier {
385	/// The text document's uri
386	pub uri:String,
387
388	/// The version number of this document
389	pub version:i32,
390}
391
392impl VersionedTextDocumentIdentifier {
393	/// Create a new versioned text document identifier
394	pub fn new(uri:String, version:i32) -> Self { Self { uri, version } }
395}
396
397/// Text document item
398#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
399pub struct TextDocumentItem {
400	/// The text document's uri
401	pub uri:String,
402
403	/// The text document's language identifier
404	#[serde(rename = "languageId")]
405	pub language_id:String,
406
407	/// The version number of this document
408	pub version:i32,
409
410	/// The content of the opened text document
411	pub text:String,
412}
413
414impl TextDocumentItem {
415	/// Create a new text document item
416	pub fn new(uri:String, language_id:String, version:i32, text:String) -> Self {
417		Self { uri, language_id, version, text }
418	}
419}
420
421/// The parameters sent in notifications/requests for user-initiated creation of
422/// files
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct CreateFileParams {
425	/// An array of all files/folders created in this operation
426	pub files:Vec<FileCreate>,
427}
428
429/// Represents information to create a file
430#[derive(Debug, Clone, Serialize, Deserialize)]
431pub struct FileCreate {
432	/// A file or folder uri
433	pub uri:String,
434
435	/// Additional options
436	#[serde(rename = "options")]
437	pub options:Option<CreateFileOptions>,
438}
439
440/// Options when creating a file
441#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct CreateFileOptions {
443	/// Overwrite existing file
444	#[serde(rename = "overwrite")]
445	pub overwrite:Option<bool>,
446
447	/// Ignore if exists
448	#[serde(rename = "ignoreIfExists")]
449	pub ignore_if_exists:Option<bool>,
450}
451
452#[cfg(test)]
453mod tests {
454
455	use super::*;
456
457	#[test]
458	fn test_position() {
459		let pos = Position::new(5, 10);
460
461		assert_eq!(pos.line, 5);
462
463		assert_eq!(pos.character, 10);
464
465		let default = Position::default();
466
467		assert_eq!(default.line, 0);
468
469		assert_eq!(default.character, 0);
470	}
471
472	#[test]
473	fn test_range() {
474		let start = Position::new(0, 0);
475
476		let end = Position::new(5, 10);
477
478		let range = Range::new(start, end);
479
480		assert!(range.contains(Position::new(3, 5)));
481
482		assert!(!range.contains(Position::new(6, 0)));
483	}
484
485	#[test]
486	fn test_text_edit_operations() {
487		let range = Range::new(Position::new(0, 0), Position::new(0, 5));
488
489		let replace = TextEdit::replace(range, "new text".to_string());
490
491		assert_eq!(replace.new_text, "new text");
492
493		let delete = TextEdit::delete(range);
494
495		assert_eq!(delete.new_text, "");
496
497		let insert = TextEdit::insert(Position::new(0, 0), "inserted".to_string());
498
499		assert_eq!(insert.new_text, "inserted");
500	}
501
502	#[test]
503	fn test_completion_item() {
504		let item = CompletionItem::new("testFunction".to_string());
505
506		assert_eq!(item.label, "testFunction");
507	}
508}