Skip to content

Commit

Permalink
improve dependent keys ux (#2553)
Browse files Browse the repository at this point in the history
  • Loading branch information
patricklx authored Apr 2, 2024
1 parent e4d8062 commit 5ad3a77
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 34 deletions.
14 changes: 9 additions & 5 deletions app/components/object-inspector/dependent-keys.hbs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<li class="mixin__property-dependency-list relative">
{{svg-jar "dependent-key-connection" width="20px" height="10px"}}
<ul class="m-0 p-0 list-none">
{{#each @keys as |depKey|}}
<li class="mixin__property-dependency-item relative text-base12 text-sm">
{{#if (match depKey "")}}
{{#each @keys as |dep|}}
<li
class="mixin__property-dependency-item relative text-base12 text-sm {{if dep.changed 'text-spec01'}}"
title={{if dep.changed 'this property changed'}}
>
{{#if dep.child}}
<span
class="mixin__property-dependency-name subkey"
data-label="object-property-name"
>
{{depKey}}
• -- {{dep.child}}
</span>
{{else}}
{{svg-jar
Expand All @@ -20,9 +23,10 @@
class="mixin__property-dependency-name"
data-label="object-property-name"
>
{{depKey}}
{{dep.name}}
</span>
{{/if}}
{{#if dep.changed}}🔸{{/if}}
</li>
{{/each}}
</ul>
Expand Down
64 changes: 43 additions & 21 deletions ember_debug/object-inspector.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
});
Expand Down
26 changes: 18 additions & 8 deletions tests/ember_debug/object-inspector-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 5ad3a77

Please sign in to comment.