1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
# Library selection record
Software Level: **DAL-C** (DO-178C)
Reviewed by: author (independence not required at DAL-C)
Records the non-obvious dependency choices for the buffer component and the
reasoning behind each, so a reviewer can judge them without re-deriving the
trade-offs.
---
## ropey 2.0.0-beta.1 — a pre-release dependency
**Decision.** ropey 2.0.0-beta.1, default features, **byte-indexed**.
`metric_chars` is deliberately **not** enabled.
**Alternatives considered.**
| Option | Assessment |
|---|---|
| ropey 1.6.1 (stable) | Char-indexed by default; exactly what Helix pins, so Helix's algorithms port near-verbatim. The conservative choice. |
| 2.0.0-beta.1 + `metric_chars` | Keeps the char API, but re-adds the per-node metadata 2.0 exists to remove — the beta risk without the benefit. |
| **2.0.0-beta.1, byte-indexed (selected)** | Lowest memory and fastest edits; requires translating Helix's algorithms into byte offsets rather than porting them. |
**Known risk.** The crate is self-described as "not battle-tested like Ropey
1.x", with minor breaking API changes possible before release. This was raised
before implementation and the requester selected it anyway; the choice is
theirs and is recorded here rather than re-litigated.
**Mitigations in force.**
1. The version is pinned exactly in `Cargo.toml`.
2. `ropey::Rope` is *owned* only by `src/buffer/document.rs`; other modules
receive `RopeSlice` parameters. Replacing the rope is therefore confined to
one module. (MJB-DR-006)
3. The buffer core carries 93.85% statement coverage against invariants we
assert ourselves, rather than trusting the library's own guarantees.
**Consequence the reviewer must accept.** Byte indexing admits a failure mode
char indexing cannot express: an offset landing inside a character, which makes
`Rope::insert`/`remove` panic. This is addressed by MJB-LLR-011 (clamp and snap
at construction) and MJB-LLR-044 (validate before mutating). See MJB-DR-002.
---
## encoding_rs — asymmetric, and the asymmetry matters
**Decision.** Used for decoding all encodings and for encoding everything
*except* UTF-16, which is encoded by hand.
**Why.** `encoding_rs::Encoding::encode` will not encode to UTF-16; it silently
substitutes UTF-8 and signals the substitution only through a return value that
is easy to discard. Delegating the save path to it wrote UTF-8 bytes beneath a
UTF-16 byte order mark — a file inconsistent with its own BOM.
This was **not caught by inspection**. It was caught by a round-trip test
(`mjb_llr_115_utf16le_round_trips`) that decoded, re-encoded, and compared
bytes. Any encoding added later should be guarded by the same test shape.
Recorded as MJB-DR-007.
---
## unicode-segmentation and unicode-width
**Decision.** `unicode_segmentation::GraphemeCursor` for cluster boundaries;
`unicode_width` for terminal display width.
**Why.** `GraphemeCursor` is chunk-aware: it reports `PrevChunk`/`NextChunk`/
`PreContext` when a cluster straddles a fragment edge, which pairs exactly with
ropey's `chunk(byte_idx) -> (&str, chunk_start)`. Clusters spanning rope chunk
boundaries therefore resolve correctly (MJB-LLR-022) without materialising the
text. This is the same pairing Helix uses.
`unicode_width` is required because byte length, character count, and display
width are three different numbers; the cursor must track the third. A CJK
character occupies two terminal columns and three UTF-8 bytes.
---
## thiserror
**Decision.** Used for error types in the buffer core.
**Why.** MJB-HLR-018 forbids abnormal termination, so every failure path must
carry a reportable message. `thiserror` generates `Display` and `From` without
runtime cost or a dynamic error type.
---
## config, with default features disabled
**Decision.** `config = { default-features = false, features = ["toml"] }`.
**Why.** MJB-HLR-014 requires that no parser for another format remain in the
dependency graph. Removing the `json5` *call site* would not satisfy that —
the crate's default features pull `json5` in regardless. Disabling default
features is what actually removes it, and is verifiable with `cargo tree`.
|