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 [`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.map(Into::into),
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 let buffer_id = if let Some(id) = anchor.buffer_id {
424 Some(BufferId::new(id).ok()?)
425 } else {
426 None
427 };
428 Some(Anchor {
429 timestamp: clock::Lamport {
430 replica_id: anchor.replica_id as ReplicaId,
431 value: anchor.timestamp,
432 },
433 offset: anchor.offset as usize,
434 bias: match proto::Bias::from_i32(anchor.bias)? {
435 proto::Bias::Left => Bias::Left,
436 proto::Bias::Right => Bias::Right,
437 },
438 buffer_id,
439 })
440}
441
442/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`].
443pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<clock::Lamport> {
444 let replica_id;
445 let value;
446 match operation.variant.as_ref()? {
447 proto::operation::Variant::Edit(op) => {
448 replica_id = op.replica_id;
449 value = op.lamport_timestamp;
450 }
451 proto::operation::Variant::Undo(op) => {
452 replica_id = op.replica_id;
453 value = op.lamport_timestamp;
454 }
455 proto::operation::Variant::UpdateDiagnostics(op) => {
456 replica_id = op.replica_id;
457 value = op.lamport_timestamp;
458 }
459 proto::operation::Variant::UpdateSelections(op) => {
460 replica_id = op.replica_id;
461 value = op.lamport_timestamp;
462 }
463 proto::operation::Variant::UpdateCompletionTriggers(op) => {
464 replica_id = op.replica_id;
465 value = op.lamport_timestamp;
466 }
467 }
468
469 Some(clock::Lamport {
470 replica_id: replica_id as ReplicaId,
471 value,
472 })
473}
474
475/// Serializes a [`Completion`] to be sent over RPC.
476pub fn serialize_completion(completion: &Completion) -> proto::Completion {
477 proto::Completion {
478 old_start: Some(serialize_anchor(&completion.old_range.start)),
479 old_end: Some(serialize_anchor(&completion.old_range.end)),
480 new_text: completion.new_text.clone(),
481 server_id: completion.server_id.0 as u64,
482 lsp_completion: serde_json::to_vec(&completion.lsp_completion).unwrap(),
483 }
484}
485
486/// Deserializes a [`Completion`] from the RPC representation.
487pub async fn deserialize_completion(
488 completion: proto::Completion,
489 language: Option<Arc<Language>>,
490) -> Result<Completion> {
491 let old_start = completion
492 .old_start
493 .and_then(deserialize_anchor)
494 .ok_or_else(|| anyhow!("invalid old start"))?;
495 let old_end = completion
496 .old_end
497 .and_then(deserialize_anchor)
498 .ok_or_else(|| anyhow!("invalid old end"))?;
499 let lsp_completion = serde_json::from_slice(&completion.lsp_completion)?;
500
501 let mut label = None;
502 if let Some(language) = language {
503 label = language.label_for_completion(&lsp_completion).await;
504 }
505
506 Ok(Completion {
507 old_range: old_start..old_end,
508 new_text: completion.new_text,
509 label: label.unwrap_or_else(|| {
510 CodeLabel::plain(
511 lsp_completion.label.clone(),
512 lsp_completion.filter_text.as_deref(),
513 )
514 }),
515 documentation: None,
516 server_id: LanguageServerId(completion.server_id as usize),
517 lsp_completion,
518 })
519}
520
521/// Serializes a [`CodeAction`] to be sent over RPC.
522pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
523 proto::CodeAction {
524 server_id: action.server_id.0 as u64,
525 start: Some(serialize_anchor(&action.range.start)),
526 end: Some(serialize_anchor(&action.range.end)),
527 lsp_action: serde_json::to_vec(&action.lsp_action).unwrap(),
528 }
529}
530
531/// Deserializes a [`CodeAction`] from the RPC representation.
532pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
533 let start = action
534 .start
535 .and_then(deserialize_anchor)
536 .ok_or_else(|| anyhow!("invalid start"))?;
537 let end = action
538 .end
539 .and_then(deserialize_anchor)
540 .ok_or_else(|| anyhow!("invalid end"))?;
541 let lsp_action = serde_json::from_slice(&action.lsp_action)?;
542 Ok(CodeAction {
543 server_id: LanguageServerId(action.server_id as usize),
544 range: start..end,
545 lsp_action,
546 })
547}
548
549/// Serializes a [`Transaction`] to be sent over RPC.
550pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
551 proto::Transaction {
552 id: Some(serialize_timestamp(transaction.id)),
553 edit_ids: transaction
554 .edit_ids
555 .iter()
556 .copied()
557 .map(serialize_timestamp)
558 .collect(),
559 start: serialize_version(&transaction.start),
560 }
561}
562
563/// Deserializes a [`Transaction`] from the RPC representation.
564pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transaction> {
565 Ok(Transaction {
566 id: deserialize_timestamp(
567 transaction
568 .id
569 .ok_or_else(|| anyhow!("missing transaction id"))?,
570 ),
571 edit_ids: transaction
572 .edit_ids
573 .into_iter()
574 .map(deserialize_timestamp)
575 .collect(),
576 start: deserialize_version(&transaction.start),
577 })
578}
579
580/// Serializes a [`clock::Lamport`] timestamp to be sent over RPC.
581pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
582 proto::LamportTimestamp {
583 replica_id: timestamp.replica_id as u32,
584 value: timestamp.value,
585 }
586}
587
588/// Deserializes a [`clock::Lamport`] timestamp from the RPC representation.
589pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
590 clock::Lamport {
591 replica_id: timestamp.replica_id as ReplicaId,
592 value: timestamp.value,
593 }
594}
595
596/// Serializes a range of [`FullOffset`]s to be sent over RPC.
597pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
598 proto::Range {
599 start: range.start.0 as u64,
600 end: range.end.0 as u64,
601 }
602}
603
604/// Deserializes a range of [`FullOffset`]s from the RPC representation.
605pub fn deserialize_range(range: proto::Range) -> Range<FullOffset> {
606 FullOffset(range.start as usize)..FullOffset(range.end as usize)
607}
608
609/// Deserializes a clock version from the RPC representation.
610pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global {
611 let mut version = clock::Global::new();
612 for entry in message {
613 version.observe(clock::Lamport {
614 replica_id: entry.replica_id as ReplicaId,
615 value: entry.timestamp,
616 });
617 }
618 version
619}
620
621/// Serializes a clock version to be sent over RPC.
622pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry> {
623 version
624 .iter()
625 .map(|entry| proto::VectorClockEntry {
626 replica_id: entry.replica_id as u32,
627 timestamp: entry.value,
628 })
629 .collect()
630}