From: Javier Sagredo Date: Thu, 7 May 2026 00:22:15 +0000 (+0200) Subject: Family node labels show tyvars, with (data) on its own italic line X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=ee1ad15ad3bc96a130275c299c576beded9d4706;p=classgraph.git Family node labels show tyvars, with (data) on its own italic line Family nodes now carry their tyvars in the label (e.g. @BftDSIGN c@, @LedgerState blk mk@), matching the recent class-node tyvar change. For data families, @(data)@ goes on a new line via @\n@ in the label and the cytoscape stylesheet italicises the whole node label via @node[kind = "family"][?isData]@. Cytoscape can't style only one line of a label, so the family name itself ends up italicised too. That's a deliberate trade-off — the italic doubles as a "this is a data family, not a type family" cue without requiring any additional iconography. Render.hs's familyNodeLabel and viewer.js's ensureFamilyNode + focused-family-node code path all updated together so the classes-view, instance-view and family-view family nodes all read the same way. Co-Authored-By: Claude Opus 4.7 (1M context) --- diff --git a/data/viewer.js b/data/viewer.js index 9102921..efbe951 100644 --- a/data/viewer.js +++ b/data/viewer.js @@ -607,9 +607,18 @@ seenNodes.add(id); const known = familyById.get(id); const isData = isDataFamily(known); + // Mirror Render.hs's familyNodeLabel: include the family's + // tyvars in the label when known, then add `\n(data)` on a new + // line for data families. The whole-node italic styling for + // data families lives in the cytoscape stylesheet (cytoscape + // can't style only one line of a label). + const head = (known && known.tfTyVars && known.tfTyVars.length > 0) + ? qn.qnName + ' ' + known.tfTyVars.map(tv => tv.tvName).join(' ') + : qn.qnName; + const label = head + (isData ? '\n(data)' : ''); els.push({ group: 'nodes', data: { id, - label: qn.qnName + (isData ? ' (data)' : ''), + label, kind: 'family', external: !known, isData: !!isData, @@ -1051,9 +1060,12 @@ const famNodeId = qid(fam.tfName); seenNodes.add(famNodeId); const focusedIsData = isDataFamily(fam); + const focusedHead = (fam.tfTyVars && fam.tfTyVars.length > 0) + ? fam.tfName.qnName + ' ' + fam.tfTyVars.map(tv => tv.tvName).join(' ') + : fam.tfName.qnName; els.push({ group: 'nodes', data: { id: famNodeId, - label: fam.tfName.qnName + (focusedIsData ? ' (data)' : ''), + label: focusedHead + (focusedIsData ? '\n(data)' : ''), kind: 'family', focused: true, isData: !!focusedIsData, @@ -1990,6 +2002,20 @@ color: '#3f2d05', shape: 'diamond', height: 'label', + // Honour explicit \n in labels — used by the data-family + // suffix that goes on its own line. + 'text-wrap': 'wrap', + }, + }, + // Data families specifically — italicise the whole label so the + // "(data)" line on the second row reads as a typographic cue. + // Cytoscape can't style only one line of a label, so the family + // name gets italicised too; that's fine, it's a consistent + // visual signal "this family carries data, not types". + { + selector: 'node[kind = "family"][?isData]', + style: { + 'font-style': 'italic', }, }, // External class node (instance view) diff --git a/src/Classgraph/Render.hs b/src/Classgraph/Render.hs index 21d1242..3bc52cc 100644 --- a/src/Classgraph/Render.hs +++ b/src/Classgraph/Render.hs @@ -153,8 +153,17 @@ buildGraph pd sourceRoots = CyGraph | f <- pdTypeFamilies pd ] + -- Family node label: family name + its tyvars (e.g. @BftDSIGN c@), + -- with @(data)@ on its own line for data families. The viewer's + -- cytoscape rule for @node[kind = "family"][?isData]@ italicises + -- the whole label — cytoscape can't style only one line, so we + -- accept whole-node italic as the visual cue for "data family". familyNodeLabel f = - qnName (tfName f) <> if isDataFamily f then " (data)" else "" + let head = case tfTyVars f of + [] -> qnName (tfName f) + tvs -> qnName (tfName f) <> " " + <> T.intercalate " " (map tvName tvs) + in head <> if isDataFamily f then "\n(data)" else "" isDataFamily f = case tfFlavor f of DataFam -> True