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