Expand description
cratefield-text-diff: a word-level diff for showing what a rewrite
changed (issue #836).
diff_words compares two texts word by word and answers with
Segments that are Op::Removed, Op::Added or Op::Same.
diff_words_locked also takes the byte ranges of each text that are
protected — a figure, a name, a quotation, a line of code, as
cratefield-text-guard finds them, or ranges from anywhere else. A
protected range is one atomic token: it is never split into words, and
when it is unchanged it comes back as Op::Locked so a UI can box it.
use cratefield_text_diff::{Op, diff_words};
let segments = diff_words("revenue grew by 18% to $4.2M", "revenue rose 18% to $4.2M");
let ops: Vec<_> = segments.iter().map(|s| (s.op, s.text.as_str())).collect();
assert_eq!(ops, [
(Op::Same, "revenue "),
(Op::Removed, "grew by "),
(Op::Added, "rose "),
(Op::Same, "18% to $4.2M"),
]);§Tokens and whitespace
A token is a protected range, a word (letters and digits, with ' ’
- . , % allowed between or after them, so don't, 4.2M and
18% stay whole), or a single other character. Whitespace is not a
token: it rides on the token before it and is not compared, so
re-wrapping a paragraph is not a change. Each segment’s text carries its
trailing whitespace from the side it came from — the rewrite’s for
Same, Locked and Added, the original’s for Removed — so the
texts of every segment that is not Removed, in order, are exactly the
rewrite.
§Algorithm
Myers’ O(ND) difference algorithm in its linear-space form (the “middle snake”), after stripping the common prefix and suffix: a full rewrite of a long text costs memory proportional to its length, not its square. The result is a shortest edit script, so a minimal diff, and within each change everything removed is listed before everything added.
Structs§
- Segment
- A run of text with one
Op. Adjacent segments never share an op, exceptOp::Locked, which is one segment per protected range.
Enums§
- Op
- What happened to a piece of text between the two versions.
Functions§
- diff_
words - The word diff of
beforeandafter, with nothing protected. - diff_
words_ locked - The word diff of
beforeandafter, wherebefore_lockedandafter_lockedare protected byte ranges of each. A range that is out of bounds, not on acharboundary, or overlaps an earlier one is ignored.