]> Repositorios git - classgraph.git/commitdiff
Family node labels show tyvars, with (data) on its own italic line
authorJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:22:15 +0000 (02:22 +0200)
committerJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:22:15 +0000 (02:22 +0200)
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) <[email protected]>
data/viewer.js
src/Classgraph/Render.hs

index 91029216a4bfd7fa2d9ee91700d6792328281b98..efbe9518dcb571ba0383643d1a62c288f59a406d 100644 (file)
       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,
     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,
           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)
index 21d1242c486c452f57c18684bb07973d18e8a10f..3bc52cc3b61311db2fecdbbe07144500250a8608 100644 (file)
@@ -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