mirror of
https://github.com/BigMarc/grammargem.git
synced 2026-10-13 17:01:55 +02:00
Replace the NSSpellChecker + regex-stub engine with harper-core 2.5.0: real subject-verb agreement, verb forms, punctuation, repetition and spelling. New harper-ffi crate (staticlib) exposes harper_check over a C ABI; the CHarper module bridges it to Swift; HarperEngine maps Harper's char spans onto UTF-16 ranges and de-dups overlapping lints. NSSpellChecker stays only as a case-preserving spelling supplement, which fixes the problemm->Problem mis-casing. Bulk apply now skips no-op suggestions. build.sh / scripts/build.sh build the universal static lib; target/ and lib/ are gitignored (regenerate via harper-ffi/build.sh). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
harper-ffi
A thin Rust static library that exposes the Harper grammar core (Apache-2.0) to Swift over a C-FFI. This directory is a placeholder for the real integration (spec §1, §2, §8).
The Swift side talks to this through the GrammarEngine protocol; today
HarperEngine.swift ships a
small pure-Swift rule stub so the system loop + tests run with zero native deps.
Planned shape
harper-ffi/
├─ Cargo.toml # crate-type = ["staticlib"]; depends on `harper-core`
├─ src/lib.rs # #[no_mangle] extern "C" fns over harper-core
└─ include/harper.h # C header consumed by Swift (module map / bridging)
Sketch of the FFI surface:
// src/lib.rs
#[no_mangle]
pub extern "C" fn harper_check(text: *const c_char,
out_json: *mut *mut c_char) -> usize { /* … */ }
#[no_mangle]
pub extern "C" fn harper_free(ptr: *mut c_char) { /* … */ }
Wiring into SwiftPM
- Build the static lib:
cargo build --release→libharper_ffi.a. - Add a
systemLibrary/binary target (orlinkerSettings/unsafeFlags) inPackage.swiftpointing at the.a+ the header's module map. - Replace the rule set in
HarperEngine.check(_:)with calls toharper_check, decoding the returned span JSON into[Suggestion].
Alternative considered: bundle
harper-lsand talk to it as a subprocess over LSP. The FFI route is leaner (no process, no JSON-RPC) and is preferred.
Why Harper (not LanguageTool)
Harper is ~1/50th the memory of LanguageTool and needs no multi-GB n-gram data, so it's the right embeddable core. LanguageTool stays an optional power-user backend, never the default (spec §2).