Skip to main content

compose_rust/
code.rs

1//! A code editor's document as the Host sees it.
2//!
3//! The Renderer owns the text. It owns the caret, the selection, the scroll position, the
4//! undo history and the composition an input method is building, for the same reason a
5//! text field owns them: a round trip to this side per keystroke would add a frame of
6//! latency to every key. What the Host gets is each committed edit, in order, with the
7//! version of the document it produced, which is exactly what a language server or an
8//! extension host needs to follow along.
9//!
10//! What the Host says back is said as ranges. Colour runs for the syntax and decorations
11//! for diagnostics, lenses, hover anchors and inline suggestions, each a blob of
12//! fixed-length records carrying the document version its ranges were written against.
13//! The Renderer moves them along with whatever the reader typed since, so a list that took
14//! a language server a second to compute still lands on the text it was about.
15//!
16//! Positions are a line and a column counted from zero, and a column counts UTF-16 code
17//! units. That is the unit a language server and VS Code count in, and any other unit
18//! would make every application recount every range on the way through. A line ends at
19//! `\n`, `\r\n` or a lone `\r`, which is also the language server's rule.
20
21use crate::schema::{
22    ColorRole, DecorationKind, EditGroupStatus, GutterDecorationKind, HoverPhase, Paint, Severity,
23};
24use std::rc::Rc;
25
26/// Bytes of one decoration record.
27pub const DECORATION_LEN: usize = 44;
28
29/// Bytes of one syntax colour run record.
30pub const SYNTAX_SPAN_LEN: usize = 28;
31
32/// Bytes of one edit record inside a grouped edit: the range as four `u32`, then the
33/// text's `(offset, length)`.
34pub const CODE_EDIT_LEN: usize = 24;
35
36/// What one field of a code editor record holds.
37#[derive(Clone, Copy, Debug, Eq, PartialEq)]
38pub enum RecordFieldType {
39    U32,
40    U64,
41    /// A role tag in a `u16`. `optional` says that zero means "not given" rather than a
42    /// broken record.
43    Role {
44        name: &'static str,
45        optional: bool,
46    },
47    /// A `Paint` in a `u64`.
48    Paint,
49    /// A `u32` that is 0 or 1. Any other value makes the record unreadable.
50    Flag,
51    /// An `f32`, little-endian like every other field.
52    F32,
53    /// An `(offset, length)` pair of `u32` naming UTF-8 bytes behind the records, measured
54    /// from the start of the blob, the way a `Canvas` names the text it draws.
55    Text,
56}
57
58/// One field of a fixed-length record, at the byte it starts at.
59#[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/// A fixed-length record that travels inside a property's blob.
67///
68/// Codegen writes the Renderer's decoder from this, so the two sides read the same bytes
69/// at the same offsets and nobody keeps a second copy of the layout by hand.
70#[derive(Clone, Copy, Debug, Eq, PartialEq)]
71pub struct RecordSchema {
72    /// The Kotlin class the decoder produces.
73    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
82/// One decoration: a version, a kind, a severity and a colour role, a range, the id the
83/// application gave it, and the text a lens or a suggestion shows. Bytes 10 and 11 are
84/// reserved and written as zero.
85pub 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
123/// One colour run of the syntax: a version, a range and the paint it is drawn in.
124pub 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
137/// Bytes of one fold record.
138pub const FOLD_LEN: usize = 16;
139
140/// Bytes of one gutter decoration record.
141pub const GUTTER_DECORATION_LEN: usize = 28;
142
143/// One stretch of lines that can be folded: a version, the line it starts on, the last line
144/// it covers and whether it is folded now. Folding keeps `start_line` and hides the lines
145/// after it up to `end_line`.
146pub 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
157/// One mark beside the line numbers: a version, the line, what it looks like, an optional
158/// colour role, the id the application gave it and the character a glyph shows. Bytes 24
159/// to 27 are reserved and written as zero.
160pub 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
187/// Bytes of a code editor's colour record.
188pub 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
201/// The colours an application gives an editor: a role for the container, the gutter, a
202/// line number, the current line's number and the current line. Zero leaves that part to
203/// the design system. Bytes 10 and 11 are reserved and written as zero.
204pub 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
216/// Bytes of one view zone record.
217pub const VIEW_ZONE_LEN: usize = 16;
218
219/// One view zone: the line it follows, its height in rows and the text it shows dimmed, which
220/// lies behind the records like a decoration's.
221pub 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
241/// Folds a record layout into the schema hash, so a Renderer built against another
242/// layout is refused at the handshake rather than reading the wrong bytes.
243pub(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/// A place in the document: a line and a column, both from zero, the column in UTF-16
269/// code units.
270#[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/// A stretch of the document from `start` up to, and not including, `end`.
283#[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    /// The empty range at one place, which is where an insertion goes.
295    pub const fn at(position: Position) -> Self {
296        Self {
297            start: position,
298            end: position,
299        }
300    }
301
302    /// A range given as four numbers, in the order a language server writes them.
303    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/// One thing drawn over the document, said as a range and never as a picture.
316///
317/// The colour of an underline is a role, never a literal, and is left to the design system
318/// where it is not given; the shape of the mark, a squiggle or a line, and its weight are
319/// always the design system's.
320#[derive(Clone, Debug, PartialEq)]
321pub struct Decoration {
322    /// The document version the range was written against.
323    pub version: u32,
324    pub kind: DecorationKind,
325    /// Only an underline has one.
326    pub severity: Option<Severity>,
327    /// The role an underline is drawn in. None leaves it to the design system, which picks
328    /// one that suits the severity.
329    pub color: Option<ColorRole>,
330    pub range: CodeRange,
331    /// The application's own name for this decoration, reported when it is pressed,
332    /// accepted or hovered. Zero means it cannot be pressed and is never reported.
333    pub id: u64,
334    /// What a lens or a suggestion shows. Empty for the other two kinds.
335    pub text: String,
336}
337
338impl Decoration {
339    /// A diagnostic mark under a range.
340    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    /// A short line of text shown above `line`, which reports `id` when pressed.
353    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    /// A range that reports `id` when a pointer comes to rest over it.
366    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    /// A suggestion shown faintly at `at`. Accepting it inserts `text` there as an
379    /// ordinary edit and then reports `id`.
380    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    /// A tint across lines `line_start` to `line_end`, both included, the whole width of the
393    /// editor and past the end of each line. `color` is a role such as `DiffAdded`,
394    /// `DiffRemoved` or `DiffModified`, and the design system decides how strong the tint
395    /// of each is. The lines follow the reader's edits like every other decoration.
396    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    /// A tint over the characters of `range`, stronger than a line background in the same
409    /// role, for the part of a changed line that changed.
410    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    /// Draws an underline in this role rather than the one the design system would pick.
423    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/// The decorations over one editor, encoded and ready to travel.
475///
476/// Records first and the text they show after them, so the records stay fixed-length.
477/// Shared rather than copied, so the same list given again compares equal and costs no
478/// record at all.
479#[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            // An empty text points at the end of the records, which is where the text
511            // region starts, so a reader never sees an offset into the records themselves.
512            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    /// Adopts an already encoded list, which is what the protocol vectors carry.
521    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    /// The decorations this list holds, read back.
537    ///
538    /// The records end where the text region begins, which is the first offset any record
539    /// names for its text, or the end of the blob where none names one. A record whose
540    /// kind, severity or colour is not one this side knows is skipped, which is what the
541    /// Renderer does with it too.
542    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/// One colour run of the syntax.
598///
599/// A paint rather than a colour, and in practice a role: the code colour roles are what a
600/// highlighter on this side maps its token kinds onto, so the same run comes out in the
601/// right colour under every design system and in both light and dark.
602#[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    /// A run drawn in one colour role.
619    pub const fn role(version: u32, range: CodeRange, role: ColorRole) -> Self {
620        Self::new(version, range, Paint::Role(role))
621    }
622}
623
624/// The colour runs of one editor's syntax, encoded and ready to travel.
625#[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    /// The runs this list holds, read back. A run whose paint this side cannot read is
666    /// skipped.
667    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
681/// Where a position falls in `text`, as a byte offset, or None where the document has no
682/// such place: a line past the last one, a column past the end of its line, or a column
683/// that would split a character written as two UTF-16 units.
684pub 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/// One committed edit, as the Renderer reported it.
718///
719/// `range` is in the document as it stood before the edit, and `version` is the version
720/// the edit produced. The shape is a language server's `TextDocumentContentChangeEvent`,
721/// so forwarding one is a matter of copying four numbers and a string.
722#[derive(Clone, Debug, PartialEq)]
723pub struct CodeChange {
724    pub version: u32,
725    pub range: CodeRange,
726    pub text: String,
727}
728
729impl CodeChange {
730    /// Applies this edit to a copy of the document the Host keeps.
731    ///
732    /// Applying every change in the order they arrived, starting from the text the editor
733    /// was opened with, gives the text the reader sees. Returns false, and leaves `text`
734    /// alone, where the range does not fit the document, which means the copy has fallen
735    /// out of step with the editor.
736    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/// A Host edit the Renderer did not apply, because the reader had changed the same place
752/// since the version the edit was written against.
753#[derive(Clone, Copy, Debug, Eq, PartialEq)]
754pub struct EditRejected {
755    /// The id the application gave the edit.
756    pub request_id: u32,
757    pub base_version: u32,
758    /// The version the document had reached when the edit arrived.
759    pub current_version: u32,
760    /// The range exactly as it was sent.
761    pub range: CodeRange,
762}
763
764/// A pointer came to rest over a place in the editor, or left it.
765///
766/// How long it has to rest is the design system's, the same kind of delay a tooltip
767/// waits, and nothing is sent again while it stays. What to show is the application's,
768/// drawn with the overlay widgets it already has.
769#[derive(Clone, Copy, Debug, Eq, PartialEq)]
770pub struct CodeHover {
771    /// The id of the hover anchor under the pointer, or None where there is none.
772    pub decoration: Option<u64>,
773    pub position: Position,
774    pub phase: HoverPhase,
775}
776
777/// The reader pressed the save shortcut: Command S on macOS, Control S elsewhere.
778///
779/// Writing the file is this side's job, on a worker, never on the thread the editor runs
780/// on.
781#[derive(Clone, Copy, Debug, Eq, PartialEq)]
782pub struct SaveRequest {
783    /// The version of the document the reader saw when they pressed it.
784    pub version: u32,
785}
786
787/// One stretch of lines the reader can fold.
788#[derive(Clone, Copy, Debug, Eq, PartialEq)]
789pub struct Fold {
790    /// The document version the lines were written against.
791    pub version: u32,
792    /// The line that stays when the stretch is folded.
793    pub start_line: u32,
794    /// The last line the fold hides. Folding hides the lines after `start_line` up to and
795    /// including this one.
796    pub end_line: u32,
797    pub collapsed: bool,
798}
799
800impl Fold {
801    /// A stretch from `start_line` to `end_line`, unfolded.
802    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    /// The same stretch, folded.
812    pub const fn collapsed(mut self) -> Self {
813        self.collapsed = true;
814        self
815    }
816}
817
818/// The stretches of one editor that can be folded, encoded and ready to travel.
819#[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    /// The folds this list holds, read back. A record whose collapsed word is neither 0
861    /// nor 1 is skipped, which is what the Renderer does with it too.
862    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/// One mark beside a line number, said as a line and never as a picture.
882///
883/// The colour is a role, never a literal, and is left to the design system where it is not
884/// given. How big a dot is, how wide a bar and where each sits are always the design
885/// system's.
886#[derive(Clone, Copy, Debug, Eq, PartialEq)]
887pub struct GutterDecoration {
888    /// The document version the line was written against.
889    pub version: u32,
890    pub line: u32,
891    pub kind: GutterDecorationKind,
892    pub color: Option<ColorRole>,
893    /// The application's own name for it, reported when it is pressed. Zero means it
894    /// cannot be pressed.
895    pub id: u64,
896    /// The character a glyph shows, as a Unicode scalar value. Zero for the other kinds.
897    pub glyph: u32,
898}
899
900impl GutterDecoration {
901    /// A dot beside `line`.
902    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    /// A bar the height of `line`.
914    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    /// One character beside `line`.
926    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    /// Draws the mark in this role rather than the one the design system would pick.
938    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/// The marks beside the line numbers of one editor, encoded and ready to travel.
950#[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    /// The marks this list holds, read back. A record whose kind or colour this side does
998    /// not know is skipped, which is what the Renderer does with it too.
999    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/// Room after a line that is not text: the blank rows a diff leaves on one side to line it up
1022/// with the other, or a note between two lines.
1023///
1024/// `after_line` is the line it follows in the document as it is when the list arrives, from
1025/// zero. The zone then follows that line through the reader's edits. `height_lines` is how
1026/// tall it is in rows, and need not be whole. The caret, a selection and a copy pass over it,
1027/// and the lines on screen are reported as the document's own lines. `text`, where there is
1028/// any, is drawn dimmed on its first row.
1029#[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    /// Shows `text`, dimmed, in the zone.
1046    pub fn with_text(mut self, text: impl Into<String>) -> Self {
1047        self.text = text.into();
1048        self
1049    }
1050}
1051
1052/// The view zones of one editor, encoded and ready to travel.
1053///
1054/// Records first and the text they show after them, the way decorations travel.
1055#[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            // An empty text points at the end of the records, as a decoration's does.
1079            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    /// Adopts an already encoded list, which is what the protocol vectors carry.
1088    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    /// The zones this list holds, read back. A record whose text is not in the list, or is
1104    /// not UTF-8, is skipped, which is what the Renderer does with it too.
1105    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
1139/// `text` as snippet text that stands for itself: a backslash in front of every `$`, `}`
1140/// and `\\`, so a name taken from the document cannot open a tab stop of its own when it is
1141/// put inside a snippet.
1142pub 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/// Where a code editor breaks a line too long for it onto the rows below.
1154///
1155/// A broken line is still one line of the document: its number is drawn on its first row
1156/// only, Up, Down, Home and End follow the rows, and the lines on screen are reported as
1157/// the document's own. A line breaks after the last space that fits, or where a word is
1158/// longer than the row, at the column itself.
1159#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1160pub enum WordWrap {
1161    /// No line breaks; a long line runs past the edge.
1162    #[default]
1163    Off,
1164    /// At the edge of the view.
1165    Viewport,
1166    /// At this many columns, whatever the width of the view.
1167    Column(u32),
1168    /// At this many columns, or at the edge of the view where that comes first.
1169    Bounded(u32),
1170}
1171
1172impl WordWrap {
1173    /// The integer the `WordWrap` property carries: the `WordWrapMode` tag in the low 16
1174    /// bits and the column count above them.
1175    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    /// Reads back what [`WordWrap::to_value`] wrote, or None where the mode is not one, the
1186    /// value is below zero, or a mode that counts columns counts none.
1187    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
1206/// Bytes of one selection in a list of them: the anchor's line and column, then the active
1207/// end's.
1208pub const SELECTION_LEN: usize = 16;
1209
1210/// Encodes selections the way `SetSelections` and `SelectionsChanged` carry them, each an
1211/// anchor and an active end, appending to `out`.
1212pub 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
1222/// Reads back the selections [`encode_selections`] wrote, or None where the bytes are not
1223/// whole selections.
1224pub 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/// Every selection of a code editor, as the Renderer reported it: sent while there is more
1242/// than one, and once more when it comes back to one. The first is the primary selection,
1243/// the one `SelectionChange` goes on reporting by itself.
1244#[derive(Clone, Debug, PartialEq, Eq)]
1245pub struct SelectionsChange {
1246    pub version: u32,
1247    /// Each an anchor and an active end.
1248    pub selections: Vec<(Position, Position)>,
1249}
1250
1251impl SelectionsChange {
1252    /// The selections a `SelectionsChanged` event carries, or None for any other event.
1253    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/// The selection of a code editor, as the Renderer reported it.
1268///
1269/// `anchor` is where the selection started and `active` where the caret is, so a selection
1270/// made backwards has the anchor after the active end. Both are read against `version`,
1271/// the same series [`CodeChange`] counts.
1272#[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    /// Whether nothing is selected and the caret is a single place.
1281    pub fn is_caret(&self) -> bool {
1282        self.anchor == self.active
1283    }
1284
1285    /// The selected stretch, with the earlier end first whichever way it was made.
1286    pub fn range(&self) -> CodeRange {
1287        CodeRange::new(self.anchor.min(self.active), self.anchor.max(self.active))
1288    }
1289}
1290
1291/// The lines of a code editor that are on screen.
1292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1293pub struct VisibleRange {
1294    pub version: u32,
1295    /// The first line the reader can see.
1296    pub first_line: u32,
1297    /// The last line the reader can see. Lines a fold hides are not counted.
1298    pub last_line: u32,
1299}
1300
1301/// The reader folded or unfolded a stretch of a code editor.
1302#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1303pub struct FoldChange {
1304    /// The line the stretch starts on.
1305    pub start_line: u32,
1306    pub collapsed: bool,
1307}
1308
1309/// The colours of an editor's own parts, each a role of the theme.
1310///
1311/// A part that is not named keeps the colour the design system gives it, so an application
1312/// can set the container and the gutter and leave the rest. Roles and never literals: the
1313/// colours follow the theme from light to dark like every other colour here.
1314#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1315pub struct CodeEditorColors {
1316    /// What the text sits on.
1317    pub container: Option<ColorRole>,
1318    /// What the line numbers sit on.
1319    pub gutter: Option<ColorRole>,
1320    pub line_number: Option<ColorRole>,
1321    /// The number of the line the caret is on.
1322    pub current_line_number: Option<ColorRole>,
1323    /// The band behind the line the caret is on.
1324    pub current_line: Option<ColorRole>,
1325}
1326
1327impl CodeEditorColors {
1328    /// Every part left to the design system.
1329    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    /// Whether every part is left to the design system, in which case there is nothing to
1365    /// send.
1366    pub fn is_empty(&self) -> bool {
1367        *self == Self::new()
1368    }
1369
1370    /// The record as it travels in the `CodeEditorColors` property.
1371    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    /// The record read back. `None` where the bytes are not one whole record or name a
1388    /// role this side does not know, which is what the Renderer refuses too.
1389    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/// How an editor's rows are drawn, as the Renderer measured them.
1410#[derive(Clone, Copy, Debug, PartialEq)]
1411pub struct CodeMetrics {
1412    /// The height of one row in physical pixels. Every row of an editor has this height,
1413    /// so line `n` starts `n` times this far down when nothing is folded and no lens sits
1414    /// above a line.
1415    pub line_height_px: f64,
1416}
1417
1418/// How far an editor is scrolled.
1419#[derive(Clone, Copy, Debug, PartialEq)]
1420pub struct CodeScroll {
1421    /// The physical pixels of the document above the top of the view. The unit
1422    /// `Batch::set_scroll_offset` takes.
1423    pub offset_px: f64,
1424}
1425
1426/// A rectangle on screen, in the dp the window lays its content out in.
1427///
1428/// The editor's own scroll is already in it, and so is the window's zoom, because the zoom
1429/// is part of what a dp is there. The origin is the top left corner of the window's
1430/// content, which is where an overlay the application opens over the editor is placed
1431/// from, so a suggestion list or a hover card can be put at `x`, `y + height` as it is.
1432#[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    /// The bottom edge, which is where a list that opens under the caret starts.
1451    pub fn bottom(&self) -> f32 {
1452        self.y + self.height
1453    }
1454
1455    /// The right edge.
1456    pub fn right(&self) -> f32 {
1457        self.x + self.width
1458    }
1459}
1460
1461/// Where an editor draws its caret, as the Renderer reported it.
1462///
1463/// Sent when the caret moves or the view scrolls, at most once a frame, and only to an
1464/// editor whose application listens for it. The rectangle is the caret itself: as tall as
1465/// a row and as wide as the caret is drawn.
1466#[derive(Clone, Copy, Debug, PartialEq)]
1467pub struct CaretRect {
1468    /// The place in the document the caret stands at.
1469    pub position: Position,
1470    pub rect: CodeRect,
1471}
1472
1473impl CaretRect {
1474    /// The caret's place and rectangle out of a `CaretRect` event, or None for any other.
1475    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
1486/// What a position query is answered with: the rectangle a caret at that position would be
1487/// drawn in, or None where the position is not on screen.
1488pub type PositionAnswer = Option<CodeRect>;
1489
1490type PositionCallback = Box<dyn FnOnce(PositionAnswer)>;
1491
1492thread_local! {
1493    /// Callbacks waiting for their answer, by request number.
1494    static POSITION_QUERIES: std::cell::RefCell<Vec<(u32, PositionCallback)>> =
1495        const { std::cell::RefCell::new(Vec::new()) };
1496    /// The next request number. 0 is skipped so a zeroed record never looks like a query.
1497    static NEXT_POSITION_QUERY: std::cell::Cell<u32> = const { std::cell::Cell::new(1) };
1498}
1499
1500/// Numbers one question about where a position of an editor is on screen, and keeps
1501/// `on_answer` until the answer arrives.
1502///
1503/// The number goes into the command that asks it, `Batch::query_position_rect`, written by
1504/// whatever holds the editor's node. The Renderer answers every query exactly once, with
1505/// the rectangle a caret at that position would be drawn in, or with None where the
1506/// position is not on screen: scrolled away, folded, outside the document, or in an editor
1507/// that is gone. `on_answer` then runs on the UI thread, inside the call that carried the
1508/// answer, and anything it changes is in the batch that call returns.
1509///
1510/// It is how a hover card, a rename box or a parameter hint is put next to a word that is
1511/// not where the caret is.
1512pub 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
1525/// Hands an answer to the callback that asked for it. Returns whether there was one; an
1526/// answer nobody is waiting for any more is ignored.
1527pub(crate) fn deliver_position_rect(request: u32, answer: PositionAnswer) -> bool {
1528    // Taken out before it runs: the callback may ask again, which edits the list.
1529    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/// Forgets every query still waiting. Used when a new `Host` takes over the thread: the
1545/// Renderer it talks to has never heard of them.
1546#[doc(hidden)]
1547pub fn reset_position_queries() {
1548    POSITION_QUERIES.with_borrow_mut(Vec::clear);
1549}
1550
1551/// A pointer event over a code editor, with the place in the text under the pointer.
1552///
1553/// Everything a node's pointer event says, under the same rules: the kinds the editor
1554/// subscribes to with `PointerKinds`, the buttons, the modifiers, the click count, a `Move`
1555/// at most once a frame, and a press held by the editor until it is released. Consuming a
1556/// `Down` keeps the caret where it was, which is what Cmd or Ctrl and a click needs to go to
1557/// a definition without moving the reader's caret.
1558#[derive(Clone, Debug)]
1559pub struct CodePointer {
1560    pub event: crate::PointerEvent,
1561    /// The character under the pointer, or None where it is over none: the gutter, past the
1562    /// end of a line, or below the last one.
1563    pub position: Option<Position>,
1564}
1565
1566impl CodePointer {
1567    /// The event out of a `CodePointer` payload, or None for any other.
1568    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
1608/// Encodes the edits of one grouped edit the way `EditCodeGroup` carries them, appending to
1609/// `out`.
1610///
1611/// The blob is a `u32` count, then one 24-byte record per edit (the range, then the text's
1612/// offset and length) and then the texts one after another. A text's offset counts from
1613/// the start of the texts, not from the start of the blob. Fails only where a count, a
1614/// length or an offset does not fit a `u32`.
1615pub 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
1640/// Reads back the edits [`encode_edit_group`] wrote, in the order they were sent.
1641///
1642/// None where the blob is not one: shorter than its count says, a text outside the blob or
1643/// not UTF-8, or bytes after the last text. The Renderer reads it under the same rules and
1644/// answers a blob it cannot read with a protocol error.
1645pub 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/// What became of one grouped edit, as the Renderer reported it.
1668///
1669/// Exactly one arrives for every grouped edit that was sent with at least one edit in it,
1670/// after the changes it made. The group went in whole or not at all: `status` is
1671/// `Applied`, or the reason it stayed out, and each reason asks for something different.
1672/// `Rejected`: the reader changed a place the group would have written, so read the
1673/// document again and send the group again. `TooOld`: the version it was written against
1674/// is older than the editor remembers, so bring the application's copy of the document
1675/// back in step first. `Invalid`: the group itself makes no sense.
1676#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1677pub struct EditGroupResult {
1678    /// The id the application gave the group.
1679    pub request_id: u32,
1680    pub status: EditGroupStatus,
1681    /// The version the group was written against, as it was sent.
1682    pub base_version: u32,
1683    /// Where it went in, the version after its last change; where it did not, the version
1684    /// the document is at.
1685    pub current_version: u32,
1686    /// Where it went in, how many edits did; where it did not, the position in the group,
1687    /// as sent, of the edit that kept it out (0 where no one edit did).
1688    pub index: u32,
1689}
1690
1691impl EditGroupResult {
1692    /// The result out of a `CodeEditGroupResult` event, or None for any other.
1693    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}