1use crate::schema::{
22 ColorRole, DecorationKind, EditGroupStatus, GutterDecorationKind, HoverPhase, Paint, Severity,
23};
24use std::rc::Rc;
25
26pub const DECORATION_LEN: usize = 44;
28
29pub const SYNTAX_SPAN_LEN: usize = 28;
31
32pub const CODE_EDIT_LEN: usize = 24;
35
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
38pub enum RecordFieldType {
39 U32,
40 U64,
41 Role {
44 name: &'static str,
45 optional: bool,
46 },
47 Paint,
49 Flag,
51 F32,
53 Text,
56}
57
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60pub struct RecordField {
61 pub name: &'static str,
62 pub offset: u16,
63 pub ty: RecordFieldType,
64}
65
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
71pub struct RecordSchema {
72 pub name: &'static str,
74 pub length: u16,
75 pub fields: &'static [RecordField],
76}
77
78const fn field(name: &'static str, offset: u16, ty: RecordFieldType) -> RecordField {
79 RecordField { name, offset, ty }
80}
81
82pub const DECORATION_RECORD: RecordSchema = RecordSchema {
86 name: "DecorationRecord",
87 length: DECORATION_LEN as u16,
88 fields: &[
89 field("version", 0, RecordFieldType::U32),
90 field(
91 "kind",
92 4,
93 RecordFieldType::Role {
94 name: "DecorationKind",
95 optional: false,
96 },
97 ),
98 field(
99 "severity",
100 6,
101 RecordFieldType::Role {
102 name: "Severity",
103 optional: true,
104 },
105 ),
106 field(
107 "color",
108 8,
109 RecordFieldType::Role {
110 name: "ColorRole",
111 optional: true,
112 },
113 ),
114 field("startLine", 12, RecordFieldType::U32),
115 field("startColumn", 16, RecordFieldType::U32),
116 field("endLine", 20, RecordFieldType::U32),
117 field("endColumn", 24, RecordFieldType::U32),
118 field("id", 28, RecordFieldType::U64),
119 field("text", 36, RecordFieldType::Text),
120 ],
121};
122
123pub const SYNTAX_SPAN_RECORD: RecordSchema = RecordSchema {
125 name: "SyntaxSpanRecord",
126 length: SYNTAX_SPAN_LEN as u16,
127 fields: &[
128 field("version", 0, RecordFieldType::U32),
129 field("startLine", 4, RecordFieldType::U32),
130 field("startColumn", 8, RecordFieldType::U32),
131 field("endLine", 12, RecordFieldType::U32),
132 field("endColumn", 16, RecordFieldType::U32),
133 field("paint", 20, RecordFieldType::Paint),
134 ],
135};
136
137pub const FOLD_LEN: usize = 16;
139
140pub const GUTTER_DECORATION_LEN: usize = 28;
142
143pub const FOLD_RECORD: RecordSchema = RecordSchema {
147 name: "FoldRecord",
148 length: FOLD_LEN as u16,
149 fields: &[
150 field("version", 0, RecordFieldType::U32),
151 field("startLine", 4, RecordFieldType::U32),
152 field("endLine", 8, RecordFieldType::U32),
153 field("collapsed", 12, RecordFieldType::Flag),
154 ],
155};
156
157pub const GUTTER_DECORATION_RECORD: RecordSchema = RecordSchema {
161 name: "GutterDecorationRecord",
162 length: GUTTER_DECORATION_LEN as u16,
163 fields: &[
164 field("version", 0, RecordFieldType::U32),
165 field("line", 4, RecordFieldType::U32),
166 field(
167 "kind",
168 8,
169 RecordFieldType::Role {
170 name: "GutterDecorationKind",
171 optional: false,
172 },
173 ),
174 field(
175 "color",
176 10,
177 RecordFieldType::Role {
178 name: "ColorRole",
179 optional: true,
180 },
181 ),
182 field("id", 12, RecordFieldType::U64),
183 field("glyph", 20, RecordFieldType::U32),
184 ],
185};
186
187pub const CODE_EDITOR_COLORS_LEN: usize = 12;
189
190const fn optional_color(name: &'static str, offset: u16) -> RecordField {
191 field(
192 name,
193 offset,
194 RecordFieldType::Role {
195 name: "ColorRole",
196 optional: true,
197 },
198 )
199}
200
201pub const CODE_EDITOR_COLORS_RECORD: RecordSchema = RecordSchema {
205 name: "CodeEditorColorsRecord",
206 length: CODE_EDITOR_COLORS_LEN as u16,
207 fields: &[
208 optional_color("container", 0),
209 optional_color("gutter", 2),
210 optional_color("lineNumber", 4),
211 optional_color("currentLineNumber", 6),
212 optional_color("currentLine", 8),
213 ],
214};
215
216pub const VIEW_ZONE_LEN: usize = 16;
218
219pub const VIEW_ZONE_RECORD: RecordSchema = RecordSchema {
222 name: "ViewZoneRecord",
223 length: VIEW_ZONE_LEN as u16,
224 fields: &[
225 field("afterLine", 0, RecordFieldType::U32),
226 field("heightLines", 4, RecordFieldType::F32),
227 field("text", 8, RecordFieldType::Text),
228 ],
229};
230
231const fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
232 let mut index = 0;
233 while index < bytes.len() {
234 hash ^= bytes[index] as u64;
235 hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
236 index += 1;
237 }
238 hash
239}
240
241pub(crate) const fn hash_record_schema(mut hash: u64, schema: RecordSchema) -> u64 {
244 hash = hash_bytes(hash, schema.name.as_bytes());
245 hash = hash_bytes(hash, &schema.length.to_le_bytes());
246 let mut index = 0;
247 while index < schema.fields.len() {
248 let field = schema.fields[index];
249 hash = hash_bytes(hash, field.name.as_bytes());
250 hash = hash_bytes(hash, &field.offset.to_le_bytes());
251 hash = match field.ty {
252 RecordFieldType::U32 => hash_bytes(hash, &[1]),
253 RecordFieldType::U64 => hash_bytes(hash, &[2]),
254 RecordFieldType::Role { name, optional } => {
255 let hash = hash_bytes(hash, &[3, optional as u8]);
256 hash_bytes(hash, name.as_bytes())
257 }
258 RecordFieldType::Paint => hash_bytes(hash, &[4]),
259 RecordFieldType::Text => hash_bytes(hash, &[5]),
260 RecordFieldType::Flag => hash_bytes(hash, &[6]),
261 RecordFieldType::F32 => hash_bytes(hash, &[7]),
262 };
263 index += 1;
264 }
265 hash
266}
267
268#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
271pub struct Position {
272 pub line: u32,
273 pub column: u32,
274}
275
276impl Position {
277 pub const fn new(line: u32, column: u32) -> Self {
278 Self { line, column }
279 }
280}
281
282#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
284pub struct CodeRange {
285 pub start: Position,
286 pub end: Position,
287}
288
289impl CodeRange {
290 pub const fn new(start: Position, end: Position) -> Self {
291 Self { start, end }
292 }
293
294 pub const fn at(position: Position) -> Self {
296 Self {
297 start: position,
298 end: position,
299 }
300 }
301
302 pub const fn of(start_line: u32, start_column: u32, end_line: u32, end_column: u32) -> Self {
304 Self {
305 start: Position::new(start_line, start_column),
306 end: Position::new(end_line, end_column),
307 }
308 }
309
310 pub fn is_empty(&self) -> bool {
311 self.start == self.end
312 }
313}
314
315#[derive(Clone, Debug, PartialEq)]
321pub struct Decoration {
322 pub version: u32,
324 pub kind: DecorationKind,
325 pub severity: Option<Severity>,
327 pub color: Option<ColorRole>,
330 pub range: CodeRange,
331 pub id: u64,
334 pub text: String,
336}
337
338impl Decoration {
339 pub fn underline(version: u32, range: CodeRange, severity: Severity) -> Self {
341 Self {
342 version,
343 kind: DecorationKind::Underline,
344 severity: Some(severity),
345 color: None,
346 range,
347 id: 0,
348 text: String::new(),
349 }
350 }
351
352 pub fn code_lens(version: u32, line: u32, text: impl Into<String>, id: u64) -> Self {
354 Self {
355 version,
356 kind: DecorationKind::CodeLens,
357 severity: None,
358 color: None,
359 range: CodeRange::at(Position::new(line, 0)),
360 id,
361 text: text.into(),
362 }
363 }
364
365 pub fn hover_anchor(version: u32, range: CodeRange, id: u64) -> Self {
367 Self {
368 version,
369 kind: DecorationKind::HoverAnchor,
370 severity: None,
371 color: None,
372 range,
373 id,
374 text: String::new(),
375 }
376 }
377
378 pub fn ghost_text(version: u32, at: Position, text: impl Into<String>, id: u64) -> Self {
381 Self {
382 version,
383 kind: DecorationKind::GhostText,
384 severity: None,
385 color: None,
386 range: CodeRange::at(at),
387 id,
388 text: text.into(),
389 }
390 }
391
392 pub fn line_background(version: u32, line_start: u32, line_end: u32, color: ColorRole) -> Self {
397 Self {
398 version,
399 kind: DecorationKind::LineBackground,
400 severity: None,
401 color: Some(color),
402 range: CodeRange::of(line_start, 0, line_end, 0),
403 id: 0,
404 text: String::new(),
405 }
406 }
407
408 pub fn range_background(version: u32, range: CodeRange, color: ColorRole) -> Self {
411 Self {
412 version,
413 kind: DecorationKind::RangeBackground,
414 severity: None,
415 color: Some(color),
416 range,
417 id: 0,
418 text: String::new(),
419 }
420 }
421
422 pub fn with_color(mut self, role: ColorRole) -> Self {
424 self.color = Some(role);
425 self
426 }
427
428 pub fn with_id(mut self, id: u64) -> Self {
429 self.id = id;
430 self
431 }
432}
433
434fn put_u16(out: &mut Vec<u8>, value: u16) {
435 out.extend_from_slice(&value.to_le_bytes());
436}
437
438fn put_u32(out: &mut Vec<u8>, value: u32) {
439 out.extend_from_slice(&value.to_le_bytes());
440}
441
442fn put_u64(out: &mut Vec<u8>, value: u64) {
443 out.extend_from_slice(&value.to_le_bytes());
444}
445
446fn put_range(out: &mut Vec<u8>, range: CodeRange) {
447 put_u32(out, range.start.line);
448 put_u32(out, range.start.column);
449 put_u32(out, range.end.line);
450 put_u32(out, range.end.column);
451}
452
453fn word(record: &[u8], at: usize) -> u32 {
454 u32::from_le_bytes([record[at], record[at + 1], record[at + 2], record[at + 3]])
455}
456
457fn half(record: &[u8], at: usize) -> u16 {
458 u16::from_le_bytes([record[at], record[at + 1]])
459}
460
461fn long(record: &[u8], at: usize) -> u64 {
462 u64::from(word(record, at)) | (u64::from(word(record, at + 4)) << 32)
463}
464
465fn range_at(record: &[u8], at: usize) -> CodeRange {
466 CodeRange::of(
467 word(record, at),
468 word(record, at + 4),
469 word(record, at + 8),
470 word(record, at + 12),
471 )
472}
473
474#[derive(Clone, Default, PartialEq)]
480pub struct Decorations {
481 bytes: Option<Rc<[u8]>>,
482}
483
484impl std::fmt::Debug for Decorations {
485 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
486 formatter
487 .debug_struct("Decorations")
488 .field("decorations", &self.decorations().len())
489 .finish()
490 }
491}
492
493impl Decorations {
494 pub fn new(decorations: impl IntoIterator<Item = Decoration>) -> Self {
495 let decorations: Vec<Decoration> = decorations.into_iter().collect();
496 let records_len = decorations.len() * DECORATION_LEN;
497 let mut bytes = Vec::with_capacity(records_len);
498 let mut texts = Vec::new();
499 for decoration in &decorations {
500 put_u32(&mut bytes, decoration.version);
501 put_u16(&mut bytes, decoration.kind as u16);
502 put_u16(
503 &mut bytes,
504 decoration.severity.map_or(0, |value| value as u16),
505 );
506 put_u16(&mut bytes, decoration.color.map_or(0, |value| value as u16));
507 put_u16(&mut bytes, 0);
508 put_range(&mut bytes, decoration.range);
509 put_u64(&mut bytes, decoration.id);
510 put_u32(&mut bytes, (records_len + texts.len()) as u32);
513 put_u32(&mut bytes, decoration.text.len() as u32);
514 texts.extend_from_slice(decoration.text.as_bytes());
515 }
516 bytes.extend_from_slice(&texts);
517 Self::from_bytes(bytes)
518 }
519
520 pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
522 let bytes = bytes.into();
523 Self {
524 bytes: (!bytes.is_empty()).then_some(bytes),
525 }
526 }
527
528 pub fn as_bytes(&self) -> &[u8] {
529 self.bytes.as_deref().unwrap_or(&[])
530 }
531
532 pub fn is_empty(&self) -> bool {
533 self.bytes.is_none()
534 }
535
536 pub fn decorations(&self) -> Vec<Decoration> {
543 let bytes = self.as_bytes();
544 let mut end = bytes.len();
545 let mut at = 0;
546 let mut found = Vec::new();
547 while at + DECORATION_LEN <= end {
548 let record = &bytes[at..at + DECORATION_LEN];
549 at += DECORATION_LEN;
550 let text_offset = word(record, 36) as usize;
551 let text_len = word(record, 40) as usize;
552 if text_len > 0 {
553 end = end.min(text_offset);
554 }
555 let Ok(kind) = DecorationKind::try_from(half(record, 4)) else {
556 continue;
557 };
558 let severity = match half(record, 6) {
559 0 => None,
560 tag => match Severity::try_from(tag) {
561 Ok(severity) => Some(severity),
562 Err(()) => continue,
563 },
564 };
565 let color = match half(record, 8) {
566 0 => None,
567 tag => match ColorRole::try_from(tag) {
568 Ok(role) => Some(role),
569 Err(()) => continue,
570 },
571 };
572 let text = if text_len == 0 {
573 String::new()
574 } else {
575 match bytes
576 .get(text_offset..text_offset + text_len)
577 .and_then(|run| std::str::from_utf8(run).ok())
578 {
579 Some(text) => text.to_owned(),
580 None => continue,
581 }
582 };
583 found.push(Decoration {
584 version: word(record, 0),
585 kind,
586 severity,
587 color,
588 range: range_at(record, 12),
589 id: long(record, 28),
590 text,
591 });
592 }
593 found
594 }
595}
596
597#[derive(Clone, Copy, Debug, PartialEq)]
603pub struct SyntaxSpan {
604 pub version: u32,
605 pub range: CodeRange,
606 pub paint: Paint,
607}
608
609impl SyntaxSpan {
610 pub const fn new(version: u32, range: CodeRange, paint: Paint) -> Self {
611 Self {
612 version,
613 range,
614 paint,
615 }
616 }
617
618 pub const fn role(version: u32, range: CodeRange, role: ColorRole) -> Self {
620 Self::new(version, range, Paint::Role(role))
621 }
622}
623
624#[derive(Clone, Default, PartialEq)]
626pub struct SyntaxSpans {
627 bytes: Option<Rc<[u8]>>,
628}
629
630impl std::fmt::Debug for SyntaxSpans {
631 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632 formatter
633 .debug_struct("SyntaxSpans")
634 .field("spans", &(self.as_bytes().len() / SYNTAX_SPAN_LEN))
635 .finish()
636 }
637}
638
639impl SyntaxSpans {
640 pub fn new(spans: impl IntoIterator<Item = SyntaxSpan>) -> Self {
641 let mut bytes = Vec::new();
642 for span in spans {
643 put_u32(&mut bytes, span.version);
644 put_range(&mut bytes, span.range);
645 put_u64(&mut bytes, span.paint.to_bits());
646 }
647 Self::from_bytes(bytes)
648 }
649
650 pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
651 let bytes = bytes.into();
652 Self {
653 bytes: (!bytes.is_empty()).then_some(bytes),
654 }
655 }
656
657 pub fn as_bytes(&self) -> &[u8] {
658 self.bytes.as_deref().unwrap_or(&[])
659 }
660
661 pub fn is_empty(&self) -> bool {
662 self.bytes.is_none()
663 }
664
665 pub fn spans(&self) -> Vec<SyntaxSpan> {
668 self.as_bytes()
669 .chunks_exact(SYNTAX_SPAN_LEN)
670 .filter_map(|record| {
671 Some(SyntaxSpan {
672 version: word(record, 0),
673 range: range_at(record, 4),
674 paint: Paint::from_bits(long(record, 20))?,
675 })
676 })
677 .collect()
678 }
679}
680
681pub fn byte_offset(text: &str, position: Position) -> Option<usize> {
685 let bytes = text.as_bytes();
686 let mut start = 0;
687 for _ in 0..position.line {
688 let at = bytes[start..]
689 .iter()
690 .position(|byte| *byte == b'\n' || *byte == b'\r')?
691 + start;
692 start = if bytes[at] == b'\r' && bytes.get(at + 1) == Some(&b'\n') {
693 at + 2
694 } else {
695 at + 1
696 };
697 }
698 let end = bytes[start..]
699 .iter()
700 .position(|byte| *byte == b'\n' || *byte == b'\r')
701 .map_or(bytes.len(), |offset| start + offset);
702 let mut units = 0_u32;
703 let mut offset = start;
704 for character in text[start..end].chars() {
705 if units == position.column {
706 return Some(offset);
707 }
708 units += character.len_utf16() as u32;
709 offset += character.len_utf8();
710 if units > position.column {
711 return None;
712 }
713 }
714 (units == position.column).then_some(offset)
715}
716
717#[derive(Clone, Debug, PartialEq)]
723pub struct CodeChange {
724 pub version: u32,
725 pub range: CodeRange,
726 pub text: String,
727}
728
729impl CodeChange {
730 pub fn apply_to(&self, text: &mut String) -> bool {
737 let (Some(start), Some(end)) = (
738 byte_offset(text, self.range.start),
739 byte_offset(text, self.range.end),
740 ) else {
741 return false;
742 };
743 if start > end {
744 return false;
745 }
746 text.replace_range(start..end, &self.text);
747 true
748 }
749}
750
751#[derive(Clone, Copy, Debug, Eq, PartialEq)]
754pub struct EditRejected {
755 pub request_id: u32,
757 pub base_version: u32,
758 pub current_version: u32,
760 pub range: CodeRange,
762}
763
764#[derive(Clone, Copy, Debug, Eq, PartialEq)]
770pub struct CodeHover {
771 pub decoration: Option<u64>,
773 pub position: Position,
774 pub phase: HoverPhase,
775}
776
777#[derive(Clone, Copy, Debug, Eq, PartialEq)]
782pub struct SaveRequest {
783 pub version: u32,
785}
786
787#[derive(Clone, Copy, Debug, Eq, PartialEq)]
789pub struct Fold {
790 pub version: u32,
792 pub start_line: u32,
794 pub end_line: u32,
797 pub collapsed: bool,
798}
799
800impl Fold {
801 pub const fn new(version: u32, start_line: u32, end_line: u32) -> Self {
803 Self {
804 version,
805 start_line,
806 end_line,
807 collapsed: false,
808 }
809 }
810
811 pub const fn collapsed(mut self) -> Self {
813 self.collapsed = true;
814 self
815 }
816}
817
818#[derive(Clone, Default, PartialEq)]
820pub struct Folds {
821 bytes: Option<Rc<[u8]>>,
822}
823
824impl std::fmt::Debug for Folds {
825 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
826 formatter
827 .debug_struct("Folds")
828 .field("folds", &(self.as_bytes().len() / FOLD_LEN))
829 .finish()
830 }
831}
832
833impl Folds {
834 pub fn new(folds: impl IntoIterator<Item = Fold>) -> Self {
835 let mut bytes = Vec::new();
836 for fold in folds {
837 put_u32(&mut bytes, fold.version);
838 put_u32(&mut bytes, fold.start_line);
839 put_u32(&mut bytes, fold.end_line);
840 put_u32(&mut bytes, u32::from(fold.collapsed));
841 }
842 Self::from_bytes(bytes)
843 }
844
845 pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
846 let bytes = bytes.into();
847 Self {
848 bytes: (!bytes.is_empty()).then_some(bytes),
849 }
850 }
851
852 pub fn as_bytes(&self) -> &[u8] {
853 self.bytes.as_deref().unwrap_or(&[])
854 }
855
856 pub fn is_empty(&self) -> bool {
857 self.bytes.is_none()
858 }
859
860 pub fn folds(&self) -> Vec<Fold> {
863 self.as_bytes()
864 .chunks_exact(FOLD_LEN)
865 .filter_map(|record| {
866 Some(Fold {
867 version: word(record, 0),
868 start_line: word(record, 4),
869 end_line: word(record, 8),
870 collapsed: match word(record, 12) {
871 0 => false,
872 1 => true,
873 _ => return None,
874 },
875 })
876 })
877 .collect()
878 }
879}
880
881#[derive(Clone, Copy, Debug, Eq, PartialEq)]
887pub struct GutterDecoration {
888 pub version: u32,
890 pub line: u32,
891 pub kind: GutterDecorationKind,
892 pub color: Option<ColorRole>,
893 pub id: u64,
896 pub glyph: u32,
898}
899
900impl GutterDecoration {
901 pub const fn dot(version: u32, line: u32, id: u64) -> Self {
903 Self {
904 version,
905 line,
906 kind: GutterDecorationKind::Dot,
907 color: None,
908 id,
909 glyph: 0,
910 }
911 }
912
913 pub const fn bar(version: u32, line: u32, id: u64) -> Self {
915 Self {
916 version,
917 line,
918 kind: GutterDecorationKind::Bar,
919 color: None,
920 id,
921 glyph: 0,
922 }
923 }
924
925 pub const fn glyph(version: u32, line: u32, glyph: char, id: u64) -> Self {
927 Self {
928 version,
929 line,
930 kind: GutterDecorationKind::Glyph,
931 color: None,
932 id,
933 glyph: glyph as u32,
934 }
935 }
936
937 pub const fn with_color(mut self, role: ColorRole) -> Self {
939 self.color = Some(role);
940 self
941 }
942
943 pub const fn with_id(mut self, id: u64) -> Self {
944 self.id = id;
945 self
946 }
947}
948
949#[derive(Clone, Default, PartialEq)]
951pub struct GutterDecorations {
952 bytes: Option<Rc<[u8]>>,
953}
954
955impl std::fmt::Debug for GutterDecorations {
956 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
957 formatter
958 .debug_struct("GutterDecorations")
959 .field(
960 "decorations",
961 &(self.as_bytes().len() / GUTTER_DECORATION_LEN),
962 )
963 .finish()
964 }
965}
966
967impl GutterDecorations {
968 pub fn new(decorations: impl IntoIterator<Item = GutterDecoration>) -> Self {
969 let mut bytes = Vec::new();
970 for decoration in decorations {
971 put_u32(&mut bytes, decoration.version);
972 put_u32(&mut bytes, decoration.line);
973 put_u16(&mut bytes, decoration.kind as u16);
974 put_u16(&mut bytes, decoration.color.map_or(0, |value| value as u16));
975 put_u64(&mut bytes, decoration.id);
976 put_u32(&mut bytes, decoration.glyph);
977 put_u32(&mut bytes, 0);
978 }
979 Self::from_bytes(bytes)
980 }
981
982 pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
983 let bytes = bytes.into();
984 Self {
985 bytes: (!bytes.is_empty()).then_some(bytes),
986 }
987 }
988
989 pub fn as_bytes(&self) -> &[u8] {
990 self.bytes.as_deref().unwrap_or(&[])
991 }
992
993 pub fn is_empty(&self) -> bool {
994 self.bytes.is_none()
995 }
996
997 pub fn decorations(&self) -> Vec<GutterDecoration> {
1000 self.as_bytes()
1001 .chunks_exact(GUTTER_DECORATION_LEN)
1002 .filter_map(|record| {
1003 let kind = GutterDecorationKind::try_from(half(record, 8)).ok()?;
1004 let color = match half(record, 10) {
1005 0 => None,
1006 tag => Some(ColorRole::try_from(tag).ok()?),
1007 };
1008 Some(GutterDecoration {
1009 version: word(record, 0),
1010 line: word(record, 4),
1011 kind,
1012 color,
1013 id: long(record, 12),
1014 glyph: word(record, 20),
1015 })
1016 })
1017 .collect()
1018 }
1019}
1020
1021#[derive(Clone, Debug, PartialEq)]
1030pub struct ViewZone {
1031 pub after_line: u32,
1032 pub height_lines: f32,
1033 pub text: String,
1034}
1035
1036impl ViewZone {
1037 pub fn new(after_line: u32, height_lines: f32) -> Self {
1038 Self {
1039 after_line,
1040 height_lines,
1041 text: String::new(),
1042 }
1043 }
1044
1045 pub fn with_text(mut self, text: impl Into<String>) -> Self {
1047 self.text = text.into();
1048 self
1049 }
1050}
1051
1052#[derive(Clone, Default, PartialEq)]
1056pub struct ViewZones {
1057 bytes: Option<Rc<[u8]>>,
1058}
1059
1060impl std::fmt::Debug for ViewZones {
1061 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1062 formatter
1063 .debug_struct("ViewZones")
1064 .field("zones", &self.zones().len())
1065 .finish()
1066 }
1067}
1068
1069impl ViewZones {
1070 pub fn new(zones: impl IntoIterator<Item = ViewZone>) -> Self {
1071 let zones: Vec<ViewZone> = zones.into_iter().collect();
1072 let records_len = zones.len() * VIEW_ZONE_LEN;
1073 let mut bytes = Vec::with_capacity(records_len);
1074 let mut texts = Vec::new();
1075 for zone in &zones {
1076 put_u32(&mut bytes, zone.after_line);
1077 put_u32(&mut bytes, zone.height_lines.to_bits());
1078 put_u32(&mut bytes, (records_len + texts.len()) as u32);
1080 put_u32(&mut bytes, zone.text.len() as u32);
1081 texts.extend_from_slice(zone.text.as_bytes());
1082 }
1083 bytes.extend_from_slice(&texts);
1084 Self::from_bytes(bytes)
1085 }
1086
1087 pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
1089 let bytes = bytes.into();
1090 Self {
1091 bytes: (!bytes.is_empty()).then_some(bytes),
1092 }
1093 }
1094
1095 pub fn as_bytes(&self) -> &[u8] {
1096 self.bytes.as_deref().unwrap_or(&[])
1097 }
1098
1099 pub fn is_empty(&self) -> bool {
1100 self.bytes.is_none()
1101 }
1102
1103 pub fn zones(&self) -> Vec<ViewZone> {
1106 let bytes = self.as_bytes();
1107 let mut end = bytes.len();
1108 let mut at = 0;
1109 let mut found = Vec::new();
1110 while at + VIEW_ZONE_LEN <= end {
1111 let record = &bytes[at..at + VIEW_ZONE_LEN];
1112 at += VIEW_ZONE_LEN;
1113 let text_offset = word(record, 8) as usize;
1114 let text_len = word(record, 12) as usize;
1115 if text_len > 0 {
1116 end = end.min(text_offset);
1117 }
1118 let text = if text_len == 0 {
1119 String::new()
1120 } else {
1121 match bytes
1122 .get(text_offset..text_offset + text_len)
1123 .and_then(|run| std::str::from_utf8(run).ok())
1124 {
1125 Some(text) => text.to_owned(),
1126 None => continue,
1127 }
1128 };
1129 found.push(ViewZone {
1130 after_line: word(record, 0),
1131 height_lines: f32::from_bits(word(record, 4)),
1132 text,
1133 });
1134 }
1135 found
1136 }
1137}
1138
1139pub fn escape_snippet_text(text: &str) -> String {
1143 let mut escaped = String::with_capacity(text.len());
1144 for character in text.chars() {
1145 if matches!(character, '$' | '}' | '\\') {
1146 escaped.push('\\');
1147 }
1148 escaped.push(character);
1149 }
1150 escaped
1151}
1152
1153#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1160pub enum WordWrap {
1161 #[default]
1163 Off,
1164 Viewport,
1166 Column(u32),
1168 Bounded(u32),
1170}
1171
1172impl WordWrap {
1173 pub fn to_value(self) -> i64 {
1176 let (mode, columns) = match self {
1177 Self::Off => (crate::schema::WordWrapMode::Off, 0),
1178 Self::Viewport => (crate::schema::WordWrapMode::Viewport, 0),
1179 Self::Column(columns) => (crate::schema::WordWrapMode::Column, columns),
1180 Self::Bounded(columns) => (crate::schema::WordWrapMode::Bounded, columns),
1181 };
1182 i64::from(u16::from(mode)) | (i64::from(columns) << 16)
1183 }
1184
1185 pub fn from_value(value: i64) -> Option<Self> {
1188 let columns = u32::try_from(value >> 16).ok()?;
1189 let mode = u16::try_from(value & 0xffff).ok()?;
1190 match mode {
1191 0 if columns == 0 => Some(Self::Off),
1192 _ if mode == u16::from(crate::schema::WordWrapMode::Viewport) && columns == 0 => {
1193 Some(Self::Viewport)
1194 }
1195 _ if mode == u16::from(crate::schema::WordWrapMode::Column) && columns > 0 => {
1196 Some(Self::Column(columns))
1197 }
1198 _ if mode == u16::from(crate::schema::WordWrapMode::Bounded) && columns > 0 => {
1199 Some(Self::Bounded(columns))
1200 }
1201 _ => None,
1202 }
1203 }
1204}
1205
1206pub const SELECTION_LEN: usize = 16;
1209
1210pub fn encode_selections(selections: &[(Position, Position)], out: &mut Vec<u8>) {
1213 out.reserve(selections.len() * SELECTION_LEN);
1214 for (anchor, active) in selections {
1215 put_u32(out, anchor.line);
1216 put_u32(out, anchor.column);
1217 put_u32(out, active.line);
1218 put_u32(out, active.column);
1219 }
1220}
1221
1222pub fn decode_selections(bytes: &[u8]) -> Option<Vec<(Position, Position)>> {
1225 if bytes.len() % SELECTION_LEN != 0 {
1226 return None;
1227 }
1228 Some(
1229 bytes
1230 .chunks_exact(SELECTION_LEN)
1231 .map(|record| {
1232 (
1233 Position::new(word(record, 0), word(record, 4)),
1234 Position::new(word(record, 8), word(record, 12)),
1235 )
1236 })
1237 .collect(),
1238 )
1239}
1240
1241#[derive(Clone, Debug, PartialEq, Eq)]
1245pub struct SelectionsChange {
1246 pub version: u32,
1247 pub selections: Vec<(Position, Position)>,
1249}
1250
1251impl SelectionsChange {
1252 pub fn from_payload(payload: &crate::schema::EventPayload<'_>) -> Option<Self> {
1254 match payload {
1255 crate::schema::EventPayload::SelectionsChanged {
1256 version,
1257 selections,
1258 } => Some(Self {
1259 version: *version,
1260 selections: decode_selections(selections)?,
1261 }),
1262 _ => None,
1263 }
1264 }
1265}
1266
1267#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1273pub struct SelectionChange {
1274 pub version: u32,
1275 pub anchor: Position,
1276 pub active: Position,
1277}
1278
1279impl SelectionChange {
1280 pub fn is_caret(&self) -> bool {
1282 self.anchor == self.active
1283 }
1284
1285 pub fn range(&self) -> CodeRange {
1287 CodeRange::new(self.anchor.min(self.active), self.anchor.max(self.active))
1288 }
1289}
1290
1291#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1293pub struct VisibleRange {
1294 pub version: u32,
1295 pub first_line: u32,
1297 pub last_line: u32,
1299}
1300
1301#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1303pub struct FoldChange {
1304 pub start_line: u32,
1306 pub collapsed: bool,
1307}
1308
1309#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1315pub struct CodeEditorColors {
1316 pub container: Option<ColorRole>,
1318 pub gutter: Option<ColorRole>,
1320 pub line_number: Option<ColorRole>,
1321 pub current_line_number: Option<ColorRole>,
1323 pub current_line: Option<ColorRole>,
1325}
1326
1327impl CodeEditorColors {
1328 pub const fn new() -> Self {
1330 Self {
1331 container: None,
1332 gutter: None,
1333 line_number: None,
1334 current_line_number: None,
1335 current_line: None,
1336 }
1337 }
1338
1339 pub const fn container(mut self, role: ColorRole) -> Self {
1340 self.container = Some(role);
1341 self
1342 }
1343
1344 pub const fn gutter(mut self, role: ColorRole) -> Self {
1345 self.gutter = Some(role);
1346 self
1347 }
1348
1349 pub const fn line_number(mut self, role: ColorRole) -> Self {
1350 self.line_number = Some(role);
1351 self
1352 }
1353
1354 pub const fn current_line_number(mut self, role: ColorRole) -> Self {
1355 self.current_line_number = Some(role);
1356 self
1357 }
1358
1359 pub const fn current_line(mut self, role: ColorRole) -> Self {
1360 self.current_line = Some(role);
1361 self
1362 }
1363
1364 pub fn is_empty(&self) -> bool {
1367 *self == Self::new()
1368 }
1369
1370 pub fn to_bytes(&self) -> [u8; CODE_EDITOR_COLORS_LEN] {
1372 let mut bytes = [0_u8; CODE_EDITOR_COLORS_LEN];
1373 let slots = [
1374 self.container,
1375 self.gutter,
1376 self.line_number,
1377 self.current_line_number,
1378 self.current_line,
1379 ];
1380 for (index, slot) in slots.into_iter().enumerate() {
1381 let tag = slot.map_or(0, |role| role as u16);
1382 bytes[index * 2..index * 2 + 2].copy_from_slice(&tag.to_le_bytes());
1383 }
1384 bytes
1385 }
1386
1387 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
1390 if bytes.len() != CODE_EDITOR_COLORS_LEN {
1391 return None;
1392 }
1393 let slot = |index: usize| -> Option<Option<ColorRole>> {
1394 match half(bytes, index * 2) {
1395 0 => Some(None),
1396 tag => ColorRole::try_from(tag).ok().map(Some),
1397 }
1398 };
1399 Some(Self {
1400 container: slot(0)?,
1401 gutter: slot(1)?,
1402 line_number: slot(2)?,
1403 current_line_number: slot(3)?,
1404 current_line: slot(4)?,
1405 })
1406 }
1407}
1408
1409#[derive(Clone, Copy, Debug, PartialEq)]
1411pub struct CodeMetrics {
1412 pub line_height_px: f64,
1416}
1417
1418#[derive(Clone, Copy, Debug, PartialEq)]
1420pub struct CodeScroll {
1421 pub offset_px: f64,
1424}
1425
1426#[derive(Clone, Copy, Debug, Default, PartialEq)]
1433pub struct CodeRect {
1434 pub x: f32,
1435 pub y: f32,
1436 pub width: f32,
1437 pub height: f32,
1438}
1439
1440impl CodeRect {
1441 pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
1442 Self {
1443 x,
1444 y,
1445 width,
1446 height,
1447 }
1448 }
1449
1450 pub fn bottom(&self) -> f32 {
1452 self.y + self.height
1453 }
1454
1455 pub fn right(&self) -> f32 {
1457 self.x + self.width
1458 }
1459}
1460
1461#[derive(Clone, Copy, Debug, PartialEq)]
1467pub struct CaretRect {
1468 pub position: Position,
1470 pub rect: CodeRect,
1471}
1472
1473impl CaretRect {
1474 pub fn from_payload(payload: &crate::schema::EventPayload<'_>) -> Option<Self> {
1476 match *payload {
1477 crate::schema::EventPayload::CaretRect { line, column, rect } => Some(Self {
1478 position: Position::new(line, column),
1479 rect,
1480 }),
1481 _ => None,
1482 }
1483 }
1484}
1485
1486pub type PositionAnswer = Option<CodeRect>;
1489
1490type PositionCallback = Box<dyn FnOnce(PositionAnswer)>;
1491
1492thread_local! {
1493 static POSITION_QUERIES: std::cell::RefCell<Vec<(u32, PositionCallback)>> =
1495 const { std::cell::RefCell::new(Vec::new()) };
1496 static NEXT_POSITION_QUERY: std::cell::Cell<u32> = const { std::cell::Cell::new(1) };
1498}
1499
1500pub fn position_query(on_answer: impl FnOnce(PositionAnswer) + 'static) -> u32 {
1513 let request = NEXT_POSITION_QUERY.with(|next| {
1514 let mut id = next.get();
1515 if id == 0 {
1516 id = 1;
1517 }
1518 next.set(id.wrapping_add(1));
1519 id
1520 });
1521 POSITION_QUERIES.with_borrow_mut(|waiting| waiting.push((request, Box::new(on_answer))));
1522 request
1523}
1524
1525pub(crate) fn deliver_position_rect(request: u32, answer: PositionAnswer) -> bool {
1528 let callback = POSITION_QUERIES.with_borrow_mut(|waiting| {
1530 waiting
1531 .iter()
1532 .position(|(id, _)| *id == request)
1533 .map(|at| waiting.remove(at).1)
1534 });
1535 match callback {
1536 Some(callback) => {
1537 callback(answer);
1538 true
1539 }
1540 None => false,
1541 }
1542}
1543
1544#[doc(hidden)]
1547pub fn reset_position_queries() {
1548 POSITION_QUERIES.with_borrow_mut(Vec::clear);
1549}
1550
1551#[derive(Clone, Debug)]
1559pub struct CodePointer {
1560 pub event: crate::PointerEvent,
1561 pub position: Option<Position>,
1564}
1565
1566impl CodePointer {
1567 pub fn from_payload(payload: &crate::schema::EventPayload<'_>) -> Option<Self> {
1569 match *payload {
1570 crate::schema::EventPayload::CodePointer {
1571 kind,
1572 pointer_id,
1573 pointer_type,
1574 x,
1575 y,
1576 buttons,
1577 button,
1578 modifiers,
1579 click_count,
1580 delta_x,
1581 delta_y,
1582 delta_mode,
1583 timestamp_nanos,
1584 position,
1585 } => Some(Self {
1586 event: crate::PointerEvent::new(
1587 kind,
1588 pointer_id,
1589 pointer_type,
1590 x,
1591 y,
1592 buttons,
1593 button,
1594 modifiers,
1595 click_count,
1596 delta_x,
1597 delta_y,
1598 delta_mode,
1599 timestamp_nanos,
1600 ),
1601 position,
1602 }),
1603 _ => None,
1604 }
1605 }
1606}
1607
1608pub fn encode_edit_group(
1616 edits: &[(CodeRange, &str)],
1617 out: &mut Vec<u8>,
1618) -> Result<(), crate::protocol::ProtocolError> {
1619 use crate::protocol::ProtocolError;
1620 let count = u32::try_from(edits.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1621 let texts: usize = edits.iter().map(|(_, text)| text.len()).sum();
1622 out.reserve(4 + edits.len() * CODE_EDIT_LEN + texts);
1623 put_u32(out, count);
1624 let mut offset = 0_u32;
1625 for (range, text) in edits {
1626 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1627 put_range(out, *range);
1628 put_u32(out, offset);
1629 put_u32(out, len);
1630 offset = offset
1631 .checked_add(len)
1632 .ok_or(ProtocolError::LengthOverflow)?;
1633 }
1634 for (_, text) in edits {
1635 out.extend_from_slice(text.as_bytes());
1636 }
1637 Ok(())
1638}
1639
1640pub fn decode_edit_group(bytes: &[u8]) -> Option<Vec<(CodeRange, &str)>> {
1646 if bytes.len() < 4 {
1647 return None;
1648 }
1649 let count = word(bytes, 0) as usize;
1650 let texts_start = count.checked_mul(CODE_EDIT_LEN)?.checked_add(4)?;
1651 if texts_start > bytes.len() {
1652 return None;
1653 }
1654 let mut edits = Vec::with_capacity(count);
1655 let mut texts_end = texts_start;
1656 for index in 0..count {
1657 let record = &bytes[4 + index * CODE_EDIT_LEN..4 + (index + 1) * CODE_EDIT_LEN];
1658 let start = texts_start.checked_add(word(record, 16) as usize)?;
1659 let end = start.checked_add(word(record, 20) as usize)?;
1660 let text = std::str::from_utf8(bytes.get(start..end)?).ok()?;
1661 texts_end = texts_end.max(end);
1662 edits.push((range_at(record, 0), text));
1663 }
1664 (texts_end == bytes.len()).then_some(edits)
1665}
1666
1667#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1677pub struct EditGroupResult {
1678 pub request_id: u32,
1680 pub status: EditGroupStatus,
1681 pub base_version: u32,
1683 pub current_version: u32,
1686 pub index: u32,
1689}
1690
1691impl EditGroupResult {
1692 pub fn from_payload(payload: &crate::schema::EventPayload<'_>) -> Option<Self> {
1694 match *payload {
1695 crate::schema::EventPayload::CodeEditGroupResult {
1696 request_id,
1697 status,
1698 base_version,
1699 current_version,
1700 index,
1701 } => Some(Self {
1702 request_id,
1703 status,
1704 base_version,
1705 current_version,
1706 index,
1707 }),
1708 _ => None,
1709 }
1710 }
1711}