1//! Handles conversions of `language` items to and from the [`rpc`] protocol.
2
3use crate::{
4 diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic,
5 Language, LanguageRegistry,
6};
7use anyhow::{anyhow, Result};
8use clock::ReplicaId;
9use lsp::{DiagnosticSeverity, LanguageServerId};
10use rpc::proto;
11use std::{ops::Range, sync::Arc};
12use text::*;
13
14pub use proto::{BufferState, Operation};
15
16/// Serializes a [`RopeFingerprint`] to be sent over RPC.
17pub fn serialize_fingerprint(fingerprint: RopeFingerprint) -> String {
18 fingerprint.to_hex()
19}
20
21/// Deserializes a `[text::LineEnding]` from the RPC representation.
22pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
23 match message {
24 proto::LineEnding::Unix => text::LineEnding::Unix,
25 proto::LineEnding::Windows => text::LineEnding::Windows,
26 }
27}
28
29/// Serializes a [`text::LineEnding`] to be sent over RPC.
30pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding {
31 match message {
32 text::LineEnding::Unix => proto::LineEnding::Unix,
33 text::LineEnding::Windows => proto::LineEnding::Windows,
34 }
35}
36
37/// Serializes a [`crate::Operation`] to be sent over RPC.
38pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
39 proto::Operation {
40 variant: Some(match operation {
41 crate::Operation::Buffer(text::Operation::Edit(edit)) => {
42 proto::operation::Variant::Edit(serialize_edit_operation(edit))
43 }
44
45 crate::Operation::Buffer(text::Operation::Undo(undo)) => {
46 proto::operation::Variant::Undo(proto::operation::Undo {
47 replica_id: undo.timestamp.replica_id as u32,
48 lamport_timestamp: undo.timestamp.value,
49 version: serialize_version(&undo.version),
50 counts: undo
51 .counts
52 .iter()
53 .map(|(edit_id, count)| proto::UndoCount {
54 replica_id: edit_id.replica_id as u32,
55 lamport_timestamp: edit_id.value,
56 count: *count,
57 })
58 .collect(),
59 })
60 }
61
62 crate::Operation::UpdateSelections {
63 selections,
64 line_mode,
65 lamport_timestamp,
66 cursor_shape,
67 } => proto::operation::Variant::UpdateSelections(proto::operation::UpdateSelections {
68 replica_id: lamport_timestamp.replica_id as u32,
69 lamport_timestamp: lamport_timestamp.value,
70 selections: serialize_selections(selections),
71 line_mode: *line_mode,
72 cursor_shape: serialize_cursor_shape(cursor_shape) as i32,
73 }),
74
75 crate::Operation::UpdateDiagnostics {
76 lamport_timestamp,
77 server_id,
78 diagnostics,
79 } => proto::operation::Variant::UpdateDiagnostics(proto::UpdateDiagnostics {
80 replica_id: lamport_timestamp.replica_id as u32,
81 lamport_timestamp: lamport_timestamp.value,
82 server_id: server_id.0 as u64,
83 diagnostics: serialize_diagnostics(diagnostics.iter()),
84 }),
85
86 crate::Operation::UpdateCompletionTriggers {
87 triggers,
88 lamport_timestamp,
89 } => proto::operation::Variant::UpdateCompletionTriggers(
90 proto::operation::UpdateCompletionTriggers {
91 replica_id: lamport_timestamp.replica_id as u32,
92 lamport_timestamp: lamport_timestamp.value,
93 triggers: triggers.clone(),
94 },
95 ),
96 }),
97 }
98}
99
100/// Serializes an [`EditOperation`] to be sent over RPC.
101pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
102 proto::operation::Edit {
103 replica_id: operation.timestamp.replica_id as u32,
104 lamport_timestamp: operation.timestamp.value,
105 version: serialize_version(&operation.version),
106 ranges: operation.ranges.iter().map(serialize_range).collect(),
107 new_text: operation
108 .new_text
109 .iter()
110 .map(|text| text.to_string())
111 .collect(),
112 }
113}
114
115/// Serializes an entry in the undo map to be sent over RPC.
116pub fn serialize_undo_map_entry(
117 (edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]),
118) -> proto::UndoMapEntry {
119 proto::UndoMapEntry {
120 replica_id: edit_id.replica_id as u32,
121 local_timestamp: edit_id.value,
122 counts: counts
123 .iter()
124 .map(|(undo_id, count)| proto::UndoCount {
125 replica_id: undo_id.replica_id as u32,
126 lamport_timestamp: undo_id.value,
127 count: *count,
128 })
129 .collect(),
130 }
131}
132
133/// Splits the given list of operations into chunks.
134pub fn split_operations(
135 mut operations: Vec<proto::Operation>,
136) -> impl Iterator<Item = Vec<proto::Operation>> {
137 #[cfg(any(test, feature = "test-support"))]
138 const CHUNK_SIZE: usize = 5;
139
140 #[cfg(not(any(test, feature = "test-support")))]
141 const CHUNK_SIZE: usize = 100;
142
143 let mut done = false;
144 std::iter::from_fn(move || {
145 if done {
146 return None;
147 }
148
149 let operations = operations
150 .drain(..std::cmp::min(CHUNK_SIZE, operations.len()))
151 .collect::<Vec<_>>();
152 if operations.is_empty() {
153 done = true;
154 }
155 Some(operations)
156 })
157}
158
159/// Serializes selections to be sent over RPC.
160pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto::Selection> {
161 selections.iter().map(serialize_selection).collect()
162}
163
164/// Serializes a [`Selection`] to be sent over RPC.
165pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
166 proto::Selection {
167 id: selection.id as u64,
168 start: Some(proto::EditorAnchor {
169 anchor: Some(serialize_anchor(&selection.start)),
170 excerpt_id: 0,
171 }),
172 end: Some(proto::EditorAnchor {
173 anchor: Some(serialize_anchor(&selection.end)),
174 excerpt_id: 0,
175 }),
176 reversed: selection.reversed,
177 }
178}
179
180/// Serializes a [`CursorShape`] to be sent over RPC.
181pub fn serialize_cursor_shape(cursor_shape: &CursorShape) -> proto::CursorShape {
182 match cursor_shape {
183 CursorShape::Bar => proto::CursorShape::CursorBar,
184 CursorShape::Block => proto::CursorShape::CursorBlock,
185 CursorShape::Underscore => proto::CursorShape::CursorUnderscore,
186 CursorShape::Hollow => proto::CursorShape::CursorHollow,
187 }
188}
189
190/// Deserializes a [`CursorShape`] from the RPC representation.
191pub fn deserialize_cursor_shape(cursor_shape: proto::CursorShape) -> CursorShape {
192 match cursor_shape {
193 proto::CursorShape::CursorBar => CursorShape::Bar,
194 proto::CursorShape::CursorBlock => CursorShape::Block,
195 proto::CursorShape::CursorUnderscore => CursorShape::Underscore,
196 proto::CursorShape::CursorHollow => CursorShape::Hollow,
197 }
198}
199
200/// Serializes a list of diagnostics to be sent over RPC.
201pub fn serialize_diagnostics<'a>(
202 diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<Anchor>>,
203) -> Vec<proto::Diagnostic> {
204 diagnostics
205 .into_iter()
206 .map(|entry| proto::Diagnostic {
207 source: entry.diagnostic.source.clone(),
208 start: Some(serialize_anchor(&entry.range.start)),
209 end: Some(serialize_anchor(&entry.range.end)),
210 message: entry.diagnostic.message.clone(),
211 severity: match entry.diagnostic.severity {
212 DiagnosticSeverity::ERROR => proto::diagnostic::Severity::Error,
213 DiagnosticSeverity::WARNING => proto::diagnostic::Severity::Warning,
214 DiagnosticSeverity::INFORMATION => proto::diagnostic::Severity::Information,
215 DiagnosticSeverity::HINT => proto::diagnostic::Severity::Hint,
216 _ => proto::diagnostic::Severity::None,
217 } as i32,
218 group_id: entry.diagnostic.group_id as u64,
219 is_primary: entry.diagnostic.is_primary,
220 is_valid: true,
221 code: entry.diagnostic.code.clone(),
222 is_disk_based: entry.diagnostic.is_disk_based,
223 is_unnecessary: entry.diagnostic.is_unnecessary,
224 })
225 .collect()
226}
227
228/// Serializes an [`Anchor`] to be sent over RPC.
229pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
230 proto::Anchor {
231 replica_id: anchor.timestamp.replica_id as u32,
232 timestamp: anchor.timestamp.value,
233 offset: anchor.offset as u64,
234 bias: match anchor.bias {
235 Bias::Left => proto::Bias::Left as i32,
236 Bias::Right => proto::Bias::Right as i32,
237 },
238 buffer_id: anchor.buffer_id.map(Into::into),
239 }
240}
241
242// This behavior is currently copied in the collab database, for snapshotting channel notes
243/// Deserializes an [`crate::Operation`] from the RPC representation.
244pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operation> {
245 Ok(
246 match message
247 .variant
248 .ok_or_else(|| anyhow!("missing operation variant"))?
249 {
250 proto::operation::Variant::Edit(edit) => {
251 crate::Operation::Buffer(text::Operation::Edit(deserialize_edit_operation(edit)))
252 }
253 proto::operation::Variant::Undo(undo) => {
254 crate::Operation::Buffer(text::Operation::Undo(UndoOperation {
255 timestamp: clock::Lamport {
256 replica_id: undo.replica_id as ReplicaId,
257 value: undo.lamport_timestamp,
258 },
259 version: deserialize_version(&undo.version),
260 counts: undo
261 .counts
262 .into_iter()
263 .map(|c| {
264 (
265 clock::Lamport {
266 replica_id: c.replica_id as ReplicaId,
267 value: c.lamport_timestamp,
268 },
269 c.count,
270 )
271 })
272 .collect(),
273 }))
274 }
275 proto::operation::Variant::UpdateSelections(message) => {
276 let selections = message
277 .selections
278 .into_iter()
279 .filter_map(|selection| {
280 Some(Selection {
281 id: selection.id as usize,
282 start: deserialize_anchor(selection.start?.anchor?)?,
283 end: deserialize_anchor(selection.end?.anchor?)?,
284 reversed: selection.reversed,
285 goal: SelectionGoal::None,
286 })
287 })
288 .collect::<Vec<_>>();
289
290 crate::Operation::UpdateSelections {
291 lamport_timestamp: clock::Lamport {
292 replica_id: message.replica_id as ReplicaId,
293 value: message.lamport_timestamp,
294 },
295 selections: Arc::from(selections),
296 line_mode: message.line_mode,
297 cursor_shape: deserialize_cursor_shape(
298 proto::CursorShape::from_i32(message.cursor_shape)
299 .ok_or_else(|| anyhow!("Missing cursor shape"))?,
300 ),
301 }
302 }
303 proto::operation::Variant::UpdateDiagnostics(message) => {
304 crate::Operation::UpdateDiagnostics {
305 lamport_timestamp: clock::Lamport {
306 replica_id: message.replica_id as ReplicaId,
307 value: message.lamport_timestamp,
308 },
309 server_id: LanguageServerId(message.server_id as usize),
310 diagnostics: deserialize_diagnostics(message.diagnostics),
311 }
312 }
313 proto::operation::Variant::UpdateCompletionTriggers(message) => {
314 crate::Operation::UpdateCompletionTriggers {
315 triggers: message.triggers,
316 lamport_timestamp: clock::Lamport {
317 replica_id: message.replica_id as ReplicaId,
318 value: message.lamport_timestamp,
319 },
320 }
321 }
322 },
323 )
324}
325
326/// Deserializes an [`EditOperation`] from the RPC representation.
327pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
328 EditOperation {
329 timestamp: clock::Lamport {
330 replica_id: edit.replica_id as ReplicaId,
331 value: edit.lamport_timestamp,
332 },
333 version: deserialize_version(&edit.version),
334 ranges: edit.ranges.into_iter().map(deserialize_range).collect(),
335 new_text: edit.new_text.into_iter().map(Arc::from).collect(),
336 }
337}
338
339/// Deserializes an entry in the undo map from the RPC representation.
340pub fn deserialize_undo_map_entry(
341 entry: proto::UndoMapEntry,
342) -> (clock::Lamport, Vec<(clock::Lamport, u32)>) {
343 (
344 clock::Lamport {
345 replica_id: entry.replica_id as u16,
346 value: entry.local_timestamp,
347 },
348 entry
349 .counts
350 .into_iter()
351 .map(|undo_count| {
352 (
353 clock::Lamport {
354 replica_id: undo_count.replica_id as u16,
355 value: undo_count.lamport_timestamp,
356 },
357 undo_count.count,
358 )
359 })
360 .collect(),
361 )
362}
363
364/// Deserializes selections from the RPC representation.
365pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selection<Anchor>]> {
366 Arc::from(
367 selections
368 .into_iter()
369 .filter_map(deserialize_selection)
370 .collect::<Vec<_>>(),
371 )
372}
373
374/// Deserializes a [`Selection`] from the RPC representation.
375pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<Anchor>> {
376 Some(Selection {
377 id: selection.id as usize,
378 start: deserialize_anchor(selection.start?.anchor?)?,
379 end: deserialize_anchor(selection.end?.anchor?)?,
380 reversed: selection.reversed,
381 goal: SelectionGoal::None,
382 })
383}
384
385/// Deserializes a list of diagnostics from the RPC representation.
386pub fn deserialize_diagnostics(
387 diagnostics: Vec<proto::Diagnostic>,
388) -> Arc<[DiagnosticEntry<Anchor>]> {
389 diagnostics
390 .into_iter()
391 .filter_map(|diagnostic| {
392 Some(DiagnosticEntry {
393 range: deserialize_anchor(diagnostic.start?)?..deserialize_anchor(diagnostic.end?)?,
394 diagnostic: Diagnostic {
395 source: diagnostic.source,
396 severity: match proto::diagnostic::Severity::from_i32(diagnostic.severity)? {
397 proto::diagnostic::Severity::Error => DiagnosticSeverity::ERROR,
398 proto::diagnostic::Severity::Warning => DiagnosticSeverity::WARNING,
399 proto::diagnostic::Severity::Information => DiagnosticSeverity::INFORMATION,
400 proto::diagnostic::Severity::Hint => DiagnosticSeverity::HINT,
401 proto::diagnostic::Severity::None => return None,
402 },
403 message: diagnostic.message,
404 group_id: diagnostic.group_id as usize,
405 code: diagnostic.code,
406 is_primary: diagnostic.is_primary,
407 is_disk_based: diagnostic.is_disk_based,
408 is_unnecessary: diagnostic.is_unnecessary,
409 },
410 })
411 })
412 .collect()
413}
414
415/// Deserializes an [`Anchor`] from the RPC representation.
416pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
417 let buffer_id = if let Some(id) = anchor.buffer_id {
418 Some(BufferId::new(id).ok()?)
419 } else {
420 None
421 };
422 Some(Anchor {
423 timestamp: clock::Lamport {
424 replica_id: anchor.replica_id as ReplicaId,
425 value: anchor.timestamp,
426 },
427 offset: anchor.offset as usize,
428 bias: match proto::Bias::from_i32(anchor.bias)? {
429 proto::Bias::Left => Bias::Left,
430 proto::Bias::Right => Bias::Right,
431 },
432 buffer_id,
433 })
434}
435
436/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`].
437pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<clock::Lamport> {
438 let replica_id;
439 let value;
440 match operation.variant.as_ref()? {
441 proto::operation::Variant::Edit(op) => {
442 replica_id = op.replica_id;
443 value = op.lamport_timestamp;
444 }
445 proto::operation::Variant::Undo(op) => {
446 replica_id = op.replica_id;
447 value = op.lamport_timestamp;
448 }
449 proto::operation::Variant::UpdateDiagnostics(op) => {
450 replica_id = op.replica_id;
451 value = op.lamport_timestamp;
452 }
453 proto::operation::Variant::UpdateSelections(op) => {
454 replica_id = op.replica_id;
455 value = op.lamport_timestamp;
456 }
457 proto::operation::Variant::UpdateCompletionTriggers(op) => {
458 replica_id = op.replica_id;
459 value = op.lamport_timestamp;
460 }
461 }
462
463 Some(clock::Lamport {
464 replica_id: replica_id as ReplicaId,
465 value,
466 })
467}
468
469/// Serializes a [`Completion`] to be sent over RPC.
470pub fn serialize_completion(completion: &Completion) -> proto::Completion {
471 proto::Completion {
472 old_start: Some(serialize_anchor(&completion.old_range.start)),
473 old_end: Some(serialize_anchor(&completion.old_range.end)),
474 new_text: completion.new_text.clone(),
475 server_id: completion.server_id.0 as u64,
476 lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(),
477 }
478}
479
480/// Deserializes a [`Completion`] from the RPC representation.
481pub async fn deserialize_completion(
482 completion: proto::Completion,
483 language: Option<Arc<Language>>,
484 language_registry: &Arc<LanguageRegistry>,
485) -> Result<Completion> {
486 let old_start = completion
487 .old_start
488 .and_then(deserialize_anchor)
489 .ok_or_else(|| anyhow!("invalid old start"))?;
490 let old_end = completion
491 .old_end
492 .and_then(deserialize_anchor)
493 .ok_or_else(|| anyhow!("invalid old end"))?;
494 let lsp_completion = serde_json::from_slice(&completion.lsp_completion)?;
495
496 let mut label = None;
497 if let Some(language) = language {
498 if let Some(adapter) = language_registry.lsp_adapters(&language).first() {
499 label = adapter
500 .label_for_completion(&lsp_completion, &language)
501 .await;
502 }
503 }
504
505 Ok(Completion {
506 old_range: old_start..old_end,
507 new_text: completion.new_text,
508 label: label.unwrap_or_else(|| {
509 CodeLabel::plain(
510 lsp_completion.label.clone(),
511 lsp_completion.filter_text.as_deref(),
512 )
513 }),
514 documentation: None,
515 server_id: LanguageServerId(completion.server_id as usize),
516 lsp_completion,
517 })
518}
519
520/// Serializes a [`CodeAction`] to be sent over RPC.
521pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
522 proto::CodeAction {
523 server_id: action.server_id.0 as u64,
524 start: Some(serialize_anchor(&action.range.start)),
525 end: Some(serialize_anchor(&action.range.end)),
526 lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
527 }
528}
529
530/// Deserializes a [`CodeAction`] from the RPC representation.
531pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
532 let start = action
533 .start
534 .and_then(deserialize_anchor)
535 .ok_or_else(|| anyhow!("invalid start"))?;
536 let end = action
537 .end
538 .and_then(deserialize_anchor)
539 .ok_or_else(|| anyhow!("invalid end"))?;
540 let lsp_action = serde_json::from_slice(&action.lsp_action)?;
541 Ok(CodeAction {
542 server_id: LanguageServerId(action.server_id as usize),
543 range: start..end,
544 lsp_action,
545 })
546}
547
548/// Serializes a [`Transaction`] to be sent over RPC.
549pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
550 proto::Transaction {
551 id: Some(serialize_timestamp(transaction.id)),
552 edit_ids: transaction
553 .edit_ids
554 .iter()
555 .copied()
556 .map(serialize_timestamp)
557 .collect(),
558 start: serialize_version(&transaction.start),
559 }
560}
561
562/// Deserializes a [`Transaction`] from the RPC representation.
563pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transaction> {
564 Ok(Transaction {
565 id: deserialize_timestamp(
566 transaction
567 .id
568 .ok_or_else(|| anyhow!("missing transaction id"))?,
569 ),
570 edit_ids: transaction
571 .edit_ids
572 .into_iter()
573 .map(deserialize_timestamp)
574 .collect(),
575 start: deserialize_version(&transaction.start),
576 })
577}
578
579/// Serializes a [`clock::Lamport`] timestamp to be sent over RPC.
580pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
581 proto::LamportTimestamp {
582 replica_id: timestamp.replica_id as u32,
583 value: timestamp.value,
584 }
585}
586
587/// Deserializes a [`clock::Lamport`] timestamp from the RPC representation.
588pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
589 clock::Lamport {
590 replica_id: timestamp.replica_id as ReplicaId,
591 value: timestamp.value,
592 }
593}
594
595/// Serializes a range of [`FullOffset`]s to be sent over RPC.
596pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
597 proto::Range {
598 start: range.start.0 as u64,
599 end: range.end.0 as u64,
600 }
601}
602
603/// Deserializes a range of [`FullOffset`]s from the RPC representation.
604pub fn deserialize_range(range: proto::Range) -> Range<FullOffset> {
605 FullOffset(range.start as usize)..FullOffset(range.end as usize)
606}
607
608/// Deserializes a clock version from the RPC representation.
609pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global {
610 let mut version = clock::Global::new();
611 for entry in message {
612 version.observe(clock::Lamport {
613 replica_id: entry.replica_id as ReplicaId,
614 value: entry.timestamp,
615 });
616 }
617 version
618}
619
620/// Serializes a clock version to be sent over RPC.
621pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry> {
622 version
623 .iter()
624 .map(|entry| proto::VectorClockEntry {
625 replica_id: entry.replica_id as u32,
626 timestamp: entry.value,
627 })
628 .collect()
629}