From 5ad3a77ef59de5399c96c894f862ea9b3969fbaa Mon Sep 17 00:00:00 2001 From: Patrick Pircher Date: Tue, 2 Apr 2024 03:21:26 +0200 Subject: [PATCH] improve dependent keys ux (#2553) --- .../object-inspector/dependent-keys.hbs | 14 ++-- ember_debug/object-inspector.js | 64 +++++++++++++------ tests/ember_debug/object-inspector-test.js | 26 +++++--- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/app/components/object-inspector/dependent-keys.hbs b/app/components/object-inspector/dependent-keys.hbs index 689a9a0938..f2c002e3be 100644 --- a/app/components/object-inspector/dependent-keys.hbs +++ b/app/components/object-inspector/dependent-keys.hbs @@ -1,14 +1,17 @@
  • {{svg-jar "dependent-key-connection" width="20px" height="10px"}} diff --git a/ember_debug/object-inspector.js b/ember_debug/object-inspector.js index fda102f50c..729829bdd4 100644 --- a/ember_debug/object-inspector.js +++ b/ember_debug/object-inspector.js @@ -148,7 +148,7 @@ function isMandatorySetter(descriptor) { return false; } -function getTagTrackedProps(tag, ownTag, level = 0) { +function getTagTrackedTags(tag, ownTag, level = 0) { const props = []; // do not include tracked properties from dependencies if (!tag || level > 1) { @@ -158,13 +158,13 @@ function getTagTrackedProps(tag, ownTag, level = 0) { if (tag.subtag && !Array.isArray(tag.subtag)) { if (tag.subtag._propertyKey) props.push(tag.subtag); - props.push(...getTagTrackedProps(tag.subtag, ownTag, level + 1)); + props.push(...getTagTrackedTags(tag.subtag, ownTag, level + 1)); } if (subtags) { subtags.forEach((t) => { if (t === ownTag) return; if (t._propertyKey) props.push(t); - props.push(...getTagTrackedProps(t, ownTag, level + 1)); + props.push(...getTagTrackedTags(t, ownTag, level + 1)); }); } return props; @@ -177,46 +177,67 @@ function getTrackedDependencies(object, property, tagInfo) { const cpDesc = emberMeta(object).peekDescriptors(property); const dependentKeys = []; if (cpDesc) { - dependentKeys.push(...(cpDesc._dependentKeys || [])); + dependentKeys.push( + ...(cpDesc._dependentKeys || []).map((k) => ({ name: k })) + ); } if (HAS_GLIMMER_TRACKING) { const ownTag = tagForProperty(object, property); - const props = getTagTrackedProps(tag, ownTag); + const tags = getTagTrackedTags(tag, ownTag); const mapping = {}; - let maxRevision = tagInfo.revision ?? 0; - let minRevision = Infinity; - props.forEach((t) => { + let maxRevision = tagValue(tag); + tags.forEach((t) => { const p = (t._object ? getObjectName(t._object) + '.' : '') + t._propertyKey; - const [objName, ...props] = p.split('.'); + const [objName, prop] = p.split('.'); mapping[objName] = mapping[objName] || new Set(); - maxRevision = Math.max(maxRevision, t.revision); - minRevision = Math.min(minRevision, t.revision); - props.forEach((p) => mapping[objName].add([p, t.revision])); + const value = tagValue(t); + if (prop) { + mapping[objName].add([prop, value]); + } }); - const hasChange = maxRevision !== minRevision; + const hasChange = + (tagInfo.revision && maxRevision !== tagInfo.revision) || false; + + const names = new Set(); Object.entries(mapping).forEach(([objName, props]) => { + if (names.has(objName)) { + return; + } + names.add(objName); if (props.size > 1) { - dependentKeys.push(objName); + dependentKeys.push({ name: objName }); props.forEach((p) => { - const changed = hasChange && p[1] >= maxRevision ? ' 🔸' : ''; - dependentKeys.push(' • -- ' + p[0] + changed); + const changed = hasChange && p[1] > tagInfo.revision; + const obj = { + child: p[0], + }; + if (changed) { + obj.changed = true; + } + dependentKeys.push(obj); }); } if (props.size === 1) { const p = [...props][0]; - const changed = hasChange && p[1] >= maxRevision ? ' 🔸' : ''; - dependentKeys.push(objName + '.' + p[0] + changed); + const changed = hasChange && p[1] > tagInfo.revision; + const obj = { + name: objName + '.' + p[0], + }; + if (changed) { + obj.changed = true; + } + dependentKeys.push(obj); } if (props.size === 0) { - dependentKeys.push(objName); + dependentKeys.push({ name: objName }); } }); } - return [...new Set([...dependentKeys])]; + return [...dependentKeys]; } export default class extends DebugPort { @@ -1078,7 +1099,8 @@ function calculateCPs( if (cache !== undefined || !item.isExpensive) { let value; if (item.canTrack && HAS_GLIMMER_TRACKING) { - const tagInfo = (tracked[item.name] = {}); + tracked[item.name] = tracked[item.name] || {}; + const tagInfo = tracked[item.name]; tagInfo.tag = track(() => { value = calculateCP(object, item, errorsForObject); }); diff --git a/tests/ember_debug/object-inspector-test.js b/tests/ember_debug/object-inspector-test.js index 7d73c9378f..e1bec13e7e 100644 --- a/tests/ember_debug/object-inspector-test.js +++ b/tests/ember_debug/object-inspector-test.js @@ -280,9 +280,14 @@ module('Ember Debug - Object Inspector', function (hooks) { assert.strictEqual(prop.value.inspect, '"item1item2tracked"'); let dependentKeys = compareVersion(VERSION, '3.16.10') === 0 - ? 'item1,item2,trackedProperty' - : 'ObjectWithTracked, • -- item1, • -- item2,Object:My Object.trackedProperty'; - assert.strictEqual(prop.dependentKeys.toString(), dependentKeys); + ? [{ name: 'item1' }, { name: 'item2' }, { name: 'trackedProperty' }] + : [ + { name: 'ObjectWithTracked' }, + { child: 'item1' }, + { child: 'item2' }, + { name: 'Object:My Object.trackedProperty' }, + ]; + assert.deepEqual(prop.dependentKeys, dependentKeys); prop = secondDetail.properties[2]; assert.strictEqual(prop.name, 'get'); @@ -305,9 +310,14 @@ module('Ember Debug - Object Inspector', function (hooks) { assert.strictEqual(prop.value.inspect, '"item1-changeditem2tracked"'); dependentKeys = compareVersion(VERSION, '3.16.10') === 0 - ? 'item1,item2,trackedProperty' - : 'ObjectWithTracked, • -- item1 🔸, • -- item2,Object:My Object.trackedProperty'; - assert.strictEqual(prop.dependentKeys.toString(), dependentKeys); + ? [{ name: 'item1' }, { name: 'item2' }, { name: 'trackedProperty' }] + : [ + { name: 'ObjectWithTracked' }, + { child: 'item1', changed: true }, + { child: 'item2' }, + { name: 'Object:My Object.trackedProperty' }, + ]; + assert.deepEqual(prop.dependentKeys, dependentKeys); }); skip('Correct mixin order with es6 class', async function (assert) { @@ -937,8 +947,8 @@ module('Ember Debug - Object Inspector', function (hooks) { let serializedComputedProperty = message.details[1].properties[2]; assert.strictEqual(serializedComputedProperty.code, computedFn.toString()); - assert.strictEqual(serializedComputedProperty.dependentKeys[0], 'foo'); - assert.strictEqual(serializedComputedProperty.dependentKeys[1], 'bar'); + assert.strictEqual(serializedComputedProperty.dependentKeys[0].name, 'foo'); + assert.strictEqual(serializedComputedProperty.dependentKeys[1].name, 'bar'); }); test('Views are correctly handled when destroyed during transitions', async function (assert) {