diff options
| author | rottedfm <rottedfm@proton.me> | 2026-08-19 11:24:55 -0400 |
|---|---|---|
| committer | rottedfm <rottedfm@proton.me> | 2026-08-19 11:24:55 -0400 |
| commit | 8e16347b0eb329e84892af8ece36886324c95f62 (patch) | |
| tree | be1267972b5de2f1ae592577dfabce67f1fe6e87 /docs/reviews/library-selection.md | |
| parent | c6ae4660d1cc2414b22c492c5e819d009c8187c2 (diff) | |
| parent | ea0bd36167b684c0accdb5ce2b2e21b8d84aeb25 (diff) | |
Establishes the first working baseline: moji <file> opens a file into a
ropey rope and edits it with Helix selection-first modal editing, under a
DO-178C DAL-C requirements and traceability process.
Prior to this, main tracked four files and src/main.rs was still
println!("Hello, world!") — there was no buildable state to build on.
Verified on a fresh clone of the branch with no untracked files:
cargo build; clippy --all-targets -D warnings clean; 294 tests passing;
scripts/check-trace.sh reports 98/98 requirements traced in both
directions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'docs/reviews/library-selection.md')
| -rw-r--r-- | docs/reviews/library-selection.md | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/docs/reviews/library-selection.md b/docs/reviews/library-selection.md new file mode 100644 index 0000000..3b9efa2 --- /dev/null +++ b/docs/reviews/library-selection.md @@ -0,0 +1,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`. |
