proto.rs

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