Body normalisation: per-method similarity score + leaf delta
Turns the dispatch axis from a binary changed/unchanged into a "how much" measure
of code change — the original goal. ams.normalize compares two body fingerprints
(the ordered leaf-call anchors) with difflib after collapsing consecutive-duplicate
anchors (a load-twice codegen artefact), yielding a 0-100 similarity and the exact
leaves that appeared/vanished.
Every dispatch `changed` entry now carries body={similarity, added, removed}, and the
block carries a summary={shared, identical, changed, mean_similarity}.
Golden pair (cross-compiler): 470 shared bodies, 131 identical, mean 66% similar;
Animo SHOW/HIDE/PAUSE/RESUME come out 100% despite MSVC6 vs MSVC8, LOAD 50% with the
swapped leaves spelled out.
- normalize.py: canonical / body_similarity / body_delta
- diff: _dispatch_diff enriches changed with body + adds summary
- render: METHOD BODIES shows %, leaf delta, summary line
- UI: similarity % + leaf delta + axis summary
- tests: 5 new -> 34/34
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -240,18 +240,34 @@ function axisCard(ax, block) {
|
||||
const sortByName = (arr) => arr.slice().sort((x, y) => ax.name(x).localeCompare(ax.name(y)));
|
||||
for (const it of sortByName(block.added)) body.append(el("div", { class: "row r-add" }, ax.fmt(it)));
|
||||
for (const it of sortByName(block.removed)) body.append(el("div", { class: "row r-del" }, ax.fmt(it)));
|
||||
const leaves = (arr) => "[" + arr.slice(0, 4).join(", ") + (arr.length > 4 ? "…+" + (arr.length - 4) : "") + "]";
|
||||
for (const ch of block.changed.slice().sort((x, y) => ax.name(x.item).localeCompare(ax.name(y.item)))) {
|
||||
const deltas = Object.entries(ch.changes).map(([f, v]) =>
|
||||
(Array.isArray(v[0]) || Array.isArray(v[1]))
|
||||
? `${f}: ${(v[0] || []).length} → ${(v[1] || []).length}`
|
||||
: `${f}: ${v[0]} → ${v[1]}`).join(", ");
|
||||
body.append(el("div", { class: "row r-chg" }, ax.name(ch.item), " ", el("span", { class: "delta" }, deltas)));
|
||||
let deltas, sim = null;
|
||||
if (ch.body) { // method-body diff: similarity score + leaf-level delta
|
||||
sim = ch.body.similarity;
|
||||
const parts = [];
|
||||
if (ch.body.added && ch.body.added.length) parts.push("+" + leaves(ch.body.added));
|
||||
if (ch.body.removed && ch.body.removed.length) parts.push("−" + leaves(ch.body.removed));
|
||||
deltas = parts.join(" ") || (ch.changes.impl ? `impl ${ch.changes.impl[0]} → ${ch.changes.impl[1]}` : "");
|
||||
} else {
|
||||
deltas = Object.entries(ch.changes).map(([f, v]) =>
|
||||
(Array.isArray(v[0]) || Array.isArray(v[1]))
|
||||
? `${f}: ${(v[0] || []).length} → ${(v[1] || []).length}`
|
||||
: `${f}: ${v[0]} → ${v[1]}`).join(", ");
|
||||
}
|
||||
const row = el("div", { class: "row r-chg" }, ax.name(ch.item), " ");
|
||||
if (sim != null) row.append(el("span", { class: "simpct" }, sim + "%"), " ");
|
||||
row.append(el("span", { class: "delta" }, deltas));
|
||||
body.append(row);
|
||||
}
|
||||
const sum = block.summary
|
||||
? el("span", { class: "axsum" }, `śr. ${block.summary.mean_similarity}% · ${block.summary.changed}/${block.summary.shared} zmienionych`)
|
||||
: null;
|
||||
return el("details", { class: "axis", open: true },
|
||||
el("summary", {}, el("span", { class: "title" }, ax.title),
|
||||
badge("b-add", "+", block.added.length),
|
||||
badge("b-del", "−", block.removed.length),
|
||||
badge("b-chg", "~", block.changed.length)),
|
||||
badge("b-chg", "~", block.changed.length), sum),
|
||||
body);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user