}
function buildInstanceView(classId) {
- const cls = classById.get(classId);
- if (!cls) return null;
+ let cls = classById.get(classId);
const all = instancesByClass.get(classId) || [];
+ if (!cls) {
+ // External class — its defining package wasn't extracted, but
+ // we may still have instances of it (orphan-style, or because
+ // the package that *defines instances* was extracted while the
+ // package that *defines the class* wasn't). Synthesise a
+ // minimal ClassInfo from the first instance's iiClass so the
+ // rest of the code path keeps working. Superclasses/methods/
+ // associated families are unknown; we leave them empty.
+ if (all.length === 0) return null;
+ const first = all[0];
+ cls = {
+ ciName: first.iiClass,
+ ciTyVars: [],
+ ciSuperclasses: [],
+ ciAssocTypes: [],
+ ciMethods: [],
+ ciSrc: null,
+ ciDoc: null,
+ _externalShim: true,
+ };
+ }
const hidden = getHidden(classId);
const insts = all.filter(i => !hidden.has(i._idx));
pinClass(n.id());
return;
}
- // Class node → drill into its instance view.
- if (data.kind === 'class' && !data.focused && classById.has(n.id())) {
- switchToInstance(n.id());
- return;
+ // Class node → drill into its instance view. We *don't* gate on
+ // classById.has(n.id()) here — buildInstanceView falls back to a
+ // synthesised ClassInfo when the class is external but local
+ // instances exist, so external classes that only show up via
+ // someone else's superclass theta still drill in usefully.
+ if (data.kind === 'class' && !data.focused) {
+ const haveLocalDef = classById.has(n.id());
+ const localInstCount = (instancesByClass.get(n.id()) || []).length;
+ if (haveLocalDef || localInstCount > 0) {
+ switchToInstance(n.id());
+ return;
+ }
+ // No instances and no local definition either — nothing to show.
+ // Warn so the user can diagnose why their double-click did
+ // nothing instead of being silently confused.
+ console.warn('classgraph: nothing to drill into for', n.id(),
+ '— class is external and no instances of it appear in the dumps.');
}
// Family node → drill into its family view.
if (data.kind === 'family' && !data.focused && familyById.has(n.id())) {