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, 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}