proto.rs

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